summaryrefslogtreecommitdiff
path: root/ext
diff options
context:
space:
mode:
Diffstat (limited to 'ext')
-rw-r--r--ext/bcmath/libbcmath/src/bcmath.h2
-rw-r--r--ext/bz2/bz2.c3
-rw-r--r--ext/bz2/bz2_filter.c4
-rw-r--r--ext/bz2/tests/003-mb.phpt40
-rw-r--r--ext/bz2/tests/003私はガラスを食べられます.txt.bz2bin0 -> 126 bytes
-rw-r--r--ext/calendar/calendar.c18
-rw-r--r--ext/com_dotnet/com_com.c4
-rw-r--r--ext/com_dotnet/com_dotnet.c6
-rw-r--r--ext/curl/config.w3211
-rw-r--r--ext/curl/interface.c191
-rw-r--r--ext/curl/multi.c219
-rw-r--r--ext/curl/php_curl.h21
-rw-r--r--ext/curl/share.c45
-rw-r--r--ext/curl/tests/bug72202.phpt36
-rw-r--r--ext/curl/tests/curl_multi_errno_strerror_001.phpt30
-rw-r--r--ext/curl/tests/curl_share_errno_strerror_001.phpt30
-rwxr-xr-xext/date/config.w321
-rw-r--r--ext/date/lib/interval.c3
-rw-r--r--ext/date/lib/parse_date.c19268
-rw-r--r--ext/date/lib/parse_date.re85
-rw-r--r--ext/date/lib/timelib.c5
-rw-r--r--ext/date/lib/timelib.h5
-rw-r--r--ext/date/lib/timelib.m42
-rw-r--r--ext/date/lib/timelib_structs.h3
-rw-r--r--ext/date/lib/timezonemap.h293
-rw-r--r--ext/date/lib/tm2unixtime.c15
-rw-r--r--ext/date/lib/unixtime2tm.c7
-rw-r--r--ext/date/php_date.c157
-rw-r--r--ext/date/php_date.h1
-rw-r--r--ext/date/tests/010.phpt19
-rw-r--r--ext/date/tests/DateTimeZone_getLocation.phpt17
-rw-r--r--ext/date/tests/DateTimeZone_listAbbreviations_basic1.phpt74
-rw-r--r--ext/date/tests/DateTime_setTime_error.phpt5
-rw-r--r--ext/date/tests/bug45682.phpt4
-rw-r--r--ext/date/tests/bug48678.phpt2
-rw-r--r--ext/date/tests/bug49081.phpt1
-rw-r--r--ext/date/tests/bug49778.phpt6
-rw-r--r--ext/date/tests/bug52113.phpt24
-rw-r--r--ext/date/tests/bug52738.phpt1
-rw-r--r--ext/date/tests/bug52808.phpt12
-rw-r--r--ext/date/tests/bug53437.phpt8
-rw-r--r--ext/date/tests/bug53437_var1.phpt7
-rw-r--r--ext/date/tests/bug53437_var2.phpt8
-rw-r--r--ext/date/tests/bug53437_var3.phpt4
-rw-r--r--ext/date/tests/bug53437_var4.phpt6
-rw-r--r--ext/date/tests/bug53437_var5.phpt4
-rw-r--r--ext/date/tests/bug53437_var6.phpt49
-rw-r--r--ext/date/tests/bug55397.phpt7
-rw-r--r--ext/date/tests/bug60774.phpt2
-rw-r--r--ext/date/tests/bug62852.phpt7
-rw-r--r--ext/date/tests/bug62852_var2.phpt8
-rw-r--r--ext/date/tests/bug62852_var3.phpt8
-rw-r--r--ext/date/tests/bug64887.phpt46
-rw-r--r--ext/date/tests/bug66721.phpt7
-rw-r--r--ext/date/tests/bug68942.phpt7
-rw-r--r--ext/date/tests/bug68942_2.phpt7
-rw-r--r--ext/date/tests/bug73091.phpt4
-rw-r--r--ext/date/tests/bug73426.phpt32
-rw-r--r--ext/date/tests/bug73837.phpt19
-rw-r--r--ext/date/tests/bug74639.phpt33
-rw-r--r--ext/date/tests/bug74652.phpt30
-rw-r--r--ext/date/tests/date_diff1.phpt6
-rw-r--r--ext/date/tests/date_interval_create_from_date_string_nullparam.phpt6
-rw-r--r--ext/date/tests/date_interval_create_from_date_string_wrongparam_002.phpt3
-rw-r--r--ext/date/tests/date_sun_info_003.phpt42
-rw-r--r--ext/date/tests/date_time_fractions.phpt99
-rw-r--r--ext/date/tests/date_time_fractions_create_from_format.phpt29
-rw-r--r--ext/date/tests/date_time_fractions_serialize.phpt25
-rw-r--r--ext/date/tests/date_time_set_error.phpt5
-rw-r--r--ext/date/tests/timezone_abbreviations_list_basic1.phpt74
-rw-r--r--ext/date/tests/timezone_name_from_abbr_basic1.phpt10
-rw-r--r--ext/dba/dba.c4
-rw-r--r--ext/dba/libinifile/inifile.c2
-rw-r--r--ext/dba/tests/dba013.phpt6
-rw-r--r--ext/dba/tests/dba014.phpt6
-rw-r--r--ext/dom/comment.c2
-rw-r--r--ext/dom/document.c14
-rw-r--r--ext/dom/dom_iterators.c1
-rw-r--r--ext/dom/domimplementationlist.c2
-rw-r--r--ext/dom/node.c2
-rw-r--r--ext/dom/php_dom.c18
-rw-r--r--ext/dom/php_dom.h2
-rw-r--r--ext/dom/xpath.c4
-rw-r--r--ext/enchant/enchant.c4
-rw-r--r--ext/exif/exif.c7
-rw-r--r--ext/exif/tests/bug34704-mb.phpt42
-rw-r--r--ext/exif/tests/bug34704私はガラスを食べられます.jpgbin0 -> 9976 bytes
-rw-r--r--ext/exif/tests/bug68113-mb.phpt17
-rw-r--r--ext/exif/tests/bug68113私はガラスを食べられます.jpgbin0 -> 368 bytes
-rw-r--r--ext/exif/tests/exif_imagetype_basic-mb.phpt23
-rw-r--r--ext/exif/tests/exif_read_exif_data_basic-mb.phpt62
-rw-r--r--ext/exif/tests/test2私はガラスを食べられます.jpgbin0 -> 1240 bytes
-rwxr-xr-xext/ext_skel4
-rw-r--r--ext/fileinfo/fileinfo.c10
-rw-r--r--ext/fileinfo/tests/67647私はガラスを食べられます.movbin0 -> 144810 bytes
-rw-r--r--ext/fileinfo/tests/bug61964-mb.phpt73
-rw-r--r--ext/fileinfo/tests/bug67647-mb.phpt17
-rw-r--r--ext/fileinfo/tests/bug71527-mb.phpt19
-rw-r--r--ext/fileinfo/tests/bug71527私はガラスを食べられます.magic1
-rw-r--r--ext/fileinfo/tests/cve-2014-1943-mb.phpt39
-rw-r--r--ext/fileinfo/tests/cve-2014-1943私はガラスを食べられます.data1
-rw-r--r--ext/fileinfo/tests/cve-2014-1943私はガラスを食べられます.magic2
-rw-r--r--ext/fileinfo/tests/cve-2014-3538-mb.phpt35
-rw-r--r--ext/fileinfo/tests/cve-2014-3538私はガラスを食べられます.data1000000
-rw-r--r--ext/fileinfo/tests/finfo_buffer_basic-mb.phpt55
-rw-r--r--ext/fileinfo/tests/finfo_buffer_variation1-mb.phpt54
-rw-r--r--ext/fileinfo/tests/finfo_set_flags_basic-mb.phpt42
-rw-r--r--ext/fileinfo/tests/magic私はガラスを食べられます21766
-rw-r--r--ext/filter/filter.c2
-rw-r--r--ext/filter/filter_private.h2
-rw-r--r--ext/filter/logical_filters.c18
-rw-r--r--ext/filter/sanitizing_filters.c6
-rw-r--r--ext/filter/tests/058.phpt57
-rw-r--r--ext/filter/tests/filter_ipv4_rfc6890.phpt35
-rw-r--r--ext/ftp/config.w326
-rw-r--r--ext/ftp/ftp.c6
-rw-r--r--ext/ftp/php_ftp.c4
-rw-r--r--ext/gd/gd.c36
-rw-r--r--ext/gd/tests/001-mb.phpt25
-rw-r--r--ext/gd/tests/Tuffy私はガラスを食べられます.ttfbin0 -> 18444 bytes
-rw-r--r--ext/gd/tests/bug22544-mb.phpt20
-rw-r--r--ext/gd/tests/bug22544私はガラスを食べられます.pngbin0 -> 284 bytes
-rw-r--r--ext/gd/tests/bug36697-mb.phpt28
-rw-r--r--ext/gd/tests/bug37346-mb.phpt12
-rw-r--r--ext/gd/tests/bug37346私はガラスを食べられます.gif4
-rw-r--r--ext/gd/tests/bug38212-mb.phpt18
-rw-r--r--ext/gd/tests/bug39286-mb.phpt13
-rw-r--r--ext/gd/tests/bug48732-mb.phpt23
-rw-r--r--ext/gd/tests/bug48801-mb.phpt23
-rw-r--r--ext/gd/tests/bug66339-mb.phpt31
-rw-r--r--ext/gd/tests/bug71912-mb.phpt16
-rw-r--r--ext/gd/tests/conv_test私はガラスを食べられます.jpegbin0 -> 2317 bytes
-rw-r--r--ext/gd/tests/createfromwbmp-mb.phpt17
-rw-r--r--ext/gd/tests/imagecopyresampled_variation1.phpt70
-rw-r--r--ext/gd/tests/imagewbmp-mb.phpt30
-rw-r--r--ext/gd/tests/invalid_neg_size私はガラスを食べられます.gd2bin0 -> 1676 bytes
-rw-r--r--ext/gd/tests/jpeg2wbmp_error2-mb.phpt31
-rw-r--r--ext/gd/tests/jpg2gd-mb.phpt42
-rw-r--r--ext/gd/tests/libgd00094-mb.phpt16
-rw-r--r--ext/gd/tests/libgd00094私はガラスを食べられます.xbm3
-rw-r--r--ext/gd/tests/png2wbmp_error1-mb.phpt42
-rw-r--r--ext/gd/tests/simpletext私はガラスを食べられます.jpgbin0 -> 1514 bytes
-rw-r--r--ext/gd/tests/src私はガラスを食べられます.wbmpbin0 -> 9 bytes
-rw-r--r--ext/gmp/gmp.c6
-rw-r--r--ext/gmp/tests/cast.phpt2
-rw-r--r--ext/hash/config.m44
-rw-r--r--ext/hash/config.w324
-rw-r--r--ext/hash/hash.c148
-rw-r--r--ext/hash/hash_adler32.c2
-rw-r--r--ext/hash/hash_fnv.c16
-rw-r--r--ext/hash/hash_gost.c14
-rw-r--r--ext/hash/hash_haval.c44
-rw-r--r--ext/hash/hash_joaat.c4
-rw-r--r--ext/hash/hash_md.c42
-rw-r--r--ext/hash/hash_ripemd.c66
-rw-r--r--ext/hash/hash_sha.c162
-rw-r--r--ext/hash/hash_sha3.c239
-rw-r--r--ext/hash/hash_snefru.c14
-rw-r--r--ext/hash/hash_tiger.c30
-rw-r--r--ext/hash/hash_whirlpool.c32
-rw-r--r--ext/hash/php_hash.h11
-rw-r--r--ext/hash/php_hash_adler32.h2
-rw-r--r--ext/hash/php_hash_crc32.h2
-rw-r--r--ext/hash/php_hash_crc32_tables.h4
-rw-r--r--ext/hash/php_hash_fnv.h16
-rw-r--r--ext/hash/php_hash_gost.h6
-rw-r--r--ext/hash/php_hash_gost_tables.h4
-rw-r--r--ext/hash/php_hash_haval.h6
-rw-r--r--ext/hash/php_hash_joaat.h4
-rw-r--r--ext/hash/php_hash_md.h8
-rw-r--r--ext/hash/php_hash_ripemd.h16
-rw-r--r--ext/hash/php_hash_sha.h28
-rw-r--r--ext/hash/php_hash_sha3.h58
-rw-r--r--ext/hash/php_hash_snefru.h4
-rw-r--r--ext/hash/php_hash_snefru_tables.h2
-rw-r--r--ext/hash/php_hash_tiger.h4
-rw-r--r--ext/hash/php_hash_tiger_tables.h2
-rw-r--r--ext/hash/php_hash_whirlpool.h2
-rw-r--r--ext/hash/php_hash_whirlpool_tables.h18
-rw-r--r--ext/hash/tests/hash_algos.phpt14
-rw-r--r--ext/hash/tests/hash_copy_001.phpt36
-rw-r--r--ext/hash/tests/hash_hkdf_basic.phpt94
-rw-r--r--ext/hash/tests/hash_hkdf_edges.phpt32
-rw-r--r--ext/hash/tests/hash_hkdf_error.phpt100
-rw-r--r--ext/hash/tests/hash_hkdf_rfc5869.phpt80
-rw-r--r--ext/hash/tests/sha3.phpt56
-rw-r--r--ext/hash/tests/sha512-224.phpt13
-rw-r--r--ext/hash/tests/sha512-256.phpt13
-rw-r--r--ext/iconv/iconv.c16
-rw-r--r--ext/iconv/tests/iconv_strpos.phpt5
-rw-r--r--ext/iconv/tests/iconv_strpos_variation3.phpt135
-rw-r--r--ext/iconv/tests/iconv_strpos_variation3_64bit.phpt133
-rw-r--r--ext/iconv/tests/iconv_strpos_variation5.phpt17
-rw-r--r--ext/imap/php_imap.c4
-rw-r--r--ext/interbase/config.m455
-rw-r--r--ext/interbase/ibase_service.c4
-rw-r--r--ext/interbase/php_ibase_includes.h2
-rw-r--r--ext/interbase/tests/interbase.inc2
-rw-r--r--ext/intl/ERROR.CONVENTIONS2
-rw-r--r--ext/intl/breakiterator/breakiterator_class.cpp8
-rw-r--r--ext/intl/calendar/calendar_class.cpp10
-rw-r--r--ext/intl/calendar/calendar_methods.cpp2
-rw-r--r--ext/intl/collator/collator_class.c8
-rw-r--r--ext/intl/collator/collator_compare.c2
-rw-r--r--ext/intl/collator/collator_convert.c8
-rw-r--r--ext/intl/collator/collator_is_numeric.c17
-rw-r--r--ext/intl/collator/collator_locale.c2
-rw-r--r--ext/intl/collator/collator_sort.c12
-rw-r--r--ext/intl/common/common_date.cpp13
-rw-r--r--ext/intl/converter/converter.c6
-rw-r--r--ext/intl/dateformat/dateformat_attr.c2
-rw-r--r--ext/intl/dateformat/dateformat_format.c5
-rw-r--r--ext/intl/dateformat/dateformat_format_object.cpp14
-rw-r--r--ext/intl/dateformat/dateformat_parse.c16
-rw-r--r--ext/intl/formatter/formatter_attr.c4
-rw-r--r--ext/intl/formatter/formatter_class.c8
-rw-r--r--ext/intl/formatter/formatter_format.c5
-rw-r--r--ext/intl/formatter/formatter_parse.c5
-rw-r--r--ext/intl/grapheme/grapheme_string.c36
-rw-r--r--ext/intl/grapheme/grapheme_util.c2
-rw-r--r--ext/intl/grapheme/grapheme_util.h2
-rw-r--r--ext/intl/locale/locale_methods.c84
-rw-r--r--ext/intl/msgformat/msgformat_class.c8
-rw-r--r--ext/intl/php_intl.c16
-rw-r--r--ext/intl/spoofchecker/spoofchecker_class.c10
-rw-r--r--ext/intl/tests/bug60192-compare.phpt6
-rw-r--r--ext/intl/tests/bug60192-getlocale.phpt6
-rw-r--r--ext/intl/tests/bug60192-getsortkey.phpt6
-rw-r--r--ext/intl/tests/bug60192-sort.phpt7
-rw-r--r--ext/intl/tests/bug60192-sortwithsortkeys.phpt7
-rw-r--r--ext/intl/tests/bug69374.phpt24
-rw-r--r--ext/intl/tests/bug69398.phpt22
-rw-r--r--ext/intl/tests/bug74298.phpt30
-rw-r--r--ext/intl/tests/dateformat_bug65683.phpt16
-rw-r--r--ext/intl/tests/dateformat_format.phpt10
-rw-r--r--ext/intl/tests/dateformat_format_variant2.phpt10
-rw-r--r--ext/intl/tests/dateformat_format_variant3.phpt10
-rw-r--r--ext/intl/tests/formatter_fail.phpt2
-rw-r--r--ext/intl/tests/grapheme.phpt24
-rw-r--r--ext/intl/tests/locale_bug72658.phpt18
-rw-r--r--ext/intl/tests/msgfmt_fail2.phpt4
-rw-r--r--ext/intl/tests/msgfmt_format_datetime.phpt3
-rw-r--r--ext/intl/tests/msgfmt_format_error5.phpt2
-rw-r--r--ext/intl/tests/timezone_IDforWindowsID_basic.phpt46
-rw-r--r--ext/intl/tests/timezone_getCanonicalID_error.phpt6
-rw-r--r--ext/intl/tests/timezone_windowsID_basic.phpt43
-rw-r--r--ext/intl/timezone/timezone_class.cpp15
-rw-r--r--ext/intl/timezone/timezone_methods.cpp78
-rw-r--r--ext/intl/timezone/timezone_methods.h5
-rw-r--r--ext/intl/transliterator/transliterator_class.c28
-rw-r--r--ext/intl/uchar/uchar.c15
-rw-r--r--ext/json/config.m42
-rw-r--r--ext/json/json.c54
-rw-r--r--ext/json/json_encoder.c200
-rw-r--r--ext/json/json_parser.tab.c176
-rw-r--r--ext/json/json_parser.tab.h6
-rw-r--r--ext/json/json_parser.y168
-rw-r--r--ext/json/php_json.h11
-rw-r--r--ext/json/php_json_encoder.h15
-rw-r--r--ext/json/php_json_parser.h56
-rw-r--r--ext/json/tests/001.phpt6
-rw-r--r--ext/json/tests/bug66025.phpt19
-rw-r--r--ext/json/tests/bug68992.phpt29
-rw-r--r--ext/json/tests/bug73254.phpt21
-rw-r--r--ext/json/tests/json_encode_u2028_u2029.phpt36
-rw-r--r--ext/json/tests/pass001.1.phpt6
-rw-r--r--ext/json/tests/pass001.1_64bit.phpt6
-rw-r--r--ext/json/tests/pass001.phpt6
-rw-r--r--ext/ldap/config.w326
-rw-r--r--ext/ldap/ldap.c151
-rw-r--r--ext/ldap/tests/bug72021.phpt14
-rw-r--r--ext/ldap/tests/ldap_get_option_package_basic.phpt21
-rw-r--r--ext/ldap/tests/ldap_set_option_cafiles_basic.phpt41
-rw-r--r--ext/ldap/tests/ldap_set_option_ciphersuite_basic.phpt22
-rw-r--r--ext/ldap/tests/ldap_set_option_crlcheck_basic.phpt40
-rw-r--r--ext/ldap/tests/ldap_set_option_crlcheck_error.phpt17
-rw-r--r--ext/ldap/tests/ldap_set_option_keepalive_basic.phpt32
-rw-r--r--ext/ldap/tests/ldap_set_option_tls_protocol_min_basic.phpt38
-rw-r--r--ext/libxml/tests/bug69753-mb.phpt19
-rw-r--r--ext/libxml/tests/bug69753私はガラスを食べられます.xml4
-rw-r--r--ext/libxml/tests/libxml_set_external_entity_loader_error1.phpt10
-rw-r--r--ext/mbstring/mb_gpc.c2
-rw-r--r--ext/mbstring/mbstring.c100
-rw-r--r--ext/mbstring/mbstring.h1
-rw-r--r--ext/mbstring/php_mbregex.c71
-rw-r--r--ext/mbstring/tests/bug43301.phpt15
-rw-r--r--ext/mbstring/tests/bug43994.phpt24
-rw-r--r--ext/mbstring/tests/bug45923.phpt234
-rw-r--r--ext/mbstring/tests/bug69151.phpt21
-rw-r--r--ext/mbstring/tests/bug72164.phpt3
-rw-r--r--ext/mbstring/tests/bug73532.phpt8
-rw-r--r--ext/mbstring/tests/bug73646.phpt11
-rw-r--r--ext/mbstring/tests/common.inc2
-rw-r--r--ext/mbstring/tests/mb_ereg1.phpt9
-rw-r--r--ext/mbstring/tests/mb_ereg_basic.phpt5
-rw-r--r--ext/mbstring/tests/mb_ereg_search_setpos.phpt70
-rw-r--r--ext/mbstring/tests/mb_ereg_variation1.phpt42
-rw-r--r--ext/mbstring/tests/mb_ereg_variation2.phpt63
-rw-r--r--ext/mbstring/tests/mb_strimwidth.phpt36
-rw-r--r--ext/mbstring/tests/mb_stripos.phpt122
-rw-r--r--ext/mbstring/tests/mb_stripos_variation3.phpt67
-rw-r--r--ext/mbstring/tests/mb_stripos_variation5_Bug45923.phpt22
-rw-r--r--ext/mbstring/tests/mb_strpos.phpt97
-rw-r--r--ext/mbstring/tests/mb_strpos_variation3.phpt62
-rw-r--r--ext/mbstring/tests/mb_strpos_variation5.phpt16
-rw-r--r--ext/mcrypt/mcrypt.c84
-rw-r--r--ext/mcrypt/mcrypt_filter.c4
-rw-r--r--ext/mcrypt/tests/blowfish.phpt206
-rw-r--r--ext/mcrypt/tests/bug35496.phpt6
-rw-r--r--ext/mcrypt/tests/bug37595.phptbin1094 -> 3402 bytes
-rw-r--r--ext/mcrypt/tests/bug41252.phpt4
-rw-r--r--ext/mcrypt/tests/bug43143.phpt4
-rw-r--r--ext/mcrypt/tests/bug46010.phpt5
-rw-r--r--ext/mcrypt/tests/bug49738.phpt8
-rw-r--r--ext/mcrypt/tests/bug55169.phpt14
-rw-r--r--ext/mcrypt/tests/bug67707.phpt3
-rw-r--r--ext/mcrypt/tests/bug70625.phpt5
-rw-r--r--ext/mcrypt/tests/mcrypt_cbc.phpt9
-rw-r--r--ext/mcrypt/tests/mcrypt_cbc_3des_decrypt.phpt14
-rw-r--r--ext/mcrypt/tests/mcrypt_cbc_3des_encrypt.phpt14
-rw-r--r--ext/mcrypt/tests/mcrypt_cfb.phpt9
-rw-r--r--ext/mcrypt/tests/mcrypt_create_iv.phpt13
-rw-r--r--ext/mcrypt/tests/mcrypt_decrypt.phpt11
-rw-r--r--ext/mcrypt/tests/mcrypt_decrypt_3des_cbc.phpt14
-rw-r--r--ext/mcrypt/tests/mcrypt_decrypt_3des_ecb.phpt14
-rw-r--r--ext/mcrypt/tests/mcrypt_decrypt_error.phpt5
-rw-r--r--ext/mcrypt/tests/mcrypt_decrypt_variation1.phpt27
-rw-r--r--ext/mcrypt/tests/mcrypt_decrypt_variation2.phpt27
-rw-r--r--ext/mcrypt/tests/mcrypt_decrypt_variation3.phpt27
-rw-r--r--ext/mcrypt/tests/mcrypt_decrypt_variation4.phpt26
-rw-r--r--ext/mcrypt/tests/mcrypt_decrypt_variation5.phpt27
-rw-r--r--ext/mcrypt/tests/mcrypt_ecb.phpt9
-rw-r--r--ext/mcrypt/tests/mcrypt_ecb_3des_decrypt.phpt14
-rw-r--r--ext/mcrypt/tests/mcrypt_ecb_3des_encrypt.phpt14
-rw-r--r--ext/mcrypt/tests/mcrypt_enc_get_algorithms_name.phpt21
-rw-r--r--ext/mcrypt/tests/mcrypt_enc_get_block_size.phpt13
-rw-r--r--ext/mcrypt/tests/mcrypt_enc_get_iv_size.phpt13
-rw-r--r--ext/mcrypt/tests/mcrypt_enc_get_key_size.phpt13
-rw-r--r--ext/mcrypt/tests/mcrypt_enc_get_mode_name.phpt25
-rw-r--r--ext/mcrypt/tests/mcrypt_enc_get_supported_key_sizes.phpt5
-rw-r--r--ext/mcrypt/tests/mcrypt_enc_is_block_algorithm.phpt13
-rw-r--r--ext/mcrypt/tests/mcrypt_enc_is_block_algorithm_mode.phpt13
-rw-r--r--ext/mcrypt/tests/mcrypt_enc_is_block_mode.phpt17
-rw-r--r--ext/mcrypt/tests/mcrypt_enc_self_test.phpt5
-rw-r--r--ext/mcrypt/tests/mcrypt_encrypt_3des_cbc.phpt14
-rw-r--r--ext/mcrypt/tests/mcrypt_encrypt_3des_ecb.phpt14
-rw-r--r--ext/mcrypt/tests/mcrypt_encrypt_error.phpt4
-rw-r--r--ext/mcrypt/tests/mcrypt_encrypt_variation1.phpt26
-rw-r--r--ext/mcrypt/tests/mcrypt_encrypt_variation2.phpt26
-rw-r--r--ext/mcrypt/tests/mcrypt_encrypt_variation3.phpt26
-rw-r--r--ext/mcrypt/tests/mcrypt_encrypt_variation4.phpt26
-rw-r--r--ext/mcrypt/tests/mcrypt_encrypt_variation5.phpt26
-rw-r--r--ext/mcrypt/tests/mcrypt_filters.phpt4
-rw-r--r--ext/mcrypt/tests/mcrypt_get_block_size.phpt7
-rw-r--r--ext/mcrypt/tests/mcrypt_get_cipher_name.phpt9
-rw-r--r--ext/mcrypt/tests/mcrypt_get_iv_size.phpt7
-rw-r--r--ext/mcrypt/tests/mcrypt_get_key_size.phpt7
-rw-r--r--ext/mcrypt/tests/mcrypt_list_algorithms.phpt3
-rw-r--r--ext/mcrypt/tests/mcrypt_list_modes.phpt3
-rw-r--r--ext/mcrypt/tests/mcrypt_module_get_algo_block_size.phpt18
-rw-r--r--ext/mcrypt/tests/mcrypt_module_get_algo_key_size.phpt18
-rw-r--r--ext/mcrypt/tests/mcrypt_module_get_supported_key_sizes.phpt5
-rw-r--r--ext/mcrypt/tests/mcrypt_module_is_block_algorithm.phpt9
-rw-r--r--ext/mcrypt/tests/mcrypt_module_is_block_algorithm_mode.phpt9
-rw-r--r--ext/mcrypt/tests/mcrypt_module_is_block_mode.phpt11
-rw-r--r--ext/mcrypt/tests/mcrypt_module_open.phpt3
-rw-r--r--ext/mcrypt/tests/mcrypt_module_self_test.phpt7
-rw-r--r--ext/mcrypt/tests/mcrypt_ofb.phpt9
-rw-r--r--ext/mcrypt/tests/mcrypt_rijndael128_128BitKey.phpt18
-rw-r--r--ext/mcrypt/tests/mcrypt_rijndael128_256BitKey.phpt20
-rw-r--r--ext/mysqli/mysqli.c17
-rw-r--r--ext/mysqli/mysqli_api.c22
-rw-r--r--ext/mysqli/mysqli_driver.c2
-rw-r--r--ext/mysqli/mysqli_exception.c2
-rw-r--r--ext/mysqli/mysqli_fe.c50
-rw-r--r--ext/mysqli/mysqli_nonapi.c8
-rw-r--r--ext/mysqli/mysqli_priv.h4
-rw-r--r--ext/mysqli/mysqli_prop.c8
-rw-r--r--ext/mysqli/mysqli_result_iterator.c1
-rw-r--r--ext/mysqli/mysqli_warning.c2
-rw-r--r--ext/mysqli/php_mysqli_structs.h4
-rw-r--r--ext/mysqli/tests/bug71863.phpt36
-rw-r--r--ext/mysqli/tests/bug72701.phpt32
-rw-r--r--ext/mysqli/tests/bug74595.phpt25
-rw-r--r--ext/mysqli/tests/mysqli_class_mysqli_reflection.phpt9
-rw-r--r--ext/mysqli/tests/mysqli_fetch_object.phpt37
-rw-r--r--ext/mysqli/tests/mysqli_fetch_object_oo.phpt15
-rw-r--r--ext/mysqli/tests/mysqli_get_client_stats.phpt8
-rw-r--r--ext/mysqlnd/config.w327
-rw-r--r--ext/mysqlnd/config9.m48
-rw-r--r--ext/mysqlnd/mysqlnd.h108
-rw-r--r--ext/mysqlnd/mysqlnd_alloc.c216
-rw-r--r--ext/mysqlnd/mysqlnd_alloc.h39
-rw-r--r--ext/mysqlnd/mysqlnd_auth.c348
-rw-r--r--ext/mysqlnd/mysqlnd_auth.h126
-rw-r--r--ext/mysqlnd/mysqlnd_block_alloc.c25
-rw-r--r--ext/mysqlnd/mysqlnd_block_alloc.h1
-rw-r--r--ext/mysqlnd/mysqlnd_charset.c1
-rw-r--r--ext/mysqlnd/mysqlnd_commands.c1470
-rw-r--r--ext/mysqlnd/mysqlnd_commands.h34
-rw-r--r--ext/mysqlnd/mysqlnd_connection.c (renamed from ext/mysqlnd/mysqlnd.c)2113
-rw-r--r--ext/mysqlnd/mysqlnd_connection.h87
-rw-r--r--ext/mysqlnd/mysqlnd_debug.c1
-rw-r--r--ext/mysqlnd/mysqlnd_debug.h15
-rw-r--r--ext/mysqlnd/mysqlnd_driver.c180
-rw-r--r--ext/mysqlnd/mysqlnd_enum_n_def.h43
-rw-r--r--ext/mysqlnd/mysqlnd_ext_plugin.c331
-rw-r--r--ext/mysqlnd/mysqlnd_ext_plugin.h195
-rw-r--r--ext/mysqlnd/mysqlnd_libmysql_compat.h4
-rw-r--r--ext/mysqlnd/mysqlnd_loaddata.c33
-rw-r--r--ext/mysqlnd/mysqlnd_net.c17
-rw-r--r--ext/mysqlnd/mysqlnd_plugin.c2
-rw-r--r--ext/mysqlnd/mysqlnd_plugin.h41
-rw-r--r--ext/mysqlnd/mysqlnd_priv.h205
-rw-r--r--ext/mysqlnd/mysqlnd_protocol_frame_codec.c508
-rw-r--r--ext/mysqlnd/mysqlnd_protocol_frame_codec.h35
-rw-r--r--ext/mysqlnd/mysqlnd_ps.c779
-rw-r--r--ext/mysqlnd/mysqlnd_ps.h54
-rw-r--r--ext/mysqlnd/mysqlnd_ps_codec.c71
-rw-r--r--ext/mysqlnd/mysqlnd_read_buffer.c96
-rw-r--r--ext/mysqlnd/mysqlnd_read_buffer.h25
-rw-r--r--ext/mysqlnd/mysqlnd_result.c460
-rw-r--r--ext/mysqlnd/mysqlnd_result.h9
-rw-r--r--ext/mysqlnd/mysqlnd_result_meta.c60
-rw-r--r--ext/mysqlnd/mysqlnd_result_meta.h2
-rw-r--r--ext/mysqlnd/mysqlnd_reverse_api.c2
-rw-r--r--ext/mysqlnd/mysqlnd_statistics.c47
-rw-r--r--ext/mysqlnd/mysqlnd_statistics.h49
-rw-r--r--ext/mysqlnd/mysqlnd_structs.h689
-rw-r--r--ext/mysqlnd/mysqlnd_vio.c805
-rw-r--r--ext/mysqlnd/mysqlnd_vio.h (renamed from ext/mysqlnd/mysqlnd_net.h)11
-rw-r--r--ext/mysqlnd/mysqlnd_wireprotocol.c1044
-rw-r--r--ext/mysqlnd/mysqlnd_wireprotocol.h55
-rw-r--r--ext/mysqlnd/php_mysqlnd.c2
-rw-r--r--ext/mysqlnd/php_mysqlnd.h1
-rw-r--r--ext/odbc/birdstep.c2
-rw-r--r--ext/odbc/config.m44
-rw-r--r--ext/odbc/config.w322
-rw-r--r--ext/opcache/Optimizer/block_pass.c2613
-rw-r--r--ext/opcache/Optimizer/compact_literals.c95
-rw-r--r--ext/opcache/Optimizer/dfa_pass.c670
-rw-r--r--ext/opcache/Optimizer/nop_removal.c70
-rw-r--r--ext/opcache/Optimizer/optimize_func_calls.c186
-rw-r--r--ext/opcache/Optimizer/optimize_temp_vars_5.c55
-rw-r--r--ext/opcache/Optimizer/pass1_5.c107
-rw-r--r--ext/opcache/Optimizer/pass2.c56
-rw-r--r--ext/opcache/Optimizer/pass3.c140
-rw-r--r--ext/opcache/Optimizer/zend_call_graph.c298
-rw-r--r--ext/opcache/Optimizer/zend_call_graph.h86
-rw-r--r--ext/opcache/Optimizer/zend_cfg.c885
-rw-r--r--ext/opcache/Optimizer/zend_cfg.h137
-rw-r--r--ext/opcache/Optimizer/zend_dfg.c254
-rw-r--r--ext/opcache/Optimizer/zend_dfg.h58
-rw-r--r--ext/opcache/Optimizer/zend_dump.c1177
-rw-r--r--ext/opcache/Optimizer/zend_dump.h51
-rw-r--r--ext/opcache/Optimizer/zend_func_info.c1288
-rw-r--r--ext/opcache/Optimizer/zend_func_info.h69
-rw-r--r--ext/opcache/Optimizer/zend_inference.c3932
-rw-r--r--ext/opcache/Optimizer/zend_inference.h275
-rw-r--r--ext/opcache/Optimizer/zend_optimizer.c674
-rw-r--r--ext/opcache/Optimizer/zend_optimizer.h48
-rw-r--r--ext/opcache/Optimizer/zend_optimizer_internal.h84
-rw-r--r--ext/opcache/Optimizer/zend_ssa.c1155
-rw-r--r--ext/opcache/Optimizer/zend_ssa.h168
-rw-r--r--ext/opcache/Optimizer/zend_worklist.h137
-rw-r--r--ext/opcache/ZendAccelerator.c148
-rw-r--r--ext/opcache/ZendAccelerator.h53
-rw-r--r--ext/opcache/config.m410
-rw-r--r--ext/opcache/config.w322
-rw-r--r--ext/opcache/shared_alloc_win32.c22
-rw-r--r--ext/opcache/tests/blacklist-win32.phpt2
-rw-r--r--ext/opcache/tests/block_pass_001.phpt10
-rw-r--r--ext/opcache/tests/bug71127.phpt2
-rw-r--r--ext/opcache/tests/bug71843.phpt4
-rw-r--r--ext/opcache/tests/bug72762.phpt23
-rw-r--r--ext/opcache/tests/bug73583.phpt19
-rw-r--r--ext/opcache/tests/bug73654.phpt17
-rw-r--r--ext/opcache/tests/bug73668.phpt8
-rw-r--r--ext/opcache/tests/bug73746.phpt28
-rw-r--r--ext/opcache/tests/bug73789.phpt30
-rw-r--r--ext/opcache/tests/bug73847.phpt44
-rw-r--r--ext/opcache/tests/bug74431.phpt28
-rw-r--r--ext/opcache/tests/bug74442.phpt39
-rw-r--r--ext/opcache/tests/bug74456.phpt24
-rw-r--r--ext/opcache/tests/bug74623.phpt22
-rw-r--r--ext/opcache/tests/bug74980.phpt30
-rw-r--r--ext/opcache/tests/issue0140.phpt1
-rw-r--r--ext/opcache/tests/ssa_bug_001.phpt19
-rw-r--r--ext/opcache/tests/ssa_bug_002.phpt16
-rw-r--r--ext/opcache/tests/ssa_bug_003.phpt38
-rw-r--r--ext/opcache/tests/ssa_bug_004.phpt19
-rw-r--r--ext/opcache/tests/ssa_bug_005.phpt24
-rw-r--r--ext/opcache/tests/ssa_bug_006.phpt20
-rw-r--r--ext/opcache/tests/verify_return_dfg.phpt16
-rw-r--r--ext/opcache/tests/wrong_inlining_001.phpt28
-rw-r--r--ext/opcache/tests/wrong_inlining_002.phpt29
-rw-r--r--ext/opcache/tests/wrong_inlining_003.phpt23
-rw-r--r--ext/opcache/tests/wrong_inlining_004.phpt23
-rw-r--r--ext/opcache/tests/wrong_inlining_005.phpt22
-rw-r--r--ext/opcache/zend_accelerator_debug.h2
-rw-r--r--ext/opcache/zend_accelerator_module.c39
-rw-r--r--ext/opcache/zend_accelerator_util_funcs.c112
-rw-r--r--ext/opcache/zend_file_cache.c115
-rw-r--r--ext/opcache/zend_persist.c214
-rw-r--r--ext/opcache/zend_persist_calc.c43
-rw-r--r--ext/opcache/zend_shared_alloc.c2
-rw-r--r--ext/openssl/config.w3219
-rw-r--r--ext/openssl/config0.m42
-rw-r--r--ext/openssl/openssl.c1357
-rw-r--r--ext/openssl/php_openssl.h33
-rw-r--r--ext/openssl/tests/001.phpt2
-rw-r--r--ext/openssl/tests/bug38261.phpt2
-rw-r--r--ext/openssl/tests/bug41033.phpt6
-rw-r--r--ext/openssl/tests/bug61124.phpt4
-rw-r--r--ext/openssl/tests/bug66501.phpt3
-rw-r--r--ext/openssl/tests/bug69882.phpt17
-rw-r--r--ext/openssl/tests/bug71917.phpt25
-rw-r--r--ext/openssl/tests/bug72362.phpt14
-rw-r--r--ext/openssl/tests/bug73478.phpt25
-rw-r--r--ext/openssl/tests/bug74099.phpt20
-rw-r--r--ext/openssl/tests/bug74720_0.phpt93
-rw-r--r--ext/openssl/tests/bug74720_1.phpt88
-rw-r--r--ext/openssl/tests/bug74798.phpt96
-rw-r--r--ext/openssl/tests/cipher_tests.inc111
-rw-r--r--ext/openssl/tests/ecc.phpt110
-rw-r--r--ext/openssl/tests/openssl_decrypt_basic.phpt5
-rw-r--r--ext/openssl/tests/openssl_decrypt_ccm.phpt41
-rw-r--r--ext/openssl/tests/openssl_decrypt_error.phpt8
-rw-r--r--ext/openssl/tests/openssl_decrypt_gcm.phpt51
-rw-r--r--ext/openssl/tests/openssl_encrypt_ccm.phpt39
-rw-r--r--ext/openssl/tests/openssl_encrypt_error.phpt13
-rw-r--r--ext/openssl/tests/openssl_encrypt_gcm.phpt60
-rw-r--r--ext/openssl/tests/openssl_pkcs7_decrypt_basic.phpt4
-rw-r--r--ext/openssl/tests/openssl_pkcs7_encrypt_basic.phpt4
-rw-r--r--ext/openssl/tests/openssl_pkcs7_sign_basic.phpt2
-rw-r--r--ext/openssl/tests/openssl_pkey_export_basic.phpt18
-rw-r--r--ext/openssl/tests/openssl_pkey_get_details_basic.phpt7
-rw-r--r--ext/openssl/xp_ssl.c148
-rw-r--r--ext/pcntl/config.m418
-rw-r--r--ext/pcntl/pcntl.c195
-rw-r--r--ext/pcntl/php_pcntl.h10
-rw-r--r--ext/pcntl/php_signal.c23
-rw-r--r--ext/pcntl/php_signal.h4
-rw-r--r--ext/pcntl/tests/async_signals.phpt25
-rw-r--r--ext/pcntl/tests/bug73783.phpt28
-rw-r--r--ext/pcntl/tests/pcntl_signal.phpt7
-rw-r--r--ext/pcntl/tests/pcntl_signal_get_handler.phpt30
-rw-r--r--ext/pcre/php_pcre.c4
-rw-r--r--ext/pcre/tests/preg_replace_callback3.phpt6
-rw-r--r--ext/pcre/tests/preg_replace_error1.phpt2
-rw-r--r--ext/pcre/tests/preg_replace_error2.phpt2
-rw-r--r--ext/pdo/pdo.c27
-rw-r--r--ext/pdo/pdo_dbh.c12
-rw-r--r--ext/pdo/pdo_sql_parser.c2
-rw-r--r--ext/pdo/pdo_sql_parser.re2
-rw-r--r--ext/pdo/pdo_sqlstate.c2
-rw-r--r--ext/pdo/pdo_stmt.c43
-rw-r--r--ext/pdo/tests/pdo_036.phpt5
-rw-r--r--ext/pdo/tests/pdorow.phpt3
-rw-r--r--ext/pdo_dblib/dblib_driver.c5
-rw-r--r--ext/pdo_dblib/dblib_stmt.c8
-rw-r--r--ext/pdo_dblib/pdo_dblib.c7
-rw-r--r--ext/pdo_dblib/php_pdo_dblib.h1
-rw-r--r--ext/pdo_dblib/php_pdo_dblib_int.h1
-rw-r--r--ext/pdo_firebird/config.m457
-rw-r--r--ext/pdo_firebird/tests/testdb.inc2
-rw-r--r--ext/pdo_mysql/mysql_driver.c15
-rw-r--r--ext/pdo_mysql/pdo_mysql.c2
-rw-r--r--ext/pdo_mysql/php_pdo_mysql_int.h6
-rw-r--r--ext/pdo_oci/pdo_oci.c2
-rw-r--r--ext/pdo_oci/tests/pdo_oci_stream_2a.phpt20
-rw-r--r--ext/pdo_odbc/pdo_odbc.c4
-rw-r--r--ext/pdo_odbc/php_pdo_odbc.h10
-rw-r--r--ext/pdo_pgsql/pdo_pgsql.c4
-rw-r--r--ext/pdo_pgsql/pgsql_driver.c26
-rw-r--r--ext/pdo_pgsql/pgsql_statement.c4
-rw-r--r--ext/pdo_sqlite/pdo_sqlite.c6
-rw-r--r--ext/pdo_sqlite/php_pdo_sqlite.h10
-rw-r--r--ext/pdo_sqlite/sqlite_driver.c16
-rw-r--r--ext/pdo_sqlite/tests/pdo_sqlite_createfunction_with_flags.phpt41
-rw-r--r--ext/pgsql/pgsql.c184
-rw-r--r--ext/pgsql/php_pgsql.h5
-rw-r--r--ext/pgsql/tests/09notice.phpt55
-rw-r--r--ext/pgsql/tests/bug72195.phpt2
-rw-r--r--ext/phar/dirstream.c3
-rw-r--r--ext/phar/func_interceptors.c2
-rw-r--r--ext/phar/phar.c65
-rw-r--r--ext/phar/phar_internal.h45
-rw-r--r--ext/phar/phar_object.c174
-rw-r--r--ext/phar/stream.c1
-rw-r--r--ext/phar/stub.h9
-rw-r--r--ext/phar/tar.c33
-rw-r--r--ext/phar/tests/bug74383.phpt2
-rw-r--r--ext/phar/tests/cache_list/copyonwrite11.phar.phpt2
-rw-r--r--ext/phar/tests/cache_list/files/frontcontroller17.pharbin315 -> 344 bytes
-rw-r--r--ext/phar/tests/cache_list/files/frontcontroller17.phar.inc2
-rw-r--r--ext/phar/tests/cache_list/files/nophar.pharbin7049 -> 7017 bytes
-rw-r--r--ext/phar/tests/cache_list/files/openssl.pharbin6901 -> 7129 bytes
-rw-r--r--ext/phar/tests/cache_list/frontcontroller32.phpt2
-rw-r--r--ext/phar/tests/files/frontcontroller17.pharbin315 -> 344 bytes
-rw-r--r--ext/phar/tests/files/frontcontroller17.phar.inc2
-rw-r--r--ext/phar/tests/files/include_path2.pharbin6837 -> 6807 bytes
-rw-r--r--ext/phar/tests/files/nophar.pharbin7049 -> 7017 bytes
-rw-r--r--ext/phar/tests/files/openssl.pharbin6901 -> 7129 bytes
-rw-r--r--ext/phar/tests/frontcontroller32.phpt2
-rw-r--r--ext/phar/tests/open_for_write_existing_b.phpt2
-rw-r--r--ext/phar/tests/open_for_write_existing_b_5_2.phpt2
-rw-r--r--ext/phar/tests/open_for_write_newfile_b.phpt2
-rw-r--r--ext/phar/tests/open_for_write_newfile_b_5_2.phpt2
-rw-r--r--ext/phar/tests/phar_commitwrite.phpt2
-rw-r--r--ext/phar/tests/phar_convert_repeated.phpt2
-rw-r--r--ext/phar/tests/phar_create_in_cwd.phpt2
-rw-r--r--ext/phar/tests/phar_createdefaultstub.phpt40
-rw-r--r--ext/phar/tests/phar_offset_check.phpt4
-rw-r--r--ext/phar/tests/phar_setdefaultstub.phpt38
-rw-r--r--ext/phar/tests/tar/open_for_write_existing_b.phpt2
-rw-r--r--ext/phar/tests/tar/open_for_write_existing_b_5_2.phpt2
-rw-r--r--ext/phar/tests/tar/open_for_write_newfile_b.phpt2
-rw-r--r--ext/phar/tests/tar/open_for_write_newfile_b_5_2.phpt2
-rw-r--r--ext/phar/tests/tar/phar_convert_phar.phpt6
-rw-r--r--ext/phar/tests/tar/phar_convert_phar2.phpt6
-rw-r--r--ext/phar/tests/tar/phar_convert_phar3.phpt6
-rw-r--r--ext/phar/tests/tar/phar_convert_phar4.phpt8
-rw-r--r--ext/phar/tests/zip/open_for_write_existing_b.phpt2
-rw-r--r--ext/phar/tests/zip/open_for_write_existing_b_5_2.phpt2
-rw-r--r--ext/phar/tests/zip/open_for_write_newfile_b.phpt2
-rw-r--r--ext/phar/tests/zip/open_for_write_newfile_b_5_2.phpt2
-rw-r--r--ext/phar/tests/zip/phar_convert_phar.phpt6
-rw-r--r--ext/phar/util.c27
-rw-r--r--ext/phar/zip.c45
-rw-r--r--ext/posix/tests/posix_getgrgid_variation.phpt2
-rw-r--r--ext/posix/tests/posix_getpgid_variation.phpt2
-rw-r--r--ext/posix/tests/posix_getpwuid_variation.phpt2
-rw-r--r--ext/posix/tests/posix_kill_variation1.phpt2
-rw-r--r--ext/posix/tests/posix_kill_variation2.phpt2
-rw-r--r--ext/posix/tests/posix_strerror_variation1.phpt2
-rw-r--r--ext/pspell/pspell.c6
-rw-r--r--ext/readline/config.w3216
-rw-r--r--ext/readline/php_readline.h2
-rw-r--r--ext/readline/readline.c10
-rw-r--r--ext/readline/readline_cli.c36
-rw-r--r--ext/readline/tests/bug72538.phpt1
-rw-r--r--ext/readline/tests/libedit_info_001-win32.phpt42
-rw-r--r--ext/readline/tests/libedit_info_001.phpt3
-rw-r--r--ext/readline/tests/libedit_write_history_001-win32.phpt29
-rw-r--r--ext/readline/tests/libedit_write_history_001.phpt3
-rw-r--r--ext/reflection/php_reflection.c713
-rw-r--r--ext/reflection/php_reflection.h1
-rw-r--r--ext/reflection/tests/004.phpt2
-rw-r--r--ext/reflection/tests/007.phpt14
-rw-r--r--ext/reflection/tests/017.phpt4
-rw-r--r--ext/reflection/tests/ReflectionClassConstant_basic1.phpt194
-rw-r--r--ext/reflection/tests/ReflectionClassConstant_getValue.phpt47
-rw-r--r--ext/reflection/tests/ReflectionClass_export_array_bug72222.phpt4
-rw-r--r--ext/reflection/tests/ReflectionClass_isArray.phpt24
-rw-r--r--ext/reflection/tests/ReflectionClass_newInstanceArgs_001.phpt40
-rw-r--r--ext/reflection/tests/ReflectionClass_newInstance_001.phpt25
-rw-r--r--ext/reflection/tests/ReflectionClass_toString_001.phpt21
-rw-r--r--ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt24
-rw-r--r--ext/reflection/tests/ReflectionMethod_invokeArgs_error1.phpt15
-rw-r--r--ext/reflection/tests/ReflectionMethod_invokeArgs_error3.phpt13
-rw-r--r--ext/reflection/tests/ReflectionMethod_invoke_basic.phpt14
-rw-r--r--ext/reflection/tests/ReflectionMethod_invoke_error1.phpt4
-rw-r--r--ext/reflection/tests/ReflectionMethod_invoke_error2.phpt15
-rw-r--r--ext/reflection/tests/ReflectionNamedType.phpt41
-rw-r--r--ext/reflection/tests/ReflectionProperty_getValue_error.phpt15
-rw-r--r--ext/reflection/tests/ReflectionType_001.phpt8
-rw-r--r--ext/reflection/tests/ReflectionType_002.phpt2
-rw-r--r--ext/reflection/tests/ReflectionType_possible_types.phpt33
-rw-r--r--ext/reflection/tests/bug29986.phpt10
-rw-r--r--ext/reflection/tests/bug38217.phpt14
-rw-r--r--ext/reflection/tests/bug42976.phpt4
-rw-r--r--ext/reflection/tests/bug45765.phpt2
-rw-r--r--ext/reflection/tests/bug69802.phpt2
-rw-r--r--ext/reflection/tests/bug72661.phpt10
-rw-r--r--ext/reflection/tests/bug72846.phpt48
-rw-r--r--ext/reflection/tests/request38992.phpt22
-rw-r--r--ext/session/mod_files.c8
-rw-r--r--ext/session/mod_mm.c24
-rw-r--r--ext/session/mod_user.c36
-rw-r--r--ext/session/mod_user_class.c34
-rw-r--r--ext/session/php_session.h22
-rw-r--r--ext/session/session.c472
-rw-r--r--ext/session/tests/016.phpt5
-rw-r--r--ext/session/tests/021.phpt26
-rw-r--r--ext/session/tests/bug26862.phpt2
-rw-r--r--ext/session/tests/bug50308.phpt6
-rw-r--r--ext/session/tests/bug55688.phpt2
-rw-r--r--ext/session/tests/bug60634.phpt14
-rw-r--r--ext/session/tests/bug60634_error_1.phpt6
-rw-r--r--ext/session/tests/bug60634_error_3.phpt3
-rw-r--r--ext/session/tests/bug60634_error_4.phpt2
-rw-r--r--ext/session/tests/bug61728.phpt30
-rw-r--r--ext/session/tests/bug67972.phpt3
-rw-r--r--ext/session/tests/bug68063.phpt14
-rw-r--r--ext/session/tests/bug69111.phpt7
-rw-r--r--ext/session/tests/bug70133.phpt41
-rw-r--r--ext/session/tests/bug71162.phpt79
-rw-r--r--ext/session/tests/bug72681.phpt7
-rw-r--r--ext/session/tests/bug74892.phpt23
-rw-r--r--ext/session/tests/rfc1867_sid_invalid.phpt5
-rw-r--r--ext/session/tests/session_basic2.phpt2
-rw-r--r--ext/session/tests/session_basic3.phpt59
-rw-r--r--ext/session/tests/session_basic4.phpt67
-rw-r--r--ext/session/tests/session_basic5.phpt446
-rw-r--r--ext/session/tests/session_create_id_basic.phpt58
-rw-r--r--ext/session/tests/session_gc_basic.phpt40
-rw-r--r--ext/session/tests/session_hash_function_basic.phpt52
-rw-r--r--ext/session/tests/session_id_basic2.phpt38
-rw-r--r--ext/session/tests/session_id_error4.phpt37
-rw-r--r--ext/session/tests/session_id_variation1.phpt48
-rw-r--r--ext/session/tests/session_id_variation2.phpt61
-rw-r--r--ext/session/tests/session_save_path_variation2.phpt8
-rw-r--r--ext/session/tests/session_save_path_variation3.phpt8
-rw-r--r--ext/session/tests/session_save_path_variation4.phpt1
-rw-r--r--ext/session/tests/session_set_save_handler_class_002.phpt2
-rw-r--r--ext/session/tests/session_set_save_handler_class_005.phpt13
-rw-r--r--ext/session/tests/session_set_save_handler_class_012.phpt13
-rw-r--r--ext/session/tests/session_set_save_handler_class_016.phpt6
-rw-r--r--ext/session/tests/session_set_save_handler_class_017.phpt2
-rw-r--r--ext/session/tests/session_set_save_handler_error4.phpt5
-rw-r--r--ext/session/tests/session_set_save_handler_iface_001.phpt2
-rw-r--r--ext/session/tests/session_set_save_handler_iface_002.phpt2
-rw-r--r--ext/session/tests/session_set_save_handler_sid_002.phpt12
-rw-r--r--ext/session/tests/session_set_save_handler_variation6.phpt1
-rw-r--r--ext/session/tests/sessionhandler_open_001.phpt7
-rw-r--r--ext/shmop/php_shmop.h2
-rw-r--r--ext/shmop/shmop.c4
-rw-r--r--ext/simplexml/php_simplexml.h3
-rw-r--r--ext/simplexml/simplexml.c39
-rw-r--r--ext/simplexml/sxe.c2
-rw-r--r--ext/simplexml/tests/001-mb.phpt43
-rw-r--r--ext/simplexml/tests/012.phpt5
-rw-r--r--ext/simplexml/tests/sxe私はガラスを食べられます.xml17
-rw-r--r--ext/snmp/config.w324
-rw-r--r--ext/snmp/snmp.c22
-rw-r--r--ext/soap/php_encoding.c201
-rw-r--r--ext/soap/php_http.c16
-rw-r--r--ext/soap/php_packet_soap.c2
-rw-r--r--ext/soap/php_sdl.c12
-rw-r--r--ext/soap/php_sdl.h2
-rw-r--r--ext/soap/soap.c14
-rw-r--r--ext/soap/tests/bugs/bug38005.phpt2
-rw-r--r--ext/soap/tests/soap12/soap12-test.inc2
-rw-r--r--ext/sockets/conversions.c22
-rw-r--r--ext/sockets/multicast.c4
-rw-r--r--ext/sockets/sendrecvmsg.c10
-rw-r--r--ext/sockets/sockaddr_conv.c2
-rw-r--r--ext/sockets/sockets.c18
-rw-r--r--ext/sockets/tests/socket_send.phpt1
-rw-r--r--ext/sockets/tests/socket_shutdown.phpt1
-rw-r--r--ext/spl/php_spl.c9
-rw-r--r--ext/spl/spl_array.c24
-rw-r--r--ext/spl/spl_directory.c27
-rw-r--r--ext/spl/spl_dllist.c5
-rw-r--r--ext/spl/spl_engine.h4
-rw-r--r--ext/spl/spl_fixedarray.c147
-rw-r--r--ext/spl/spl_heap.c14
-rw-r--r--ext/spl/spl_iterators.c40
-rw-r--r--ext/spl/spl_observer.c101
-rw-r--r--ext/spl/tests/array_013.phpt6
-rw-r--r--ext/spl/tests/bug62904.phpt11
-rw-r--r--ext/spl/tests/bug64106.phpt4
-rw-r--r--ext/spl/tests/bug72888.phpt18
-rw-r--r--ext/spl/tests/bug73686.phpt45
-rw-r--r--ext/spl/tests/bug74058.phpt81
-rw-r--r--ext/spl/tests/bug74977.phpt13
-rw-r--r--ext/spl/tests/spl_004.phpt2
-rw-r--r--ext/sqlite3/libsqlite/sqlite3.c7141
-rw-r--r--ext/sqlite3/libsqlite/sqlite3.h65
-rw-r--r--ext/sqlite3/sqlite3.c38
-rw-r--r--ext/sqlite3/tests/sqlite3_01_open-mb.phpt22
-rw-r--r--ext/sqlite3/tests/sqlite3_37_createfunction_flags.phpt32
-rw-r--r--ext/standard/array.c619
-rw-r--r--ext/standard/assert.c13
-rw-r--r--ext/standard/base64.c26
-rw-r--r--ext/standard/basic_functions.c195
-rw-r--r--ext/standard/basic_functions.h38
-rw-r--r--ext/standard/browscap.c4
-rw-r--r--ext/standard/config.m497
-rw-r--r--ext/standard/config.w322
-rw-r--r--ext/standard/crc32.c4
-rw-r--r--ext/standard/credits_ext.h6
-rw-r--r--ext/standard/crypt.c88
-rw-r--r--ext/standard/crypt_freesec.c4
-rw-r--r--ext/standard/dir.c10
-rw-r--r--ext/standard/exec.c8
-rw-r--r--ext/standard/file.c22
-rw-r--r--ext/standard/filestat.c111
-rw-r--r--ext/standard/formatted_print.c2
-rw-r--r--ext/standard/ftok.c4
-rw-r--r--ext/standard/ftp_fopen_wrapper.c11
-rw-r--r--ext/standard/html.c2
-rw-r--r--ext/standard/html_tables.h76
-rw-r--r--ext/standard/html_tables/html_table_gen.php2
-rw-r--r--ext/standard/http.c2
-rw-r--r--ext/standard/http_fopen_wrapper.c126
-rw-r--r--ext/standard/image.c79
-rw-r--r--ext/standard/incomplete_class.c3
-rw-r--r--ext/standard/info.c4
-rw-r--r--ext/standard/iptc.c6
-rw-r--r--ext/standard/lcg.c4
-rw-r--r--ext/standard/link_win32.c42
-rw-r--r--ext/standard/mail.c41
-rw-r--r--ext/standard/math.c18
-rw-r--r--ext/standard/md5.c20
-rw-r--r--ext/standard/md5.h6
-rw-r--r--ext/standard/metaphone.c4
-rw-r--r--ext/standard/mt_rand.c336
-rw-r--r--ext/standard/pack.c34
-rw-r--r--ext/standard/password.c8
-rw-r--r--ext/standard/php_crypt.h2
-rw-r--r--ext/standard/php_crypt_r.c2
-rw-r--r--ext/standard/php_fopen_wrapper.c7
-rw-r--r--ext/standard/php_image.h1
-rw-r--r--ext/standard/php_incomplete_class.h2
-rw-r--r--ext/standard/php_lcg.h4
-rw-r--r--ext/standard/php_math.h2
-rw-r--r--ext/standard/php_mt_rand.h44
-rw-r--r--ext/standard/php_rand.h47
-rw-r--r--ext/standard/php_smart_string.h4
-rw-r--r--ext/standard/php_type.h1
-rw-r--r--ext/standard/php_var.h72
-rw-r--r--ext/standard/proc_open.c64
-rw-r--r--ext/standard/rand.c325
-rw-r--r--ext/standard/sha1.c38
-rw-r--r--ext/standard/sha1.h4
-rw-r--r--ext/standard/streamsfuncs.c4
-rw-r--r--ext/standard/string.c73
-rw-r--r--ext/standard/tests/array/array_filter_variation10.phpt22
-rw-r--r--ext/standard/tests/array/array_map_error.phpt27
-rw-r--r--ext/standard/tests/array/array_map_variation10.phpt42
-rw-r--r--ext/standard/tests/array/array_map_variation9.phpt45
-rw-r--r--ext/standard/tests/array/array_multisort_variation7.phpt22
-rw-r--r--ext/standard/tests/array/array_multisort_variation8.phpt8
-rw-r--r--ext/standard/tests/array/array_multisort_variation9.phpt28
-rw-r--r--ext/standard/tests/array/array_rand.phpt4
-rw-r--r--ext/standard/tests/array/array_reduce_variation1.phpt14
-rw-r--r--ext/standard/tests/array/array_udiff_assoc_variation5.phpt14
-rw-r--r--ext/standard/tests/array/array_udiff_uassoc_variation6.phpt13
-rw-r--r--ext/standard/tests/array/array_udiff_variation5.phpt11
-rw-r--r--ext/standard/tests/array/array_uintersect_assoc_variation5.phpt11
-rw-r--r--ext/standard/tests/array/array_uintersect_uassoc_variation6.phpt15
-rw-r--r--ext/standard/tests/array/array_uintersect_variation5.phpt15
-rw-r--r--ext/standard/tests/array/array_walk_error2.phpt50
-rw-r--r--ext/standard/tests/array/array_walk_recursive_error2.phpt50
-rw-r--r--ext/standard/tests/array/bug61730.phpt7
-rw-r--r--ext/standard/tests/array/bug61967.phpt25
-rw-r--r--ext/standard/tests/array/bug62607.phpt12
-rw-r--r--ext/standard/tests/array/bug69068.phpt26
-rw-r--r--ext/standard/tests/array/bug69068_2.phpt34
-rw-r--r--ext/standard/tests/array/bug70713.phpt26
-rw-r--r--ext/standard/tests/array/bug74361.phpt11
-rw-r--r--ext/standard/tests/array/bug74361_2.phpt24
-rw-r--r--ext/standard/tests/array/compact_no_this.phpt25
-rw-r--r--ext/standard/tests/array/compact_order.phpt25
-rw-r--r--ext/standard/tests/array/compact_this.phpt46
-rw-r--r--ext/standard/tests/assert/assert02.phpt46
-rw-r--r--ext/standard/tests/assert/assert_error2.phpt9
-rw-r--r--ext/standard/tests/assert/assert_error3.phpt14
-rw-r--r--ext/standard/tests/assert/assert_error4.phpt14
-rw-r--r--ext/standard/tests/assert/bug73303.phpt23
-rw-r--r--ext/standard/tests/class_object/forward_static_call_002.phpt7
-rw-r--r--ext/standard/tests/dir/bug72625.phpt53
-rw-r--r--ext/standard/tests/dir/bug73877.phpt50
-rw-r--r--ext/standard/tests/dir/chdir_basic-win32-mb.phpt60
-rw-r--r--ext/standard/tests/dir/chdir_error2-win32-mb.phpt32
-rw-r--r--ext/standard/tests/dir/chdir_variation1-win32-mb.phpt241
-rw-r--r--ext/standard/tests/dir/chdir_variation2-win32-mb.phpt115
-rw-r--r--ext/standard/tests/dir/closedir_basic-win32-mb.phpt62
-rw-r--r--ext/standard/tests/dir/closedir_error-win32-mb.phpt51
-rw-r--r--ext/standard/tests/dir/closedir_variation2-win32-mb.phpt57
-rw-r--r--ext/standard/tests/dir/dir_basic-win32-mb.phpt92
-rw-r--r--ext/standard/tests/dir/dir_variation2-win32-mb.phpt229
-rw-r--r--ext/standard/tests/dir/dir_variation4-win32-mb.phpt78
-rw-r--r--ext/standard/tests/dir/getcwd_basic-win32-mb.phpt40
-rw-r--r--ext/standard/tests/dir/opendir_basic-win32-mb.phpt68
-rw-r--r--ext/standard/tests/dir/opendir_error1-win32-mb.phpt53
-rw-r--r--ext/standard/tests/dir/opendir_variation2-win32-mb.phpt245
-rw-r--r--ext/standard/tests/dir/opendir_variation3-win32-mb.phpt56
-rw-r--r--ext/standard/tests/dir/opendir_variation4-win32-mb.phpt113
-rw-r--r--ext/standard/tests/dir/opendir_variation6-win32-mb.phpt75
-rw-r--r--ext/standard/tests/dir/readdir_basic-win32-mb.phpt79
-rw-r--r--ext/standard/tests/dir/readdir_error-win32-mb.phpt49
-rw-r--r--ext/standard/tests/dir/readdir_variation2-win32-mb.phpt54
-rw-r--r--ext/standard/tests/dir/readdir_variation3-win32-mb.phpt74
-rw-r--r--ext/standard/tests/dir/readdir_variation4-win32-mb.phpt184
-rw-r--r--ext/standard/tests/dir/readdir_variation6-win32-mb.phpt86
-rw-r--r--ext/standard/tests/dir/rewinddir_basic-win32-mb.phpt102
-rw-r--r--ext/standard/tests/dir/rewinddir_error-win32-mb.phpt48
-rw-r--r--ext/standard/tests/dir/rewinddir_variation2-win32-mb.phpt51
-rw-r--r--ext/standard/tests/dir/scandir_basic-win32-mb.phpt76
-rw-r--r--ext/standard/tests/dir/scandir_error1-win32-mb.phpt53
-rw-r--r--ext/standard/tests/dir/scandir_variation10-win32-mb.phpt85
-rw-r--r--ext/standard/tests/dir/scandir_variation2-mb.phpt285
-rw-r--r--ext/standard/tests/dir/scandir_variation3-win32-mb.phpt244
-rw-r--r--ext/standard/tests/dir/scandir_variation4-win32-mb.phpt175
-rw-r--r--ext/standard/tests/dir/scandir_variation8-win32-mb.phpt160
-rw-r--r--ext/standard/tests/dir/scandir_variation9-win32-mb.phpt78
-rw-r--r--ext/standard/tests/directory/DirectoryClass_error_001-mb.phpt69
-rw-r--r--ext/standard/tests/directory/bug74589_utf8.phpt49
-rw-r--r--ext/standard/tests/file/001-win32-mb.phpt103
-rw-r--r--ext/standard/tests/file/007_variation11-win32-mb.phpt78
-rw-r--r--ext/standard/tests/file/bug38450.phpt3
-rw-r--r--ext/standard/tests/file/bug38450_1.phpt3
-rw-r--r--ext/standard/tests/file/bug38450_2.phpt3
-rw-r--r--ext/standard/tests/file/bug38450_3.phpt2
-rw-r--r--ext/standard/tests/file/bug39673.phpt6
-rw-r--r--ext/standard/tests/file/bug43353-win32.phpt2
-rw-r--r--ext/standard/tests/file/bug47517.phpt21
-rw-r--r--ext/standard/tests/file/bug52624.phpt2
-rw-r--r--ext/standard/tests/file/bug71882.phpt1
-rw-r--r--ext/standard/tests/file/chmod_basic-win32-mb.phpt545
-rw-r--r--ext/standard/tests/file/chmod_variation2-win32-mb.phpt74
-rw-r--r--ext/standard/tests/file/copy_variation2-win32-mb.phpt218
-rw-r--r--ext/standard/tests/file/dirname_no_path_normalization-win32.phpt27
-rw-r--r--ext/standard/tests/file/fflush_variation1-win32-mb.phpt531
-rw-r--r--ext/standard/tests/file/fgets_variation4-win32-mb.phpt574
-rw-r--r--ext/standard/tests/file/fgetss_basic2-win32-mb.phpt216
-rw-r--r--ext/standard/tests/file/file_get_contents_variation5_32bit.phpt236
-rw-r--r--ext/standard/tests/file/file_get_contents_variation5_64bit.phpt (renamed from ext/standard/tests/file/file_get_contents_variation5.phpt)19
-rw-r--r--ext/standard/tests/file/file_get_contents_variation7-win32-mb.phpt115
-rw-r--r--ext/standard/tests/file/file_put_contents_variation7-win32.phpt6
-rw-r--r--ext/standard/tests/file/file_put_contents_variation7.phpt6
-rw-r--r--ext/standard/tests/file/file_variation5-win32-mb.phpt74
-rw-r--r--ext/standard/tests/file/filesize_variation1-win32-mb.phpt45
-rw-r--r--ext/standard/tests/file/filesize_variation3-win32.phpt2
-rw-r--r--ext/standard/tests/file/filesize_variation3.phpt2
-rw-r--r--ext/standard/tests/file/fread_variation3-win32-mb.phpt498
-rw-r--r--ext/standard/tests/file/fseek_ftell_rewind_basic2-win32-mb.phpt464
-rw-r--r--ext/standard/tests/file/ftruncate.phptbin1077 -> 1148 bytes
-rw-r--r--ext/standard/tests/file/ftruncate_variation1-win32-mb.phpt461
-rw-r--r--ext/standard/tests/file/ftruncate_variation4-win32.phpt96
-rw-r--r--ext/standard/tests/file/ftruncate_variation4.phpt96
-rw-r--r--ext/standard/tests/file/fwrite_basic-win32-mb.phpt424
-rw-r--r--ext/standard/tests/file/fwrite_variation1-win32-mb.phpt235
-rw-r--r--ext/standard/tests/file/glob_error_002-win32-mb.phpt27
-rw-r--r--ext/standard/tests/file/glob_variation-win32-mb.phpt466
-rw-r--r--ext/standard/tests/file/is_executable_basic-win32-mb.phpt1064
-rw-r--r--ext/standard/tests/file/is_readable_basic-win32-mb.phpt1066
-rw-r--r--ext/standard/tests/file/mkdir_rmdir_variation-win32-mb.phpt1613
-rw-r--r--ext/standard/tests/file/parse_ini_file_variation6-win32-mb.phpt143
-rw-r--r--ext/standard/tests/file/popen_pclose_basic-win32-mb.phpt75
-rw-r--r--ext/standard/tests/file/readfile_variation8-win32-mb.phpt109
-rw-r--r--ext/standard/tests/file/realpath_basic-win32-mb.phpt89
-rw-r--r--ext/standard/tests/file/realpath_variation-win32-mb.phpt102
-rw-r--r--ext/standard/tests/file/stat_basic-win32-mb.phpt192
-rw-r--r--ext/standard/tests/file/stat_variation1-win32-mb.phpt94
-rw-r--r--ext/standard/tests/file/stream_rfc2397_006.phpt4
-rw-r--r--ext/standard/tests/file/tempnam_variation1-win32-mb.phpt103
-rw-r--r--ext/standard/tests/file/tempnam_variation2-win32.phpt10
-rw-r--r--ext/standard/tests/file/tempnam_variation2.phpt10
-rw-r--r--ext/standard/tests/file/tempnam_variation3-win32.phpt2
-rw-r--r--ext/standard/tests/file/tempnam_variation4-0.phpt638
-rw-r--r--ext/standard/tests/file/tempnam_variation4-1.phpt638
-rw-r--r--ext/standard/tests/file/tempnam_variation4.phpt1092
-rw-r--r--ext/standard/tests/file/tempnam_variation7-win32.phpt10
-rw-r--r--ext/standard/tests/file/tempnam_variation7.phpt10
-rw-r--r--ext/standard/tests/file/tempnam_variation8-win32.phpt6
-rw-r--r--ext/standard/tests/file/touch_basic-win32-mb.phpt96
-rw-r--r--ext/standard/tests/file/touch_variation3-win32-mb.phpt200
-rw-r--r--ext/standard/tests/file/unlink_error-win32-mb.phpt113
-rw-r--r--ext/standard/tests/file/unlink_variation1-win32-mb.phpt87
-rw-r--r--ext/standard/tests/file/unlink_variation8-win32.phpt4
-rw-r--r--ext/standard/tests/file/unlink_variation9-win32.phpt4
-rw-r--r--ext/standard/tests/file/userstreams_005.phpt2
-rw-r--r--ext/standard/tests/file/windows_links/bug73962.phpt77
-rw-r--r--ext/standard/tests/file/windows_mb_path/bug54028.phpt69
-rw-r--r--ext/standard/tests/file/windows_mb_path/bug54028_2.phpt66
-rw-r--r--ext/standard/tests/file/windows_mb_path/bug54977.phpt52
-rw-r--r--ext/standard/tests/file/windows_mb_path/bug61315.phpt63
-rw-r--r--ext/standard/tests/file/windows_mb_path/bug64506.phpt51
-rw-r--r--ext/standard/tests/file/windows_mb_path/bug64699.phpt63
-rw-r--r--ext/standard/tests/file/windows_mb_path/bug70903.phpt49
-rw-r--r--ext/standard/tests/file/windows_mb_path/bug71509.phpt44
-rw-r--r--ext/standard/tests/file/windows_mb_path/bug74923.phpt26
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_big5_0.phpt45
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_big5_1.phpt54
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_big5_2.phpt58
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_big5_to_utf8_0.phpt42
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_big5_to_utf8_1.phpt51
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_big5_to_utf8_2.phpt55
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp1250_to_utf8_0.phpt42
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp1250_to_utf8_1.phpt51
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp1250_to_utf8_2.phpt52
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp1250_to_utf8_3.phpt42
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp1250_to_utf8_4.phpt51
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp1250_to_utf8_5.phpt52
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp1251_0.phpt44
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp1251_1.phpt54
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp1251_2.phpt57
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp1251_to_utf8_0.phpt41
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp1251_to_utf8_1.phpt51
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp1251_to_utf8_2.phpt54
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp1251_zend_multibyte_0.phpt44
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp1251_zend_multibyte_1.phpt54
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp1251_zend_multibyte_2.phpt57
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp1252_0.phpt42
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp1252_to_utf8_0.phpt42
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp1252_to_utf8_1.phpt51
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp1252_to_utf8_2.phpt54
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp1252_to_utf8_3.phpt41
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp1252_to_utf8_4.phpt51
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp1252_to_utf8_5.phpt54
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp1253_0.phpt45
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp1253_1.phpt54
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp1253_2.phpt55
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp1253_to_utf8_0.phpt42
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp1253_to_utf8_1.phpt51
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp1253_to_utf8_2.phpt52
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp1254_0.phpt45
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp1254_1.phpt54
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp1254_2.phpt55
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp1254_3.phpt43
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp1254_to_utf8_0.phpt42
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp1254_to_utf8_1.phpt51
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp1254_to_utf8_2.phpt52
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp1254_to_utf8_3.phpt40
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp1255_0.phpt45
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp1255_1.phpt54
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp1255_2.phpt55
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp1255_to_utf8_0.phpt42
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp1255_to_utf8_1.phpt51
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp1255_to_utf8_2.phpt52
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp1256_0.phpt45
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp1256_1.phpt54
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp1256_2.phpt55
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp1256_to_utf8_0.phpt42
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp1256_to_utf8_1.phpt51
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp1256_to_utf8_2.phpt52
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp874_0.phpt114
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp874_1.phpt43
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp874_to_utf8_0.phpt111
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp874_to_utf8_1.phpt40
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp932_0.phpt45
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp932_1.phpt54
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp932_2.phpt55
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp932_3.phpt43
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp932_to_utf8_0.phpt42
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp932_to_utf8_1.phpt51
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp932_to_utf8_2.phpt52
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp936_0.phpt45
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp936_1.phpt54
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp936_2.phpt58
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp936_to_utf8_0.phpt42
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp936_to_utf8_1.phpt51
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cp936_to_utf8_2.phpt55
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_cwd_mb_names.phpt52
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_eucjp_to_utf8_0.phpt42
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_eucjp_to_utf8_1.phpt51
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_eucjp_to_utf8_2.phpt52
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_kartuli_utf8_0.phpt42
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_kartuli_utf8_1.phpt51
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_kartuli_utf8_2.phpt52
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_kartuli_utf8_3.phpt40
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_long_path_0.phpt47
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_long_path_1.phpt55
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_long_path_2.phpt64
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_long_path_bug30730.phpt45
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_long_path_bug70943.phpt32
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_long_path_bug71103.phpt65
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_readdir_mb_names.phpt79
-rw-r--r--ext/standard/tests/file/windows_mb_path/test_rename_mb_names.phpt74
-rw-r--r--ext/standard/tests/file/windows_mb_path/util.inc149
-rw-r--r--ext/standard/tests/file/windows_mb_path/util_utf8.inc95
-rw-r--r--ext/standard/tests/general_functions/bug41970.phpt4
-rw-r--r--ext/standard/tests/general_functions/bug44394.phpt3
-rw-r--r--ext/standard/tests/general_functions/bug44394_2.phpt4
-rw-r--r--ext/standard/tests/general_functions/bug72920.phpt15
-rw-r--r--ext/standard/tests/general_functions/escapeshellarg_basic-win32-mb.phpt31
-rw-r--r--ext/standard/tests/general_functions/floatval.phpt4
-rw-r--r--ext/standard/tests/general_functions/floatval_variation1.phpt7
-rw-r--r--ext/standard/tests/general_functions/getenv.phpt16
-rw-r--r--ext/standard/tests/general_functions/getopt_006.phpt15
-rw-r--r--ext/standard/tests/general_functions/getopt_007.phpt15
-rw-r--r--ext/standard/tests/general_functions/getopt_008.phpt15
-rw-r--r--ext/standard/tests/general_functions/getopt_009.phpt15
-rw-r--r--ext/standard/tests/general_functions/gettype_settype_variation2.phpt16
-rw-r--r--ext/standard/tests/general_functions/intval_binary_prefix.phpt119
-rw-r--r--ext/standard/tests/general_functions/is_iterable.phpt24
-rw-r--r--ext/standard/tests/general_functions/output_add_rewrite_var_basic1.phpt122
-rw-r--r--ext/standard/tests/general_functions/output_add_rewrite_var_basic2.phpt122
-rw-r--r--ext/standard/tests/general_functions/output_add_rewrite_var_basic3.phpt121
-rw-r--r--ext/standard/tests/general_functions/output_add_rewrite_var_basic4.phpt121
-rw-r--r--ext/standard/tests/general_functions/proc_open-win32-mb0.phpt55
-rw-r--r--ext/standard/tests/general_functions/proc_open-win32-mb1.phpt52
-rw-r--r--ext/standard/tests/general_functions/url_rewriter.phpt139
-rw-r--r--ext/standard/tests/http/http_response_header_01.phpt38
-rw-r--r--ext/standard/tests/http/http_response_header_02.phpt44
-rw-r--r--ext/standard/tests/http/http_response_header_03.phpt45
-rw-r--r--ext/standard/tests/image/getimagesize.phpt47
-rw-r--r--ext/standard/tests/image/image_type_to_extension.phpt22
-rw-r--r--ext/standard/tests/image/image_type_to_mime_type.phpt8
-rw-r--r--ext/standard/tests/image/image_type_to_mime_type_basic.phpt4
-rw-r--r--ext/standard/tests/image/image_type_to_mime_type_variation3.phpt3
-rw-r--r--ext/standard/tests/image/test12pix.webpbin0 -> 2494 bytes
-rw-r--r--ext/standard/tests/image/test3llpix.webpbin0 -> 38 bytes
-rw-r--r--ext/standard/tests/image/test3pix.webpbin0 -> 44 bytes
-rw-r--r--ext/standard/tests/mail/mail_log.phpt2
-rw-r--r--ext/standard/tests/math/base_convert_error.phpt2
-rw-r--r--ext/standard/tests/math/bindec_error.phpt2
-rw-r--r--ext/standard/tests/math/decbin_basic.phpt2
-rw-r--r--ext/standard/tests/math/dechex_basic.phpt2
-rw-r--r--ext/standard/tests/math/decoct_basic.phpt2
-rw-r--r--ext/standard/tests/math/hexdec_error.phpt2
-rw-r--r--ext/standard/tests/math/mt_rand_value.phpt35
-rw-r--r--ext/standard/tests/math/mt_srand_error.phpt4
-rw-r--r--ext/standard/tests/math/octdec_error.phpt2
-rw-r--r--ext/standard/tests/math/pow_variation1.phpt10
-rw-r--r--ext/standard/tests/math/pow_variation1_64bit.phpt10
-rw-r--r--ext/standard/tests/math/pow_variation2.phpt10
-rw-r--r--ext/standard/tests/math/srand_error.phpt4
-rw-r--r--ext/standard/tests/network/ip.phpt12
-rw-r--r--ext/standard/tests/network/ip_x86_64.phpt12
-rw-r--r--ext/standard/tests/network/long2ip_variation1.phpt33
-rw-r--r--ext/standard/tests/serialize/bug69425.phpt4
-rw-r--r--ext/standard/tests/serialize/bug72785.phpt24
-rw-r--r--ext/standard/tests/serialize/unserialize_error_001.phpt45
-rw-r--r--ext/standard/tests/streams/bug61115.phpt2
-rw-r--r--ext/standard/tests/streams/stream_context_tcp_nodelay.phpt25
-rw-r--r--ext/standard/tests/streams/stream_context_tcp_nodelay_fopen.phpt24
-rw-r--r--ext/standard/tests/streams/stream_context_tcp_nodelay_server.phpt47
-rw-r--r--ext/standard/tests/streams/stream_socket_enable_crypto-win32.phpt2
-rw-r--r--ext/standard/tests/strings/bug40754.phpt6
-rw-r--r--ext/standard/tests/strings/bug53021.phpt6
-rw-r--r--ext/standard/tests/strings/bug55871.phpt2
-rw-r--r--ext/standard/tests/strings/bug74041.phpt12
-rw-r--r--ext/standard/tests/strings/html_entity_decode3.phpt45
-rw-r--r--ext/standard/tests/strings/lcfirst.phptbin6906 -> 6908 bytes
-rw-r--r--ext/standard/tests/strings/parse_str_memory_error.phpt19
-rw-r--r--ext/standard/tests/strings/str_replace.phpt2
-rw-r--r--ext/standard/tests/strings/str_replace_variation3.phpt2
-rw-r--r--ext/standard/tests/strings/strcasecmp.phptbin22269 -> 22271 bytes
-rw-r--r--ext/standard/tests/strings/strcmp.phptbin20035 -> 20037 bytes
-rw-r--r--ext/standard/tests/strings/stripos_basic2.phpt20
-rw-r--r--ext/standard/tests/strings/stripos_error.phpt15
-rw-r--r--ext/standard/tests/strings/stripos_variation14.phpt6
-rw-r--r--ext/standard/tests/strings/strlen.phptbin7099 -> 7101 bytes
-rw-r--r--ext/standard/tests/strings/strpos.phptbin9999 -> 10241 bytes
-rw-r--r--ext/standard/tests/strings/strstr.phptbin10539 -> 10541 bytes
-rw-r--r--ext/standard/tests/strings/strval_error.phpt2
-rw-r--r--ext/standard/tests/strings/substr_count_basic.phpt8
-rw-r--r--ext/standard/tests/strings/substr_count_error.phpt40
-rw-r--r--ext/standard/tests/strings/ucfirst.phptbin6130 -> 6132 bytes
-rw-r--r--ext/standard/tests/strings/unpack_error.phpt6
-rw-r--r--ext/standard/tests/strings/unpack_offset.phpt17
-rw-r--r--ext/standard/tests/url/base64_decode_basic_001.phpt8
-rw-r--r--ext/standard/tests/url/base64_decode_basic_003.phpt120
-rw-r--r--ext/standard/tests/url/base64_decode_variation_001.phpt10
-rw-r--r--ext/standard/tests/url/get_headers_error_001.phpt7
-rw-r--r--ext/standard/tests/url/get_headers_error_003.phpt31
-rw-r--r--ext/standard/tests/url/parse_url_basic_001.phpt15
-rw-r--r--ext/standard/tests/url/parse_url_basic_002.phpt1
-rw-r--r--ext/standard/tests/url/parse_url_basic_003.phpt1
-rw-r--r--ext/standard/tests/url/parse_url_basic_004.phpt1
-rw-r--r--ext/standard/tests/url/parse_url_basic_005.phpt1
-rw-r--r--ext/standard/tests/url/parse_url_basic_006.phpt1
-rw-r--r--ext/standard/tests/url/parse_url_basic_007.phpt1
-rw-r--r--ext/standard/tests/url/parse_url_basic_008.phpt1
-rw-r--r--ext/standard/tests/url/parse_url_basic_009.phpt1
-rw-r--r--ext/standard/tests/url/urls.inc1
-rw-r--r--ext/standard/type.c59
-rw-r--r--ext/standard/url.c10
-rw-r--r--ext/standard/url_scanner_ex.c962
-rw-r--r--ext/standard/url_scanner_ex.h13
-rw-r--r--ext/standard/url_scanner_ex.re672
-rw-r--r--ext/standard/var.c101
-rw-r--r--ext/standard/var_unserializer.c135
-rw-r--r--ext/standard/var_unserializer.re73
-rw-r--r--ext/sysvmsg/sysvmsg.c4
-rw-r--r--ext/sysvsem/sysvsem.c16
-rw-r--r--ext/sysvshm/php_sysvshm.h2
-rw-r--r--ext/sysvshm/sysvshm.c14
-rw-r--r--ext/tidy/config.m425
-rw-r--r--ext/tidy/config.w327
-rw-r--r--ext/tidy/tests/003.phpt4
-rw-r--r--ext/tidy/tests/004.phpt6
-rw-r--r--ext/tidy/tests/005-mb.phpt18
-rw-r--r--ext/tidy/tests/005私はガラスを食べられます.html1
-rw-r--r--ext/tidy/tests/010.phpt8
-rw-r--r--ext/tidy/tests/012.phpt12
-rw-r--r--ext/tidy/tests/016.phpt21
-rw-r--r--ext/tidy/tests/017.phpt4
-rw-r--r--ext/tidy/tests/020.phpt5
-rw-r--r--ext/tidy/tests/024.phpt13
-rw-r--r--ext/tidy/tests/026.phpt4
-rw-r--r--ext/tidy/tidy.c10
-rw-r--r--ext/tokenizer/tokenizer.c18
-rw-r--r--ext/wddx/tests/bug72750.phpt4
-rw-r--r--ext/wddx/wddx.c10
-rw-r--r--ext/xml/xml.c70
-rw-r--r--ext/xmlreader/tests/003-mb.phpt84
-rw-r--r--ext/xmlrpc/xmlrpc-epi-php.c4
-rw-r--r--ext/xmlwriter/php_xmlwriter.c8
-rw-r--r--ext/xmlwriter/tests/005-mb.phpt33
-rw-r--r--ext/xsl/php_xsl.h10
-rw-r--r--ext/xsl/tests/xslt008-mb.phpt31
-rw-r--r--ext/xsl/tests/私はガラスを食べられますstreamsinclude.xsl6
-rw-r--r--ext/xsl/xsltprocessor.c11
-rw-r--r--ext/zip/php_zip.c21
-rw-r--r--ext/zip/php_zip.h17
-rw-r--r--ext/zip/tests/bug40228-mb.phpt23
-rw-r--r--ext/zip/tests/bug40228私はガラスを食べられます.zipbin0 -> 274 bytes
-rw-r--r--ext/zip/tests/bug64342_1-mb.phpt37
-rw-r--r--ext/zip/zip_stream.c5
-rw-r--r--ext/zlib/tests/004-mb.phpt66
-rw-r--r--ext/zlib/tests/004私はガラスを食べられます.txt.gzbin0 -> 150 bytes
-rw-r--r--ext/zlib/tests/gzfilegzreadfile.phpt2
-rw-r--r--ext/zlib/tests/gzreadgzwrite.phpt2
-rw-r--r--ext/zlib/tests/gzreadgzwriteplain.phpt2
-rw-r--r--ext/zlib/zlib.c14
-rw-r--r--ext/zlib/zlib_filter.c8
-rw-r--r--ext/zlib/zlib_fopen_wrapper.c3
1213 files changed, 1097146 insertions, 26115 deletions
diff --git a/ext/bcmath/libbcmath/src/bcmath.h b/ext/bcmath/libbcmath/src/bcmath.h
index 513725f5c2..28d275d32c 100644
--- a/ext/bcmath/libbcmath/src/bcmath.h
+++ b/ext/bcmath/libbcmath/src/bcmath.h
@@ -92,7 +92,7 @@ typedef struct bc_struct
/* Define the _PROTOTYPE macro if it is needed. */
#ifndef _PROTOTYPE
-#ifdef __STDC__
+#if defined(__STDC__) || defined(PHP_WIN32) && defined(__clang__)
#define _PROTOTYPE(func, args) func args
#else
#define _PROTOTYPE(func, args) func()
diff --git a/ext/bz2/bz2.c b/ext/bz2/bz2.c
index 192d079de5..ff7e28a525 100644
--- a/ext/bz2/bz2.c
+++ b/ext/bz2/bz2.c
@@ -327,7 +327,8 @@ static php_stream_wrapper_ops bzip2_stream_wops = {
NULL, /* unlink */
NULL, /* rename */
NULL, /* mkdir */
- NULL /* rmdir */
+ NULL, /* rmdir */
+ NULL
};
static php_stream_wrapper php_stream_bzip2_wrapper = {
diff --git a/ext/bz2/bz2_filter.c b/ext/bz2/bz2_filter.c
index 5454108aa0..8736a7ad0d 100644
--- a/ext/bz2/bz2_filter.c
+++ b/ext/bz2/bz2_filter.c
@@ -379,7 +379,7 @@ static php_stream_filter *php_bz2_filter_create(const char *filtername, zval *fi
/* How much memory to allocate (1 - 9) x 100kb */
zend_long blocks = zval_get_long(tmpzval);
if (blocks < 1 || blocks > 9) {
- php_error_docref(NULL, E_WARNING, "Invalid parameter given for number of blocks to allocate. (%pd)", blocks);
+ php_error_docref(NULL, E_WARNING, "Invalid parameter given for number of blocks to allocate. (" ZEND_LONG_FMT ")", blocks);
} else {
blockSize100k = (int) blocks;
}
@@ -389,7 +389,7 @@ static php_stream_filter *php_bz2_filter_create(const char *filtername, zval *fi
/* Work Factor (0 - 250) */
zend_long work = zval_get_long(tmpzval);
if (work < 0 || work > 250) {
- php_error_docref(NULL, E_WARNING, "Invalid parameter given for work factor. (%pd)", work);
+ php_error_docref(NULL, E_WARNING, "Invalid parameter given for work factor. (" ZEND_LONG_FMT ")", work);
} else {
workFactor = (int) work;
}
diff --git a/ext/bz2/tests/003-mb.phpt b/ext/bz2/tests/003-mb.phpt
new file mode 100644
index 0000000000..fbc683cb72
--- /dev/null
+++ b/ext/bz2/tests/003-mb.phpt
@@ -0,0 +1,40 @@
+--TEST--
+bzread() tests
+--SKIPIF--
+<?php if (!extension_loaded("bz2")) print "skip"; ?>
+--FILE--
+<?php
+
+$fd = bzopen(dirname(__FILE__)."/003私はガラスを食べられます.txt.bz2","r");
+var_dump(bzread());
+var_dump(bzread($fd, 1 ,0));
+var_dump(bzread($fd, 0));
+var_dump(bzread($fd, -10));
+var_dump(bzread($fd, 1));
+var_dump(bzread($fd, 2));
+var_dump(bzread($fd, 100000));
+
+echo "Done\n";
+?>
+--EXPECTF--
+Warning: bzread() expects at least 1 parameter, 0 given in %s on line %d
+bool(false)
+
+Warning: bzread() expects at most 2 parameters, 3 given in %s on line %d
+bool(false)
+string(0) ""
+
+Warning: bzread(): length may not be negative in %s on line %d
+bool(false)
+string(1) "R"
+string(2) "is"
+string(251) "ing up from the heart of the desert
+Rising up for Jerusalem
+Rising up from the heat of the desert
+Building up Old Jerusalem
+Rising up from the heart of the desert
+Rising up for Jerusalem
+Rising up from the heat of the desert
+Heading out for Jerusalem
+"
+Done
diff --git a/ext/bz2/tests/003私はガラスを食べられます.txt.bz2 b/ext/bz2/tests/003私はガラスを食べられます.txt.bz2
new file mode 100644
index 0000000000..034cd4d8b7
--- /dev/null
+++ b/ext/bz2/tests/003私はガラスを食べられます.txt.bz2
Binary files differ
diff --git a/ext/calendar/calendar.c b/ext/calendar/calendar.c
index 7ff99880de..c00507d86a 100644
--- a/ext/calendar/calendar.c
+++ b/ext/calendar/calendar.c
@@ -24,10 +24,6 @@
#include "config.h"
#endif
-#ifdef PHP_WIN32
-#define _WINNLS_
-#endif
-
#include "php.h"
#include "ext/standard/info.h"
#include "php_calendar.h"
@@ -35,6 +31,12 @@
#include <stdio.h>
+#ifdef PHP_WIN32
+/* This conflicts with a define in winnls.h, but that header is needed
+ to have GetACP(). */
+#undef CAL_GREGORIAN
+#endif
+
/* {{{ arginfo */
ZEND_BEGIN_ARG_INFO_EX(arginfo_unixtojd, 0, 0, 0)
ZEND_ARG_INFO(0, timestamp)
@@ -311,7 +313,7 @@ PHP_FUNCTION(cal_info)
if (cal != -1 && (cal < 0 || cal >= CAL_NUM_CALS)) {
- php_error_docref(NULL, E_WARNING, "invalid calendar ID %pd.", cal);
+ php_error_docref(NULL, E_WARNING, "invalid calendar ID " ZEND_LONG_FMT ".", cal);
RETURN_FALSE;
}
@@ -333,7 +335,7 @@ PHP_FUNCTION(cal_days_in_month)
}
if (cal < 0 || cal >= CAL_NUM_CALS) {
- php_error_docref(NULL, E_WARNING, "invalid calendar ID %pd.", cal);
+ php_error_docref(NULL, E_WARNING, "invalid calendar ID " ZEND_LONG_FMT ".", cal);
RETURN_FALSE;
}
@@ -379,7 +381,7 @@ PHP_FUNCTION(cal_to_jd)
}
if (cal < 0 || cal >= CAL_NUM_CALS) {
- php_error_docref(NULL, E_WARNING, "invalid calendar ID %pd.", cal);
+ php_error_docref(NULL, E_WARNING, "invalid calendar ID " ZEND_LONG_FMT ".", cal);
RETURN_FALSE;
}
@@ -401,7 +403,7 @@ PHP_FUNCTION(cal_from_jd)
}
if (cal < 0 || cal >= CAL_NUM_CALS) {
- php_error_docref(NULL, E_WARNING, "invalid calendar ID %pd", cal);
+ php_error_docref(NULL, E_WARNING, "invalid calendar ID " ZEND_LONG_FMT "", cal);
RETURN_FALSE;
}
calendar = &cal_conversion_table[cal];
diff --git a/ext/com_dotnet/com_com.c b/ext/com_dotnet/com_com.c
index 6bfd8ad6db..4b93a47dd3 100644
--- a/ext/com_dotnet/com_com.c
+++ b/ext/com_dotnet/com_com.c
@@ -53,6 +53,7 @@ PHP_FUNCTION(com_create_instance)
&authid, EOAC_NONE
};
zend_long cp = GetACP();
+ const struct php_win32_cp *cp_it;
php_com_initialize();
obj = CDNO_FETCH(object);
@@ -70,7 +71,8 @@ PHP_FUNCTION(com_create_instance)
return;
}
- if (Z_L(0) > cp || ZEND_LONG_INT_OVFL(cp)) {
+ cp_it = php_win32_cp_get_by_id((DWORD)cp);
+ if (!cp_it) {
php_com_throw_exception(E_INVALIDARG, "Could not create COM object - invalid codepage!");
return;
}
diff --git a/ext/com_dotnet/com_dotnet.c b/ext/com_dotnet/com_dotnet.c
index cedacd60c4..f062b0a39a 100644
--- a/ext/com_dotnet/com_dotnet.c
+++ b/ext/com_dotnet/com_dotnet.c
@@ -197,6 +197,7 @@ PHP_FUNCTION(com_dotnet_create_instance)
char *where = "";
IUnknown *unk = NULL;
zend_long cp = GetACP();
+ const struct php_win32_cp *cp_it;
php_com_initialize();
stuff = (struct dotnet_runtime_stuff*)COMG(dotnet_runtime_stuff);
@@ -251,11 +252,12 @@ PHP_FUNCTION(com_dotnet_create_instance)
return;
}
- if (Z_L(0) > cp || ZEND_LONG_INT_OVFL(cp)) {
+ cp_it = php_win32_cp_get_by_id((DWORD)cp);
+ if (!cp_it) {
php_com_throw_exception(E_INVALIDARG, "Could not create .Net object - invalid codepage!");
return;
}
- obj->code_page = (int)cp;
+ obj->code_page = (int)cp_it->id;
oletype = php_com_string_to_olestring(datatype_name, datatype_name_len, obj->code_page);
oleassembly = php_com_string_to_olestring(assembly_name, assembly_name_len, obj->code_page);
diff --git a/ext/curl/config.w32 b/ext/curl/config.w32
index 8ea5f6524c..d900d48c41 100644
--- a/ext/curl/config.w32
+++ b/ext/curl/config.w32
@@ -5,12 +5,11 @@ ARG_WITH("curl", "cURL support", "no");
if (PHP_CURL != "no") {
if (CHECK_LIB("libcurl_a.lib;libcurl.lib", "curl", PHP_CURL) &&
- CHECK_HEADER_ADD_INCLUDE("curl/easy.h", "CFLAGS_CURL") &&
- CHECK_LIB("ssleay32.lib", "curl", PHP_CURL) &&
- CHECK_LIB("libeay32.lib", "curl", PHP_CURL)
- && CHECK_LIB("winmm.lib", "curl", PHP_CURL)
- && CHECK_LIB("wldap32.lib", "curl", PHP_CURL)
- && (((PHP_ZLIB=="no") && (CHECK_LIB("zlib_a.lib;zlib.lib", "curl", PHP_CURL))) ||
+ CHECK_HEADER_ADD_INCLUDE("curl/easy.h", "CFLAGS_CURL") &&
+ SETUP_OPENSSL("curl", PHP_CURL) > 0 &&
+ CHECK_LIB("winmm.lib", "curl", PHP_CURL) &&
+ CHECK_LIB("wldap32.lib", "curl", PHP_CURL) &&
+ (((PHP_ZLIB=="no") && (CHECK_LIB("zlib_a.lib;zlib.lib", "curl", PHP_CURL))) ||
(PHP_ZLIB_SHARED && CHECK_LIB("zlib.lib", "curl", PHP_CURL)) || (PHP_ZLIB == "yes" && (!PHP_ZLIB_SHARED)))
) {
EXTENSION("curl", "interface.c multi.c share.c curl_file.c");
diff --git a/ext/curl/interface.c b/ext/curl/interface.c
index e81968d9b7..c88bbaef93 100644
--- a/ext/curl/interface.c
+++ b/ext/curl/interface.c
@@ -167,7 +167,7 @@ static void _php_curl_close(zend_resource *rsrc);
# define php_curl_ret(__ret) RETVAL_FALSE; return;
#endif
-static int php_curl_option_str(php_curl *ch, zend_long option, const char *str, const int len, zend_bool make_copy)
+static int php_curl_option_str(php_curl *ch, zend_long option, const char *str, const size_t len, zend_bool make_copy)
{
CURLcode error = CURLE_OK;
@@ -196,7 +196,7 @@ static int php_curl_option_str(php_curl *ch, zend_long option, const char *str,
return error == CURLE_OK ? SUCCESS : FAILURE;
}
-static int php_curl_option_url(php_curl *ch, const char *url, const int len) /* {{{ */
+static int php_curl_option_url(php_curl *ch, const char *url, const size_t len) /* {{{ */
{
/* Disable file:// if open_basedir are used */
if (PG(open_basedir) && *PG(open_basedir)) {
@@ -392,6 +392,10 @@ ZEND_BEGIN_ARG_INFO(arginfo_curl_multi_close, 0)
ZEND_ARG_INFO(0, mh)
ZEND_END_ARG_INFO()
+ZEND_BEGIN_ARG_INFO(arginfo_curl_multi_errno, 0)
+ ZEND_ARG_INFO(0, mh)
+ZEND_END_ARG_INFO()
+
#if LIBCURL_VERSION_NUM >= 0x070c00 /* Available since 7.12.0 */
ZEND_BEGIN_ARG_INFO(arginfo_curl_strerror, 0)
ZEND_ARG_INFO(0, errornum)
@@ -400,6 +404,10 @@ ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(arginfo_curl_multi_strerror, 0)
ZEND_ARG_INFO(0, errornum)
ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_curl_share_strerror, 0)
+ ZEND_ARG_INFO(0, errornum)
+ZEND_END_ARG_INFO()
#endif
ZEND_BEGIN_ARG_INFO(arginfo_curl_share_init, 0)
@@ -415,6 +423,10 @@ ZEND_BEGIN_ARG_INFO(arginfo_curl_share_setopt, 0)
ZEND_ARG_INFO(0, value)
ZEND_END_ARG_INFO()
+ZEND_BEGIN_ARG_INFO(arginfo_curl_share_errno, 0)
+ ZEND_ARG_INFO(0, sh)
+ZEND_END_ARG_INFO()
+
#if LIBCURL_VERSION_NUM >= 0x071200 /* Available since 7.18.0 */
ZEND_BEGIN_ARG_INFO(arginfo_curl_pause, 0)
ZEND_ARG_INFO(0, ch)
@@ -445,6 +457,7 @@ const zend_function_entry curl_functions[] = {
#if LIBCURL_VERSION_NUM >= 0x070c00 /* 7.12.0 */
PHP_FE(curl_strerror, arginfo_curl_strerror)
PHP_FE(curl_multi_strerror, arginfo_curl_multi_strerror)
+ PHP_FE(curl_share_strerror, arginfo_curl_share_strerror)
#endif
#if LIBCURL_VERSION_NUM >= 0x070c01 /* 7.12.1 */
PHP_FE(curl_reset, arginfo_curl_reset)
@@ -464,12 +477,14 @@ const zend_function_entry curl_functions[] = {
PHP_FE(curl_multi_getcontent, arginfo_curl_multi_getcontent)
PHP_FE(curl_multi_info_read, arginfo_curl_multi_info_read)
PHP_FE(curl_multi_close, arginfo_curl_multi_close)
+ PHP_FE(curl_multi_errno, arginfo_curl_multi_errno)
#if LIBCURL_VERSION_NUM >= 0x070f04 /* 7.15.4 */
PHP_FE(curl_multi_setopt, arginfo_curl_multi_setopt)
#endif
PHP_FE(curl_share_init, arginfo_curl_share_init)
PHP_FE(curl_share_close, arginfo_curl_share_close)
PHP_FE(curl_share_setopt, arginfo_curl_share_setopt)
+ PHP_FE(curl_share_errno, arginfo_curl_share_errno)
PHP_FE(curl_file_create, arginfo_curlfile_create)
PHP_FE_END
};
@@ -1325,6 +1340,9 @@ PHP_MINIT_FUNCTION(curl)
#if LIBCURL_VERSION_NUM >= 0x072e00 /* Available since 7.46.0 */
REGISTER_CURL_CONSTANT(CURLOPT_STREAM_WEIGHT);
+ REGISTER_CURL_CONSTANT(CURLMOPT_PUSHFUNCTION);
+ REGISTER_CURL_CONSTANT(CURL_PUSH_OK);
+ REGISTER_CURL_CONSTANT(CURL_PUSH_DENY);
#endif
#if LIBCURL_VERSION_NUM >= 0x072f00 /* Available since 7.47.0 */
@@ -1452,14 +1470,12 @@ static size_t curl_write(char *data, size_t size, size_t nmemb, void *ctx)
ZVAL_STRINGL(&argv[1], data, length);
fci.size = sizeof(fci);
- fci.function_table = EG(function_table);
fci.object = NULL;
ZVAL_COPY_VALUE(&fci.function_name, &t->func_name);
fci.retval = &retval;
fci.param_count = 2;
fci.params = argv;
fci.no_separation = 0;
- fci.symbol_table = NULL;
ch->in_callback = 1;
error = zend_call_function(&fci, &t->fci_cache);
@@ -1503,14 +1519,12 @@ static int curl_fnmatch(void *ctx, const char *pattern, const char *string)
ZVAL_STRING(&argv[2], string);
fci.size = sizeof(fci);
- fci.function_table = EG(function_table);
ZVAL_COPY_VALUE(&fci.function_name, &t->func_name);
fci.object = NULL;
fci.retval = &retval;
fci.param_count = 3;
fci.params = argv;
fci.no_separation = 0;
- fci.symbol_table = NULL;
ch->in_callback = 1;
error = zend_call_function(&fci, &t->fci_cache);
@@ -1560,14 +1574,12 @@ static size_t curl_progress(void *clientp, double dltotal, double dlnow, double
ZVAL_LONG(&argv[4], (zend_long)ulnow);
fci.size = sizeof(fci);
- fci.function_table = EG(function_table);
ZVAL_COPY_VALUE(&fci.function_name, &t->func_name);
fci.object = NULL;
fci.retval = &retval;
fci.param_count = 5;
fci.params = argv;
fci.no_separation = 0;
- fci.symbol_table = NULL;
ch->in_callback = 1;
error = zend_call_function(&fci, &t->fci_cache);
@@ -1623,14 +1635,12 @@ static size_t curl_read(char *data, size_t size, size_t nmemb, void *ctx)
ZVAL_LONG(&argv[2], (int)size * nmemb);
fci.size = sizeof(fci);
- fci.function_table = EG(function_table);
ZVAL_COPY_VALUE(&fci.function_name, &t->func_name);
fci.object = NULL;
fci.retval = &retval;
fci.param_count = 3;
fci.params = argv;
fci.no_separation = 0;
- fci.symbol_table = NULL;
ch->in_callback = 1;
error = zend_call_function(&fci, &t->fci_cache);
@@ -1691,9 +1701,7 @@ static size_t curl_write_header(char *data, size_t size, size_t nmemb, void *ctx
ZVAL_STRINGL(&argv[1], data, length);
fci.size = sizeof(fci);
- fci.function_table = EG(function_table);
ZVAL_COPY_VALUE(&fci.function_name, &t->func_name);
- fci.symbol_table = NULL;
fci.object = NULL;
fci.retval = &retval;
fci.param_count = 2;
@@ -1851,7 +1859,7 @@ PHP_FUNCTION(curl_version)
/* {{{ alloc_curl_handle
*/
-static php_curl *alloc_curl_handle()
+php_curl *alloc_curl_handle()
{
php_curl *ch = ecalloc(1, sizeof(php_curl));
ch->to_free = ecalloc(1, sizeof(struct _php_curl_free));
@@ -1990,6 +1998,79 @@ PHP_FUNCTION(curl_init)
}
/* }}} */
+void _php_setup_easy_copy_handlers(php_curl *ch, php_curl *source)
+{
+ if (!Z_ISUNDEF(source->handlers->write->stream)) {
+ Z_ADDREF(source->handlers->write->stream);
+ }
+ ch->handlers->write->stream = source->handlers->write->stream;
+ ch->handlers->write->method = source->handlers->write->method;
+ if (!Z_ISUNDEF(source->handlers->read->stream)) {
+ Z_ADDREF(source->handlers->read->stream);
+ }
+ ch->handlers->read->stream = source->handlers->read->stream;
+ ch->handlers->read->method = source->handlers->read->method;
+ ch->handlers->write_header->method = source->handlers->write_header->method;
+ if (!Z_ISUNDEF(source->handlers->write_header->stream)) {
+ Z_ADDREF(source->handlers->write_header->stream);
+ }
+ ch->handlers->write_header->stream = source->handlers->write_header->stream;
+
+ ch->handlers->write->fp = source->handlers->write->fp;
+ ch->handlers->write_header->fp = source->handlers->write_header->fp;
+ ch->handlers->read->fp = source->handlers->read->fp;
+ ch->handlers->read->res = source->handlers->read->res;
+#if CURLOPT_PASSWDDATA != 0
+ if (!Z_ISUNDEF(source->handlers->passwd)) {
+ ZVAL_COPY(&ch->handlers->passwd, &source->handlers->passwd);
+ curl_easy_setopt(source->cp, CURLOPT_PASSWDDATA, (void *) ch);
+ }
+#endif
+ if (!Z_ISUNDEF(source->handlers->write->func_name)) {
+ ZVAL_COPY(&ch->handlers->write->func_name, &source->handlers->write->func_name);
+ }
+ if (!Z_ISUNDEF(source->handlers->read->func_name)) {
+ ZVAL_COPY(&ch->handlers->read->func_name, &source->handlers->read->func_name);
+ }
+ if (!Z_ISUNDEF(source->handlers->write_header->func_name)) {
+ ZVAL_COPY(&ch->handlers->write_header->func_name, &source->handlers->write_header->func_name);
+ }
+
+ curl_easy_setopt(ch->cp, CURLOPT_ERRORBUFFER, ch->err.str);
+ curl_easy_setopt(ch->cp, CURLOPT_FILE, (void *) ch);
+ curl_easy_setopt(ch->cp, CURLOPT_INFILE, (void *) ch);
+ curl_easy_setopt(ch->cp, CURLOPT_WRITEHEADER, (void *) ch);
+
+ if (source->handlers->progress) {
+ ch->handlers->progress = ecalloc(1, sizeof(php_curl_progress));
+ if (!Z_ISUNDEF(source->handlers->progress->func_name)) {
+ ZVAL_COPY(&ch->handlers->progress->func_name, &source->handlers->progress->func_name);
+ }
+ ch->handlers->progress->method = source->handlers->progress->method;
+ curl_easy_setopt(ch->cp, CURLOPT_PROGRESSDATA, (void *) ch);
+ }
+
+#if LIBCURL_VERSION_NUM >= 0x071500
+ if (source->handlers->fnmatch) {
+ ch->handlers->fnmatch = ecalloc(1, sizeof(php_curl_fnmatch));
+ if (!Z_ISUNDEF(source->handlers->fnmatch->func_name)) {
+ ZVAL_COPY(&ch->handlers->fnmatch->func_name, &source->handlers->fnmatch->func_name);
+ }
+ ch->handlers->fnmatch->method = source->handlers->fnmatch->method;
+ curl_easy_setopt(ch->cp, CURLOPT_FNMATCH_DATA, (void *) ch);
+ }
+#endif
+
+ efree(ch->to_free->slist);
+ efree(ch->to_free);
+ ch->to_free = source->to_free;
+ efree(ch->clone);
+ ch->clone = source->clone;
+
+ /* Keep track of cloned copies to avoid invoking curl destructors for every clone */
+ (*source->clone)++;
+}
+
/* {{{ proto resource curl_copy_handle(resource ch)
Copy a cURL handle along with all of it's preferences */
PHP_FUNCTION(curl_copy_handle)
@@ -2013,79 +2094,11 @@ PHP_FUNCTION(curl_copy_handle)
}
dupch = alloc_curl_handle();
-
dupch->cp = cp;
- Z_ADDREF_P(zid);
- if (!Z_ISUNDEF(ch->handlers->write->stream)) {
- Z_ADDREF(ch->handlers->write->stream);
- }
- dupch->handlers->write->stream = ch->handlers->write->stream;
- dupch->handlers->write->method = ch->handlers->write->method;
- if (!Z_ISUNDEF(ch->handlers->read->stream)) {
- Z_ADDREF(ch->handlers->read->stream);
- }
- dupch->handlers->read->stream = ch->handlers->read->stream;
- dupch->handlers->read->method = ch->handlers->read->method;
- dupch->handlers->write_header->method = ch->handlers->write_header->method;
- if (!Z_ISUNDEF(ch->handlers->write_header->stream)) {
- Z_ADDREF(ch->handlers->write_header->stream);
- }
- dupch->handlers->write_header->stream = ch->handlers->write_header->stream;
-
- dupch->handlers->write->fp = ch->handlers->write->fp;
- dupch->handlers->write_header->fp = ch->handlers->write_header->fp;
- dupch->handlers->read->fp = ch->handlers->read->fp;
- dupch->handlers->read->res = ch->handlers->read->res;
-#if CURLOPT_PASSWDDATA != 0
- if (!Z_ISUNDEF(ch->handlers->passwd)) {
- ZVAL_COPY(&dupch->handlers->passwd, &ch->handlers->passwd);
- curl_easy_setopt(ch->cp, CURLOPT_PASSWDDATA, (void *) dupch);
- }
-#endif
- if (!Z_ISUNDEF(ch->handlers->write->func_name)) {
- ZVAL_COPY(&dupch->handlers->write->func_name, &ch->handlers->write->func_name);
- }
- if (!Z_ISUNDEF(ch->handlers->read->func_name)) {
- ZVAL_COPY(&dupch->handlers->read->func_name, &ch->handlers->read->func_name);
- }
- if (!Z_ISUNDEF(ch->handlers->write_header->func_name)) {
- ZVAL_COPY(&dupch->handlers->write_header->func_name, &ch->handlers->write_header->func_name);
- }
-
- curl_easy_setopt(dupch->cp, CURLOPT_ERRORBUFFER, dupch->err.str);
- curl_easy_setopt(dupch->cp, CURLOPT_FILE, (void *) dupch);
- curl_easy_setopt(dupch->cp, CURLOPT_INFILE, (void *) dupch);
- curl_easy_setopt(dupch->cp, CURLOPT_WRITEHEADER, (void *) dupch);
-
- if (ch->handlers->progress) {
- dupch->handlers->progress = ecalloc(1, sizeof(php_curl_progress));
- if (!Z_ISUNDEF(ch->handlers->progress->func_name)) {
- ZVAL_COPY(&dupch->handlers->progress->func_name, &ch->handlers->progress->func_name);
- }
- dupch->handlers->progress->method = ch->handlers->progress->method;
- curl_easy_setopt(dupch->cp, CURLOPT_PROGRESSDATA, (void *) dupch);
- }
-
-/* Available since 7.21.0 */
-#if LIBCURL_VERSION_NUM >= 0x071500
- if (ch->handlers->fnmatch) {
- dupch->handlers->fnmatch = ecalloc(1, sizeof(php_curl_fnmatch));
- if (!Z_ISUNDEF(ch->handlers->fnmatch->func_name)) {
- ZVAL_COPY(&dupch->handlers->fnmatch->func_name, &ch->handlers->fnmatch->func_name);
- }
- dupch->handlers->fnmatch->method = ch->handlers->fnmatch->method;
- curl_easy_setopt(dupch->cp, CURLOPT_FNMATCH_DATA, (void *) dupch);
- }
-#endif
- efree(dupch->to_free->slist);
- efree(dupch->to_free);
- dupch->to_free = ch->to_free;
- efree(dupch->clone);
- dupch->clone = ch->clone;
+ _php_setup_easy_copy_handlers(dupch, ch);
- /* Keep track of cloned copies to avoid invoking curl destructors for every clone */
- (*ch->clone)++;
+ Z_ADDREF_P(zid);
ZVAL_RES(return_value, zend_register_resource(dupch, le_curl));
dupch->res = Z_RES_P(return_value);
@@ -3324,9 +3337,7 @@ PHP_FUNCTION(curl_close)
return;
}
- if (Z_REFCOUNT_P(zid) <= 2) {
- zend_list_close(Z_RES_P(zid));
- }
+ zend_list_close(Z_RES_P(zid));
}
/* }}} */
@@ -3351,10 +3362,12 @@ static void _php_curl_close_ex(php_curl *ch)
*
* Libcurl commit d021f2e8a00 fix this issue and should be part of 7.28.2
*/
- curl_easy_setopt(ch->cp, CURLOPT_HEADERFUNCTION, curl_write_nothing);
- curl_easy_setopt(ch->cp, CURLOPT_WRITEFUNCTION, curl_write_nothing);
+ if (ch->cp != NULL) {
+ curl_easy_setopt(ch->cp, CURLOPT_HEADERFUNCTION, curl_write_nothing);
+ curl_easy_setopt(ch->cp, CURLOPT_WRITEFUNCTION, curl_write_nothing);
- curl_easy_cleanup(ch->cp);
+ curl_easy_cleanup(ch->cp);
+ }
/* cURL destructors should be invoked only by last curl handle */
if (--(*ch->clone) == 0) {
diff --git a/ext/curl/multi.c b/ext/curl/multi.c
index 4c56d5a458..488cfde224 100644
--- a/ext/curl/multi.c
+++ b/ext/curl/multi.c
@@ -49,6 +49,8 @@
#include <unistd.h>
#endif
+#define SAVE_CURLM_ERROR(__handle, __err) (__handle)->err.no = (int) __err;
+
/* {{{ proto resource curl_multi_init(void)
Returns a new cURL multi handle */
PHP_FUNCTION(curl_multi_init)
@@ -61,6 +63,7 @@ PHP_FUNCTION(curl_multi_init)
mh = ecalloc(1, sizeof(php_curlm));
mh->multi = curl_multi_init();
+ mh->handlers = ecalloc(1, sizeof(php_curlm_handlers));
zend_llist_init(&mh->easyh, sizeof(zval), _php_curl_multi_cleanup_list, 0);
@@ -77,6 +80,7 @@ PHP_FUNCTION(curl_multi_add_handle)
php_curlm *mh;
php_curl *ch;
zval tmp_val;
+ CURLMcode error = CURLM_OK;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "rr", &z_mh, &z_ch) == FAILURE) {
return;
@@ -97,7 +101,10 @@ PHP_FUNCTION(curl_multi_add_handle)
zend_llist_add_element(&mh->easyh, &tmp_val);
- RETURN_LONG((zend_long)curl_multi_add_handle(mh->multi, ch->cp));
+ error = curl_multi_add_handle(mh->multi, ch->cp);
+ SAVE_CURLM_ERROR(mh, error);
+
+ RETURN_LONG((zend_long) error);
}
/* }}} */
@@ -129,6 +136,29 @@ static int curl_compare_resources( zval *z1, zval *z2 ) /* {{{ */
}
/* }}} */
+/* Used to find the php_curl resource for a given curl easy handle */
+static zval *_php_curl_multi_find_easy_handle(php_curlm *mh, CURL *easy) /* {{{ */
+{
+ php_curl *tmp_ch;
+ zend_llist_position pos;
+ zval *pz_ch_temp;
+
+ for(pz_ch_temp = (zval *)zend_llist_get_first_ex(&mh->easyh, &pos); pz_ch_temp;
+ pz_ch_temp = (zval *)zend_llist_get_next_ex(&mh->easyh, &pos)) {
+
+ if ((tmp_ch = (php_curl *)zend_fetch_resource(Z_RES_P(pz_ch_temp), le_curl_name, le_curl)) == NULL) {
+ return NULL;
+ }
+
+ if (tmp_ch->cp == easy) {
+ return pz_ch_temp;
+ }
+ }
+
+ return NULL;
+}
+/* }}} */
+
/* {{{ proto int curl_multi_remove_handle(resource mh, resource ch)
Remove a multi handle from a set of cURL handles */
PHP_FUNCTION(curl_multi_remove_handle)
@@ -137,6 +167,7 @@ PHP_FUNCTION(curl_multi_remove_handle)
zval *z_ch;
php_curlm *mh;
php_curl *ch;
+ CURLMcode error = CURLM_OK;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "rr", &z_mh, &z_ch) == FAILURE) {
return;
@@ -150,7 +181,10 @@ PHP_FUNCTION(curl_multi_remove_handle)
RETURN_FALSE;
}
- RETVAL_LONG((zend_long)curl_multi_remove_handle(mh->multi, ch->cp));
+ error = curl_multi_remove_handle(mh->multi, ch->cp);
+ SAVE_CURLM_ERROR(mh, error);
+
+ RETVAL_LONG((zend_long) error);
zend_llist_del_element(&mh->easyh, z_ch, (int (*)(void *, void *))curl_compare_resources);
}
@@ -178,6 +212,7 @@ PHP_FUNCTION(curl_multi_select)
int maxfd;
double timeout = 1.0;
struct timeval to;
+ CURLMcode error = CURLM_OK;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|d", &z_mh, &timeout) == FAILURE) {
return;
@@ -193,7 +228,9 @@ PHP_FUNCTION(curl_multi_select)
FD_ZERO(&writefds);
FD_ZERO(&exceptfds);
- curl_multi_fdset(mh->multi, &readfds, &writefds, &exceptfds, &maxfd);
+ error = curl_multi_fdset(mh->multi, &readfds, &writefds, &exceptfds, &maxfd);
+ SAVE_CURLM_ERROR(mh, error);
+
if (maxfd == -1) {
RETURN_LONG(-1);
}
@@ -209,7 +246,7 @@ PHP_FUNCTION(curl_multi_exec)
zval *z_still_running;
php_curlm *mh;
int still_running;
- int result;
+ CURLMcode error = CURLM_OK;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "rz/", &z_mh, &z_still_running) == FAILURE) {
return;
@@ -237,10 +274,11 @@ PHP_FUNCTION(curl_multi_exec)
convert_to_long(z_still_running);
still_running = Z_LVAL_P(z_still_running);
- result = curl_multi_perform(mh->multi, &still_running);
+ error = curl_multi_perform(mh->multi, &still_running);
ZVAL_LONG(z_still_running, still_running);
- RETURN_LONG(result);
+ SAVE_CURLM_ERROR(mh, error);
+ RETURN_LONG((zend_long) error);
}
/* }}} */
@@ -304,35 +342,21 @@ PHP_FUNCTION(curl_multi_info_read)
/* find the original easy curl handle */
{
- zend_llist_position pos;
- php_curl *ch;
- zval *pz_ch;
-
- /* search the list of easy handles hanging off the multi-handle */
- for(pz_ch = (zval *)zend_llist_get_first_ex(&mh->easyh, &pos); pz_ch;
- pz_ch = (zval *)zend_llist_get_next_ex(&mh->easyh, &pos)) {
-
- if ((ch = (php_curl *)zend_fetch_resource(Z_RES_P(pz_ch), le_curl_name, le_curl)) == NULL) {
- RETURN_FALSE;
- }
- if (ch->cp == tmp_msg->easy_handle) {
-
- /* we are adding a reference to the underlying php_curl
- resource, so we need to add one to the resource's refcount
- in order to ensure it doesn't get destroyed when the
- underlying curl easy handle goes out of scope.
- Normally you would call zval_copy_ctor( pz_ch ), or
- SEPARATE_ZVAL, but those create new zvals, which is already
- being done in add_assoc_resource */
- Z_ADDREF_P(pz_ch);
-
- /* add_assoc_resource automatically creates a new zval to
- wrap the "resource" represented by the current pz_ch */
-
- add_assoc_zval(return_value, "handle", pz_ch);
-
- break;
- }
+ zval *pz_ch = _php_curl_multi_find_easy_handle(mh, tmp_msg->easy_handle);
+ if (pz_ch != NULL) {
+ /* we are adding a reference to the underlying php_curl
+ resource, so we need to add one to the resource's refcount
+ in order to ensure it doesn't get destroyed when the
+ underlying curl easy handle goes out of scope.
+ Normally you would call zval_copy_ctor( pz_ch ), or
+ SEPARATE_ZVAL, but those create new zvals, which is already
+ being done in add_assoc_resource */
+ Z_ADDREF_P(pz_ch);
+
+ /* add_assoc_resource automatically creates a new zval to
+ wrap the "resource" represented by the current pz_ch */
+
+ add_assoc_zval(return_value, "handle", pz_ch);
}
}
}
@@ -377,12 +401,37 @@ void _php_curl_multi_close(zend_resource *rsrc) /* {{{ */
curl_multi_cleanup(mh->multi);
zend_llist_clean(&mh->easyh);
+ if (mh->handlers->server_push) {
+ efree(mh->handlers->server_push);
+ }
+ if (mh->handlers) {
+ efree(mh->handlers);
+ }
efree(mh);
rsrc->ptr = NULL;
}
}
/* }}} */
+/* {{{ proto int curl_multi_errno(resource mh)
+ Return an integer containing the last multi curl error number */
+PHP_FUNCTION(curl_multi_errno)
+{
+ zval *z_mh;
+ php_curlm *mh;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &z_mh) == FAILURE) {
+ return;
+ }
+
+ if ((mh = (php_curlm *)zend_fetch_resource(Z_RES_P(z_mh), le_curl_multi_handle_name, le_curl_multi_handle)) == NULL) {
+ RETURN_FALSE;
+ }
+
+ RETURN_LONG(mh->err.no);
+}
+/* }}} */
+
#if LIBCURL_VERSION_NUM >= 0x070c00 /* Available since 7.12.0 */
/* {{{ proto bool curl_multi_strerror(int code)
return string describing error code */
@@ -405,6 +454,83 @@ PHP_FUNCTION(curl_multi_strerror)
/* }}} */
#endif
+#if LIBCURL_VERSION_NUM >= 0x072C00 /* Available since 7.44.0 */
+
+static int _php_server_push_callback(CURL *parent_ch, CURL *easy, size_t num_headers, struct curl_pushheaders *push_headers, void *userp) /* {{{ */
+{
+ php_curl *ch;
+ php_curl *parent;
+ php_curlm *mh = (php_curlm *)userp;
+ size_t rval = CURL_PUSH_DENY;
+ php_curlm_server_push *t = mh->handlers->server_push;
+ zval *pz_parent_ch = NULL;
+ zval pz_ch;
+ zval headers;
+ zval retval;
+ zend_resource *res;
+ char *header;
+ int error;
+ zend_fcall_info fci = empty_fcall_info;
+
+ pz_parent_ch = _php_curl_multi_find_easy_handle(mh, parent_ch);
+ if (pz_parent_ch == NULL) {
+ return rval;
+ }
+
+ parent = (php_curl*)zend_fetch_resource(Z_RES_P(pz_parent_ch), le_curl_name, le_curl);
+
+ ch = alloc_curl_handle();
+ ch->cp = easy;
+ _php_setup_easy_copy_handlers(ch, parent);
+
+ Z_ADDREF_P(pz_parent_ch);
+
+ res = zend_register_resource(ch, le_curl);
+ ZVAL_RES(&pz_ch, res);
+
+ size_t i;
+ array_init(&headers);
+ for(i=0; i<num_headers; i++) {
+ header = curl_pushheader_bynum(push_headers, i);
+ add_next_index_string(&headers, header);
+ }
+
+ zend_fcall_info_init(&t->func_name, 0, &fci, &t->fci_cache, NULL, NULL);
+
+ zend_fcall_info_argn(
+ &fci, 3,
+ pz_parent_ch,
+ &pz_ch,
+ &headers
+ );
+
+ fci.retval = &retval;
+
+ error = zend_call_function(&fci, &t->fci_cache);
+ zend_fcall_info_args_clear(&fci, 1);
+ zval_dtor(&headers);
+
+ if (error == FAILURE) {
+ php_error_docref(NULL, E_WARNING, "Cannot call the CURLMOPT_PUSHFUNCTION");
+ } else if (!Z_ISUNDEF(retval)) {
+ if (CURL_PUSH_DENY != zval_get_long(&retval)) {
+ rval = CURL_PUSH_OK;
+
+ /* we want to create a copy of this zval that we store in the multihandle structure element "easyh" */
+ zval tmp_val;
+ ZVAL_DUP(&tmp_val, &pz_ch);
+ zend_llist_add_element(&mh->easyh, &tmp_val);
+ } else {
+ /* libcurl will free this easy handle, avoid double free */
+ ch->cp = NULL;
+ }
+ }
+
+ return rval;
+}
+
+#endif
+
#if LIBCURL_VERSION_NUM >= 0x070f04 /* 7.15.4 */
static int _php_curl_multi_setopt(php_curlm *mh, zend_long option, zval *zvalue, zval *return_value) /* {{{ */
{
@@ -426,13 +552,36 @@ static int _php_curl_multi_setopt(php_curlm *mh, zend_long option, zval *zvalue,
#endif
error = curl_multi_setopt(mh->multi, option, zval_get_long(zvalue));
break;
+#if LIBCURL_VERSION_NUM > 0x072D00 /* Available since 7.46.0 */
+ case CURLMOPT_PUSHFUNCTION:
+ if (mh->handlers->server_push == NULL) {
+ mh->handlers->server_push = ecalloc(1, sizeof(php_curlm_server_push));
+ } else if (!Z_ISUNDEF(mh->handlers->server_push->func_name)) {
+ zval_ptr_dtor(&mh->handlers->server_push->func_name);
+ mh->handlers->server_push->fci_cache = empty_fcall_info_cache;
+ }
+ ZVAL_COPY(&mh->handlers->server_push->func_name, zvalue);
+ mh->handlers->server_push->method = PHP_CURL_USER;
+ if (!Z_ISUNDEF(mh->handlers->server_push->func_name)) {
+ zval_ptr_dtor(&mh->handlers->server_push->func_name);
+ mh->handlers->server_push->fci_cache = empty_fcall_info_cache;
+
+ }
+ error = curl_multi_setopt(mh->multi, option, _php_server_push_callback);
+ if (error != CURLM_OK) {
+ return 0;
+ }
+ error = curl_multi_setopt(mh->multi, CURLMOPT_PUSHDATA, mh);
+ break;
+#endif
default:
php_error_docref(NULL, E_WARNING, "Invalid curl multi configuration option");
error = CURLM_UNKNOWN_OPTION;
break;
}
+ SAVE_CURLM_ERROR(mh, error);
if (error != CURLM_OK) {
return 1;
} else {
diff --git a/ext/curl/php_curl.h b/ext/curl/php_curl.h
index 40edc951bc..ba0d7b0aef 100644
--- a/ext/curl/php_curl.h
+++ b/ext/curl/php_curl.h
@@ -66,6 +66,8 @@ extern int le_curl_multi_handle;
#define le_curl_multi_handle_name "cURL Multi Handle"
extern int le_curl_share_handle;
#define le_curl_share_handle_name "cURL Share Handle"
+//extern int le_curl_pushheaders;
+//#define le_curl_pushheaders "cURL Push Headers"
PHP_MINIT_FUNCTION(curl);
PHP_MSHUTDOWN_FUNCTION(curl);
@@ -90,14 +92,17 @@ PHP_FUNCTION(curl_multi_info_read);
PHP_FUNCTION(curl_multi_init);
PHP_FUNCTION(curl_multi_remove_handle);
PHP_FUNCTION(curl_multi_select);
+PHP_FUNCTION(curl_multi_errno);
PHP_FUNCTION(curl_share_close);
PHP_FUNCTION(curl_share_init);
PHP_FUNCTION(curl_share_setopt);
+PHP_FUNCTION(curl_share_errno);
#if LIBCURL_VERSION_NUM >= 0x070c00 /* 7.12.0 */
PHP_FUNCTION(curl_strerror);
PHP_FUNCTION(curl_multi_strerror);
+PHP_FUNCTION(curl_share_strerror);
#endif
#if LIBCURL_VERSION_NUM >= 0x070c01 /* 7.12.1 */
@@ -114,6 +119,7 @@ PHP_FUNCTION(curl_multi_setopt);
#if LIBCURL_VERSION_NUM >= 0x071200 /* 7.18.0 */
PHP_FUNCTION(curl_pause);
#endif
+
PHP_FUNCTION(curl_file_create);
@@ -142,7 +148,7 @@ typedef struct {
zval func_name;
zend_fcall_info_cache fci_cache;
int method;
-} php_curl_progress, php_curl_fnmatch;
+} php_curl_progress, php_curl_fnmatch, php_curlm_server_push;
typedef struct {
php_curl_write *write;
@@ -187,18 +193,31 @@ typedef struct {
#define CURLOPT_SAFE_UPLOAD -1
typedef struct {
+ php_curlm_server_push *server_push;
+} php_curlm_handlers;
+
+typedef struct {
int still_running;
CURLM *multi;
zend_llist easyh;
+ php_curlm_handlers *handlers;
+ struct {
+ int no;
+ } err;
} php_curlm;
typedef struct {
CURLSH *share;
+ struct {
+ int no;
+ } err;
} php_curlsh;
+php_curl *alloc_curl_handle();
void _php_curl_cleanup_handle(php_curl *);
void _php_curl_multi_cleanup_list(void *data);
void _php_curl_verify_handlers(php_curl *ch, int reporterror);
+void _php_setup_easy_copy_handlers(php_curl *ch, php_curl *source);
void curlfile_register_class(void);
PHP_CURL_API extern zend_class_entry *curl_CURLFile_class;
diff --git a/ext/curl/share.c b/ext/curl/share.c
index d8eeab4967..04bc4f22f7 100644
--- a/ext/curl/share.c
+++ b/ext/curl/share.c
@@ -32,6 +32,8 @@
#include <curl/curl.h>
+#define SAVE_CURLSH_ERROR(__handle, __err) (__handle)->err.no = (int) __err;
+
/* {{{ proto void curl_share_init()
Initialize a share curl handle */
PHP_FUNCTION(curl_share_init)
@@ -85,6 +87,7 @@ static int _php_curl_share_setopt(php_curlsh *sh, zend_long option, zval *zvalue
break;
}
+ SAVE_CURLSH_ERROR(sh, error);
if (error != CURLSHE_OK) {
return 1;
} else {
@@ -128,6 +131,48 @@ void _php_curl_share_close(zend_resource *rsrc) /* {{{ */
}
/* }}} */
+/* {{{ proto int curl_share_errno(resource mh)
+ Return an integer containing the last share curl error number */
+PHP_FUNCTION(curl_share_errno)
+{
+ zval *z_sh;
+ php_curlsh *sh;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &z_sh) == FAILURE) {
+ return;
+ }
+
+ if ((sh = (php_curlsh *)zend_fetch_resource(Z_RES_P(z_sh), le_curl_share_handle_name, le_curl_share_handle)) == NULL) {
+ RETURN_FALSE;
+ }
+
+ RETURN_LONG(sh->err.no);
+}
+/* }}} */
+
+
+#if LIBCURL_VERSION_NUM >= 0x070c00 /* Available since 7.12.0 */
+/* {{{ proto bool curl_share_strerror(int code)
+ return string describing error code */
+PHP_FUNCTION(curl_share_strerror)
+{
+ zend_long code;
+ const char *str;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &code) == FAILURE) {
+ return;
+ }
+
+ str = curl_share_strerror(code);
+ if (str) {
+ RETURN_STRING(str);
+ } else {
+ RETURN_NULL();
+ }
+}
+/* }}} */
+#endif
+
#endif
/*
diff --git a/ext/curl/tests/bug72202.phpt b/ext/curl/tests/bug72202.phpt
new file mode 100644
index 0000000000..63138d9ba6
--- /dev/null
+++ b/ext/curl/tests/bug72202.phpt
@@ -0,0 +1,36 @@
+--TEST--
+Bug #72202 (curl_close doesn't close cURL handle)
+--SKIPIF--
+<?php
+if (!extension_loaded("curl")) {
+ exit("skip curl extension not loaded");
+}
+if (getenv("SKIP_ONLINE_TESTS")) {
+ die("skip online test");
+}
+?>
+--FILE--
+<?php
+$a = fopen(__FILE__, "r");
+$b = $a;
+var_dump($a, $b);
+fclose($a);
+var_dump($a, $b);
+unset($a, $b);
+
+$a = curl_init();
+$b = $a;
+var_dump($a, $b);
+curl_close($a);
+var_dump($a, $b);
+unset($a, $b);
+?>
+--EXPECTF--
+resource(%d) of type (stream)
+resource(%d) of type (stream)
+resource(%d) of type (Unknown)
+resource(%d) of type (Unknown)
+resource(%d) of type (curl)
+resource(%d) of type (curl)
+resource(%d) of type (Unknown)
+resource(%d) of type (Unknown)
diff --git a/ext/curl/tests/curl_multi_errno_strerror_001.phpt b/ext/curl/tests/curl_multi_errno_strerror_001.phpt
new file mode 100644
index 0000000000..1fcdfe9558
--- /dev/null
+++ b/ext/curl/tests/curl_multi_errno_strerror_001.phpt
@@ -0,0 +1,30 @@
+--TEST--
+curl_multi_errno and curl_multi_strerror basic test
+--SKIPIF--
+<?php
+if (!extension_loaded("curl")) {
+ exit("skip curl extension not loaded");
+}
+$curl_version = curl_version();
+if ($curl_version['version_number'] < 0x070f04) {
+ exit("skip: test works only with curl >= 7.15.4");
+}
+?>
+--FILE--
+<?php
+
+$mh = curl_multi_init();
+$errno = curl_multi_errno($mh);
+echo $errno . PHP_EOL;
+echo curl_multi_strerror($errno) . PHP_EOL;
+
+@curl_multi_setopt($mh, -1, -1);
+$errno = curl_multi_errno($mh);
+echo $errno . PHP_EOL;
+echo curl_multi_strerror($errno) . PHP_EOL;
+?>
+--EXPECTF--
+0
+No error
+6
+Unknown option
diff --git a/ext/curl/tests/curl_share_errno_strerror_001.phpt b/ext/curl/tests/curl_share_errno_strerror_001.phpt
new file mode 100644
index 0000000000..91476cdaff
--- /dev/null
+++ b/ext/curl/tests/curl_share_errno_strerror_001.phpt
@@ -0,0 +1,30 @@
+--TEST--
+curl_share_errno and curl_share_strerror basic test
+--SKIPIF--
+<?php
+if (!extension_loaded("curl")) {
+ exit("skip curl extension not loaded");
+}
+$curl_version = curl_version();
+if ($curl_version['version_number'] < 0x070c00) {
+ exit("skip: test works only with curl >= 7.12.0");
+}
+?>
+--FILE--
+<?php
+
+$sh = curl_share_init();
+$errno = curl_share_errno($sh);
+echo $errno . PHP_EOL;
+echo curl_share_strerror($errno) . PHP_EOL;
+
+@curl_share_setopt($sh, -1, -1);
+$errno = curl_share_errno($sh);
+echo $errno . PHP_EOL;
+echo curl_share_strerror($errno) . PHP_EOL;
+?>
+--EXPECTF--
+0
+No error
+1
+Unknown share option
diff --git a/ext/date/config.w32 b/ext/date/config.w32
index ff5c4fff1c..889032aa85 100755
--- a/ext/date/config.w32
+++ b/ext/date/config.w32
@@ -11,6 +11,7 @@ var tl_config = FSO.CreateTextFile("ext/date/lib/timelib_config.h", true);
tl_config.WriteLine("#include \"config.w32.h\"");
tl_config.WriteLine("#include <php_stdint.h>");
tl_config.WriteLine("#define TIMELIB_OMIT_STDINT 1");
+tl_config.WriteLine("#define HAVE_GETTIMEOFDAY 1");
tl_config.WriteLine("#include \"zend.h\"");
tl_config.WriteLine("#define timelib_malloc emalloc");
tl_config.WriteLine("#define timelib_realloc erealloc");
diff --git a/ext/date/lib/interval.c b/ext/date/lib/interval.c
index 9c1cc3b273..dee9f92eed 100644
--- a/ext/date/lib/interval.c
+++ b/ext/date/lib/interval.c
@@ -65,6 +65,7 @@ timelib_rel_time *timelib_diff(timelib_time *one, timelib_time *two)
rt->h = two->h - one->h;
rt->i = two->i - one->i;
rt->s = two->s - one->s;
+ rt->f = two->f - one->f;
if (one_backup.dst == 0 && two_backup.dst == 1 && two->sse >= one->sse + 86400 - dst_corr) {
rt->h += dst_h_corr;
rt->i += dst_m_corr;
@@ -110,6 +111,7 @@ timelib_time *timelib_add(timelib_time *old_time, timelib_rel_time *interval)
t->relative.h = interval->h * bias;
t->relative.i = interval->i * bias;
t->relative.s = interval->s * bias;
+ t->relative.f = interval->f * bias;
}
t->have_relative = 1;
t->sse_uptodate = 0;
@@ -145,6 +147,7 @@ timelib_time *timelib_sub(timelib_time *old_time, timelib_rel_time *interval)
t->relative.h = 0 - (interval->h * bias);
t->relative.i = 0 - (interval->i * bias);
t->relative.s = 0 - (interval->s * bias);
+ t->relative.f = 0 - (interval->f * bias);
t->have_relative = 1;
t->sse_uptodate = 0;
diff --git a/ext/date/lib/parse_date.c b/ext/date/lib/parse_date.c
index aebd2b2087..0cc708d84f 100644
--- a/ext/date/lib/parse_date.c
+++ b/ext/date/lib/parse_date.c
@@ -1,4 +1,4 @@
-/* Generated by re2c 0.15.3 on Tue Jul 4 19:43:56 2017 */
+/* Generated by re2c 0.15.3 on Tue Jul 4 21:15:17 2017 */
#line 1 "ext/date/lib/parse_date.re"
/*
* The MIT License (MIT)
@@ -54,14 +54,15 @@
#define TIMELIB_UNSET -99999
-#define TIMELIB_SECOND 1
-#define TIMELIB_MINUTE 2
-#define TIMELIB_HOUR 3
-#define TIMELIB_DAY 4
-#define TIMELIB_MONTH 5
-#define TIMELIB_YEAR 6
-#define TIMELIB_WEEKDAY 7
-#define TIMELIB_SPECIAL 8
+#define TIMELIB_SECOND 1
+#define TIMELIB_MINUTE 2
+#define TIMELIB_HOUR 3
+#define TIMELIB_DAY 4
+#define TIMELIB_MONTH 5
+#define TIMELIB_YEAR 6
+#define TIMELIB_WEEKDAY 7
+#define TIMELIB_SPECIAL 8
+#define TIMELIB_MICROSEC 9
#define EOI 257
#define TIME 258
@@ -192,6 +193,18 @@ const static timelib_tz_lookup_table timelib_timezone_utc[] = {
};
static timelib_relunit const timelib_relunit_lookup[] = {
+ { "ms", TIMELIB_MICROSEC, 1000 },
+ { "msec", TIMELIB_MICROSEC, 1000 },
+ { "msecs", TIMELIB_MICROSEC, 1000 },
+ { "millisecond", TIMELIB_MICROSEC, 1000 },
+ { "milliseconds", TIMELIB_MICROSEC, 1000 },
+ { "µs", TIMELIB_MICROSEC, 1 },
+ { "usec", TIMELIB_MICROSEC, 1 },
+ { "usecs", TIMELIB_MICROSEC, 1 },
+ { "µsec", TIMELIB_MICROSEC, 1 },
+ { "µsecs", TIMELIB_MICROSEC, 1 },
+ { "microsecond", TIMELIB_MICROSEC, 1 },
+ { "microseconds", TIMELIB_MICROSEC, 1 },
{ "sec", TIMELIB_SECOND, 1 },
{ "secs", TIMELIB_SECOND, 1 },
{ "second", TIMELIB_SECOND, 1 },
@@ -657,12 +670,13 @@ static void timelib_set_relative(char **ptr, timelib_sll amount, int behavior, S
}
switch (relunit->unit) {
- case TIMELIB_SECOND: s->time->relative.s += amount * relunit->multiplier; break;
- case TIMELIB_MINUTE: s->time->relative.i += amount * relunit->multiplier; break;
- case TIMELIB_HOUR: s->time->relative.h += amount * relunit->multiplier; break;
- case TIMELIB_DAY: s->time->relative.d += amount * relunit->multiplier; break;
- case TIMELIB_MONTH: s->time->relative.m += amount * relunit->multiplier; break;
- case TIMELIB_YEAR: s->time->relative.y += amount * relunit->multiplier; break;
+ case TIMELIB_MICROSEC: s->time->relative.f += (((double) amount * (double) relunit->multiplier) / 1000000); break;
+ case TIMELIB_SECOND: s->time->relative.s += amount * relunit->multiplier; break;
+ case TIMELIB_MINUTE: s->time->relative.i += amount * relunit->multiplier; break;
+ case TIMELIB_HOUR: s->time->relative.h += amount * relunit->multiplier; break;
+ case TIMELIB_DAY: s->time->relative.d += amount * relunit->multiplier; break;
+ case TIMELIB_MONTH: s->time->relative.m += amount * relunit->multiplier; break;
+ case TIMELIB_YEAR: s->time->relative.y += amount * relunit->multiplier; break;
case TIMELIB_WEEKDAY:
TIMELIB_HAVE_WEEKDAY_RELATIVE();
@@ -824,11 +838,11 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
std:
s->tok = cursor;
s->len = 0;
-#line 950 "ext/date/lib/parse_date.re"
+#line 965 "ext/date/lib/parse_date.re"
-#line 832 "ext/date/lib/parse_date.c"
+#line 846 "ext/date/lib/parse_date.c"
{
YYCTYPE yych;
unsigned int yyaccept = 0;
@@ -947,23 +961,23 @@ yy2:
++YYCURSOR;
if ((yych = *YYCURSOR) <= 'E') {
if (yych <= ')') {
- if (yych >= ')') goto yy139;
+ if (yych >= ')') goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'D') goto yy140;
- goto yy1463;
+ if (yych <= 'D') goto yy165;
+ goto yy1521;
}
} else {
if (yych <= 'd') {
- if (yych <= 'Z') goto yy140;
- if (yych >= 'a') goto yy145;
+ if (yych <= 'Z') goto yy165;
+ if (yych >= 'a') goto yy170;
} else {
- if (yych <= 'e') goto yy1472;
- if (yych <= 'z') goto yy145;
+ if (yych <= 'e') goto yy1530;
+ if (yych <= 'z') goto yy170;
}
}
yy3:
-#line 1630 "ext/date/lib/parse_date.re"
+#line 1673 "ext/date/lib/parse_date.re"
{
int tz_not_found;
DEBUG_OUTPUT("tzcorrection | tz");
@@ -976,26 +990,26 @@ yy3:
TIMELIB_DEINIT;
return TIMELIB_TIMEZONE;
}
-#line 980 "ext/date/lib/parse_date.c"
+#line 994 "ext/date/lib/parse_date.c"
yy4:
yych = *++YYCURSOR;
if (yych <= 'E') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'D') goto yy140;
- goto yy1463;
+ if (yych <= 'D') goto yy165;
+ goto yy1521;
}
} else {
if (yych <= 'd') {
- if (yych <= 'Z') goto yy140;
+ if (yych <= 'Z') goto yy165;
if (yych <= '`') goto yy3;
- goto yy140;
+ goto yy165;
} else {
- if (yych <= 'e') goto yy1463;
- if (yych <= 'z') goto yy140;
+ if (yych <= 'e') goto yy1521;
+ if (yych <= 'z') goto yy165;
goto yy3;
}
}
@@ -1003,35 +1017,35 @@ yy5:
yych = *++YYCURSOR;
if (yych <= 'O') {
if (yych <= 'D') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= '@') goto yy3;
- goto yy140;
+ goto yy165;
} else {
if (yych <= 'H') {
- if (yych <= 'E') goto yy1434;
- goto yy140;
+ if (yych <= 'E') goto yy1492;
+ goto yy165;
} else {
- if (yych <= 'I') goto yy1435;
- if (yych <= 'N') goto yy140;
- goto yy1433;
+ if (yych <= 'I') goto yy1493;
+ if (yych <= 'N') goto yy165;
+ goto yy1491;
}
}
} else {
if (yych <= 'h') {
if (yych <= '`') {
- if (yych <= 'Z') goto yy140;
+ if (yych <= 'Z') goto yy165;
goto yy3;
} else {
- if (yych == 'e') goto yy1450;
- goto yy145;
+ if (yych == 'e') goto yy1508;
+ goto yy170;
}
} else {
if (yych <= 'n') {
- if (yych <= 'i') goto yy1451;
- goto yy145;
+ if (yych <= 'i') goto yy1509;
+ goto yy170;
} else {
- if (yych <= 'o') goto yy1449;
- if (yych <= 'z') goto yy145;
+ if (yych <= 'o') goto yy1507;
+ if (yych <= 'z') goto yy170;
goto yy3;
}
}
@@ -1040,35 +1054,35 @@ yy6:
yych = *++YYCURSOR;
if (yych <= 'O') {
if (yych <= 'D') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= '@') goto yy3;
- goto yy140;
+ goto yy165;
} else {
if (yych <= 'H') {
- if (yych <= 'E') goto yy1434;
- goto yy140;
+ if (yych <= 'E') goto yy1492;
+ goto yy165;
} else {
- if (yych <= 'I') goto yy1435;
- if (yych <= 'N') goto yy140;
- goto yy1433;
+ if (yych <= 'I') goto yy1493;
+ if (yych <= 'N') goto yy165;
+ goto yy1491;
}
}
} else {
if (yych <= 'h') {
if (yych <= '`') {
- if (yych <= 'Z') goto yy140;
+ if (yych <= 'Z') goto yy165;
goto yy3;
} else {
- if (yych == 'e') goto yy1434;
- goto yy140;
+ if (yych == 'e') goto yy1492;
+ goto yy165;
}
} else {
if (yych <= 'n') {
- if (yych <= 'i') goto yy1435;
- goto yy140;
+ if (yych <= 'i') goto yy1493;
+ goto yy165;
} else {
- if (yych <= 'o') goto yy1433;
- if (yych <= 'z') goto yy140;
+ if (yych <= 'o') goto yy1491;
+ if (yych <= 'z') goto yy165;
goto yy3;
}
}
@@ -1077,27 +1091,27 @@ yy7:
yych = *++YYCURSOR;
if (yych <= 'O') {
if (yych <= 'A') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= '@') goto yy3;
- goto yy1403;
+ goto yy1461;
} else {
- if (yych == 'I') goto yy1404;
- if (yych <= 'N') goto yy140;
- goto yy1405;
+ if (yych == 'I') goto yy1462;
+ if (yych <= 'N') goto yy165;
+ goto yy1463;
}
} else {
if (yych <= 'h') {
- if (yych <= 'Z') goto yy140;
+ if (yych <= 'Z') goto yy165;
if (yych <= '`') goto yy3;
- if (yych <= 'a') goto yy1418;
- goto yy145;
+ if (yych <= 'a') goto yy1476;
+ goto yy170;
} else {
if (yych <= 'n') {
- if (yych <= 'i') goto yy1419;
- goto yy145;
+ if (yych <= 'i') goto yy1477;
+ goto yy170;
} else {
- if (yych <= 'o') goto yy1420;
- if (yych <= 'z') goto yy145;
+ if (yych <= 'o') goto yy1478;
+ if (yych <= 'z') goto yy170;
goto yy3;
}
}
@@ -1106,27 +1120,27 @@ yy8:
yych = *++YYCURSOR;
if (yych <= 'O') {
if (yych <= 'A') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= '@') goto yy3;
- goto yy1403;
+ goto yy1461;
} else {
- if (yych == 'I') goto yy1404;
- if (yych <= 'N') goto yy140;
- goto yy1405;
+ if (yych == 'I') goto yy1462;
+ if (yych <= 'N') goto yy165;
+ goto yy1463;
}
} else {
if (yych <= 'h') {
- if (yych <= 'Z') goto yy140;
+ if (yych <= 'Z') goto yy165;
if (yych <= '`') goto yy3;
- if (yych <= 'a') goto yy1403;
- goto yy140;
+ if (yych <= 'a') goto yy1461;
+ goto yy165;
} else {
if (yych <= 'n') {
- if (yych <= 'i') goto yy1404;
- goto yy140;
+ if (yych <= 'i') goto yy1462;
+ goto yy165;
} else {
- if (yych <= 'o') goto yy1405;
- if (yych <= 'z') goto yy140;
+ if (yych <= 'o') goto yy1463;
+ if (yych <= 'z') goto yy165;
goto yy3;
}
}
@@ -1135,17 +1149,17 @@ yy9:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
switch (yych) {
- case ')': goto yy139;
+ case ')': goto yy164;
case '0':
- case '1': goto yy1333;
- case '2': goto yy1334;
+ case '1': goto yy1391;
+ case '2': goto yy1392;
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
- case '9': goto yy1335;
+ case '9': goto yy1393;
case 'A':
case 'B':
case 'C':
@@ -1166,12 +1180,12 @@ yy9:
case 'V':
case 'X':
case 'Y':
- case 'Z': goto yy140;
- case 'E': goto yy1328;
- case 'H': goto yy1329;
- case 'O': goto yy1330;
- case 'U': goto yy1331;
- case 'W': goto yy1332;
+ case 'Z': goto yy165;
+ case 'E': goto yy1386;
+ case 'H': goto yy1387;
+ case 'O': goto yy1388;
+ case 'U': goto yy1389;
+ case 'W': goto yy1390;
case 'a':
case 'b':
case 'c':
@@ -1192,29 +1206,29 @@ yy9:
case 'v':
case 'x':
case 'y':
- case 'z': goto yy145;
- case 'e': goto yy1371;
- case 'h': goto yy1372;
- case 'o': goto yy1373;
- case 'u': goto yy1374;
- case 'w': goto yy1375;
+ case 'z': goto yy170;
+ case 'e': goto yy1429;
+ case 'h': goto yy1430;
+ case 'o': goto yy1431;
+ case 'u': goto yy1432;
+ case 'w': goto yy1433;
default: goto yy3;
}
yy10:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
switch (yych) {
- case ')': goto yy139;
+ case ')': goto yy164;
case '0':
- case '1': goto yy1333;
- case '2': goto yy1334;
+ case '1': goto yy1391;
+ case '2': goto yy1392;
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
- case '9': goto yy1335;
+ case '9': goto yy1393;
case 'A':
case 'B':
case 'C':
@@ -1256,72 +1270,72 @@ yy10:
case 'v':
case 'x':
case 'y':
- case 'z': goto yy140;
+ case 'z': goto yy165;
case 'E':
- case 'e': goto yy1328;
+ case 'e': goto yy1386;
case 'H':
- case 'h': goto yy1329;
+ case 'h': goto yy1387;
case 'O':
- case 'o': goto yy1330;
+ case 'o': goto yy1388;
case 'U':
- case 'u': goto yy1331;
+ case 'u': goto yy1389;
case 'W':
- case 'w': goto yy1332;
+ case 'w': goto yy1390;
default: goto yy3;
}
yy11:
yyaccept = 1;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych == '-') goto yy1324;
+ if (yych == '-') goto yy1374;
if (yych <= '/') goto yy12;
- if (yych <= '9') goto yy1325;
+ if (yych <= '9') goto yy1375;
yy12:
-#line 1725 "ext/date/lib/parse_date.re"
+#line 1768 "ext/date/lib/parse_date.re"
{
add_error(s, "Unexpected character");
goto std;
}
-#line 1285 "ext/date/lib/parse_date.c"
+#line 1299 "ext/date/lib/parse_date.c"
yy13:
yych = *++YYCURSOR;
if (yych <= 'R') {
if (yych <= 'E') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'D') goto yy140;
- goto yy1260;
+ if (yych <= 'D') goto yy165;
+ goto yy1310;
}
} else {
if (yych <= 'N') {
- if (yych == 'I') goto yy1261;
- goto yy140;
+ if (yych == 'I') goto yy1311;
+ goto yy165;
} else {
- if (yych <= 'O') goto yy1262;
- if (yych <= 'Q') goto yy140;
- goto yy1263;
+ if (yych <= 'O') goto yy1312;
+ if (yych <= 'Q') goto yy165;
+ goto yy1313;
}
}
} else {
if (yych <= 'i') {
if (yych <= 'd') {
- if (yych <= 'Z') goto yy140;
+ if (yych <= 'Z') goto yy165;
if (yych <= '`') goto yy3;
- goto yy145;
+ goto yy170;
} else {
- if (yych <= 'e') goto yy1301;
- if (yych <= 'h') goto yy145;
- goto yy1302;
+ if (yych <= 'e') goto yy1351;
+ if (yych <= 'h') goto yy170;
+ goto yy1352;
}
} else {
if (yych <= 'q') {
- if (yych == 'o') goto yy1303;
- goto yy145;
+ if (yych == 'o') goto yy1353;
+ goto yy170;
} else {
- if (yych <= 'r') goto yy1304;
- if (yych <= 'z') goto yy145;
+ if (yych <= 'r') goto yy1354;
+ if (yych <= 'z') goto yy170;
goto yy3;
}
}
@@ -1332,40 +1346,40 @@ yy14:
if (yych <= 'E') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'D') goto yy140;
- goto yy1260;
+ if (yych <= 'D') goto yy165;
+ goto yy1310;
}
} else {
if (yych <= 'N') {
- if (yych == 'I') goto yy1261;
- goto yy140;
+ if (yych == 'I') goto yy1311;
+ goto yy165;
} else {
- if (yych <= 'O') goto yy1262;
- if (yych <= 'Q') goto yy140;
- goto yy1263;
+ if (yych <= 'O') goto yy1312;
+ if (yych <= 'Q') goto yy165;
+ goto yy1313;
}
}
} else {
if (yych <= 'i') {
if (yych <= 'd') {
- if (yych <= 'Z') goto yy140;
+ if (yych <= 'Z') goto yy165;
if (yych <= '`') goto yy3;
- goto yy140;
+ goto yy165;
} else {
- if (yych <= 'e') goto yy1260;
- if (yych <= 'h') goto yy140;
- goto yy1261;
+ if (yych <= 'e') goto yy1310;
+ if (yych <= 'h') goto yy165;
+ goto yy1311;
}
} else {
if (yych <= 'q') {
- if (yych == 'o') goto yy1262;
- goto yy140;
+ if (yych == 'o') goto yy1312;
+ goto yy165;
} else {
- if (yych <= 'r') goto yy1263;
- if (yych <= 'z') goto yy140;
+ if (yych <= 'r') goto yy1313;
+ if (yych <= 'z') goto yy165;
goto yy3;
}
}
@@ -1373,64 +1387,64 @@ yy14:
yy15:
yych = *++YYCURSOR;
if (yych <= 'A') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= '@') goto yy3;
- goto yy1246;
+ goto yy1296;
} else {
if (yych <= '`') {
- if (yych <= 'Z') goto yy140;
+ if (yych <= 'Z') goto yy165;
goto yy3;
} else {
- if (yych <= 'a') goto yy1257;
- if (yych <= 'z') goto yy145;
+ if (yych <= 'a') goto yy1307;
+ if (yych <= 'z') goto yy170;
goto yy3;
}
}
yy16:
yych = *++YYCURSOR;
if (yych <= 'A') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= '@') goto yy3;
- goto yy1246;
+ goto yy1296;
} else {
if (yych <= '`') {
- if (yych <= 'Z') goto yy140;
+ if (yych <= 'Z') goto yy165;
goto yy3;
} else {
- if (yych <= 'a') goto yy1246;
- if (yych <= 'z') goto yy140;
+ if (yych <= 'a') goto yy1296;
+ if (yych <= 'z') goto yy165;
goto yy3;
}
}
yy17:
yych = *++YYCURSOR;
if (yych <= 'A') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= '@') goto yy3;
- goto yy1226;
+ goto yy1276;
} else {
if (yych <= '`') {
- if (yych <= 'Z') goto yy140;
+ if (yych <= 'Z') goto yy165;
goto yy3;
} else {
- if (yych <= 'a') goto yy1243;
- if (yych <= 'z') goto yy145;
+ if (yych <= 'a') goto yy1293;
+ if (yych <= 'z') goto yy170;
goto yy3;
}
}
yy18:
yych = *++YYCURSOR;
if (yych <= 'A') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= '@') goto yy3;
- goto yy1226;
+ goto yy1276;
} else {
if (yych <= '`') {
- if (yych <= 'Z') goto yy140;
+ if (yych <= 'Z') goto yy165;
goto yy3;
} else {
- if (yych <= 'a') goto yy1226;
- if (yych <= 'z') goto yy140;
+ if (yych <= 'a') goto yy1276;
+ if (yych <= 'z') goto yy165;
goto yy3;
}
}
@@ -1440,39 +1454,39 @@ yy19:
if (yych <= 'D') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'A') goto yy1169;
- goto yy140;
+ if (yych <= 'A') goto yy1219;
+ goto yy165;
}
} else {
if (yych <= 'H') {
- if (yych <= 'E') goto yy1168;
- goto yy140;
+ if (yych <= 'E') goto yy1218;
+ goto yy165;
} else {
- if (yych <= 'I') goto yy1170;
- if (yych <= 'T') goto yy140;
- goto yy1171;
+ if (yych <= 'I') goto yy1220;
+ if (yych <= 'T') goto yy165;
+ goto yy1221;
}
}
} else {
if (yych <= 'e') {
if (yych <= '`') {
- if (yych <= 'Z') goto yy140;
+ if (yych <= 'Z') goto yy165;
goto yy3;
} else {
- if (yych <= 'a') goto yy1198;
- if (yych <= 'd') goto yy145;
- goto yy1197;
+ if (yych <= 'a') goto yy1248;
+ if (yych <= 'd') goto yy170;
+ goto yy1247;
}
} else {
if (yych <= 't') {
- if (yych == 'i') goto yy1199;
- goto yy145;
+ if (yych == 'i') goto yy1249;
+ goto yy170;
} else {
- if (yych <= 'u') goto yy1200;
- if (yych <= 'z') goto yy145;
+ if (yych <= 'u') goto yy1250;
+ if (yych <= 'z') goto yy170;
goto yy3;
}
}
@@ -1483,39 +1497,39 @@ yy20:
if (yych <= 'D') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'A') goto yy1169;
- goto yy140;
+ if (yych <= 'A') goto yy1219;
+ goto yy165;
}
} else {
if (yych <= 'H') {
- if (yych <= 'E') goto yy1168;
- goto yy140;
+ if (yych <= 'E') goto yy1218;
+ goto yy165;
} else {
- if (yych <= 'I') goto yy1170;
- if (yych <= 'T') goto yy140;
- goto yy1171;
+ if (yych <= 'I') goto yy1220;
+ if (yych <= 'T') goto yy165;
+ goto yy1221;
}
}
} else {
if (yych <= 'e') {
if (yych <= '`') {
- if (yych <= 'Z') goto yy140;
+ if (yych <= 'Z') goto yy165;
goto yy3;
} else {
- if (yych <= 'a') goto yy1169;
- if (yych <= 'd') goto yy140;
- goto yy1168;
+ if (yych <= 'a') goto yy1219;
+ if (yych <= 'd') goto yy165;
+ goto yy1218;
}
} else {
if (yych <= 't') {
- if (yych == 'i') goto yy1170;
- goto yy140;
+ if (yych == 'i') goto yy1220;
+ goto yy165;
} else {
- if (yych <= 'u') goto yy1171;
- if (yych <= 'z') goto yy140;
+ if (yych <= 'u') goto yy1221;
+ if (yych <= 'z') goto yy165;
goto yy3;
}
}
@@ -1524,22 +1538,22 @@ yy21:
yych = *++YYCURSOR;
if (yych <= 'L') {
if (yych <= '@') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
- if (yych == 'I') goto yy1138;
- if (yych <= 'K') goto yy140;
- goto yy1139;
+ if (yych == 'I') goto yy1188;
+ if (yych <= 'K') goto yy165;
+ goto yy1189;
}
} else {
if (yych <= 'i') {
- if (yych <= 'Z') goto yy140;
+ if (yych <= 'Z') goto yy165;
if (yych <= '`') goto yy3;
- if (yych <= 'h') goto yy145;
- goto yy1156;
+ if (yych <= 'h') goto yy170;
+ goto yy1206;
} else {
- if (yych == 'l') goto yy1157;
- if (yych <= 'z') goto yy145;
+ if (yych == 'l') goto yy1207;
+ if (yych <= 'z') goto yy170;
goto yy3;
}
}
@@ -1547,22 +1561,22 @@ yy22:
yych = *++YYCURSOR;
if (yych <= 'L') {
if (yych <= '@') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
- if (yych == 'I') goto yy1138;
- if (yych <= 'K') goto yy140;
- goto yy1139;
+ if (yych == 'I') goto yy1188;
+ if (yych <= 'K') goto yy165;
+ goto yy1189;
}
} else {
if (yych <= 'i') {
- if (yych <= 'Z') goto yy140;
+ if (yych <= 'Z') goto yy165;
if (yych <= '`') goto yy3;
- if (yych <= 'h') goto yy140;
- goto yy1138;
+ if (yych <= 'h') goto yy165;
+ goto yy1188;
} else {
- if (yych == 'l') goto yy1139;
- if (yych <= 'z') goto yy140;
+ if (yych == 'l') goto yy1189;
+ if (yych <= 'z') goto yy165;
goto yy3;
}
}
@@ -1571,20 +1585,20 @@ yy23:
if (yych <= 'R') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'Q') goto yy140;
- goto yy1037;
+ if (yych <= 'Q') goto yy165;
+ goto yy1062;
}
} else {
if (yych <= 'q') {
- if (yych <= 'Z') goto yy140;
+ if (yych <= 'Z') goto yy165;
if (yych <= '`') goto yy3;
- goto yy145;
+ goto yy170;
} else {
- if (yych <= 'r') goto yy1131;
- if (yych <= 'z') goto yy145;
+ if (yych <= 'r') goto yy1181;
+ if (yych <= 'z') goto yy170;
goto yy3;
}
}
@@ -1593,280 +1607,473 @@ yy24:
if (yych <= 'R') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'Q') goto yy140;
- goto yy1037;
+ if (yych <= 'Q') goto yy165;
+ goto yy1062;
}
} else {
if (yych <= 'q') {
- if (yych <= 'Z') goto yy140;
+ if (yych <= 'Z') goto yy165;
if (yych <= '`') goto yy3;
- goto yy140;
+ goto yy165;
} else {
- if (yych <= 'r') goto yy1037;
- if (yych <= 'z') goto yy140;
+ if (yych <= 'r') goto yy1062;
+ if (yych <= 'z') goto yy165;
goto yy3;
}
}
yy25:
yyaccept = 1;
yych = *(YYMARKER = ++YYCURSOR);
- switch (yych) {
- case '\t': goto yy991;
- case ' ':
- case 'A':
- case 'D':
- case 'F':
- case 'H':
- case 'I':
- case 'J':
- case 'M':
- case 'N':
- case 'O':
- case 'S':
- case 'T':
- case 'V':
- case 'W':
- case 'X':
- case 'Y':
- case 'a':
- case 'd':
- case 'f':
- case 'h':
- case 'j':
- case 'm':
- case 'o':
- case 'w':
- case 'y': goto yy993;
- case '-': goto yy412;
- case '.': goto yy1003;
- case '/': goto yy411;
- case '0': goto yy1036;
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9': goto yy1035;
- case ':': goto yy1004;
- case 'n': goto yy409;
- case 'r': goto yy410;
- case 's': goto yy403;
- case 't': goto yy407;
- default: goto yy12;
+ if (yych <= '`') {
+ if (yych <= '@') {
+ if (yych <= '-') {
+ if (yych <= 0x1F) {
+ if (yych == '\t') goto yy1016;
+ goto yy12;
+ } else {
+ if (yych <= ' ') goto yy1018;
+ if (yych <= ',') goto yy12;
+ goto yy437;
+ }
+ } else {
+ if (yych <= '0') {
+ if (yych <= '.') goto yy1028;
+ if (yych <= '/') goto yy436;
+ goto yy1061;
+ } else {
+ if (yych <= '9') goto yy1060;
+ if (yych <= ':') goto yy1029;
+ goto yy12;
+ }
+ }
+ } else {
+ if (yych <= 'G') {
+ if (yych <= 'D') {
+ if (yych <= 'A') goto yy1018;
+ if (yych <= 'C') goto yy12;
+ goto yy1018;
+ } else {
+ if (yych == 'F') goto yy1018;
+ goto yy12;
+ }
+ } else {
+ if (yych <= 'O') {
+ if (yych <= 'J') goto yy1018;
+ if (yych <= 'L') goto yy12;
+ goto yy1018;
+ } else {
+ if (yych <= 'R') goto yy12;
+ if (yych <= 'Y') goto yy1018;
+ goto yy12;
+ }
+ }
+ }
+ } else {
+ if (yych <= 'n') {
+ if (yych <= 'g') {
+ if (yych <= 'd') {
+ if (yych <= 'a') goto yy1018;
+ if (yych <= 'c') goto yy12;
+ goto yy1018;
+ } else {
+ if (yych == 'f') goto yy1018;
+ goto yy12;
+ }
+ } else {
+ if (yych <= 'j') {
+ if (yych == 'i') goto yy12;
+ goto yy1018;
+ } else {
+ if (yych <= 'l') goto yy12;
+ if (yych <= 'm') goto yy1018;
+ goto yy434;
+ }
+ }
+ } else {
+ if (yych <= 'u') {
+ if (yych <= 'r') {
+ if (yych <= 'o') goto yy1018;
+ if (yych <= 'q') goto yy12;
+ goto yy435;
+ } else {
+ if (yych <= 's') goto yy429;
+ if (yych <= 't') goto yy432;
+ goto yy1018;
+ }
+ } else {
+ if (yych <= 'x') {
+ if (yych == 'w') goto yy1018;
+ goto yy12;
+ } else {
+ if (yych <= 'y') goto yy1018;
+ if (yych == 0xC2) goto yy1018;
+ goto yy12;
+ }
+ }
+ }
}
yy26:
yyaccept = 1;
yych = *(YYMARKER = ++YYCURSOR);
- switch (yych) {
- case '\t': goto yy399;
- case ' ':
- case 'A':
- case 'D':
- case 'F':
- case 'H':
- case 'I':
- case 'J':
- case 'M':
- case 'N':
- case 'O':
- case 'P':
- case 'S':
- case 'T':
- case 'V':
- case 'W':
- case 'X':
- case 'Y':
- case 'a':
- case 'd':
- case 'f':
- case 'h':
- case 'j':
- case 'm':
- case 'o':
- case 'p':
- case 'w':
- case 'y': goto yy401;
- case '-': goto yy412;
- case '.': goto yy413;
- case '/': goto yy411;
- case '0':
- case '1':
- case '2': goto yy1035;
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9': goto yy1002;
- case ':': goto yy422;
- case 'n': goto yy409;
- case 'r': goto yy410;
- case 's': goto yy403;
- case 't': goto yy407;
- default: goto yy12;
+ if (yych <= '`') {
+ if (yych <= '@') {
+ if (yych <= '-') {
+ if (yych <= 0x1F) {
+ if (yych == '\t') goto yy424;
+ goto yy12;
+ } else {
+ if (yych <= ' ') goto yy426;
+ if (yych <= ',') goto yy12;
+ goto yy437;
+ }
+ } else {
+ if (yych <= '2') {
+ if (yych <= '.') goto yy438;
+ if (yych <= '/') goto yy436;
+ goto yy1060;
+ } else {
+ if (yych <= '9') goto yy1027;
+ if (yych <= ':') goto yy447;
+ goto yy12;
+ }
+ }
+ } else {
+ if (yych <= 'G') {
+ if (yych <= 'D') {
+ if (yych <= 'A') goto yy426;
+ if (yych <= 'C') goto yy12;
+ goto yy426;
+ } else {
+ if (yych == 'F') goto yy426;
+ goto yy12;
+ }
+ } else {
+ if (yych <= 'P') {
+ if (yych <= 'J') goto yy426;
+ if (yych <= 'L') goto yy12;
+ goto yy426;
+ } else {
+ if (yych <= 'R') goto yy12;
+ if (yych <= 'Y') goto yy426;
+ goto yy12;
+ }
+ }
+ }
+ } else {
+ if (yych <= 'n') {
+ if (yych <= 'g') {
+ if (yych <= 'd') {
+ if (yych <= 'a') goto yy426;
+ if (yych <= 'c') goto yy12;
+ goto yy426;
+ } else {
+ if (yych == 'f') goto yy426;
+ goto yy12;
+ }
+ } else {
+ if (yych <= 'j') {
+ if (yych == 'i') goto yy12;
+ goto yy426;
+ } else {
+ if (yych <= 'l') goto yy12;
+ if (yych <= 'm') goto yy426;
+ goto yy434;
+ }
+ }
+ } else {
+ if (yych <= 'u') {
+ if (yych <= 'r') {
+ if (yych <= 'p') goto yy426;
+ if (yych <= 'q') goto yy12;
+ goto yy435;
+ } else {
+ if (yych <= 's') goto yy429;
+ if (yych <= 't') goto yy432;
+ goto yy426;
+ }
+ } else {
+ if (yych <= 'x') {
+ if (yych == 'w') goto yy426;
+ goto yy12;
+ } else {
+ if (yych <= 'y') goto yy426;
+ if (yych == 0xC2) goto yy426;
+ goto yy12;
+ }
+ }
+ }
}
yy27:
yyaccept = 1;
yych = *(YYMARKER = ++YYCURSOR);
- switch (yych) {
- case '\t': goto yy399;
- case ' ':
- case 'A':
- case 'D':
- case 'F':
- case 'H':
- case 'I':
- case 'J':
- case 'M':
- case 'N':
- case 'O':
- case 'P':
- case 'S':
- case 'T':
- case 'V':
- case 'W':
- case 'X':
- case 'Y':
- case 'a':
- case 'd':
- case 'f':
- case 'h':
- case 'j':
- case 'm':
- case 'o':
- case 'p':
- case 'w':
- case 'y': goto yy401;
- case '-': goto yy412;
- case '.': goto yy413;
- case '/': goto yy411;
- case '0':
- case '1':
- case '2':
- case '3':
- case '4': goto yy1002;
- case '5':
- case '6':
- case '7':
- case '8':
- case '9': goto yy989;
- case ':': goto yy422;
- case 'n': goto yy409;
- case 'r': goto yy410;
- case 's': goto yy403;
- case 't': goto yy407;
- default: goto yy12;
+ if (yych <= '`') {
+ if (yych <= '@') {
+ if (yych <= '-') {
+ if (yych <= 0x1F) {
+ if (yych == '\t') goto yy424;
+ goto yy12;
+ } else {
+ if (yych <= ' ') goto yy426;
+ if (yych <= ',') goto yy12;
+ goto yy437;
+ }
+ } else {
+ if (yych <= '4') {
+ if (yych <= '.') goto yy438;
+ if (yych <= '/') goto yy436;
+ goto yy1027;
+ } else {
+ if (yych <= '9') goto yy1014;
+ if (yych <= ':') goto yy447;
+ goto yy12;
+ }
+ }
+ } else {
+ if (yych <= 'G') {
+ if (yych <= 'D') {
+ if (yych <= 'A') goto yy426;
+ if (yych <= 'C') goto yy12;
+ goto yy426;
+ } else {
+ if (yych == 'F') goto yy426;
+ goto yy12;
+ }
+ } else {
+ if (yych <= 'P') {
+ if (yych <= 'J') goto yy426;
+ if (yych <= 'L') goto yy12;
+ goto yy426;
+ } else {
+ if (yych <= 'R') goto yy12;
+ if (yych <= 'Y') goto yy426;
+ goto yy12;
+ }
+ }
+ }
+ } else {
+ if (yych <= 'n') {
+ if (yych <= 'g') {
+ if (yych <= 'd') {
+ if (yych <= 'a') goto yy426;
+ if (yych <= 'c') goto yy12;
+ goto yy426;
+ } else {
+ if (yych == 'f') goto yy426;
+ goto yy12;
+ }
+ } else {
+ if (yych <= 'j') {
+ if (yych == 'i') goto yy12;
+ goto yy426;
+ } else {
+ if (yych <= 'l') goto yy12;
+ if (yych <= 'm') goto yy426;
+ goto yy434;
+ }
+ }
+ } else {
+ if (yych <= 'u') {
+ if (yych <= 'r') {
+ if (yych <= 'p') goto yy426;
+ if (yych <= 'q') goto yy12;
+ goto yy435;
+ } else {
+ if (yych <= 's') goto yy429;
+ if (yych <= 't') goto yy432;
+ goto yy426;
+ }
+ } else {
+ if (yych <= 'x') {
+ if (yych == 'w') goto yy426;
+ goto yy12;
+ } else {
+ if (yych <= 'y') goto yy426;
+ if (yych == 0xC2) goto yy426;
+ goto yy12;
+ }
+ }
+ }
}
yy28:
yyaccept = 1;
yych = *(YYMARKER = ++YYCURSOR);
- switch (yych) {
- case '\t': goto yy399;
- case ' ':
- case 'A':
- case 'D':
- case 'F':
- case 'H':
- case 'I':
- case 'J':
- case 'M':
- case 'N':
- case 'O':
- case 'P':
- case 'S':
- case 'T':
- case 'V':
- case 'W':
- case 'X':
- case 'Y':
- case 'a':
- case 'd':
- case 'f':
- case 'h':
- case 'j':
- case 'm':
- case 'o':
- case 'p':
- case 'w':
- case 'y': goto yy401;
- case '-': goto yy412;
- case '.': goto yy413;
- case '/': goto yy411;
- case '0':
- case '1': goto yy989;
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9': goto yy408;
- case ':': goto yy422;
- case 'n': goto yy409;
- case 'r': goto yy410;
- case 's': goto yy403;
- case 't': goto yy407;
- default: goto yy12;
+ if (yych <= '`') {
+ if (yych <= '@') {
+ if (yych <= '-') {
+ if (yych <= 0x1F) {
+ if (yych == '\t') goto yy424;
+ goto yy12;
+ } else {
+ if (yych <= ' ') goto yy426;
+ if (yych <= ',') goto yy12;
+ goto yy437;
+ }
+ } else {
+ if (yych <= '1') {
+ if (yych <= '.') goto yy438;
+ if (yych <= '/') goto yy436;
+ goto yy1014;
+ } else {
+ if (yych <= '9') goto yy433;
+ if (yych <= ':') goto yy447;
+ goto yy12;
+ }
+ }
+ } else {
+ if (yych <= 'G') {
+ if (yych <= 'D') {
+ if (yych <= 'A') goto yy426;
+ if (yych <= 'C') goto yy12;
+ goto yy426;
+ } else {
+ if (yych == 'F') goto yy426;
+ goto yy12;
+ }
+ } else {
+ if (yych <= 'P') {
+ if (yych <= 'J') goto yy426;
+ if (yych <= 'L') goto yy12;
+ goto yy426;
+ } else {
+ if (yych <= 'R') goto yy12;
+ if (yych <= 'Y') goto yy426;
+ goto yy12;
+ }
+ }
+ }
+ } else {
+ if (yych <= 'n') {
+ if (yych <= 'g') {
+ if (yych <= 'd') {
+ if (yych <= 'a') goto yy426;
+ if (yych <= 'c') goto yy12;
+ goto yy426;
+ } else {
+ if (yych == 'f') goto yy426;
+ goto yy12;
+ }
+ } else {
+ if (yych <= 'j') {
+ if (yych == 'i') goto yy12;
+ goto yy426;
+ } else {
+ if (yych <= 'l') goto yy12;
+ if (yych <= 'm') goto yy426;
+ goto yy434;
+ }
+ }
+ } else {
+ if (yych <= 'u') {
+ if (yych <= 'r') {
+ if (yych <= 'p') goto yy426;
+ if (yych <= 'q') goto yy12;
+ goto yy435;
+ } else {
+ if (yych <= 's') goto yy429;
+ if (yych <= 't') goto yy432;
+ goto yy426;
+ }
+ } else {
+ if (yych <= 'x') {
+ if (yych == 'w') goto yy426;
+ goto yy12;
+ } else {
+ if (yych <= 'y') goto yy426;
+ if (yych == 0xC2) goto yy426;
+ goto yy12;
+ }
+ }
+ }
}
yy29:
yyaccept = 1;
yych = *(YYMARKER = ++YYCURSOR);
- switch (yych) {
- case '\t': goto yy399;
- case ' ':
- case 'A':
- case 'D':
- case 'F':
- case 'H':
- case 'I':
- case 'J':
- case 'M':
- case 'N':
- case 'O':
- case 'P':
- case 'S':
- case 'T':
- case 'V':
- case 'W':
- case 'X':
- case 'Y':
- case 'a':
- case 'd':
- case 'f':
- case 'h':
- case 'j':
- case 'm':
- case 'o':
- case 'p':
- case 'w':
- case 'y': goto yy401;
- case '-': goto yy412;
- case '.': goto yy413;
- case '/': goto yy411;
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9': goto yy408;
- case ':': goto yy422;
- case 'n': goto yy409;
- case 'r': goto yy410;
- case 's': goto yy403;
- case 't': goto yy407;
- default: goto yy12;
+ if (yych <= 'a') {
+ if (yych <= 'A') {
+ if (yych <= '-') {
+ if (yych <= 0x1F) {
+ if (yych == '\t') goto yy424;
+ goto yy12;
+ } else {
+ if (yych <= ' ') goto yy426;
+ if (yych <= ',') goto yy12;
+ goto yy437;
+ }
+ } else {
+ if (yych <= '9') {
+ if (yych <= '.') goto yy438;
+ if (yych <= '/') goto yy436;
+ goto yy433;
+ } else {
+ if (yych <= ':') goto yy447;
+ if (yych <= '@') goto yy12;
+ goto yy426;
+ }
+ }
+ } else {
+ if (yych <= 'J') {
+ if (yych <= 'E') {
+ if (yych == 'D') goto yy426;
+ goto yy12;
+ } else {
+ if (yych == 'G') goto yy12;
+ goto yy426;
+ }
+ } else {
+ if (yych <= 'R') {
+ if (yych <= 'L') goto yy12;
+ if (yych <= 'P') goto yy426;
+ goto yy12;
+ } else {
+ if (yych <= 'Y') goto yy426;
+ if (yych <= '`') goto yy12;
+ goto yy426;
+ }
+ }
+ }
+ } else {
+ if (yych <= 'p') {
+ if (yych <= 'h') {
+ if (yych <= 'e') {
+ if (yych == 'd') goto yy426;
+ goto yy12;
+ } else {
+ if (yych == 'g') goto yy12;
+ goto yy426;
+ }
+ } else {
+ if (yych <= 'l') {
+ if (yych == 'j') goto yy426;
+ goto yy12;
+ } else {
+ if (yych == 'n') goto yy434;
+ goto yy426;
+ }
+ }
+ } else {
+ if (yych <= 'v') {
+ if (yych <= 's') {
+ if (yych <= 'q') goto yy12;
+ if (yych <= 'r') goto yy435;
+ goto yy429;
+ } else {
+ if (yych <= 't') goto yy432;
+ if (yych <= 'u') goto yy426;
+ goto yy12;
+ }
+ } else {
+ if (yych <= 'y') {
+ if (yych == 'x') goto yy12;
+ goto yy426;
+ } else {
+ if (yych == 0xC2) goto yy426;
+ goto yy12;
+ }
+ }
+ }
}
yy30:
yyaccept = 1;
@@ -1876,17 +2083,17 @@ yy30:
}
switch (yych) {
case '+':
- case '-': goto yy379;
+ case '-': goto yy404;
case '0':
- case '1': goto yy376;
- case '2': goto yy377;
+ case '1': goto yy401;
+ case '2': goto yy402;
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
- case '9': goto yy378;
+ case '9': goto yy403;
default: goto yy12;
}
yy31:
@@ -1896,37 +2103,37 @@ yy31:
if (yych <= '(') {
if (yych <= '\t') {
if (yych <= 0x08) goto yy3;
- goto yy195;
+ goto yy220;
} else {
- if (yych == ' ') goto yy195;
+ if (yych == ' ') goto yy220;
goto yy3;
}
} else {
if (yych <= ',') {
- if (yych <= ')') goto yy139;
+ if (yych <= ')') goto yy164;
goto yy3;
} else {
if (yych == '/') goto yy3;
- goto yy195;
+ goto yy220;
}
}
} else {
if (yych <= 'V') {
if (yych <= 'H') {
if (yych <= '@') goto yy3;
- goto yy140;
+ goto yy165;
} else {
- if (yych <= 'I') goto yy375;
- if (yych <= 'U') goto yy140;
- goto yy374;
+ if (yych <= 'I') goto yy400;
+ if (yych <= 'U') goto yy165;
+ goto yy399;
}
} else {
if (yych <= 'Z') {
- if (yych == 'X') goto yy374;
- goto yy140;
+ if (yych == 'X') goto yy399;
+ goto yy165;
} else {
if (yych <= '`') goto yy3;
- if (yych <= 'z') goto yy145;
+ if (yych <= 'z') goto yy170;
goto yy3;
}
}
@@ -1936,27 +2143,27 @@ yy32:
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '.') {
if (yych <= ' ') {
- if (yych == '\t') goto yy195;
+ if (yych == '\t') goto yy220;
if (yych <= 0x1F) goto yy3;
- goto yy195;
+ goto yy220;
} else {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy195;
+ goto yy220;
}
} else {
if (yych <= 'H') {
if (yych <= '/') goto yy3;
- if (yych <= '9') goto yy195;
+ if (yych <= '9') goto yy220;
if (yych <= '@') goto yy3;
- goto yy140;
+ goto yy165;
} else {
if (yych <= 'Z') {
- if (yych <= 'I') goto yy371;
- goto yy140;
+ if (yych <= 'I') goto yy396;
+ goto yy165;
} else {
if (yych <= '`') goto yy3;
- if (yych <= 'z') goto yy145;
+ if (yych <= 'z') goto yy170;
goto yy3;
}
}
@@ -1966,27 +2173,27 @@ yy33:
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '.') {
if (yych <= ' ') {
- if (yych == '\t') goto yy195;
+ if (yych == '\t') goto yy220;
if (yych <= 0x1F) goto yy3;
- goto yy195;
+ goto yy220;
} else {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy195;
+ goto yy220;
}
} else {
if (yych <= 'H') {
if (yych <= '/') goto yy3;
- if (yych <= '9') goto yy195;
+ if (yych <= '9') goto yy220;
if (yych <= '@') goto yy3;
- goto yy140;
+ goto yy165;
} else {
if (yych <= 'Z') {
- if (yych <= 'I') goto yy369;
- goto yy140;
+ if (yych <= 'I') goto yy394;
+ goto yy165;
} else {
if (yych <= '`') goto yy3;
- if (yych <= 'z') goto yy145;
+ if (yych <= 'z') goto yy170;
goto yy3;
}
}
@@ -1995,21 +2202,21 @@ yy34:
yych = *++YYCURSOR;
if (yych <= 'U') {
if (yych <= '@') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
- if (yych <= 'A') goto yy352;
- if (yych <= 'T') goto yy140;
- goto yy351;
+ if (yych <= 'A') goto yy377;
+ if (yych <= 'T') goto yy165;
+ goto yy376;
}
} else {
if (yych <= 'a') {
- if (yych <= 'Z') goto yy140;
+ if (yych <= 'Z') goto yy165;
if (yych <= '`') goto yy3;
- goto yy361;
+ goto yy386;
} else {
- if (yych == 'u') goto yy360;
- if (yych <= 'z') goto yy145;
+ if (yych == 'u') goto yy385;
+ if (yych <= 'z') goto yy170;
goto yy3;
}
}
@@ -2017,21 +2224,21 @@ yy35:
yych = *++YYCURSOR;
if (yych <= 'U') {
if (yych <= '@') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
- if (yych <= 'A') goto yy352;
- if (yych <= 'T') goto yy140;
- goto yy351;
+ if (yych <= 'A') goto yy377;
+ if (yych <= 'T') goto yy165;
+ goto yy376;
}
} else {
if (yych <= 'a') {
- if (yych <= 'Z') goto yy140;
+ if (yych <= 'Z') goto yy165;
if (yych <= '`') goto yy3;
- goto yy352;
+ goto yy377;
} else {
- if (yych == 'u') goto yy351;
- if (yych <= 'z') goto yy140;
+ if (yych == 'u') goto yy376;
+ if (yych <= 'z') goto yy165;
goto yy3;
}
}
@@ -2039,35 +2246,35 @@ yy36:
yych = *++YYCURSOR;
if (yych <= 'U') {
if (yych <= 'F') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= '@') goto yy3;
- goto yy140;
+ goto yy165;
} else {
if (yych <= 'O') {
- if (yych <= 'G') goto yy330;
- goto yy140;
+ if (yych <= 'G') goto yy355;
+ goto yy165;
} else {
- if (yych <= 'P') goto yy329;
- if (yych <= 'T') goto yy140;
- goto yy328;
+ if (yych <= 'P') goto yy354;
+ if (yych <= 'T') goto yy165;
+ goto yy353;
}
}
} else {
if (yych <= 'o') {
if (yych <= '`') {
- if (yych <= 'Z') goto yy140;
+ if (yych <= 'Z') goto yy165;
goto yy3;
} else {
- if (yych == 'g') goto yy342;
- goto yy145;
+ if (yych == 'g') goto yy367;
+ goto yy170;
}
} else {
if (yych <= 't') {
- if (yych <= 'p') goto yy341;
- goto yy145;
+ if (yych <= 'p') goto yy366;
+ goto yy170;
} else {
- if (yych <= 'u') goto yy340;
- if (yych <= 'z') goto yy145;
+ if (yych <= 'u') goto yy365;
+ if (yych <= 'z') goto yy170;
goto yy3;
}
}
@@ -2076,35 +2283,35 @@ yy37:
yych = *++YYCURSOR;
if (yych <= 'U') {
if (yych <= 'F') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= '@') goto yy3;
- goto yy140;
+ goto yy165;
} else {
if (yych <= 'O') {
- if (yych <= 'G') goto yy330;
- goto yy140;
+ if (yych <= 'G') goto yy355;
+ goto yy165;
} else {
- if (yych <= 'P') goto yy329;
- if (yych <= 'T') goto yy140;
- goto yy328;
+ if (yych <= 'P') goto yy354;
+ if (yych <= 'T') goto yy165;
+ goto yy353;
}
}
} else {
if (yych <= 'o') {
if (yych <= '`') {
- if (yych <= 'Z') goto yy140;
+ if (yych <= 'Z') goto yy165;
goto yy3;
} else {
- if (yych == 'g') goto yy330;
- goto yy140;
+ if (yych == 'g') goto yy355;
+ goto yy165;
}
} else {
if (yych <= 't') {
- if (yych <= 'p') goto yy329;
- goto yy140;
+ if (yych <= 'p') goto yy354;
+ goto yy165;
} else {
- if (yych <= 'u') goto yy328;
- if (yych <= 'z') goto yy140;
+ if (yych <= 'u') goto yy353;
+ if (yych <= 'z') goto yy165;
goto yy3;
}
}
@@ -2114,20 +2321,20 @@ yy38:
if (yych <= 'C') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'B') goto yy140;
- goto yy318;
+ if (yych <= 'B') goto yy165;
+ goto yy343;
}
} else {
if (yych <= 'b') {
- if (yych <= 'Z') goto yy140;
+ if (yych <= 'Z') goto yy165;
if (yych <= '`') goto yy3;
- goto yy145;
+ goto yy170;
} else {
- if (yych <= 'c') goto yy323;
- if (yych <= 'z') goto yy145;
+ if (yych <= 'c') goto yy348;
+ if (yych <= 'z') goto yy170;
goto yy3;
}
}
@@ -2136,20 +2343,20 @@ yy39:
if (yych <= 'C') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'B') goto yy140;
- goto yy318;
+ if (yych <= 'B') goto yy165;
+ goto yy343;
}
} else {
if (yych <= 'b') {
- if (yych <= 'Z') goto yy140;
+ if (yych <= 'Z') goto yy165;
if (yych <= '`') goto yy3;
- goto yy140;
+ goto yy165;
} else {
- if (yych <= 'c') goto yy318;
- if (yych <= 'z') goto yy140;
+ if (yych <= 'c') goto yy343;
+ if (yych <= 'z') goto yy165;
goto yy3;
}
}
@@ -2158,20 +2365,20 @@ yy40:
if (yych <= 'E') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'D') goto yy140;
- goto yy191;
+ if (yych <= 'D') goto yy165;
+ goto yy216;
}
} else {
if (yych <= 'd') {
- if (yych <= 'Z') goto yy140;
+ if (yych <= 'Z') goto yy165;
if (yych <= '`') goto yy3;
- goto yy145;
+ goto yy170;
} else {
- if (yych <= 'e') goto yy309;
- if (yych <= 'z') goto yy145;
+ if (yych <= 'e') goto yy334;
+ if (yych <= 'z') goto yy170;
goto yy3;
}
}
@@ -2180,20 +2387,20 @@ yy41:
if (yych <= 'E') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'D') goto yy140;
- goto yy191;
+ if (yych <= 'D') goto yy165;
+ goto yy216;
}
} else {
if (yych <= 'd') {
- if (yych <= 'Z') goto yy140;
+ if (yych <= 'Z') goto yy165;
if (yych <= '`') goto yy3;
- goto yy140;
+ goto yy165;
} else {
- if (yych <= 'e') goto yy191;
- if (yych <= 'z') goto yy140;
+ if (yych <= 'e') goto yy216;
+ if (yych <= 'z') goto yy165;
goto yy3;
}
}
@@ -2202,20 +2409,20 @@ yy42:
if (yych <= 'E') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'D') goto yy140;
- goto yy164;
+ if (yych <= 'D') goto yy165;
+ goto yy189;
}
} else {
if (yych <= 'd') {
- if (yych <= 'Z') goto yy140;
+ if (yych <= 'Z') goto yy165;
if (yych <= '`') goto yy3;
- goto yy145;
+ goto yy170;
} else {
- if (yych <= 'e') goto yy178;
- if (yych <= 'z') goto yy145;
+ if (yych <= 'e') goto yy203;
+ if (yych <= 'z') goto yy170;
goto yy3;
}
}
@@ -2224,66 +2431,66 @@ yy43:
if (yych <= 'E') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'D') goto yy140;
- goto yy164;
+ if (yych <= 'D') goto yy165;
+ goto yy189;
}
} else {
if (yych <= 'd') {
- if (yych <= 'Z') goto yy140;
+ if (yych <= 'Z') goto yy165;
if (yych <= '`') goto yy3;
- goto yy140;
+ goto yy165;
} else {
- if (yych <= 'e') goto yy164;
- if (yych <= 'z') goto yy140;
+ if (yych <= 'e') goto yy189;
+ if (yych <= 'z') goto yy165;
goto yy3;
}
}
yy44:
yych = *++YYCURSOR;
if (yych <= 'L') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= '@') goto yy3;
- goto yy140;
+ goto yy165;
} else {
if (yych <= 'Z') {
- if (yych <= 'M') goto yy156;
- goto yy140;
+ if (yych <= 'M') goto yy181;
+ goto yy165;
} else {
if (yych <= '`') goto yy3;
- if (yych <= 'z') goto yy145;
+ if (yych <= 'z') goto yy170;
goto yy3;
}
}
yy45:
yych = *++YYCURSOR;
if (yych <= '@') goto yy12;
- if (yych <= 'Z') goto yy155;
+ if (yych <= 'Z') goto yy180;
if (yych <= '`') goto yy12;
- if (yych <= 'z') goto yy155;
+ if (yych <= 'z') goto yy180;
goto yy12;
yy46:
yych = *++YYCURSOR;
if (yych <= '@') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
- if (yych <= 'Z') goto yy140;
+ if (yych <= 'Z') goto yy165;
if (yych <= '`') goto yy3;
- if (yych <= 'z') goto yy145;
+ if (yych <= 'z') goto yy170;
goto yy3;
}
yy47:
yych = *++YYCURSOR;
if (yych <= '@') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
- if (yych <= 'Z') goto yy140;
+ if (yych <= 'Z') goto yy165;
if (yych <= '`') goto yy3;
- if (yych <= 'z') goto yy140;
+ if (yych <= 'z') goto yy165;
goto yy3;
}
yy48:
@@ -2295,77 +2502,91 @@ yy48:
if (yych <= '/') goto yy49;
if (yych <= '9') goto yy54;
yy49:
-#line 1714 "ext/date/lib/parse_date.re"
+#line 1757 "ext/date/lib/parse_date.re"
{
goto std;
}
-#line 2303 "ext/date/lib/parse_date.c"
+#line 2510 "ext/date/lib/parse_date.c"
yy50:
yych = *++YYCURSOR;
goto yy49;
yy51:
++YYCURSOR;
-#line 1719 "ext/date/lib/parse_date.re"
+#line 1762 "ext/date/lib/parse_date.re"
{
s->pos = cursor; s->line++;
goto std;
}
-#line 2314 "ext/date/lib/parse_date.c"
+#line 2521 "ext/date/lib/parse_date.c"
yy53:
yych = *++YYCURSOR;
goto yy12;
yy54:
++YYCURSOR;
- if ((YYLIMIT - YYCURSOR) < 11) YYFILL(11);
+ if ((YYLIMIT - YYCURSOR) < 12) YYFILL(12);
yych = *YYCURSOR;
if (yybm[0+yych] & 2) {
goto yy54;
}
- if (yych <= 'W') {
- if (yych <= 'F') {
+ if (yych <= 'X') {
+ if (yych <= 'G') {
if (yych <= ' ') {
if (yych == '\t') goto yy59;
if (yych >= ' ') goto yy59;
} else {
- if (yych == 'D') goto yy64;
- if (yych >= 'F') goto yy65;
+ if (yych <= 'D') {
+ if (yych >= 'D') goto yy66;
+ } else {
+ if (yych == 'F') goto yy67;
+ }
}
} else {
- if (yych <= 'M') {
- if (yych == 'H') goto yy63;
- if (yych >= 'M') goto yy62;
+ if (yych <= 'S') {
+ if (yych <= 'L') {
+ if (yych <= 'H') goto yy65;
+ } else {
+ if (yych <= 'M') goto yy61;
+ if (yych >= 'S') goto yy64;
+ }
} else {
- if (yych <= 'S') {
- if (yych >= 'S') goto yy61;
+ if (yych <= 'U') {
+ if (yych <= 'T') goto yy70;
+ goto yy63;
} else {
- if (yych <= 'T') goto yy68;
- if (yych >= 'W') goto yy67;
+ if (yych == 'W') goto yy69;
}
}
}
} else {
- if (yych <= 'l') {
- if (yych <= 'd') {
- if (yych == 'Y') goto yy66;
- if (yych >= 'd') goto yy64;
+ if (yych <= 'r') {
+ if (yych <= 'f') {
+ if (yych <= 'c') {
+ if (yych <= 'Y') goto yy68;
+ } else {
+ if (yych <= 'd') goto yy66;
+ if (yych >= 'f') goto yy67;
+ }
} else {
- if (yych <= 'f') {
- if (yych >= 'f') goto yy65;
+ if (yych <= 'h') {
+ if (yych >= 'h') goto yy65;
} else {
- if (yych == 'h') goto yy63;
+ if (yych == 'm') goto yy61;
}
}
} else {
- if (yych <= 't') {
- if (yych <= 'm') goto yy62;
- if (yych <= 'r') goto yy56;
- if (yych <= 's') goto yy61;
- goto yy68;
+ if (yych <= 'w') {
+ if (yych <= 't') {
+ if (yych <= 's') goto yy64;
+ goto yy70;
+ } else {
+ if (yych <= 'u') goto yy63;
+ if (yych >= 'w') goto yy69;
+ }
} else {
- if (yych <= 'w') {
- if (yych >= 'w') goto yy67;
+ if (yych <= 'y') {
+ if (yych >= 'y') goto yy68;
} else {
- if (yych == 'y') goto yy66;
+ if (yych == 0xC2) goto yy62;
}
}
}
@@ -2387,23 +2608,23 @@ yy56:
}
} else {
if (yyaccept == 3) {
- goto yy72;
+ goto yy74;
} else {
- goto yy166;
+ goto yy191;
}
}
} else {
if (yyaccept <= 6) {
if (yyaccept == 5) {
- goto yy193;
+ goto yy218;
} else {
- goto yy207;
+ goto yy232;
}
} else {
if (yyaccept == 7) {
- goto yy221;
+ goto yy246;
} else {
- goto yy332;
+ goto yy357;
}
}
}
@@ -2411,91 +2632,95 @@ yy56:
if (yyaccept <= 12) {
if (yyaccept <= 10) {
if (yyaccept == 9) {
- goto yy415;
+ goto yy440;
} else {
- goto yy430;
+ goto yy455;
}
} else {
if (yyaccept == 11) {
- goto yy551;
+ goto yy576;
} else {
- goto yy596;
+ goto yy621;
}
}
} else {
if (yyaccept <= 14) {
if (yyaccept == 13) {
- goto yy606;
+ goto yy631;
} else {
- goto yy703;
+ goto yy728;
}
} else {
if (yyaccept == 15) {
- goto yy723;
+ goto yy748;
} else {
- goto yy754;
+ goto yy779;
}
}
}
}
} else {
- if (yyaccept <= 24) {
- if (yyaccept <= 20) {
- if (yyaccept <= 18) {
- if (yyaccept == 17) {
- goto yy761;
+ if (yyaccept <= 25) {
+ if (yyaccept <= 21) {
+ if (yyaccept <= 19) {
+ if (yyaccept <= 18) {
+ if (yyaccept == 17) {
+ goto yy786;
+ } else {
+ goto yy813;
+ }
} else {
- goto yy788;
+ goto yy758;
}
} else {
- if (yyaccept == 19) {
- goto yy733;
+ if (yyaccept == 20) {
+ goto yy419;
} else {
- goto yy394;
+ goto yy938;
}
}
} else {
- if (yyaccept <= 22) {
- if (yyaccept == 21) {
- goto yy913;
+ if (yyaccept <= 23) {
+ if (yyaccept == 22) {
+ goto yy807;
} else {
- goto yy782;
+ goto yy1032;
}
} else {
- if (yyaccept == 23) {
- goto yy1007;
+ if (yyaccept == 24) {
+ goto yy1040;
} else {
- goto yy1015;
+ goto yy1084;
}
}
}
} else {
- if (yyaccept <= 28) {
- if (yyaccept <= 26) {
- if (yyaccept == 25) {
- goto yy1057;
+ if (yyaccept <= 29) {
+ if (yyaccept <= 27) {
+ if (yyaccept == 26) {
+ goto yy1108;
} else {
- goto yy1081;
+ goto yy1284;
}
} else {
- if (yyaccept == 27) {
- goto yy1234;
+ if (yyaccept == 28) {
+ goto yy1377;
} else {
- goto yy1357;
+ goto yy1415;
}
}
} else {
- if (yyaccept <= 30) {
- if (yyaccept == 29) {
- goto yy1360;
+ if (yyaccept <= 31) {
+ if (yyaccept == 30) {
+ goto yy1418;
} else {
- goto yy1440;
+ goto yy1498;
}
} else {
- if (yyaccept == 31) {
- goto yy1448;
+ if (yyaccept == 32) {
+ goto yy1506;
} else {
- goto yy1471;
+ goto yy1529;
}
}
}
@@ -2513,61 +2738,79 @@ yy57:
goto yy56;
yy59:
++YYCURSOR;
- if ((YYLIMIT - YYCURSOR) < 11) YYFILL(11);
+ if ((YYLIMIT - YYCURSOR) < 12) YYFILL(12);
yych = *YYCURSOR;
yy60:
- if (yych <= 'W') {
- if (yych <= 'F') {
+ if (yych <= 'X') {
+ if (yych <= 'G') {
if (yych <= ' ') {
if (yych == '\t') goto yy59;
if (yych <= 0x1F) goto yy56;
goto yy59;
} else {
- if (yych == 'D') goto yy64;
- if (yych <= 'E') goto yy56;
- goto yy65;
+ if (yych <= 'D') {
+ if (yych <= 'C') goto yy56;
+ goto yy66;
+ } else {
+ if (yych == 'F') goto yy67;
+ goto yy56;
+ }
}
} else {
- if (yych <= 'M') {
- if (yych == 'H') goto yy63;
- if (yych <= 'L') goto yy56;
- goto yy62;
- } else {
- if (yych <= 'S') {
+ if (yych <= 'S') {
+ if (yych <= 'L') {
+ if (yych <= 'H') goto yy65;
+ goto yy56;
+ } else {
+ if (yych <= 'M') goto yy61;
if (yych <= 'R') goto yy56;
+ goto yy64;
+ }
+ } else {
+ if (yych <= 'U') {
+ if (yych <= 'T') goto yy70;
+ goto yy63;
} else {
- if (yych <= 'T') goto yy68;
- if (yych <= 'V') goto yy56;
- goto yy67;
+ if (yych == 'W') goto yy69;
+ goto yy56;
}
}
}
} else {
- if (yych <= 'l') {
- if (yych <= 'd') {
- if (yych == 'Y') goto yy66;
- if (yych <= 'c') goto yy56;
- goto yy64;
- } else {
- if (yych <= 'f') {
+ if (yych <= 'r') {
+ if (yych <= 'f') {
+ if (yych <= 'c') {
+ if (yych <= 'Y') goto yy68;
+ goto yy56;
+ } else {
+ if (yych <= 'd') goto yy66;
if (yych <= 'e') goto yy56;
+ goto yy67;
+ }
+ } else {
+ if (yych <= 'h') {
+ if (yych <= 'g') goto yy56;
goto yy65;
} else {
- if (yych == 'h') goto yy63;
- goto yy56;
+ if (yych != 'm') goto yy56;
}
}
} else {
- if (yych <= 't') {
- if (yych <= 'm') goto yy62;
- if (yych <= 'r') goto yy56;
- if (yych >= 't') goto yy68;
- } else {
- if (yych <= 'w') {
+ if (yych <= 'w') {
+ if (yych <= 't') {
+ if (yych <= 's') goto yy64;
+ goto yy70;
+ } else {
+ if (yych <= 'u') goto yy63;
if (yych <= 'v') goto yy56;
- goto yy67;
+ goto yy69;
+ }
+ } else {
+ if (yych <= 'y') {
+ if (yych <= 'x') goto yy56;
+ goto yy68;
} else {
- if (yych == 'y') goto yy66;
+ if (yych == 0xC2) goto yy62;
goto yy56;
}
}
@@ -2575,105 +2818,120 @@ yy60:
}
yy61:
yych = *++YYCURSOR;
- if (yych <= 'U') {
- if (yych <= 'D') {
- if (yych == 'A') goto yy126;
+ if (yych <= 'S') {
+ if (yych <= 'N') {
+ if (yych == 'I') goto yy138;
goto yy56;
} else {
- if (yych <= 'E') goto yy127;
- if (yych <= 'T') goto yy56;
- goto yy125;
+ if (yych <= 'O') goto yy137;
+ if (yych <= 'R') goto yy56;
+ goto yy139;
}
} else {
- if (yych <= 'd') {
- if (yych == 'a') goto yy126;
+ if (yych <= 'n') {
+ if (yych == 'i') goto yy138;
goto yy56;
} else {
- if (yych <= 'e') goto yy127;
- if (yych == 'u') goto yy125;
+ if (yych <= 'o') goto yy137;
+ if (yych == 's') goto yy139;
goto yy56;
}
}
yy62:
yych = *++YYCURSOR;
- if (yych <= 'O') {
- if (yych == 'I') goto yy117;
- if (yych <= 'N') goto yy56;
- goto yy116;
+ if (yych == 0xB5) goto yy134;
+ goto yy56;
+yy63:
+ yych = *++YYCURSOR;
+ if (yych == 'S') goto yy132;
+ if (yych == 's') goto yy132;
+ goto yy56;
+yy64:
+ yych = *++YYCURSOR;
+ if (yych <= 'U') {
+ if (yych <= 'D') {
+ if (yych == 'A') goto yy119;
+ goto yy56;
+ } else {
+ if (yych <= 'E') goto yy120;
+ if (yych <= 'T') goto yy56;
+ goto yy118;
+ }
} else {
- if (yych <= 'i') {
- if (yych <= 'h') goto yy56;
- goto yy117;
+ if (yych <= 'd') {
+ if (yych == 'a') goto yy119;
+ goto yy56;
} else {
- if (yych == 'o') goto yy116;
+ if (yych <= 'e') goto yy120;
+ if (yych == 'u') goto yy118;
goto yy56;
}
}
-yy63:
+yy65:
yych = *++YYCURSOR;
- if (yych == 'O') goto yy114;
- if (yych == 'o') goto yy114;
+ if (yych == 'O') goto yy116;
+ if (yych == 'o') goto yy116;
goto yy56;
-yy64:
+yy66:
yych = *++YYCURSOR;
- if (yych == 'A') goto yy113;
- if (yych == 'a') goto yy113;
+ if (yych == 'A') goto yy115;
+ if (yych == 'a') goto yy115;
goto yy56;
-yy65:
+yy67:
yych = *++YYCURSOR;
if (yych <= 'R') {
- if (yych == 'O') goto yy98;
+ if (yych == 'O') goto yy100;
if (yych <= 'Q') goto yy56;
- goto yy97;
+ goto yy99;
} else {
if (yych <= 'o') {
if (yych <= 'n') goto yy56;
- goto yy98;
+ goto yy100;
} else {
- if (yych == 'r') goto yy97;
+ if (yych == 'r') goto yy99;
goto yy56;
}
}
-yy66:
+yy68:
yych = *++YYCURSOR;
- if (yych == 'E') goto yy94;
- if (yych == 'e') goto yy94;
+ if (yych == 'E') goto yy96;
+ if (yych == 'e') goto yy96;
goto yy56;
-yy67:
+yy69:
yych = *++YYCURSOR;
- if (yych == 'E') goto yy82;
- if (yych == 'e') goto yy82;
+ if (yych == 'E') goto yy84;
+ if (yych == 'e') goto yy84;
goto yy56;
-yy68:
+yy70:
yych = *++YYCURSOR;
if (yych <= 'U') {
- if (yych == 'H') goto yy69;
+ if (yych == 'H') goto yy71;
if (yych <= 'T') goto yy56;
- goto yy70;
+ goto yy72;
} else {
if (yych <= 'h') {
if (yych <= 'g') goto yy56;
} else {
- if (yych == 'u') goto yy70;
+ if (yych == 'u') goto yy72;
goto yy56;
}
}
-yy69:
+yy71:
yych = *++YYCURSOR;
- if (yych == 'U') goto yy77;
- if (yych == 'u') goto yy77;
+ if (yych == 'U') goto yy79;
+ if (yych == 'u') goto yy79;
goto yy56;
-yy70:
+yy72:
yych = *++YYCURSOR;
- if (yych == 'E') goto yy71;
+ if (yych == 'E') goto yy73;
if (yych != 'e') goto yy56;
-yy71:
+yy73:
yyaccept = 3;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych == 'S') goto yy73;
- if (yych == 's') goto yy73;
-yy72:
-#line 1698 "ext/date/lib/parse_date.re"
+ if (yych == 'S') goto yy75;
+ if (yych == 's') goto yy75;
+yy74:
+#line 1741 "ext/date/lib/parse_date.re"
{
timelib_ull i;
DEBUG_OUTPUT("relative");
@@ -2688,427 +2946,545 @@ yy72:
TIMELIB_DEINIT;
return TIMELIB_RELATIVE;
}
-#line 2692 "ext/date/lib/parse_date.c"
-yy73:
+#line 2950 "ext/date/lib/parse_date.c"
+yy75:
yych = *++YYCURSOR;
- if (yych == 'D') goto yy74;
+ if (yych == 'D') goto yy76;
if (yych != 'd') goto yy56;
-yy74:
+yy76:
yych = *++YYCURSOR;
- if (yych == 'A') goto yy75;
+ if (yych == 'A') goto yy77;
if (yych != 'a') goto yy56;
-yy75:
+yy77:
yych = *++YYCURSOR;
- if (yych == 'Y') goto yy76;
+ if (yych == 'Y') goto yy78;
if (yych != 'y') goto yy56;
-yy76:
+yy78:
yych = *++YYCURSOR;
- goto yy72;
-yy77:
+ goto yy74;
+yy79:
yyaccept = 3;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych == 'R') goto yy78;
- if (yych != 'r') goto yy72;
-yy78:
+ if (yych == 'R') goto yy80;
+ if (yych != 'r') goto yy74;
+yy80:
yych = *++YYCURSOR;
- if (yych == 'S') goto yy79;
+ if (yych == 'S') goto yy81;
if (yych != 's') goto yy56;
-yy79:
+yy81:
yych = *++YYCURSOR;
- if (yych == 'D') goto yy80;
+ if (yych == 'D') goto yy82;
if (yych != 'd') goto yy56;
-yy80:
+yy82:
yych = *++YYCURSOR;
- if (yych == 'A') goto yy81;
+ if (yych == 'A') goto yy83;
if (yych != 'a') goto yy56;
-yy81:
+yy83:
yych = *++YYCURSOR;
- if (yych == 'Y') goto yy76;
- if (yych == 'y') goto yy76;
+ if (yych == 'Y') goto yy78;
+ if (yych == 'y') goto yy78;
goto yy56;
-yy82:
+yy84:
yych = *++YYCURSOR;
if (yych <= 'E') {
if (yych <= 'C') goto yy56;
- if (yych <= 'D') goto yy84;
+ if (yych <= 'D') goto yy86;
} else {
if (yych <= 'c') goto yy56;
- if (yych <= 'd') goto yy84;
+ if (yych <= 'd') goto yy86;
if (yych >= 'f') goto yy56;
}
yych = *++YYCURSOR;
- if (yych == 'K') goto yy90;
- if (yych == 'k') goto yy90;
+ if (yych == 'K') goto yy92;
+ if (yych == 'k') goto yy92;
goto yy56;
-yy84:
+yy86:
yyaccept = 3;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych == 'N') goto yy85;
- if (yych != 'n') goto yy72;
-yy85:
+ if (yych == 'N') goto yy87;
+ if (yych != 'n') goto yy74;
+yy87:
yych = *++YYCURSOR;
- if (yych == 'E') goto yy86;
+ if (yych == 'E') goto yy88;
if (yych != 'e') goto yy56;
-yy86:
+yy88:
yych = *++YYCURSOR;
- if (yych == 'S') goto yy87;
+ if (yych == 'S') goto yy89;
if (yych != 's') goto yy56;
-yy87:
+yy89:
yych = *++YYCURSOR;
- if (yych == 'D') goto yy88;
+ if (yych == 'D') goto yy90;
if (yych != 'd') goto yy56;
-yy88:
+yy90:
yych = *++YYCURSOR;
- if (yych == 'A') goto yy89;
+ if (yych == 'A') goto yy91;
if (yych != 'a') goto yy56;
-yy89:
+yy91:
yych = *++YYCURSOR;
- if (yych == 'Y') goto yy76;
- if (yych == 'y') goto yy76;
+ if (yych == 'Y') goto yy78;
+ if (yych == 'y') goto yy78;
goto yy56;
-yy90:
+yy92:
yyaccept = 3;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'S') {
- if (yych == 'D') goto yy91;
- if (yych <= 'R') goto yy72;
- goto yy76;
+ if (yych == 'D') goto yy93;
+ if (yych <= 'R') goto yy74;
+ goto yy78;
} else {
if (yych <= 'd') {
- if (yych <= 'c') goto yy72;
+ if (yych <= 'c') goto yy74;
} else {
- if (yych == 's') goto yy76;
- goto yy72;
+ if (yych == 's') goto yy78;
+ goto yy74;
}
}
-yy91:
+yy93:
yych = *++YYCURSOR;
- if (yych == 'A') goto yy92;
+ if (yych == 'A') goto yy94;
if (yych != 'a') goto yy56;
-yy92:
+yy94:
yych = *++YYCURSOR;
- if (yych == 'Y') goto yy93;
+ if (yych == 'Y') goto yy95;
if (yych != 'y') goto yy56;
-yy93:
+yy95:
yych = *++YYCURSOR;
- if (yych == 'S') goto yy76;
- if (yych == 's') goto yy76;
- goto yy72;
-yy94:
+ if (yych == 'S') goto yy78;
+ if (yych == 's') goto yy78;
+ goto yy74;
+yy96:
yych = *++YYCURSOR;
- if (yych == 'A') goto yy95;
+ if (yych == 'A') goto yy97;
if (yych != 'a') goto yy56;
-yy95:
+yy97:
yych = *++YYCURSOR;
- if (yych == 'R') goto yy96;
+ if (yych == 'R') goto yy98;
if (yych != 'r') goto yy56;
-yy96:
+yy98:
yych = *++YYCURSOR;
- if (yych == 'S') goto yy76;
- if (yych == 's') goto yy76;
- goto yy72;
-yy97:
+ if (yych == 'S') goto yy78;
+ if (yych == 's') goto yy78;
+ goto yy74;
+yy99:
yych = *++YYCURSOR;
- if (yych == 'I') goto yy110;
- if (yych == 'i') goto yy110;
+ if (yych == 'I') goto yy112;
+ if (yych == 'i') goto yy112;
goto yy56;
-yy98:
+yy100:
yych = *++YYCURSOR;
- if (yych == 'R') goto yy99;
+ if (yych == 'R') goto yy101;
if (yych != 'r') goto yy56;
-yy99:
+yy101:
yych = *++YYCURSOR;
- if (yych == 'T') goto yy100;
+ if (yych == 'T') goto yy102;
if (yych != 't') goto yy56;
-yy100:
+yy102:
yych = *++YYCURSOR;
if (yych <= 'N') {
- if (yych == 'H') goto yy102;
+ if (yych == 'H') goto yy104;
if (yych <= 'M') goto yy56;
} else {
if (yych <= 'h') {
if (yych <= 'g') goto yy56;
- goto yy102;
+ goto yy104;
} else {
if (yych != 'n') goto yy56;
}
}
yych = *++YYCURSOR;
- if (yych == 'I') goto yy107;
- if (yych == 'i') goto yy107;
+ if (yych == 'I') goto yy109;
+ if (yych == 'i') goto yy109;
goto yy56;
-yy102:
+yy104:
yych = *++YYCURSOR;
- if (yych == 'N') goto yy103;
+ if (yych == 'N') goto yy105;
if (yych != 'n') goto yy56;
-yy103:
+yy105:
yych = *++YYCURSOR;
- if (yych == 'I') goto yy104;
+ if (yych == 'I') goto yy106;
if (yych != 'i') goto yy56;
-yy104:
+yy106:
yych = *++YYCURSOR;
- if (yych == 'G') goto yy105;
+ if (yych == 'G') goto yy107;
if (yych != 'g') goto yy56;
-yy105:
+yy107:
yych = *++YYCURSOR;
- if (yych == 'H') goto yy106;
+ if (yych == 'H') goto yy108;
if (yych != 'h') goto yy56;
-yy106:
+yy108:
yych = *++YYCURSOR;
- if (yych == 'T') goto yy96;
- if (yych == 't') goto yy96;
+ if (yych == 'T') goto yy98;
+ if (yych == 't') goto yy98;
goto yy56;
-yy107:
+yy109:
yych = *++YYCURSOR;
- if (yych == 'G') goto yy108;
+ if (yych == 'G') goto yy110;
if (yych != 'g') goto yy56;
-yy108:
+yy110:
yych = *++YYCURSOR;
- if (yych == 'H') goto yy109;
+ if (yych == 'H') goto yy111;
if (yych != 'h') goto yy56;
-yy109:
+yy111:
yych = *++YYCURSOR;
- if (yych == 'T') goto yy96;
- if (yych == 't') goto yy96;
+ if (yych == 'T') goto yy98;
+ if (yych == 't') goto yy98;
goto yy56;
-yy110:
+yy112:
yyaccept = 3;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych == 'D') goto yy111;
- if (yych != 'd') goto yy72;
-yy111:
- yych = *++YYCURSOR;
- if (yych == 'A') goto yy112;
- if (yych != 'a') goto yy56;
-yy112:
- yych = *++YYCURSOR;
- if (yych == 'Y') goto yy76;
- if (yych == 'y') goto yy76;
- goto yy56;
+ if (yych == 'D') goto yy113;
+ if (yych != 'd') goto yy74;
yy113:
yych = *++YYCURSOR;
- if (yych == 'Y') goto yy96;
- if (yych == 'y') goto yy96;
- goto yy56;
+ if (yych == 'A') goto yy114;
+ if (yych != 'a') goto yy56;
yy114:
yych = *++YYCURSOR;
- if (yych == 'U') goto yy115;
- if (yych != 'u') goto yy56;
+ if (yych == 'Y') goto yy78;
+ if (yych == 'y') goto yy78;
+ goto yy56;
yy115:
yych = *++YYCURSOR;
- if (yych == 'R') goto yy96;
- if (yych == 'r') goto yy96;
+ if (yych == 'Y') goto yy98;
+ if (yych == 'y') goto yy98;
goto yy56;
yy116:
yych = *++YYCURSOR;
- if (yych == 'N') goto yy121;
- if (yych == 'n') goto yy121;
- goto yy56;
+ if (yych == 'U') goto yy117;
+ if (yych != 'u') goto yy56;
yy117:
yych = *++YYCURSOR;
- if (yych == 'N') goto yy118;
- if (yych != 'n') goto yy56;
+ if (yych == 'R') goto yy98;
+ if (yych == 'r') goto yy98;
+ goto yy56;
yy118:
- yyaccept = 3;
- yych = *(YYMARKER = ++YYCURSOR);
- if (yych <= 'U') {
- if (yych == 'S') goto yy76;
- if (yych <= 'T') goto yy72;
- } else {
- if (yych <= 's') {
- if (yych <= 'r') goto yy72;
- goto yy76;
- } else {
- if (yych != 'u') goto yy72;
- }
- }
yych = *++YYCURSOR;
- if (yych == 'T') goto yy120;
- if (yych != 't') goto yy56;
-yy120:
+ if (yych == 'N') goto yy129;
+ if (yych == 'n') goto yy129;
+ goto yy56;
+yy119:
yych = *++YYCURSOR;
- if (yych == 'E') goto yy96;
- if (yych == 'e') goto yy96;
+ if (yych == 'T') goto yy124;
+ if (yych == 't') goto yy124;
goto yy56;
+yy120:
+ yych = *++YYCURSOR;
+ if (yych == 'C') goto yy121;
+ if (yych != 'c') goto yy56;
yy121:
yyaccept = 3;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych <= 'T') {
- if (yych == 'D') goto yy122;
- if (yych <= 'S') goto yy72;
- goto yy123;
+ if (yych <= 'S') {
+ if (yych == 'O') goto yy122;
+ if (yych <= 'R') goto yy74;
+ goto yy78;
} else {
- if (yych <= 'd') {
- if (yych <= 'c') goto yy72;
+ if (yych <= 'o') {
+ if (yych <= 'n') goto yy74;
} else {
- if (yych == 't') goto yy123;
- goto yy72;
+ if (yych == 's') goto yy78;
+ goto yy74;
}
}
yy122:
yych = *++YYCURSOR;
- if (yych == 'A') goto yy124;
- if (yych == 'a') goto yy124;
- goto yy56;
+ if (yych == 'N') goto yy123;
+ if (yych != 'n') goto yy56;
yy123:
yych = *++YYCURSOR;
- if (yych == 'H') goto yy96;
- if (yych == 'h') goto yy96;
+ if (yych == 'D') goto yy98;
+ if (yych == 'd') goto yy98;
goto yy56;
yy124:
- yych = *++YYCURSOR;
- if (yych == 'Y') goto yy76;
- if (yych == 'y') goto yy76;
- goto yy56;
+ yyaccept = 3;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych == 'U') goto yy125;
+ if (yych != 'u') goto yy74;
yy125:
yych = *++YYCURSOR;
- if (yych == 'N') goto yy136;
- if (yych == 'n') goto yy136;
- goto yy56;
+ if (yych == 'R') goto yy126;
+ if (yych != 'r') goto yy56;
yy126:
yych = *++YYCURSOR;
- if (yych == 'T') goto yy131;
- if (yych == 't') goto yy131;
- goto yy56;
+ if (yych == 'D') goto yy127;
+ if (yych != 'd') goto yy56;
yy127:
yych = *++YYCURSOR;
- if (yych == 'C') goto yy128;
- if (yych != 'c') goto yy56;
+ if (yych == 'A') goto yy128;
+ if (yych != 'a') goto yy56;
yy128:
+ yych = *++YYCURSOR;
+ if (yych == 'Y') goto yy78;
+ if (yych == 'y') goto yy78;
+ goto yy56;
+yy129:
yyaccept = 3;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych <= 'S') {
- if (yych == 'O') goto yy129;
- if (yych <= 'R') goto yy72;
- goto yy76;
+ if (yych == 'D') goto yy130;
+ if (yych != 'd') goto yy74;
+yy130:
+ yych = *++YYCURSOR;
+ if (yych == 'A') goto yy131;
+ if (yych != 'a') goto yy56;
+yy131:
+ yych = *++YYCURSOR;
+ if (yych == 'Y') goto yy78;
+ if (yych == 'y') goto yy78;
+ goto yy56;
+yy132:
+ yych = *++YYCURSOR;
+ if (yych == 'E') goto yy133;
+ if (yych != 'e') goto yy56;
+yy133:
+ yych = *++YYCURSOR;
+ if (yych == 'C') goto yy98;
+ if (yych == 'c') goto yy98;
+ goto yy56;
+yy134:
+ yych = *++YYCURSOR;
+ if (yych == 'S') goto yy135;
+ if (yych != 's') goto yy56;
+yy135:
+ yyaccept = 3;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych == 'E') goto yy136;
+ if (yych != 'e') goto yy74;
+yy136:
+ yych = *++YYCURSOR;
+ if (yych == 'C') goto yy98;
+ if (yych == 'c') goto yy98;
+ goto yy56;
+yy137:
+ yych = *++YYCURSOR;
+ if (yych == 'N') goto yy160;
+ if (yych == 'n') goto yy160;
+ goto yy56;
+yy138:
+ yych = *++YYCURSOR;
+ if (yych <= 'N') {
+ if (yych <= 'K') {
+ if (yych == 'C') goto yy142;
+ goto yy56;
+ } else {
+ if (yych <= 'L') goto yy141;
+ if (yych <= 'M') goto yy56;
+ goto yy143;
+ }
} else {
- if (yych <= 'o') {
- if (yych <= 'n') goto yy72;
+ if (yych <= 'k') {
+ if (yych == 'c') goto yy142;
+ goto yy56;
} else {
- if (yych == 's') goto yy76;
- goto yy72;
+ if (yych <= 'l') goto yy141;
+ if (yych == 'n') goto yy143;
+ goto yy56;
}
}
-yy129:
+yy139:
+ yyaccept = 3;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych == 'E') goto yy140;
+ if (yych != 'e') goto yy74;
+yy140:
yych = *++YYCURSOR;
- if (yych == 'N') goto yy130;
- if (yych != 'n') goto yy56;
-yy130:
+ if (yych == 'C') goto yy98;
+ if (yych == 'c') goto yy98;
+ goto yy56;
+yy141:
yych = *++YYCURSOR;
- if (yych == 'D') goto yy96;
- if (yych == 'd') goto yy96;
+ if (yych == 'L') goto yy153;
+ if (yych == 'l') goto yy153;
goto yy56;
-yy131:
+yy142:
+ yych = *++YYCURSOR;
+ if (yych == 'R') goto yy146;
+ if (yych == 'r') goto yy146;
+ goto yy56;
+yy143:
yyaccept = 3;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych == 'U') goto yy132;
- if (yych != 'u') goto yy72;
-yy132:
+ if (yych <= 'U') {
+ if (yych == 'S') goto yy78;
+ if (yych <= 'T') goto yy74;
+ } else {
+ if (yych <= 's') {
+ if (yych <= 'r') goto yy74;
+ goto yy78;
+ } else {
+ if (yych != 'u') goto yy74;
+ }
+ }
yych = *++YYCURSOR;
- if (yych == 'R') goto yy133;
- if (yych != 'r') goto yy56;
-yy133:
+ if (yych == 'T') goto yy145;
+ if (yych != 't') goto yy56;
+yy145:
yych = *++YYCURSOR;
- if (yych == 'D') goto yy134;
- if (yych != 'd') goto yy56;
-yy134:
+ if (yych == 'E') goto yy98;
+ if (yych == 'e') goto yy98;
+ goto yy56;
+yy146:
yych = *++YYCURSOR;
- if (yych == 'A') goto yy135;
- if (yych != 'a') goto yy56;
-yy135:
+ if (yych == 'O') goto yy147;
+ if (yych != 'o') goto yy56;
+yy147:
+ yych = *++YYCURSOR;
+ if (yych == 'S') goto yy148;
+ if (yych != 's') goto yy56;
+yy148:
+ yych = *++YYCURSOR;
+ if (yych == 'E') goto yy149;
+ if (yych != 'e') goto yy56;
+yy149:
+ yych = *++YYCURSOR;
+ if (yych == 'C') goto yy150;
+ if (yych != 'c') goto yy56;
+yy150:
yych = *++YYCURSOR;
- if (yych == 'Y') goto yy76;
- if (yych == 'y') goto yy76;
+ if (yych == 'O') goto yy151;
+ if (yych != 'o') goto yy56;
+yy151:
+ yych = *++YYCURSOR;
+ if (yych == 'N') goto yy152;
+ if (yych != 'n') goto yy56;
+yy152:
+ yych = *++YYCURSOR;
+ if (yych == 'D') goto yy98;
+ if (yych == 'd') goto yy98;
goto yy56;
-yy136:
+yy153:
+ yych = *++YYCURSOR;
+ if (yych == 'I') goto yy154;
+ if (yych != 'i') goto yy56;
+yy154:
+ yych = *++YYCURSOR;
+ if (yych == 'S') goto yy155;
+ if (yych != 's') goto yy56;
+yy155:
+ yych = *++YYCURSOR;
+ if (yych == 'E') goto yy156;
+ if (yych != 'e') goto yy56;
+yy156:
+ yych = *++YYCURSOR;
+ if (yych == 'C') goto yy157;
+ if (yych != 'c') goto yy56;
+yy157:
+ yych = *++YYCURSOR;
+ if (yych == 'O') goto yy158;
+ if (yych != 'o') goto yy56;
+yy158:
+ yych = *++YYCURSOR;
+ if (yych == 'N') goto yy159;
+ if (yych != 'n') goto yy56;
+yy159:
+ yych = *++YYCURSOR;
+ if (yych == 'D') goto yy98;
+ if (yych == 'd') goto yy98;
+ goto yy56;
+yy160:
yyaccept = 3;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych == 'D') goto yy137;
- if (yych != 'd') goto yy72;
-yy137:
+ if (yych <= 'T') {
+ if (yych == 'D') goto yy161;
+ if (yych <= 'S') goto yy74;
+ goto yy162;
+ } else {
+ if (yych <= 'd') {
+ if (yych <= 'c') goto yy74;
+ } else {
+ if (yych == 't') goto yy162;
+ goto yy74;
+ }
+ }
+yy161:
yych = *++YYCURSOR;
- if (yych == 'A') goto yy138;
- if (yych != 'a') goto yy56;
-yy138:
+ if (yych == 'A') goto yy163;
+ if (yych == 'a') goto yy163;
+ goto yy56;
+yy162:
yych = *++YYCURSOR;
- if (yych == 'Y') goto yy76;
- if (yych == 'y') goto yy76;
+ if (yych == 'H') goto yy98;
+ if (yych == 'h') goto yy98;
goto yy56;
-yy139:
+yy163:
+ yych = *++YYCURSOR;
+ if (yych == 'Y') goto yy78;
+ if (yych == 'y') goto yy78;
+ goto yy56;
+yy164:
yych = *++YYCURSOR;
goto yy3;
-yy140:
+yy165:
yych = *++YYCURSOR;
if (yych <= '@') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'Z') goto yy166;
if (yych <= '`') goto yy3;
if (yych >= '{') goto yy3;
}
-yy141:
+yy166:
yych = *++YYCURSOR;
if (yych <= '@') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
- if (yych <= 'Z') goto yy142;
+ if (yych <= 'Z') goto yy167;
if (yych <= '`') goto yy3;
if (yych >= '{') goto yy3;
}
-yy142:
+yy167:
yych = *++YYCURSOR;
if (yych <= '@') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'Z') goto yy168;
if (yych <= '`') goto yy3;
if (yych >= '{') goto yy3;
}
-yy143:
+yy168:
yych = *++YYCURSOR;
if (yych <= '@') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
- if (yych <= 'Z') goto yy144;
+ if (yych <= 'Z') goto yy169;
if (yych <= '`') goto yy3;
if (yych >= '{') goto yy3;
}
-yy144:
+yy169:
yych = *++YYCURSOR;
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
-yy145:
+yy170:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '/') {
if (yych <= ',') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
if (yych == '.') goto yy3;
- goto yy147;
+ goto yy172;
}
} else {
if (yych <= '^') {
if (yych <= '@') goto yy3;
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'Z') goto yy166;
goto yy3;
} else {
- if (yych <= '_') goto yy147;
+ if (yych <= '_') goto yy172;
if (yych <= '`') goto yy3;
if (yych >= '{') goto yy3;
}
}
-yy146:
+yy171:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '/') {
if (yych <= ',') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
if (yych == '.') goto yy3;
@@ -3116,141 +3492,141 @@ yy146:
} else {
if (yych <= '^') {
if (yych <= '@') goto yy3;
- if (yych <= 'Z') goto yy142;
+ if (yych <= 'Z') goto yy167;
goto yy3;
} else {
- if (yych <= '_') goto yy147;
+ if (yych <= '_') goto yy172;
if (yych <= '`') goto yy3;
- if (yych <= 'z') goto yy150;
+ if (yych <= 'z') goto yy175;
goto yy3;
}
}
-yy147:
+yy172:
++YYCURSOR;
if (YYLIMIT <= YYCURSOR) YYFILL(1);
yych = *YYCURSOR;
if (yybm[0+yych] & 8) {
- goto yy148;
+ goto yy173;
}
goto yy56;
-yy148:
+yy173:
yyaccept = 0;
YYMARKER = ++YYCURSOR;
if (YYLIMIT <= YYCURSOR) YYFILL(1);
yych = *YYCURSOR;
if (yybm[0+yych] & 8) {
- goto yy148;
+ goto yy173;
}
if (yych <= '.') {
- if (yych == '-') goto yy147;
+ if (yych == '-') goto yy172;
goto yy3;
} else {
- if (yych <= '/') goto yy147;
- if (yych == '_') goto yy147;
+ if (yych <= '/') goto yy172;
+ if (yych == '_') goto yy172;
goto yy3;
}
-yy150:
+yy175:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '/') {
if (yych <= ',') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
if (yych == '.') goto yy3;
- goto yy147;
+ goto yy172;
}
} else {
if (yych <= '^') {
if (yych <= '@') goto yy3;
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'Z') goto yy168;
goto yy3;
} else {
- if (yych <= '_') goto yy147;
+ if (yych <= '_') goto yy172;
if (yych <= '`') goto yy3;
if (yych >= '{') goto yy3;
}
}
-yy151:
+yy176:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '/') {
if (yych <= ',') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
if (yych == '.') goto yy3;
- goto yy147;
+ goto yy172;
}
} else {
if (yych <= '^') {
if (yych <= '@') goto yy3;
- if (yych <= 'Z') goto yy144;
+ if (yych <= 'Z') goto yy169;
goto yy3;
} else {
- if (yych <= '_') goto yy147;
+ if (yych <= '_') goto yy172;
if (yych <= '`') goto yy3;
if (yych >= '{') goto yy3;
}
}
-yy152:
+yy177:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yybm[0+yych] & 16) {
- goto yy153;
+ goto yy178;
}
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= '/') {
if (yych <= '.') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '_') goto yy147;
+ if (yych == '_') goto yy172;
goto yy3;
}
}
-yy153:
+yy178:
++YYCURSOR;
if (YYLIMIT <= YYCURSOR) YYFILL(1);
yych = *YYCURSOR;
-yy154:
+yy179:
if (yybm[0+yych] & 16) {
- goto yy153;
+ goto yy178;
}
if (yych <= '.') {
- if (yych == '-') goto yy147;
+ if (yych == '-') goto yy172;
goto yy56;
} else {
- if (yych <= '/') goto yy147;
- if (yych == '_') goto yy147;
+ if (yych <= '/') goto yy172;
+ if (yych == '_') goto yy172;
goto yy56;
}
-yy155:
+yy180:
yych = *++YYCURSOR;
if (yych <= '@') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
- if (yych <= 'Z') goto yy140;
+ if (yych <= 'Z') goto yy165;
if (yych <= '`') goto yy3;
- if (yych <= 'z') goto yy140;
+ if (yych <= 'z') goto yy165;
goto yy3;
}
-yy156:
+yy181:
yych = *++YYCURSOR;
if (yych <= 'S') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= '@') goto yy3;
- goto yy141;
+ goto yy166;
} else {
if (yych <= 'Z') {
- if (yych >= 'U') goto yy141;
+ if (yych >= 'U') goto yy166;
} else {
if (yych <= '`') goto yy3;
- if (yych <= 'z') goto yy141;
+ if (yych <= 'z') goto yy166;
goto yy3;
}
}
@@ -3259,104 +3635,104 @@ yy156:
if (yych <= ',') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych != '+') goto yy3;
}
} else {
if (yych <= 'Z') {
- if (yych <= '-') goto yy158;
+ if (yych <= '-') goto yy183;
if (yych <= '@') goto yy3;
- goto yy142;
+ goto yy167;
} else {
if (yych <= '`') goto yy3;
- if (yych <= 'z') goto yy142;
+ if (yych <= 'z') goto yy167;
goto yy3;
}
}
-yy158:
+yy183:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '1') goto yy159;
- if (yych <= '2') goto yy160;
- if (yych <= '9') goto yy161;
+ if (yych <= '1') goto yy184;
+ if (yych <= '2') goto yy185;
+ if (yych <= '9') goto yy186;
goto yy56;
-yy159:
+yy184:
yych = *++YYCURSOR;
if (yych <= '/') goto yy3;
- if (yych <= '9') goto yy161;
- if (yych <= ':') goto yy162;
+ if (yych <= '9') goto yy186;
+ if (yych <= ':') goto yy187;
goto yy3;
-yy160:
+yy185:
yych = *++YYCURSOR;
if (yych <= '5') {
if (yych <= '/') goto yy3;
- if (yych >= '5') goto yy163;
+ if (yych >= '5') goto yy188;
} else {
- if (yych <= '9') goto yy139;
- if (yych <= ':') goto yy162;
+ if (yych <= '9') goto yy164;
+ if (yych <= ':') goto yy187;
goto yy3;
}
-yy161:
+yy186:
yych = *++YYCURSOR;
if (yych <= '/') goto yy3;
- if (yych <= '5') goto yy163;
- if (yych <= '9') goto yy139;
+ if (yych <= '5') goto yy188;
+ if (yych <= '9') goto yy164;
if (yych >= ';') goto yy3;
-yy162:
+yy187:
yych = *++YYCURSOR;
if (yych <= '/') goto yy3;
- if (yych <= '5') goto yy163;
- if (yych <= '9') goto yy139;
+ if (yych <= '5') goto yy188;
+ if (yych <= '9') goto yy164;
goto yy3;
-yy163:
+yy188:
yych = *++YYCURSOR;
if (yych <= '/') goto yy3;
- if (yych <= '9') goto yy139;
+ if (yych <= '9') goto yy164;
goto yy3;
-yy164:
+yy189:
yych = *++YYCURSOR;
if (yych <= 'E') {
if (yych <= '@') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
- if (yych <= 'C') goto yy141;
- if (yych >= 'E') goto yy167;
+ if (yych <= 'C') goto yy166;
+ if (yych >= 'E') goto yy192;
}
} else {
if (yych <= 'c') {
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'Z') goto yy166;
if (yych <= '`') goto yy3;
- goto yy141;
+ goto yy166;
} else {
- if (yych <= 'd') goto yy165;
- if (yych <= 'e') goto yy167;
- if (yych <= 'z') goto yy141;
+ if (yych <= 'd') goto yy190;
+ if (yych <= 'e') goto yy192;
+ if (yych <= 'z') goto yy166;
goto yy3;
}
}
-yy165:
+yy190:
++YYCURSOR;
if ((yych = *YYCURSOR) <= 'N') {
if (yych <= ')') {
- if (yych >= ')') goto yy139;
+ if (yych >= ')') goto yy164;
} else {
- if (yych <= '@') goto yy166;
- if (yych <= 'M') goto yy142;
- goto yy173;
+ if (yych <= '@') goto yy191;
+ if (yych <= 'M') goto yy167;
+ goto yy198;
}
} else {
if (yych <= 'm') {
- if (yych <= 'Z') goto yy142;
- if (yych >= 'a') goto yy142;
+ if (yych <= 'Z') goto yy167;
+ if (yych >= 'a') goto yy167;
} else {
- if (yych <= 'n') goto yy173;
- if (yych <= 'z') goto yy142;
+ if (yych <= 'n') goto yy198;
+ if (yych <= 'z') goto yy167;
}
}
-yy166:
-#line 1561 "ext/date/lib/parse_date.re"
+yy191:
+#line 1604 "ext/date/lib/parse_date.re"
{
const timelib_relunit* relunit;
DEBUG_OUTPUT("daytext");
@@ -3373,174 +3749,174 @@ yy166:
TIMELIB_DEINIT;
return TIMELIB_WEEKDAY;
}
-#line 3377 "ext/date/lib/parse_date.c"
-yy167:
+#line 3753 "ext/date/lib/parse_date.c"
+yy192:
yych = *++YYCURSOR;
if (yych <= 'K') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'J') goto yy142;
+ if (yych <= 'J') goto yy167;
}
} else {
if (yych <= 'j') {
- if (yych <= 'Z') goto yy142;
+ if (yych <= 'Z') goto yy167;
if (yych <= '`') goto yy3;
- goto yy142;
+ goto yy167;
} else {
- if (yych <= 'k') goto yy168;
- if (yych <= 'z') goto yy142;
+ if (yych <= 'k') goto yy193;
+ if (yych <= 'z') goto yy167;
goto yy3;
}
}
-yy168:
+yy193:
yych = *++YYCURSOR;
if (yych <= 'D') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'C') goto yy143;
+ if (yych <= 'C') goto yy168;
}
} else {
if (yych <= 'c') {
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'Z') goto yy168;
if (yych <= '`') goto yy3;
- goto yy143;
+ goto yy168;
} else {
- if (yych <= 'd') goto yy169;
- if (yych <= 'z') goto yy143;
+ if (yych <= 'd') goto yy194;
+ if (yych <= 'z') goto yy168;
goto yy3;
}
}
-yy169:
+yy194:
yych = *++YYCURSOR;
if (yych <= 'A') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= '@') goto yy3;
} else {
if (yych <= '`') {
- if (yych <= 'Z') goto yy144;
+ if (yych <= 'Z') goto yy169;
goto yy3;
} else {
- if (yych <= 'a') goto yy170;
- if (yych <= 'z') goto yy144;
+ if (yych <= 'a') goto yy195;
+ if (yych <= 'z') goto yy169;
goto yy3;
}
}
-yy170:
+yy195:
yych = *++YYCURSOR;
if (yych <= 'X') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
- if (yych <= 'Y') goto yy171;
+ if (yych <= 'Y') goto yy196;
if (yych != 'y') goto yy3;
}
-yy171:
+yy196:
yych = *++YYCURSOR;
- if (yych == 'S') goto yy172;
- if (yych != 's') goto yy166;
-yy172:
+ if (yych == 'S') goto yy197;
+ if (yych != 's') goto yy191;
+yy197:
yych = *++YYCURSOR;
- goto yy166;
-yy173:
+ goto yy191;
+yy198:
yych = *++YYCURSOR;
if (yych <= 'E') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'D') goto yy143;
+ if (yych <= 'D') goto yy168;
}
} else {
if (yych <= 'd') {
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'Z') goto yy168;
if (yych <= '`') goto yy3;
- goto yy143;
+ goto yy168;
} else {
- if (yych <= 'e') goto yy174;
- if (yych <= 'z') goto yy143;
+ if (yych <= 'e') goto yy199;
+ if (yych <= 'z') goto yy168;
goto yy3;
}
}
-yy174:
+yy199:
yych = *++YYCURSOR;
if (yych <= 'S') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'R') goto yy144;
+ if (yych <= 'R') goto yy169;
}
} else {
if (yych <= 'r') {
- if (yych <= 'Z') goto yy144;
+ if (yych <= 'Z') goto yy169;
if (yych <= '`') goto yy3;
- goto yy144;
+ goto yy169;
} else {
- if (yych <= 's') goto yy175;
- if (yych <= 'z') goto yy144;
+ if (yych <= 's') goto yy200;
+ if (yych <= 'z') goto yy169;
goto yy3;
}
}
-yy175:
+yy200:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'C') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
- if (yych <= 'D') goto yy176;
+ if (yych <= 'D') goto yy201;
if (yych != 'd') goto yy3;
}
-yy176:
+yy201:
yych = *++YYCURSOR;
- if (yych == 'A') goto yy177;
+ if (yych == 'A') goto yy202;
if (yych != 'a') goto yy56;
-yy177:
+yy202:
yych = *++YYCURSOR;
- if (yych == 'Y') goto yy172;
- if (yych == 'y') goto yy172;
+ if (yych == 'Y') goto yy197;
+ if (yych == 'y') goto yy197;
goto yy56;
-yy178:
+yy203:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'D') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= '/') {
if (yych <= '.') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'C') goto yy141;
- goto yy165;
+ if (yych <= 'C') goto yy166;
+ goto yy190;
}
}
} else {
if (yych <= '`') {
if (yych <= 'Z') {
- if (yych <= 'E') goto yy167;
- goto yy141;
+ if (yych <= 'E') goto yy192;
+ goto yy166;
} else {
- if (yych == '_') goto yy147;
+ if (yych == '_') goto yy172;
goto yy3;
}
} else {
if (yych <= 'd') {
- if (yych <= 'c') goto yy146;
+ if (yych <= 'c') goto yy171;
} else {
- if (yych <= 'e') goto yy180;
- if (yych <= 'z') goto yy146;
+ if (yych <= 'e') goto yy205;
+ if (yych <= 'z') goto yy171;
goto yy3;
}
}
@@ -3549,324 +3925,324 @@ yy178:
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'M') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
- if (yych <= ',') goto yy166;
- goto yy147;
+ if (yych == ')') goto yy164;
+ if (yych <= ',') goto yy191;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
- if (yych <= '@') goto yy166;
- goto yy142;
+ if (yych == '/') goto yy172;
+ if (yych <= '@') goto yy191;
+ goto yy167;
}
} else {
if (yych <= '_') {
- if (yych <= 'N') goto yy173;
- if (yych <= 'Z') goto yy142;
- if (yych <= '^') goto yy166;
- goto yy147;
+ if (yych <= 'N') goto yy198;
+ if (yych <= 'Z') goto yy167;
+ if (yych <= '^') goto yy191;
+ goto yy172;
} else {
if (yych <= 'm') {
- if (yych <= '`') goto yy166;
- goto yy150;
+ if (yych <= '`') goto yy191;
+ goto yy175;
} else {
- if (yych <= 'n') goto yy186;
- if (yych <= 'z') goto yy150;
- goto yy166;
+ if (yych <= 'n') goto yy211;
+ if (yych <= 'z') goto yy175;
+ goto yy191;
}
}
}
-yy180:
+yy205:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'J') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy142;
+ goto yy167;
}
} else {
if (yych <= '_') {
- if (yych <= 'K') goto yy168;
- if (yych <= 'Z') goto yy142;
+ if (yych <= 'K') goto yy193;
+ if (yych <= 'Z') goto yy167;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'j') {
if (yych <= '`') goto yy3;
- goto yy150;
+ goto yy175;
} else {
- if (yych <= 'k') goto yy181;
- if (yych <= 'z') goto yy150;
+ if (yych <= 'k') goto yy206;
+ if (yych <= 'z') goto yy175;
goto yy3;
}
}
}
-yy181:
+yy206:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'C') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy143;
+ goto yy168;
}
} else {
if (yych <= '_') {
- if (yych <= 'D') goto yy169;
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'D') goto yy194;
+ if (yych <= 'Z') goto yy168;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'c') {
if (yych <= '`') goto yy3;
- goto yy151;
+ goto yy176;
} else {
- if (yych <= 'd') goto yy182;
- if (yych <= 'z') goto yy151;
+ if (yych <= 'd') goto yy207;
+ if (yych <= 'z') goto yy176;
goto yy3;
}
}
}
-yy182:
+yy207:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '@') {
if (yych <= ',') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
if (yych == '.') goto yy3;
- if (yych <= '/') goto yy147;
+ if (yych <= '/') goto yy172;
goto yy3;
}
} else {
if (yych <= '_') {
- if (yych <= 'A') goto yy170;
- if (yych <= 'Z') goto yy144;
+ if (yych <= 'A') goto yy195;
+ if (yych <= 'Z') goto yy169;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= '`') goto yy3;
- if (yych <= 'a') goto yy183;
- if (yych <= 'z') goto yy152;
+ if (yych <= 'a') goto yy208;
+ if (yych <= 'z') goto yy177;
goto yy3;
}
}
-yy183:
+yy208:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'X') {
if (yych <= ',') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
if (yych == '.') goto yy3;
- if (yych <= '/') goto yy147;
+ if (yych <= '/') goto yy172;
goto yy3;
}
} else {
if (yych <= '`') {
- if (yych <= 'Y') goto yy171;
- if (yych == '_') goto yy147;
+ if (yych <= 'Y') goto yy196;
+ if (yych == '_') goto yy172;
goto yy3;
} else {
- if (yych == 'y') goto yy184;
- if (yych <= 'z') goto yy153;
+ if (yych == 'y') goto yy209;
+ if (yych <= 'z') goto yy178;
goto yy3;
}
}
-yy184:
+yy209:
yyaccept = 4;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'S') {
if (yych <= '.') {
- if (yych == '-') goto yy147;
- goto yy166;
+ if (yych == '-') goto yy172;
+ goto yy191;
} else {
- if (yych <= '/') goto yy147;
- if (yych <= 'R') goto yy166;
- goto yy172;
+ if (yych <= '/') goto yy172;
+ if (yych <= 'R') goto yy191;
+ goto yy197;
}
} else {
if (yych <= '`') {
- if (yych == '_') goto yy147;
- goto yy166;
+ if (yych == '_') goto yy172;
+ goto yy191;
} else {
- if (yych == 's') goto yy185;
- if (yych <= 'z') goto yy153;
- goto yy166;
+ if (yych == 's') goto yy210;
+ if (yych <= 'z') goto yy178;
+ goto yy191;
}
}
-yy185:
+yy210:
yyaccept = 4;
yych = *(YYMARKER = ++YYCURSOR);
if (yybm[0+yych] & 16) {
- goto yy153;
+ goto yy178;
}
if (yych <= '.') {
- if (yych == '-') goto yy147;
- goto yy166;
+ if (yych == '-') goto yy172;
+ goto yy191;
} else {
- if (yych <= '/') goto yy147;
- if (yych == '_') goto yy147;
- goto yy166;
+ if (yych <= '/') goto yy172;
+ if (yych == '_') goto yy172;
+ goto yy191;
}
-yy186:
+yy211:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'D') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy143;
+ goto yy168;
}
} else {
if (yych <= '_') {
- if (yych <= 'E') goto yy174;
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'E') goto yy199;
+ if (yych <= 'Z') goto yy168;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'd') {
if (yych <= '`') goto yy3;
- goto yy151;
+ goto yy176;
} else {
- if (yych <= 'e') goto yy187;
- if (yych <= 'z') goto yy151;
+ if (yych <= 'e') goto yy212;
+ if (yych <= 'z') goto yy176;
goto yy3;
}
}
}
-yy187:
+yy212:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'R') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy144;
+ goto yy169;
}
} else {
if (yych <= '_') {
- if (yych <= 'S') goto yy175;
- if (yych <= 'Z') goto yy144;
+ if (yych <= 'S') goto yy200;
+ if (yych <= 'Z') goto yy169;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'r') {
if (yych <= '`') goto yy3;
- goto yy152;
+ goto yy177;
} else {
- if (yych <= 's') goto yy188;
- if (yych <= 'z') goto yy152;
+ if (yych <= 's') goto yy213;
+ if (yych <= 'z') goto yy177;
goto yy3;
}
}
}
-yy188:
+yy213:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'C') {
if (yych <= ',') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
if (yych == '.') goto yy3;
- if (yych <= '/') goto yy147;
+ if (yych <= '/') goto yy172;
goto yy3;
}
} else {
if (yych <= '`') {
- if (yych <= 'D') goto yy176;
- if (yych == '_') goto yy147;
+ if (yych <= 'D') goto yy201;
+ if (yych == '_') goto yy172;
goto yy3;
} else {
- if (yych == 'd') goto yy189;
- if (yych <= 'z') goto yy153;
+ if (yych == 'd') goto yy214;
+ if (yych <= 'z') goto yy178;
goto yy3;
}
}
-yy189:
+yy214:
yych = *++YYCURSOR;
- if (yych == 'A') goto yy177;
- if (yych != 'a') goto yy154;
+ if (yych == 'A') goto yy202;
+ if (yych != 'a') goto yy179;
yych = *++YYCURSOR;
- if (yych == 'Y') goto yy172;
- if (yych == 'y') goto yy185;
- goto yy154;
-yy191:
+ if (yych == 'Y') goto yy197;
+ if (yych == 'y') goto yy210;
+ goto yy179;
+yy216:
yych = *++YYCURSOR;
if (yych <= 'C') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'B') goto yy141;
+ if (yych <= 'B') goto yy166;
}
} else {
if (yych <= 'b') {
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'Z') goto yy166;
if (yych <= '`') goto yy3;
- goto yy141;
+ goto yy166;
} else {
- if (yych <= 'c') goto yy192;
- if (yych <= 'z') goto yy141;
+ if (yych <= 'c') goto yy217;
+ if (yych <= 'z') goto yy166;
goto yy3;
}
}
-yy192:
+yy217:
yyaccept = 5;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '/') {
if (yych <= '(') {
if (yych <= '\t') {
- if (yych >= '\t') goto yy195;
+ if (yych >= '\t') goto yy220;
} else {
- if (yych == ' ') goto yy195;
+ if (yych == ' ') goto yy220;
}
} else {
if (yych <= ',') {
- if (yych <= ')') goto yy139;
+ if (yych <= ')') goto yy164;
} else {
- if (yych <= '-') goto yy196;
- if (yych <= '.') goto yy195;
+ if (yych <= '-') goto yy221;
+ if (yych <= '.') goto yy220;
}
}
} else {
if (yych <= 'Z') {
if (yych <= '@') {
- if (yych <= '9') goto yy195;
+ if (yych <= '9') goto yy220;
} else {
- if (yych == 'E') goto yy200;
- goto yy142;
+ if (yych == 'E') goto yy225;
+ goto yy167;
}
} else {
if (yych <= 'd') {
- if (yych >= 'a') goto yy142;
+ if (yych >= 'a') goto yy167;
} else {
- if (yych <= 'e') goto yy200;
- if (yych <= 'z') goto yy142;
+ if (yych <= 'e') goto yy225;
+ if (yych <= 'z') goto yy167;
}
}
}
-yy193:
-#line 1620 "ext/date/lib/parse_date.re"
+yy218:
+#line 1663 "ext/date/lib/parse_date.re"
{
DEBUG_OUTPUT("monthtext");
TIMELIB_INIT;
@@ -3875,242 +4251,242 @@ yy193:
TIMELIB_DEINIT;
return TIMELIB_DATE_TEXT;
}
-#line 3879 "ext/date/lib/parse_date.c"
-yy194:
+#line 4255 "ext/date/lib/parse_date.c"
+yy219:
++YYCURSOR;
if ((YYLIMIT - YYCURSOR) < 23) YYFILL(23);
yych = *YYCURSOR;
-yy195:
+yy220:
if (yybm[0+yych] & 32) {
- goto yy194;
+ goto yy219;
}
if (yych <= '/') goto yy56;
- if (yych <= '2') goto yy197;
- if (yych <= '3') goto yy198;
- if (yych <= '9') goto yy199;
+ if (yych <= '2') goto yy222;
+ if (yych <= '3') goto yy223;
+ if (yych <= '9') goto yy224;
goto yy56;
-yy196:
+yy221:
yych = *++YYCURSOR;
- if (yych <= '/') goto yy195;
- if (yych <= '0') goto yy299;
- if (yych <= '2') goto yy300;
- if (yych <= '3') goto yy301;
- goto yy195;
-yy197:
+ if (yych <= '/') goto yy220;
+ if (yych <= '0') goto yy324;
+ if (yych <= '2') goto yy325;
+ if (yych <= '3') goto yy326;
+ goto yy220;
+yy222:
yych = *++YYCURSOR;
if (yych <= '9') {
if (yych <= ' ') {
if (yych <= 0x08) {
- if (yych <= 0x00) goto yy213;
+ if (yych <= 0x00) goto yy238;
goto yy56;
} else {
- if (yych <= '\t') goto yy211;
+ if (yych <= '\t') goto yy236;
if (yych <= 0x1F) goto yy56;
- goto yy211;
+ goto yy236;
}
} else {
if (yych <= '-') {
- if (yych == ',') goto yy211;
+ if (yych == ',') goto yy236;
goto yy56;
} else {
- if (yych <= '.') goto yy211;
+ if (yych <= '.') goto yy236;
if (yych <= '/') goto yy56;
- goto yy298;
+ goto yy323;
}
}
} else {
if (yych <= 'm') {
if (yych <= 'd') {
if (yych <= 'c') goto yy56;
- goto yy211;
+ goto yy236;
} else {
- if (yych == 'h') goto yy211;
+ if (yych == 'h') goto yy236;
goto yy56;
}
} else {
if (yych <= 'r') {
- if (yych <= 'n') goto yy208;
+ if (yych <= 'n') goto yy233;
if (yych <= 'q') goto yy56;
- goto yy209;
+ goto yy234;
} else {
- if (yych <= 's') goto yy206;
- if (yych <= 't') goto yy210;
+ if (yych <= 's') goto yy231;
+ if (yych <= 't') goto yy235;
goto yy56;
}
}
}
-yy198:
+yy223:
yych = *++YYCURSOR;
if (yych <= '1') {
if (yych <= ' ') {
if (yych <= 0x08) {
- if (yych <= 0x00) goto yy213;
+ if (yych <= 0x00) goto yy238;
goto yy56;
} else {
- if (yych <= '\t') goto yy211;
+ if (yych <= '\t') goto yy236;
if (yych <= 0x1F) goto yy56;
- goto yy211;
+ goto yy236;
}
} else {
if (yych <= '-') {
- if (yych == ',') goto yy211;
+ if (yych == ',') goto yy236;
goto yy56;
} else {
- if (yych <= '.') goto yy211;
+ if (yych <= '.') goto yy236;
if (yych <= '/') goto yy56;
- goto yy298;
+ goto yy323;
}
}
} else {
if (yych <= 'm') {
if (yych <= 'd') {
- if (yych <= '9') goto yy205;
+ if (yych <= '9') goto yy230;
if (yych <= 'c') goto yy56;
- goto yy211;
+ goto yy236;
} else {
- if (yych == 'h') goto yy211;
+ if (yych == 'h') goto yy236;
goto yy56;
}
} else {
if (yych <= 'r') {
- if (yych <= 'n') goto yy208;
+ if (yych <= 'n') goto yy233;
if (yych <= 'q') goto yy56;
- goto yy209;
+ goto yy234;
} else {
- if (yych <= 's') goto yy206;
- if (yych <= 't') goto yy210;
+ if (yych <= 's') goto yy231;
+ if (yych <= 't') goto yy235;
goto yy56;
}
}
}
-yy199:
+yy224:
yych = *++YYCURSOR;
if (yych <= '9') {
if (yych <= ' ') {
if (yych <= 0x08) {
- if (yych <= 0x00) goto yy213;
+ if (yych <= 0x00) goto yy238;
goto yy56;
} else {
- if (yych <= '\t') goto yy211;
+ if (yych <= '\t') goto yy236;
if (yych <= 0x1F) goto yy56;
- goto yy211;
+ goto yy236;
}
} else {
if (yych <= '-') {
- if (yych == ',') goto yy211;
+ if (yych == ',') goto yy236;
goto yy56;
} else {
- if (yych <= '.') goto yy211;
+ if (yych <= '.') goto yy236;
if (yych <= '/') goto yy56;
- goto yy205;
+ goto yy230;
}
}
} else {
if (yych <= 'm') {
if (yych <= 'd') {
if (yych <= 'c') goto yy56;
- goto yy211;
+ goto yy236;
} else {
- if (yych == 'h') goto yy211;
+ if (yych == 'h') goto yy236;
goto yy56;
}
} else {
if (yych <= 'r') {
- if (yych <= 'n') goto yy208;
+ if (yych <= 'n') goto yy233;
if (yych <= 'q') goto yy56;
- goto yy209;
+ goto yy234;
} else {
- if (yych <= 's') goto yy206;
- if (yych <= 't') goto yy210;
+ if (yych <= 's') goto yy231;
+ if (yych <= 't') goto yy235;
goto yy56;
}
}
}
-yy200:
+yy225:
yych = *++YYCURSOR;
if (yych <= 'M') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'L') goto yy143;
+ if (yych <= 'L') goto yy168;
}
} else {
if (yych <= 'l') {
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'Z') goto yy168;
if (yych <= '`') goto yy3;
- goto yy143;
+ goto yy168;
} else {
- if (yych <= 'm') goto yy201;
- if (yych <= 'z') goto yy143;
+ if (yych <= 'm') goto yy226;
+ if (yych <= 'z') goto yy168;
goto yy3;
}
}
-yy201:
+yy226:
yych = *++YYCURSOR;
if (yych <= 'B') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'A') goto yy144;
+ if (yych <= 'A') goto yy169;
}
} else {
if (yych <= 'a') {
- if (yych <= 'Z') goto yy144;
+ if (yych <= 'Z') goto yy169;
if (yych <= '`') goto yy3;
- goto yy144;
+ goto yy169;
} else {
- if (yych <= 'b') goto yy202;
- if (yych <= 'z') goto yy144;
+ if (yych <= 'b') goto yy227;
+ if (yych <= 'z') goto yy169;
goto yy3;
}
}
-yy202:
+yy227:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'D') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
- if (yych <= 'E') goto yy203;
+ if (yych <= 'E') goto yy228;
if (yych != 'e') goto yy3;
}
-yy203:
+yy228:
yych = *++YYCURSOR;
- if (yych == 'R') goto yy204;
+ if (yych == 'R') goto yy229;
if (yych != 'r') goto yy56;
-yy204:
+yy229:
yyaccept = 5;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= ' ') {
- if (yych == '\t') goto yy195;
- if (yych <= 0x1F) goto yy193;
- goto yy195;
+ if (yych == '\t') goto yy220;
+ if (yych <= 0x1F) goto yy218;
+ goto yy220;
} else {
if (yych <= '.') {
- if (yych <= ',') goto yy193;
- goto yy195;
+ if (yych <= ',') goto yy218;
+ goto yy220;
} else {
- if (yych <= '/') goto yy193;
- if (yych <= '9') goto yy195;
- goto yy193;
+ if (yych <= '/') goto yy218;
+ if (yych <= '9') goto yy220;
+ goto yy218;
}
}
-yy205:
+yy230:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '9') goto yy295;
+ if (yych <= '9') goto yy320;
goto yy56;
-yy206:
+yy231:
yyaccept = 6;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych == 't') goto yy294;
- goto yy212;
-yy207:
-#line 1366 "ext/date/lib/parse_date.re"
+ if (yych == 't') goto yy319;
+ goto yy237;
+yy232:
+#line 1409 "ext/date/lib/parse_date.re"
{
int length = 0;
DEBUG_OUTPUT("datetextual | datenoyear");
@@ -4123,120 +4499,120 @@ yy207:
TIMELIB_DEINIT;
return TIMELIB_DATE_TEXT;
}
-#line 4127 "ext/date/lib/parse_date.c"
-yy208:
+#line 4503 "ext/date/lib/parse_date.c"
+yy233:
yyaccept = 6;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych == 'd') goto yy294;
- goto yy212;
-yy209:
+ if (yych == 'd') goto yy319;
+ goto yy237;
+yy234:
yyaccept = 6;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych == 'd') goto yy294;
- goto yy212;
-yy210:
+ if (yych == 'd') goto yy319;
+ goto yy237;
+yy235:
yyaccept = 6;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych == 'h') goto yy294;
- goto yy212;
-yy211:
+ if (yych == 'h') goto yy319;
+ goto yy237;
+yy236:
yyaccept = 6;
YYMARKER = ++YYCURSOR;
if ((YYLIMIT - YYCURSOR) < 18) YYFILL(18);
yych = *YYCURSOR;
-yy212:
+yy237:
if (yybm[0+yych] & 64) {
- goto yy211;
+ goto yy236;
}
if (yych <= '2') {
- if (yych <= '/') goto yy207;
- if (yych <= '0') goto yy285;
- if (yych <= '1') goto yy286;
- goto yy287;
+ if (yych <= '/') goto yy232;
+ if (yych <= '0') goto yy310;
+ if (yych <= '1') goto yy311;
+ goto yy312;
} else {
- if (yych <= '9') goto yy288;
- if (yych == 'T') goto yy214;
- goto yy207;
+ if (yych <= '9') goto yy313;
+ if (yych == 'T') goto yy239;
+ goto yy232;
}
-yy213:
+yy238:
yyaccept = 6;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '9') {
if (yych <= '0') {
- if (yych <= '/') goto yy207;
- goto yy215;
+ if (yych <= '/') goto yy232;
+ goto yy240;
} else {
- if (yych <= '1') goto yy216;
- if (yych <= '2') goto yy217;
- goto yy218;
+ if (yych <= '1') goto yy241;
+ if (yych <= '2') goto yy242;
+ goto yy243;
}
} else {
if (yych <= 'T') {
- if (yych <= 'S') goto yy207;
+ if (yych <= 'S') goto yy232;
} else {
- if (yych != 't') goto yy207;
+ if (yych != 't') goto yy232;
}
}
-yy214:
+yy239:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '1') goto yy283;
- if (yych <= '2') goto yy284;
- if (yych <= '9') goto yy273;
+ if (yych <= '1') goto yy308;
+ if (yych <= '2') goto yy309;
+ if (yych <= '9') goto yy298;
goto yy56;
-yy215:
+yy240:
yych = *++YYCURSOR;
if (yych <= '/') {
- if (yych == '.') goto yy274;
+ if (yych == '.') goto yy299;
goto yy56;
} else {
- if (yych <= '0') goto yy273;
- if (yych <= '9') goto yy218;
- if (yych <= ':') goto yy274;
+ if (yych <= '0') goto yy298;
+ if (yych <= '9') goto yy243;
+ if (yych <= ':') goto yy299;
goto yy56;
}
-yy216:
+yy241:
yych = *++YYCURSOR;
if (yych <= '/') {
- if (yych == '.') goto yy219;
+ if (yych == '.') goto yy244;
goto yy56;
} else {
- if (yych <= '2') goto yy218;
- if (yych <= '9') goto yy273;
- if (yych <= ':') goto yy219;
+ if (yych <= '2') goto yy243;
+ if (yych <= '9') goto yy298;
+ if (yych <= ':') goto yy244;
goto yy56;
}
-yy217:
+yy242:
yych = *++YYCURSOR;
if (yych <= '/') {
- if (yych == '.') goto yy219;
+ if (yych == '.') goto yy244;
goto yy56;
} else {
- if (yych <= '4') goto yy273;
- if (yych == ':') goto yy219;
+ if (yych <= '4') goto yy298;
+ if (yych == ':') goto yy244;
goto yy56;
}
-yy218:
+yy243:
yych = *++YYCURSOR;
- if (yych == '.') goto yy219;
+ if (yych == '.') goto yy244;
if (yych != ':') goto yy56;
-yy219:
+yy244:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '5') goto yy220;
- if (yych <= '9') goto yy222;
+ if (yych <= '5') goto yy245;
+ if (yych <= '9') goto yy247;
goto yy56;
-yy220:
+yy245:
yyaccept = 7;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '/') {
- if (yych == '.') goto yy223;
+ if (yych == '.') goto yy248;
} else {
- if (yych <= '9') goto yy266;
- if (yych <= ':') goto yy223;
+ if (yych <= '9') goto yy291;
+ if (yych <= ':') goto yy248;
}
-yy221:
-#line 1668 "ext/date/lib/parse_date.re"
+yy246:
+#line 1711 "ext/date/lib/parse_date.re"
{
int tz_not_found;
DEBUG_OUTPUT("dateshortwithtimeshort | dateshortwithtimelong | dateshortwithtimelongtz");
@@ -4265,280 +4641,280 @@ yy221:
TIMELIB_DEINIT;
return TIMELIB_SHORTDATE_WITH_TIME;
}
-#line 4269 "ext/date/lib/parse_date.c"
-yy222:
+#line 4645 "ext/date/lib/parse_date.c"
+yy247:
yyaccept = 7;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych == '.') goto yy223;
- if (yych != ':') goto yy221;
-yy223:
+ if (yych == '.') goto yy248;
+ if (yych != ':') goto yy246;
+yy248:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '5') goto yy224;
- if (yych <= '6') goto yy225;
- if (yych <= '9') goto yy226;
+ if (yych <= '5') goto yy249;
+ if (yych <= '6') goto yy250;
+ if (yych <= '9') goto yy251;
goto yy56;
-yy224:
+yy249:
yych = *++YYCURSOR;
- if (yych <= '/') goto yy221;
- if (yych <= '9') goto yy227;
- goto yy221;
-yy225:
+ if (yych <= '/') goto yy246;
+ if (yych <= '9') goto yy252;
+ goto yy246;
+yy250:
yych = *++YYCURSOR;
- if (yych == '0') goto yy227;
- goto yy221;
-yy226:
+ if (yych == '0') goto yy252;
+ goto yy246;
+yy251:
yych = *++YYCURSOR;
- goto yy221;
-yy227:
+ goto yy246;
+yy252:
yyaccept = 7;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '*') {
if (yych <= 0x1F) {
- if (yych == '\t') goto yy229;
- goto yy221;
+ if (yych == '\t') goto yy254;
+ goto yy246;
} else {
- if (yych <= ' ') goto yy229;
- if (yych == '(') goto yy229;
- goto yy221;
+ if (yych <= ' ') goto yy254;
+ if (yych == '(') goto yy254;
+ goto yy246;
}
} else {
if (yych <= '@') {
- if (yych == ',') goto yy221;
- if (yych <= '-') goto yy229;
- goto yy221;
+ if (yych == ',') goto yy246;
+ if (yych <= '-') goto yy254;
+ goto yy246;
} else {
- if (yych <= 'Z') goto yy229;
- if (yych <= '`') goto yy221;
- if (yych <= 'z') goto yy229;
- goto yy221;
+ if (yych <= 'Z') goto yy254;
+ if (yych <= '`') goto yy246;
+ if (yych <= 'z') goto yy254;
+ goto yy246;
}
}
-yy228:
+yy253:
++YYCURSOR;
if ((YYLIMIT - YYCURSOR) < 9) YYFILL(9);
yych = *YYCURSOR;
-yy229:
+yy254:
if (yych <= '@') {
if (yych <= '\'') {
if (yych <= '\t') {
if (yych <= 0x08) goto yy56;
- goto yy228;
+ goto yy253;
} else {
- if (yych == ' ') goto yy228;
+ if (yych == ' ') goto yy253;
goto yy56;
}
} else {
if (yych <= '+') {
- if (yych <= '(') goto yy232;
+ if (yych <= '(') goto yy257;
if (yych <= '*') goto yy56;
- goto yy231;
+ goto yy256;
} else {
- if (yych == '-') goto yy231;
+ if (yych == '-') goto yy256;
goto yy56;
}
}
} else {
if (yych <= 'Z') {
if (yych <= 'G') {
- if (yych <= 'A') goto yy233;
- if (yych <= 'F') goto yy234;
+ if (yych <= 'A') goto yy258;
+ if (yych <= 'F') goto yy259;
} else {
- if (yych == 'P') goto yy233;
- goto yy234;
+ if (yych == 'P') goto yy258;
+ goto yy259;
}
} else {
if (yych <= 'o') {
if (yych <= '`') goto yy56;
- if (yych <= 'a') goto yy235;
- goto yy236;
+ if (yych <= 'a') goto yy260;
+ goto yy261;
} else {
- if (yych <= 'p') goto yy235;
- if (yych <= 'z') goto yy236;
+ if (yych <= 'p') goto yy260;
+ if (yych <= 'z') goto yy261;
goto yy56;
}
}
}
-yy230:
+yy255:
yych = *++YYCURSOR;
if (yych <= 'L') {
- if (yych == ')') goto yy226;
- if (yych <= '@') goto yy221;
- goto yy237;
+ if (yych == ')') goto yy251;
+ if (yych <= '@') goto yy246;
+ goto yy262;
} else {
if (yych <= 'Z') {
- if (yych <= 'M') goto yy264;
- goto yy237;
+ if (yych <= 'M') goto yy289;
+ goto yy262;
} else {
- if (yych <= '`') goto yy221;
- if (yych <= 'z') goto yy248;
- goto yy221;
+ if (yych <= '`') goto yy246;
+ if (yych <= 'z') goto yy273;
+ goto yy246;
}
}
-yy231:
+yy256:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '1') goto yy259;
- if (yych <= '2') goto yy260;
- if (yych <= '9') goto yy261;
+ if (yych <= '1') goto yy284;
+ if (yych <= '2') goto yy285;
+ if (yych <= '9') goto yy286;
goto yy56;
-yy232:
+yy257:
yych = *++YYCURSOR;
if (yych <= '@') goto yy56;
- if (yych <= 'Z') goto yy236;
+ if (yych <= 'Z') goto yy261;
if (yych <= '`') goto yy56;
- if (yych <= 'z') goto yy236;
+ if (yych <= 'z') goto yy261;
goto yy56;
-yy233:
+yy258:
yyaccept = 7;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'L') {
if (yych <= '-') {
- if (yych == ')') goto yy226;
- goto yy221;
+ if (yych == ')') goto yy251;
+ goto yy246;
} else {
- if (yych <= '.') goto yy242;
- if (yych <= '@') goto yy221;
- goto yy237;
+ if (yych <= '.') goto yy267;
+ if (yych <= '@') goto yy246;
+ goto yy262;
}
} else {
if (yych <= '`') {
- if (yych <= 'M') goto yy243;
- if (yych <= 'Z') goto yy237;
- goto yy221;
+ if (yych <= 'M') goto yy268;
+ if (yych <= 'Z') goto yy262;
+ goto yy246;
} else {
- if (yych == 'm') goto yy258;
- if (yych <= 'z') goto yy248;
- goto yy221;
+ if (yych == 'm') goto yy283;
+ if (yych <= 'z') goto yy273;
+ goto yy246;
}
}
-yy234:
+yy259:
yych = *++YYCURSOR;
if (yych <= '@') {
- if (yych == ')') goto yy226;
- goto yy221;
+ if (yych == ')') goto yy251;
+ goto yy246;
} else {
- if (yych <= 'Z') goto yy237;
- if (yych <= '`') goto yy221;
- if (yych <= 'z') goto yy248;
- goto yy221;
+ if (yych <= 'Z') goto yy262;
+ if (yych <= '`') goto yy246;
+ if (yych <= 'z') goto yy273;
+ goto yy246;
}
-yy235:
+yy260:
yyaccept = 7;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'L') {
if (yych <= '-') {
- if (yych == ')') goto yy226;
- goto yy221;
+ if (yych == ')') goto yy251;
+ goto yy246;
} else {
- if (yych <= '.') goto yy242;
- if (yych <= '@') goto yy221;
- goto yy237;
+ if (yych <= '.') goto yy267;
+ if (yych <= '@') goto yy246;
+ goto yy262;
}
} else {
if (yych <= '`') {
- if (yych <= 'M') goto yy243;
- if (yych <= 'Z') goto yy237;
- goto yy221;
+ if (yych <= 'M') goto yy268;
+ if (yych <= 'Z') goto yy262;
+ goto yy246;
} else {
- if (yych == 'm') goto yy243;
- if (yych <= 'z') goto yy237;
- goto yy221;
+ if (yych == 'm') goto yy268;
+ if (yych <= 'z') goto yy262;
+ goto yy246;
}
}
-yy236:
+yy261:
yych = *++YYCURSOR;
if (yych <= '@') {
- if (yych == ')') goto yy226;
- goto yy221;
+ if (yych == ')') goto yy251;
+ goto yy246;
} else {
- if (yych <= 'Z') goto yy237;
- if (yych <= '`') goto yy221;
- if (yych >= '{') goto yy221;
+ if (yych <= 'Z') goto yy262;
+ if (yych <= '`') goto yy246;
+ if (yych >= '{') goto yy246;
}
-yy237:
+yy262:
yych = *++YYCURSOR;
if (yych <= '@') {
- if (yych == ')') goto yy226;
- goto yy221;
+ if (yych == ')') goto yy251;
+ goto yy246;
} else {
- if (yych <= 'Z') goto yy238;
- if (yych <= '`') goto yy221;
- if (yych >= '{') goto yy221;
+ if (yych <= 'Z') goto yy263;
+ if (yych <= '`') goto yy246;
+ if (yych >= '{') goto yy246;
}
-yy238:
+yy263:
yych = *++YYCURSOR;
if (yych <= '@') {
- if (yych == ')') goto yy226;
- goto yy221;
+ if (yych == ')') goto yy251;
+ goto yy246;
} else {
- if (yych <= 'Z') goto yy239;
- if (yych <= '`') goto yy221;
- if (yych >= '{') goto yy221;
+ if (yych <= 'Z') goto yy264;
+ if (yych <= '`') goto yy246;
+ if (yych >= '{') goto yy246;
}
-yy239:
+yy264:
yych = *++YYCURSOR;
if (yych <= '@') {
- if (yych == ')') goto yy226;
- goto yy221;
+ if (yych == ')') goto yy251;
+ goto yy246;
} else {
- if (yych <= 'Z') goto yy240;
- if (yych <= '`') goto yy221;
- if (yych >= '{') goto yy221;
+ if (yych <= 'Z') goto yy265;
+ if (yych <= '`') goto yy246;
+ if (yych >= '{') goto yy246;
}
-yy240:
+yy265:
yych = *++YYCURSOR;
if (yych <= '@') {
- if (yych == ')') goto yy226;
- goto yy221;
+ if (yych == ')') goto yy251;
+ goto yy246;
} else {
- if (yych <= 'Z') goto yy241;
- if (yych <= '`') goto yy221;
- if (yych >= '{') goto yy221;
+ if (yych <= 'Z') goto yy266;
+ if (yych <= '`') goto yy246;
+ if (yych >= '{') goto yy246;
}
-yy241:
+yy266:
yych = *++YYCURSOR;
- if (yych == ')') goto yy226;
- goto yy221;
-yy242:
+ if (yych == ')') goto yy251;
+ goto yy246;
+yy267:
yych = *++YYCURSOR;
- if (yych == 'M') goto yy247;
- if (yych == 'm') goto yy247;
+ if (yych == 'M') goto yy272;
+ if (yych == 'm') goto yy272;
goto yy56;
-yy243:
+yy268:
yyaccept = 7;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= ')') {
if (yych <= '\t') {
- if (yych <= 0x00) goto yy245;
- if (yych <= 0x08) goto yy221;
- goto yy245;
+ if (yych <= 0x00) goto yy270;
+ if (yych <= 0x08) goto yy246;
+ goto yy270;
} else {
- if (yych == ' ') goto yy245;
- if (yych <= '(') goto yy221;
- goto yy226;
+ if (yych == ' ') goto yy270;
+ if (yych <= '(') goto yy246;
+ goto yy251;
}
} else {
if (yych <= '@') {
- if (yych != '.') goto yy221;
+ if (yych != '.') goto yy246;
} else {
- if (yych <= 'Z') goto yy238;
- if (yych <= '`') goto yy221;
- if (yych <= 'z') goto yy238;
- goto yy221;
+ if (yych <= 'Z') goto yy263;
+ if (yych <= '`') goto yy246;
+ if (yych <= 'z') goto yy263;
+ goto yy246;
}
}
-yy244:
+yy269:
yych = *++YYCURSOR;
if (yych <= '\t') {
- if (yych <= 0x00) goto yy245;
+ if (yych <= 0x00) goto yy270;
if (yych <= 0x08) goto yy56;
} else {
if (yych != ' ') goto yy56;
}
-yy245:
+yy270:
++YYCURSOR;
-#line 1644 "ext/date/lib/parse_date.re"
+#line 1687 "ext/date/lib/parse_date.re"
{
DEBUG_OUTPUT("dateshortwithtimeshort12 | dateshortwithtimelong12");
TIMELIB_INIT;
@@ -4561,563 +4937,563 @@ yy245:
TIMELIB_DEINIT;
return TIMELIB_SHORTDATE_WITH_TIME;
}
-#line 4565 "ext/date/lib/parse_date.c"
-yy247:
+#line 4941 "ext/date/lib/parse_date.c"
+yy272:
yych = *++YYCURSOR;
if (yych <= 0x1F) {
- if (yych <= 0x00) goto yy245;
- if (yych == '\t') goto yy245;
+ if (yych <= 0x00) goto yy270;
+ if (yych == '\t') goto yy270;
goto yy56;
} else {
- if (yych <= ' ') goto yy245;
- if (yych == '.') goto yy244;
+ if (yych <= ' ') goto yy270;
+ if (yych == '.') goto yy269;
goto yy56;
}
-yy248:
+yy273:
yyaccept = 7;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '/') {
if (yych <= ',') {
- if (yych == ')') goto yy226;
- goto yy221;
+ if (yych == ')') goto yy251;
+ goto yy246;
} else {
- if (yych == '.') goto yy221;
- goto yy250;
+ if (yych == '.') goto yy246;
+ goto yy275;
}
} else {
if (yych <= '^') {
- if (yych <= '@') goto yy221;
- if (yych <= 'Z') goto yy238;
- goto yy221;
+ if (yych <= '@') goto yy246;
+ if (yych <= 'Z') goto yy263;
+ goto yy246;
} else {
- if (yych <= '_') goto yy250;
- if (yych <= '`') goto yy221;
- if (yych >= '{') goto yy221;
+ if (yych <= '_') goto yy275;
+ if (yych <= '`') goto yy246;
+ if (yych >= '{') goto yy246;
}
}
-yy249:
+yy274:
yyaccept = 7;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '/') {
if (yych <= ',') {
- if (yych == ')') goto yy226;
- goto yy221;
+ if (yych == ')') goto yy251;
+ goto yy246;
} else {
- if (yych == '.') goto yy221;
+ if (yych == '.') goto yy246;
}
} else {
if (yych <= '^') {
- if (yych <= '@') goto yy221;
- if (yych <= 'Z') goto yy239;
- goto yy221;
+ if (yych <= '@') goto yy246;
+ if (yych <= 'Z') goto yy264;
+ goto yy246;
} else {
- if (yych <= '_') goto yy250;
- if (yych <= '`') goto yy221;
- if (yych <= 'z') goto yy253;
- goto yy221;
+ if (yych <= '_') goto yy275;
+ if (yych <= '`') goto yy246;
+ if (yych <= 'z') goto yy278;
+ goto yy246;
}
}
-yy250:
+yy275:
++YYCURSOR;
if (YYLIMIT <= YYCURSOR) YYFILL(1);
yych = *YYCURSOR;
if (yych <= '@') goto yy56;
- if (yych <= 'Z') goto yy251;
+ if (yych <= 'Z') goto yy276;
if (yych <= '`') goto yy56;
if (yych >= '{') goto yy56;
-yy251:
+yy276:
yyaccept = 7;
YYMARKER = ++YYCURSOR;
if (YYLIMIT <= YYCURSOR) YYFILL(1);
yych = *YYCURSOR;
if (yych <= '@') {
if (yych <= '-') {
- if (yych <= ',') goto yy221;
- goto yy250;
+ if (yych <= ',') goto yy246;
+ goto yy275;
} else {
- if (yych == '/') goto yy250;
- goto yy221;
+ if (yych == '/') goto yy275;
+ goto yy246;
}
} else {
if (yych <= '_') {
- if (yych <= 'Z') goto yy251;
- if (yych <= '^') goto yy221;
- goto yy250;
+ if (yych <= 'Z') goto yy276;
+ if (yych <= '^') goto yy246;
+ goto yy275;
} else {
- if (yych <= '`') goto yy221;
- if (yych <= 'z') goto yy251;
- goto yy221;
+ if (yych <= '`') goto yy246;
+ if (yych <= 'z') goto yy276;
+ goto yy246;
}
}
-yy253:
+yy278:
yyaccept = 7;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '/') {
if (yych <= ',') {
- if (yych == ')') goto yy226;
- goto yy221;
+ if (yych == ')') goto yy251;
+ goto yy246;
} else {
- if (yych == '.') goto yy221;
- goto yy250;
+ if (yych == '.') goto yy246;
+ goto yy275;
}
} else {
if (yych <= '^') {
- if (yych <= '@') goto yy221;
- if (yych <= 'Z') goto yy240;
- goto yy221;
+ if (yych <= '@') goto yy246;
+ if (yych <= 'Z') goto yy265;
+ goto yy246;
} else {
- if (yych <= '_') goto yy250;
- if (yych <= '`') goto yy221;
- if (yych >= '{') goto yy221;
+ if (yych <= '_') goto yy275;
+ if (yych <= '`') goto yy246;
+ if (yych >= '{') goto yy246;
}
}
yyaccept = 7;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '/') {
if (yych <= ',') {
- if (yych == ')') goto yy226;
- goto yy221;
+ if (yych == ')') goto yy251;
+ goto yy246;
} else {
- if (yych == '.') goto yy221;
- goto yy250;
+ if (yych == '.') goto yy246;
+ goto yy275;
}
} else {
if (yych <= '^') {
- if (yych <= '@') goto yy221;
- if (yych <= 'Z') goto yy241;
- goto yy221;
+ if (yych <= '@') goto yy246;
+ if (yych <= 'Z') goto yy266;
+ goto yy246;
} else {
- if (yych <= '_') goto yy250;
- if (yych <= '`') goto yy221;
- if (yych >= '{') goto yy221;
+ if (yych <= '_') goto yy275;
+ if (yych <= '`') goto yy246;
+ if (yych >= '{') goto yy246;
}
}
yyaccept = 7;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '.') {
if (yych <= ')') {
- if (yych <= '(') goto yy221;
- goto yy226;
+ if (yych <= '(') goto yy246;
+ goto yy251;
} else {
- if (yych == '-') goto yy250;
- goto yy221;
+ if (yych == '-') goto yy275;
+ goto yy246;
}
} else {
if (yych <= '_') {
- if (yych <= '/') goto yy250;
- if (yych <= '^') goto yy221;
- goto yy250;
+ if (yych <= '/') goto yy275;
+ if (yych <= '^') goto yy246;
+ goto yy275;
} else {
- if (yych <= '`') goto yy221;
- if (yych >= '{') goto yy221;
+ if (yych <= '`') goto yy246;
+ if (yych >= '{') goto yy246;
}
}
-yy256:
+yy281:
++YYCURSOR;
if (YYLIMIT <= YYCURSOR) YYFILL(1);
yych = *YYCURSOR;
if (yych <= '/') {
- if (yych == '-') goto yy250;
+ if (yych == '-') goto yy275;
if (yych <= '.') goto yy56;
- goto yy250;
+ goto yy275;
} else {
if (yych <= '_') {
if (yych <= '^') goto yy56;
- goto yy250;
+ goto yy275;
} else {
if (yych <= '`') goto yy56;
- if (yych <= 'z') goto yy256;
+ if (yych <= 'z') goto yy281;
goto yy56;
}
}
-yy258:
+yy283:
yyaccept = 7;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '-') {
if (yych <= 0x1F) {
- if (yych <= 0x00) goto yy245;
- if (yych == '\t') goto yy245;
- goto yy221;
+ if (yych <= 0x00) goto yy270;
+ if (yych == '\t') goto yy270;
+ goto yy246;
} else {
if (yych <= '(') {
- if (yych <= ' ') goto yy245;
- goto yy221;
+ if (yych <= ' ') goto yy270;
+ goto yy246;
} else {
- if (yych <= ')') goto yy226;
- if (yych <= ',') goto yy221;
- goto yy250;
+ if (yych <= ')') goto yy251;
+ if (yych <= ',') goto yy246;
+ goto yy275;
}
}
} else {
if (yych <= 'Z') {
- if (yych <= '.') goto yy244;
- if (yych <= '/') goto yy250;
- if (yych <= '@') goto yy221;
- goto yy238;
+ if (yych <= '.') goto yy269;
+ if (yych <= '/') goto yy275;
+ if (yych <= '@') goto yy246;
+ goto yy263;
} else {
if (yych <= '_') {
- if (yych <= '^') goto yy221;
- goto yy250;
+ if (yych <= '^') goto yy246;
+ goto yy275;
} else {
- if (yych <= '`') goto yy221;
- if (yych <= 'z') goto yy249;
- goto yy221;
+ if (yych <= '`') goto yy246;
+ if (yych <= 'z') goto yy274;
+ goto yy246;
}
}
}
-yy259:
+yy284:
yych = *++YYCURSOR;
- if (yych <= '/') goto yy221;
- if (yych <= '9') goto yy261;
- if (yych <= ':') goto yy262;
- goto yy221;
-yy260:
+ if (yych <= '/') goto yy246;
+ if (yych <= '9') goto yy286;
+ if (yych <= ':') goto yy287;
+ goto yy246;
+yy285:
yych = *++YYCURSOR;
if (yych <= '5') {
- if (yych <= '/') goto yy221;
- if (yych >= '5') goto yy263;
+ if (yych <= '/') goto yy246;
+ if (yych >= '5') goto yy288;
} else {
- if (yych <= '9') goto yy226;
- if (yych <= ':') goto yy262;
- goto yy221;
+ if (yych <= '9') goto yy251;
+ if (yych <= ':') goto yy287;
+ goto yy246;
}
-yy261:
+yy286:
yych = *++YYCURSOR;
- if (yych <= '/') goto yy221;
- if (yych <= '5') goto yy263;
- if (yych <= '9') goto yy226;
- if (yych >= ';') goto yy221;
-yy262:
+ if (yych <= '/') goto yy246;
+ if (yych <= '5') goto yy288;
+ if (yych <= '9') goto yy251;
+ if (yych >= ';') goto yy246;
+yy287:
yych = *++YYCURSOR;
- if (yych <= '/') goto yy221;
- if (yych <= '5') goto yy263;
- if (yych <= '9') goto yy226;
- goto yy221;
-yy263:
+ if (yych <= '/') goto yy246;
+ if (yych <= '5') goto yy288;
+ if (yych <= '9') goto yy251;
+ goto yy246;
+yy288:
yych = *++YYCURSOR;
- if (yych <= '/') goto yy221;
- if (yych <= '9') goto yy226;
- goto yy221;
-yy264:
+ if (yych <= '/') goto yy246;
+ if (yych <= '9') goto yy251;
+ goto yy246;
+yy289:
yych = *++YYCURSOR;
if (yych <= 'S') {
- if (yych == ')') goto yy226;
- if (yych <= '@') goto yy221;
- goto yy238;
+ if (yych == ')') goto yy251;
+ if (yych <= '@') goto yy246;
+ goto yy263;
} else {
if (yych <= 'Z') {
- if (yych >= 'U') goto yy238;
+ if (yych >= 'U') goto yy263;
} else {
- if (yych <= '`') goto yy221;
- if (yych <= 'z') goto yy238;
- goto yy221;
+ if (yych <= '`') goto yy246;
+ if (yych <= 'z') goto yy263;
+ goto yy246;
}
}
yyaccept = 7;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= ',') {
if (yych <= ')') {
- if (yych <= '(') goto yy221;
- goto yy226;
+ if (yych <= '(') goto yy246;
+ goto yy251;
} else {
- if (yych == '+') goto yy231;
- goto yy221;
+ if (yych == '+') goto yy256;
+ goto yy246;
}
} else {
if (yych <= 'Z') {
- if (yych <= '-') goto yy231;
- if (yych <= '@') goto yy221;
- goto yy239;
+ if (yych <= '-') goto yy256;
+ if (yych <= '@') goto yy246;
+ goto yy264;
} else {
- if (yych <= '`') goto yy221;
- if (yych <= 'z') goto yy239;
- goto yy221;
+ if (yych <= '`') goto yy246;
+ if (yych <= 'z') goto yy264;
+ goto yy246;
}
}
-yy266:
+yy291:
yyaccept = 7;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= ':') {
if (yych <= ' ') {
- if (yych == '\t') goto yy267;
- if (yych <= 0x1F) goto yy221;
+ if (yych == '\t') goto yy292;
+ if (yych <= 0x1F) goto yy246;
} else {
- if (yych == '.') goto yy223;
- if (yych <= '9') goto yy221;
- goto yy223;
+ if (yych == '.') goto yy248;
+ if (yych <= '9') goto yy246;
+ goto yy248;
}
} else {
if (yych <= 'P') {
- if (yych == 'A') goto yy269;
- if (yych <= 'O') goto yy221;
- goto yy269;
+ if (yych == 'A') goto yy294;
+ if (yych <= 'O') goto yy246;
+ goto yy294;
} else {
if (yych <= 'a') {
- if (yych <= '`') goto yy221;
- goto yy269;
+ if (yych <= '`') goto yy246;
+ goto yy294;
} else {
- if (yych == 'p') goto yy269;
- goto yy221;
+ if (yych == 'p') goto yy294;
+ goto yy246;
}
}
}
-yy267:
+yy292:
++YYCURSOR;
if ((YYLIMIT - YYCURSOR) < 5) YYFILL(5);
yych = *YYCURSOR;
if (yych <= 'A') {
if (yych <= 0x1F) {
- if (yych == '\t') goto yy267;
+ if (yych == '\t') goto yy292;
goto yy56;
} else {
- if (yych <= ' ') goto yy267;
+ if (yych <= ' ') goto yy292;
if (yych <= '@') goto yy56;
}
} else {
if (yych <= '`') {
if (yych != 'P') goto yy56;
} else {
- if (yych <= 'a') goto yy269;
+ if (yych <= 'a') goto yy294;
if (yych != 'p') goto yy56;
}
}
-yy269:
+yy294:
yych = *++YYCURSOR;
if (yych <= 'L') {
if (yych != '.') goto yy56;
} else {
- if (yych <= 'M') goto yy271;
- if (yych == 'm') goto yy271;
+ if (yych <= 'M') goto yy296;
+ if (yych == 'm') goto yy296;
goto yy56;
}
yych = *++YYCURSOR;
- if (yych == 'M') goto yy271;
+ if (yych == 'M') goto yy296;
if (yych != 'm') goto yy56;
-yy271:
+yy296:
yych = *++YYCURSOR;
if (yych <= 0x1F) {
- if (yych <= 0x00) goto yy245;
- if (yych == '\t') goto yy245;
+ if (yych <= 0x00) goto yy270;
+ if (yych == '\t') goto yy270;
goto yy56;
} else {
- if (yych <= ' ') goto yy245;
+ if (yych <= ' ') goto yy270;
if (yych != '.') goto yy56;
}
yych = *++YYCURSOR;
if (yych <= '\t') {
- if (yych <= 0x00) goto yy245;
+ if (yych <= 0x00) goto yy270;
if (yych <= 0x08) goto yy56;
- goto yy245;
+ goto yy270;
} else {
- if (yych == ' ') goto yy245;
+ if (yych == ' ') goto yy270;
goto yy56;
}
-yy273:
+yy298:
yych = *++YYCURSOR;
- if (yych == '.') goto yy274;
+ if (yych == '.') goto yy299;
if (yych != ':') goto yy56;
-yy274:
+yy299:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '5') goto yy275;
- if (yych <= '9') goto yy276;
+ if (yych <= '5') goto yy300;
+ if (yych <= '9') goto yy301;
goto yy56;
-yy275:
+yy300:
yyaccept = 7;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '/') {
- if (yych == '.') goto yy277;
- goto yy221;
+ if (yych == '.') goto yy302;
+ goto yy246;
} else {
- if (yych <= '9') goto yy276;
- if (yych <= ':') goto yy277;
- goto yy221;
+ if (yych <= '9') goto yy301;
+ if (yych <= ':') goto yy302;
+ goto yy246;
}
-yy276:
+yy301:
yyaccept = 7;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych == '.') goto yy277;
- if (yych != ':') goto yy221;
-yy277:
+ if (yych == '.') goto yy302;
+ if (yych != ':') goto yy246;
+yy302:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '5') goto yy278;
- if (yych <= '6') goto yy279;
- if (yych <= '9') goto yy226;
+ if (yych <= '5') goto yy303;
+ if (yych <= '6') goto yy304;
+ if (yych <= '9') goto yy251;
goto yy56;
-yy278:
+yy303:
yych = *++YYCURSOR;
- if (yych <= '/') goto yy221;
- if (yych <= '9') goto yy280;
- goto yy221;
-yy279:
+ if (yych <= '/') goto yy246;
+ if (yych <= '9') goto yy305;
+ goto yy246;
+yy304:
yych = *++YYCURSOR;
- if (yych != '0') goto yy221;
-yy280:
+ if (yych != '0') goto yy246;
+yy305:
yyaccept = 7;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '*') {
if (yych <= 0x1F) {
- if (yych == '\t') goto yy282;
- goto yy221;
+ if (yych == '\t') goto yy307;
+ goto yy246;
} else {
- if (yych <= ' ') goto yy282;
- if (yych == '(') goto yy282;
- goto yy221;
+ if (yych <= ' ') goto yy307;
+ if (yych == '(') goto yy307;
+ goto yy246;
}
} else {
if (yych <= '@') {
- if (yych == ',') goto yy221;
- if (yych <= '-') goto yy282;
- goto yy221;
+ if (yych == ',') goto yy246;
+ if (yych <= '-') goto yy307;
+ goto yy246;
} else {
- if (yych <= 'Z') goto yy282;
- if (yych <= '`') goto yy221;
- if (yych <= 'z') goto yy282;
- goto yy221;
+ if (yych <= 'Z') goto yy307;
+ if (yych <= '`') goto yy246;
+ if (yych <= 'z') goto yy307;
+ goto yy246;
}
}
-yy281:
+yy306:
++YYCURSOR;
if ((YYLIMIT - YYCURSOR) < 9) YYFILL(9);
yych = *YYCURSOR;
-yy282:
+yy307:
if (yych <= '+') {
if (yych <= ' ') {
- if (yych == '\t') goto yy281;
+ if (yych == '\t') goto yy306;
if (yych <= 0x1F) goto yy56;
- goto yy281;
+ goto yy306;
} else {
- if (yych == '(') goto yy232;
+ if (yych == '(') goto yy257;
if (yych <= '*') goto yy56;
- goto yy231;
+ goto yy256;
}
} else {
if (yych <= 'F') {
- if (yych == '-') goto yy231;
+ if (yych == '-') goto yy256;
if (yych <= '@') goto yy56;
- goto yy234;
+ goto yy259;
} else {
if (yych <= 'Z') {
- if (yych <= 'G') goto yy230;
- goto yy234;
+ if (yych <= 'G') goto yy255;
+ goto yy259;
} else {
if (yych <= '`') goto yy56;
- if (yych <= 'z') goto yy236;
+ if (yych <= 'z') goto yy261;
goto yy56;
}
}
}
-yy283:
+yy308:
yych = *++YYCURSOR;
if (yych <= '/') {
- if (yych == '.') goto yy274;
+ if (yych == '.') goto yy299;
goto yy56;
} else {
- if (yych <= '9') goto yy273;
- if (yych <= ':') goto yy274;
+ if (yych <= '9') goto yy298;
+ if (yych <= ':') goto yy299;
goto yy56;
}
-yy284:
+yy309:
yych = *++YYCURSOR;
if (yych <= '/') {
- if (yych == '.') goto yy274;
+ if (yych == '.') goto yy299;
goto yy56;
} else {
- if (yych <= '4') goto yy273;
- if (yych == ':') goto yy274;
+ if (yych <= '4') goto yy298;
+ if (yych == ':') goto yy299;
goto yy56;
}
-yy285:
+yy310:
yyaccept = 6;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '/') {
- if (yych == '.') goto yy274;
- goto yy207;
+ if (yych == '.') goto yy299;
+ goto yy232;
} else {
- if (yych <= '0') goto yy292;
- if (yych <= '9') goto yy293;
- if (yych <= ':') goto yy274;
- goto yy207;
+ if (yych <= '0') goto yy317;
+ if (yych <= '9') goto yy318;
+ if (yych <= ':') goto yy299;
+ goto yy232;
}
-yy286:
+yy311:
yyaccept = 6;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '/') {
- if (yych == '.') goto yy219;
- goto yy207;
+ if (yych == '.') goto yy244;
+ goto yy232;
} else {
- if (yych <= '2') goto yy293;
- if (yych <= '9') goto yy292;
- if (yych <= ':') goto yy219;
- goto yy207;
+ if (yych <= '2') goto yy318;
+ if (yych <= '9') goto yy317;
+ if (yych <= ':') goto yy244;
+ goto yy232;
}
-yy287:
+yy312:
yyaccept = 6;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '/') {
- if (yych == '.') goto yy219;
- goto yy207;
+ if (yych == '.') goto yy244;
+ goto yy232;
} else {
- if (yych <= '4') goto yy292;
- if (yych <= '9') goto yy289;
- if (yych <= ':') goto yy219;
- goto yy207;
+ if (yych <= '4') goto yy317;
+ if (yych <= '9') goto yy314;
+ if (yych <= ':') goto yy244;
+ goto yy232;
}
-yy288:
+yy313:
yyaccept = 6;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '/') {
- if (yych == '.') goto yy219;
- goto yy207;
+ if (yych == '.') goto yy244;
+ goto yy232;
} else {
- if (yych <= '9') goto yy289;
- if (yych <= ':') goto yy219;
- goto yy207;
+ if (yych <= '9') goto yy314;
+ if (yych <= ':') goto yy244;
+ goto yy232;
}
-yy289:
+yy314:
yych = *++YYCURSOR;
- if (yych <= '/') goto yy207;
- if (yych >= ':') goto yy207;
-yy290:
+ if (yych <= '/') goto yy232;
+ if (yych >= ':') goto yy232;
+yy315:
yych = *++YYCURSOR;
- if (yych <= '/') goto yy207;
- if (yych >= ':') goto yy207;
+ if (yych <= '/') goto yy232;
+ if (yych >= ':') goto yy232;
yych = *++YYCURSOR;
- goto yy207;
-yy292:
+ goto yy232;
+yy317:
yyaccept = 6;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '/') {
- if (yych == '.') goto yy274;
- goto yy207;
+ if (yych == '.') goto yy299;
+ goto yy232;
} else {
- if (yych <= '9') goto yy290;
- if (yych <= ':') goto yy274;
- goto yy207;
+ if (yych <= '9') goto yy315;
+ if (yych <= ':') goto yy299;
+ goto yy232;
}
-yy293:
+yy318:
yyaccept = 6;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '/') {
- if (yych == '.') goto yy219;
- goto yy207;
+ if (yych == '.') goto yy244;
+ goto yy232;
} else {
- if (yych <= '9') goto yy290;
- if (yych <= ':') goto yy219;
- goto yy207;
+ if (yych <= '9') goto yy315;
+ if (yych <= ':') goto yy244;
+ goto yy232;
}
-yy294:
+yy319:
yyaccept = 6;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych <= 0x00) goto yy213;
- goto yy212;
-yy295:
+ if (yych <= 0x00) goto yy238;
+ goto yy237;
+yy320:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
if (yych >= ':') goto yy56;
++YYCURSOR;
-#line 1338 "ext/date/lib/parse_date.re"
+#line 1381 "ext/date/lib/parse_date.re"
{
int length = 0;
DEBUG_OUTPUT("datenoday");
@@ -5130,218 +5506,218 @@ yy295:
TIMELIB_DEINIT;
return TIMELIB_DATE_NO_DAY;
}
-#line 5134 "ext/date/lib/parse_date.c"
-yy298:
+#line 5510 "ext/date/lib/parse_date.c"
+yy323:
yych = *++YYCURSOR;
if (yych <= '9') {
if (yych <= ' ') {
if (yych <= 0x08) {
- if (yych <= 0x00) goto yy213;
+ if (yych <= 0x00) goto yy238;
goto yy56;
} else {
- if (yych <= '\t') goto yy211;
+ if (yych <= '\t') goto yy236;
if (yych <= 0x1F) goto yy56;
- goto yy211;
+ goto yy236;
}
} else {
if (yych <= '-') {
- if (yych == ',') goto yy211;
+ if (yych == ',') goto yy236;
goto yy56;
} else {
- if (yych <= '.') goto yy211;
+ if (yych <= '.') goto yy236;
if (yych <= '/') goto yy56;
- goto yy295;
+ goto yy320;
}
}
} else {
if (yych <= 'm') {
if (yych <= 'd') {
if (yych <= 'c') goto yy56;
- goto yy211;
+ goto yy236;
} else {
- if (yych == 'h') goto yy211;
+ if (yych == 'h') goto yy236;
goto yy56;
}
} else {
if (yych <= 'r') {
- if (yych <= 'n') goto yy208;
+ if (yych <= 'n') goto yy233;
if (yych <= 'q') goto yy56;
- goto yy209;
+ goto yy234;
} else {
- if (yych <= 's') goto yy206;
- if (yych <= 't') goto yy210;
+ if (yych <= 's') goto yy231;
+ if (yych <= 't') goto yy235;
goto yy56;
}
}
}
-yy299:
+yy324:
yych = *++YYCURSOR;
if (yych <= '9') {
if (yych <= ' ') {
if (yych <= 0x08) {
- if (yych <= 0x00) goto yy213;
+ if (yych <= 0x00) goto yy238;
goto yy56;
} else {
- if (yych <= '\t') goto yy211;
+ if (yych <= '\t') goto yy236;
if (yych <= 0x1F) goto yy56;
- goto yy211;
+ goto yy236;
}
} else {
if (yych <= '-') {
- if (yych == ',') goto yy211;
+ if (yych == ',') goto yy236;
goto yy56;
} else {
- if (yych <= '.') goto yy211;
+ if (yych <= '.') goto yy236;
if (yych <= '/') goto yy56;
- goto yy302;
+ goto yy327;
}
}
} else {
if (yych <= 'm') {
if (yych <= 'd') {
if (yych <= 'c') goto yy56;
- goto yy211;
+ goto yy236;
} else {
- if (yych == 'h') goto yy211;
+ if (yych == 'h') goto yy236;
goto yy56;
}
} else {
if (yych <= 'r') {
- if (yych <= 'n') goto yy208;
+ if (yych <= 'n') goto yy233;
if (yych <= 'q') goto yy56;
- goto yy209;
+ goto yy234;
} else {
- if (yych <= 's') goto yy206;
- if (yych <= 't') goto yy210;
+ if (yych <= 's') goto yy231;
+ if (yych <= 't') goto yy235;
goto yy56;
}
}
}
-yy300:
+yy325:
yych = *++YYCURSOR;
if (yych <= '9') {
if (yych <= ' ') {
if (yych <= 0x08) {
- if (yych <= 0x00) goto yy213;
+ if (yych <= 0x00) goto yy238;
goto yy56;
} else {
- if (yych <= '\t') goto yy211;
+ if (yych <= '\t') goto yy236;
if (yych <= 0x1F) goto yy56;
- goto yy211;
+ goto yy236;
}
} else {
if (yych <= '-') {
- if (yych == ',') goto yy211;
+ if (yych == ',') goto yy236;
goto yy56;
} else {
- if (yych <= '.') goto yy211;
+ if (yych <= '.') goto yy236;
if (yych <= '/') goto yy56;
- goto yy302;
+ goto yy327;
}
}
} else {
if (yych <= 'm') {
if (yych <= 'd') {
if (yych <= 'c') goto yy56;
- goto yy211;
+ goto yy236;
} else {
- if (yych == 'h') goto yy211;
+ if (yych == 'h') goto yy236;
goto yy56;
}
} else {
if (yych <= 'r') {
- if (yych <= 'n') goto yy208;
+ if (yych <= 'n') goto yy233;
if (yych <= 'q') goto yy56;
- goto yy209;
+ goto yy234;
} else {
- if (yych <= 's') goto yy206;
- if (yych <= 't') goto yy210;
+ if (yych <= 's') goto yy231;
+ if (yych <= 't') goto yy235;
goto yy56;
}
}
}
-yy301:
+yy326:
yych = *++YYCURSOR;
if (yych <= '1') {
if (yych <= ' ') {
if (yych <= 0x08) {
- if (yych <= 0x00) goto yy213;
+ if (yych <= 0x00) goto yy238;
goto yy56;
} else {
- if (yych <= '\t') goto yy211;
+ if (yych <= '\t') goto yy236;
if (yych <= 0x1F) goto yy56;
- goto yy211;
+ goto yy236;
}
} else {
if (yych <= '-') {
- if (yych == ',') goto yy211;
+ if (yych == ',') goto yy236;
goto yy56;
} else {
- if (yych <= '.') goto yy211;
+ if (yych <= '.') goto yy236;
if (yych <= '/') goto yy56;
}
}
} else {
if (yych <= 'm') {
if (yych <= 'd') {
- if (yych <= '9') goto yy205;
+ if (yych <= '9') goto yy230;
if (yych <= 'c') goto yy56;
- goto yy211;
+ goto yy236;
} else {
- if (yych == 'h') goto yy211;
+ if (yych == 'h') goto yy236;
goto yy56;
}
} else {
if (yych <= 'r') {
- if (yych <= 'n') goto yy208;
+ if (yych <= 'n') goto yy233;
if (yych <= 'q') goto yy56;
- goto yy209;
+ goto yy234;
} else {
- if (yych <= 's') goto yy206;
- if (yych <= 't') goto yy210;
+ if (yych <= 's') goto yy231;
+ if (yych <= 't') goto yy235;
goto yy56;
}
}
}
-yy302:
+yy327:
yych = *++YYCURSOR;
if (yych <= '9') {
if (yych <= ' ') {
if (yych <= 0x08) {
- if (yych <= 0x00) goto yy213;
+ if (yych <= 0x00) goto yy238;
goto yy56;
} else {
- if (yych <= '\t') goto yy211;
+ if (yych <= '\t') goto yy236;
if (yych <= 0x1F) goto yy56;
- goto yy211;
+ goto yy236;
}
} else {
if (yych <= '-') {
if (yych <= '+') goto yy56;
- if (yych <= ',') goto yy211;
+ if (yych <= ',') goto yy236;
} else {
- if (yych <= '.') goto yy211;
+ if (yych <= '.') goto yy236;
if (yych <= '/') goto yy56;
- goto yy295;
+ goto yy320;
}
}
} else {
if (yych <= 'm') {
if (yych <= 'd') {
if (yych <= 'c') goto yy56;
- goto yy211;
+ goto yy236;
} else {
- if (yych == 'h') goto yy211;
+ if (yych == 'h') goto yy236;
goto yy56;
}
} else {
if (yych <= 'r') {
- if (yych <= 'n') goto yy208;
+ if (yych <= 'n') goto yy233;
if (yych <= 'q') goto yy56;
- goto yy209;
+ goto yy234;
} else {
- if (yych <= 's') goto yy206;
- if (yych <= 't') goto yy210;
+ if (yych <= 's') goto yy231;
+ if (yych <= 't') goto yy235;
goto yy56;
}
}
@@ -5350,10 +5726,10 @@ yy302:
if (yych <= '/') goto yy56;
if (yych >= ':') goto yy56;
++YYCURSOR;
- if ((yych = *YYCURSOR) <= '/') goto yy305;
- if (yych <= '9') goto yy306;
-yy305:
-#line 1482 "ext/date/lib/parse_date.re"
+ if ((yych = *YYCURSOR) <= '/') goto yy330;
+ if (yych <= '9') goto yy331;
+yy330:
+#line 1525 "ext/date/lib/parse_date.re"
{
int length = 0;
DEBUG_OUTPUT("pgtextshort");
@@ -5366,562 +5742,562 @@ yy305:
TIMELIB_DEINIT;
return TIMELIB_PG_TEXT;
}
-#line 5370 "ext/date/lib/parse_date.c"
-yy306:
+#line 5746 "ext/date/lib/parse_date.c"
+yy331:
yych = *++YYCURSOR;
- if (yych <= '/') goto yy305;
- if (yych >= ':') goto yy305;
+ if (yych <= '/') goto yy330;
+ if (yych >= ':') goto yy330;
yych = *++YYCURSOR;
- if (yych <= '/') goto yy305;
- if (yych >= ':') goto yy305;
+ if (yych <= '/') goto yy330;
+ if (yych >= ':') goto yy330;
yych = *++YYCURSOR;
- goto yy305;
-yy309:
+ goto yy330;
+yy334:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'B') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy141;
+ goto yy166;
}
} else {
if (yych <= '_') {
- if (yych <= 'C') goto yy192;
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'C') goto yy217;
+ if (yych <= 'Z') goto yy166;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'b') {
if (yych <= '`') goto yy3;
- goto yy146;
+ goto yy171;
} else {
- if (yych <= 'c') goto yy310;
- if (yych <= 'z') goto yy146;
+ if (yych <= 'c') goto yy335;
+ if (yych <= 'z') goto yy171;
goto yy3;
}
}
}
-yy310:
+yy335:
yyaccept = 5;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '9') {
if (yych <= '(') {
if (yych <= '\t') {
- if (yych <= 0x08) goto yy193;
- goto yy195;
+ if (yych <= 0x08) goto yy218;
+ goto yy220;
} else {
- if (yych == ' ') goto yy195;
- goto yy193;
+ if (yych == ' ') goto yy220;
+ goto yy218;
}
} else {
if (yych <= '-') {
- if (yych <= ')') goto yy139;
- if (yych <= ',') goto yy193;
+ if (yych <= ')') goto yy164;
+ if (yych <= ',') goto yy218;
} else {
- if (yych == '/') goto yy147;
- goto yy195;
+ if (yych == '/') goto yy172;
+ goto yy220;
}
}
} else {
if (yych <= '^') {
if (yych <= 'D') {
- if (yych <= '@') goto yy193;
- goto yy142;
+ if (yych <= '@') goto yy218;
+ goto yy167;
} else {
- if (yych <= 'E') goto yy200;
- if (yych <= 'Z') goto yy142;
- goto yy193;
+ if (yych <= 'E') goto yy225;
+ if (yych <= 'Z') goto yy167;
+ goto yy218;
}
} else {
if (yych <= 'd') {
- if (yych <= '_') goto yy147;
- if (yych <= '`') goto yy193;
- goto yy150;
+ if (yych <= '_') goto yy172;
+ if (yych <= '`') goto yy218;
+ goto yy175;
} else {
- if (yych <= 'e') goto yy312;
- if (yych <= 'z') goto yy150;
- goto yy193;
+ if (yych <= 'e') goto yy337;
+ if (yych <= 'z') goto yy175;
+ goto yy218;
}
}
}
-yy311:
+yy336:
yych = *++YYCURSOR;
if (yybm[0+yych] & 8) {
- goto yy148;
+ goto yy173;
}
- if (yych <= '/') goto yy195;
- if (yych <= '0') goto yy299;
- if (yych <= '2') goto yy300;
- if (yych <= '3') goto yy301;
- goto yy195;
-yy312:
+ if (yych <= '/') goto yy220;
+ if (yych <= '0') goto yy324;
+ if (yych <= '2') goto yy325;
+ if (yych <= '3') goto yy326;
+ goto yy220;
+yy337:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'L') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy143;
+ goto yy168;
}
} else {
if (yych <= '_') {
- if (yych <= 'M') goto yy201;
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'M') goto yy226;
+ if (yych <= 'Z') goto yy168;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'l') {
if (yych <= '`') goto yy3;
- goto yy151;
+ goto yy176;
} else {
- if (yych <= 'm') goto yy313;
- if (yych <= 'z') goto yy151;
+ if (yych <= 'm') goto yy338;
+ if (yych <= 'z') goto yy176;
goto yy3;
}
}
}
-yy313:
+yy338:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'A') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy144;
+ goto yy169;
}
} else {
if (yych <= '_') {
- if (yych <= 'B') goto yy202;
- if (yych <= 'Z') goto yy144;
+ if (yych <= 'B') goto yy227;
+ if (yych <= 'Z') goto yy169;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'a') {
if (yych <= '`') goto yy3;
- goto yy152;
+ goto yy177;
} else {
- if (yych <= 'b') goto yy314;
- if (yych <= 'z') goto yy152;
+ if (yych <= 'b') goto yy339;
+ if (yych <= 'z') goto yy177;
goto yy3;
}
}
}
-yy314:
+yy339:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'D') {
if (yych <= ',') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
if (yych == '.') goto yy3;
- if (yych <= '/') goto yy147;
+ if (yych <= '/') goto yy172;
goto yy3;
}
} else {
if (yych <= '`') {
- if (yych <= 'E') goto yy203;
- if (yych == '_') goto yy147;
+ if (yych <= 'E') goto yy228;
+ if (yych == '_') goto yy172;
goto yy3;
} else {
- if (yych == 'e') goto yy315;
- if (yych <= 'z') goto yy153;
+ if (yych == 'e') goto yy340;
+ if (yych <= 'z') goto yy178;
goto yy3;
}
}
-yy315:
+yy340:
yych = *++YYCURSOR;
- if (yych == 'R') goto yy204;
- if (yych != 'r') goto yy154;
-yy316:
+ if (yych == 'R') goto yy229;
+ if (yych != 'r') goto yy179;
+yy341:
yyaccept = 5;
yych = *(YYMARKER = ++YYCURSOR);
if (yybm[0+yych] & 16) {
- goto yy153;
+ goto yy178;
}
if (yych <= '-') {
if (yych <= 0x1F) {
- if (yych == '\t') goto yy195;
- goto yy193;
+ if (yych == '\t') goto yy220;
+ goto yy218;
} else {
- if (yych <= ' ') goto yy195;
- if (yych <= ',') goto yy193;
+ if (yych <= ' ') goto yy220;
+ if (yych <= ',') goto yy218;
}
} else {
if (yych <= '9') {
- if (yych == '/') goto yy147;
- goto yy195;
+ if (yych == '/') goto yy172;
+ goto yy220;
} else {
- if (yych == '_') goto yy147;
- goto yy193;
+ if (yych == '_') goto yy172;
+ goto yy218;
}
}
-yy317:
+yy342:
yych = *++YYCURSOR;
if (yybm[0+yych] & 8) {
- goto yy148;
+ goto yy173;
}
- goto yy195;
-yy318:
+ goto yy220;
+yy343:
yych = *++YYCURSOR;
if (yych <= 'T') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'S') goto yy141;
+ if (yych <= 'S') goto yy166;
}
} else {
if (yych <= 's') {
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'Z') goto yy166;
if (yych <= '`') goto yy3;
- goto yy141;
+ goto yy166;
} else {
- if (yych <= 't') goto yy319;
- if (yych <= 'z') goto yy141;
+ if (yych <= 't') goto yy344;
+ if (yych <= 'z') goto yy166;
goto yy3;
}
}
-yy319:
+yy344:
yyaccept = 5;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '/') {
if (yych <= '(') {
if (yych <= '\t') {
- if (yych <= 0x08) goto yy193;
- goto yy195;
+ if (yych <= 0x08) goto yy218;
+ goto yy220;
} else {
- if (yych == ' ') goto yy195;
- goto yy193;
+ if (yych == ' ') goto yy220;
+ goto yy218;
}
} else {
if (yych <= ',') {
- if (yych <= ')') goto yy139;
- goto yy193;
+ if (yych <= ')') goto yy164;
+ goto yy218;
} else {
- if (yych <= '-') goto yy196;
- if (yych <= '.') goto yy195;
- goto yy193;
+ if (yych <= '-') goto yy221;
+ if (yych <= '.') goto yy220;
+ goto yy218;
}
}
} else {
if (yych <= 'Z') {
if (yych <= '@') {
- if (yych <= '9') goto yy195;
- goto yy193;
+ if (yych <= '9') goto yy220;
+ goto yy218;
} else {
- if (yych != 'O') goto yy142;
+ if (yych != 'O') goto yy167;
}
} else {
if (yych <= 'n') {
- if (yych <= '`') goto yy193;
- goto yy142;
+ if (yych <= '`') goto yy218;
+ goto yy167;
} else {
- if (yych <= 'o') goto yy320;
- if (yych <= 'z') goto yy142;
- goto yy193;
+ if (yych <= 'o') goto yy345;
+ if (yych <= 'z') goto yy167;
+ goto yy218;
}
}
}
-yy320:
+yy345:
yych = *++YYCURSOR;
if (yych <= 'B') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'A') goto yy143;
+ if (yych <= 'A') goto yy168;
}
} else {
if (yych <= 'a') {
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'Z') goto yy168;
if (yych <= '`') goto yy3;
- goto yy143;
+ goto yy168;
} else {
- if (yych <= 'b') goto yy321;
- if (yych <= 'z') goto yy143;
+ if (yych <= 'b') goto yy346;
+ if (yych <= 'z') goto yy168;
goto yy3;
}
}
-yy321:
+yy346:
yych = *++YYCURSOR;
if (yych <= 'E') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'D') goto yy144;
+ if (yych <= 'D') goto yy169;
}
} else {
if (yych <= 'd') {
- if (yych <= 'Z') goto yy144;
+ if (yych <= 'Z') goto yy169;
if (yych <= '`') goto yy3;
- goto yy144;
+ goto yy169;
} else {
- if (yych <= 'e') goto yy322;
- if (yych <= 'z') goto yy144;
+ if (yych <= 'e') goto yy347;
+ if (yych <= 'z') goto yy169;
goto yy3;
}
}
-yy322:
+yy347:
yych = *++YYCURSOR;
if (yych <= 'Q') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
- if (yych <= 'R') goto yy204;
- if (yych == 'r') goto yy204;
+ if (yych <= 'R') goto yy229;
+ if (yych == 'r') goto yy229;
goto yy3;
}
-yy323:
+yy348:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'S') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy141;
+ goto yy166;
}
} else {
if (yych <= '_') {
- if (yych <= 'T') goto yy319;
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'T') goto yy344;
+ if (yych <= 'Z') goto yy166;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 's') {
if (yych <= '`') goto yy3;
- goto yy146;
+ goto yy171;
} else {
- if (yych <= 't') goto yy324;
- if (yych <= 'z') goto yy146;
+ if (yych <= 't') goto yy349;
+ if (yych <= 'z') goto yy171;
goto yy3;
}
}
}
-yy324:
+yy349:
yyaccept = 5;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '9') {
if (yych <= '(') {
if (yych <= '\t') {
- if (yych <= 0x08) goto yy193;
- goto yy195;
+ if (yych <= 0x08) goto yy218;
+ goto yy220;
} else {
- if (yych == ' ') goto yy195;
- goto yy193;
+ if (yych == ' ') goto yy220;
+ goto yy218;
}
} else {
if (yych <= '-') {
- if (yych <= ')') goto yy139;
- if (yych <= ',') goto yy193;
- goto yy311;
+ if (yych <= ')') goto yy164;
+ if (yych <= ',') goto yy218;
+ goto yy336;
} else {
- if (yych == '/') goto yy147;
- goto yy195;
+ if (yych == '/') goto yy172;
+ goto yy220;
}
}
} else {
if (yych <= '^') {
if (yych <= 'N') {
- if (yych <= '@') goto yy193;
- goto yy142;
+ if (yych <= '@') goto yy218;
+ goto yy167;
} else {
- if (yych <= 'O') goto yy320;
- if (yych <= 'Z') goto yy142;
- goto yy193;
+ if (yych <= 'O') goto yy345;
+ if (yych <= 'Z') goto yy167;
+ goto yy218;
}
} else {
if (yych <= 'n') {
- if (yych <= '_') goto yy147;
- if (yych <= '`') goto yy193;
- goto yy150;
+ if (yych <= '_') goto yy172;
+ if (yych <= '`') goto yy218;
+ goto yy175;
} else {
- if (yych <= 'o') goto yy325;
- if (yych <= 'z') goto yy150;
- goto yy193;
+ if (yych <= 'o') goto yy350;
+ if (yych <= 'z') goto yy175;
+ goto yy218;
}
}
}
-yy325:
+yy350:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'A') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy143;
+ goto yy168;
}
} else {
if (yych <= '_') {
- if (yych <= 'B') goto yy321;
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'B') goto yy346;
+ if (yych <= 'Z') goto yy168;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'a') {
if (yych <= '`') goto yy3;
- goto yy151;
+ goto yy176;
} else {
- if (yych <= 'b') goto yy326;
- if (yych <= 'z') goto yy151;
+ if (yych <= 'b') goto yy351;
+ if (yych <= 'z') goto yy176;
goto yy3;
}
}
}
-yy326:
+yy351:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'D') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy144;
+ goto yy169;
}
} else {
if (yych <= '_') {
- if (yych <= 'E') goto yy322;
- if (yych <= 'Z') goto yy144;
+ if (yych <= 'E') goto yy347;
+ if (yych <= 'Z') goto yy169;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'd') {
if (yych <= '`') goto yy3;
- goto yy152;
+ goto yy177;
} else {
- if (yych <= 'e') goto yy327;
- if (yych <= 'z') goto yy152;
+ if (yych <= 'e') goto yy352;
+ if (yych <= 'z') goto yy177;
goto yy3;
}
}
}
-yy327:
+yy352:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'Q') {
if (yych <= ',') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
if (yych == '.') goto yy3;
- if (yych <= '/') goto yy147;
+ if (yych <= '/') goto yy172;
goto yy3;
}
} else {
if (yych <= '`') {
- if (yych <= 'R') goto yy204;
- if (yych == '_') goto yy147;
+ if (yych <= 'R') goto yy229;
+ if (yych == '_') goto yy172;
goto yy3;
} else {
- if (yych == 'r') goto yy316;
- if (yych <= 'z') goto yy153;
+ if (yych == 'r') goto yy341;
+ if (yych <= 'z') goto yy178;
goto yy3;
}
}
-yy328:
+yy353:
yych = *++YYCURSOR;
if (yych <= 'G') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'F') goto yy141;
- goto yy336;
+ if (yych <= 'F') goto yy166;
+ goto yy361;
}
} else {
if (yych <= 'f') {
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'Z') goto yy166;
if (yych <= '`') goto yy3;
- goto yy141;
+ goto yy166;
} else {
- if (yych <= 'g') goto yy336;
- if (yych <= 'z') goto yy141;
+ if (yych <= 'g') goto yy361;
+ if (yych <= 'z') goto yy166;
goto yy3;
}
}
-yy329:
+yy354:
yych = *++YYCURSOR;
if (yych <= 'R') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'Q') goto yy141;
- goto yy333;
+ if (yych <= 'Q') goto yy166;
+ goto yy358;
}
} else {
if (yych <= 'q') {
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'Z') goto yy166;
if (yych <= '`') goto yy3;
- goto yy141;
+ goto yy166;
} else {
- if (yych <= 'r') goto yy333;
- if (yych <= 'z') goto yy141;
+ if (yych <= 'r') goto yy358;
+ if (yych <= 'z') goto yy166;
goto yy3;
}
}
-yy330:
+yy355:
yych = *++YYCURSOR;
if (yych <= 'O') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'N') goto yy141;
+ if (yych <= 'N') goto yy166;
}
} else {
if (yych <= 'n') {
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'Z') goto yy166;
if (yych <= '`') goto yy3;
- goto yy141;
+ goto yy166;
} else {
- if (yych <= 'o') goto yy331;
- if (yych <= 'z') goto yy141;
+ if (yych <= 'o') goto yy356;
+ if (yych <= 'z') goto yy166;
goto yy3;
}
}
-yy331:
+yy356:
++YYCURSOR;
if ((yych = *YYCURSOR) <= '@') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
} else {
- if (yych <= 'Z') goto yy142;
- if (yych <= '`') goto yy332;
- if (yych <= 'z') goto yy142;
+ if (yych <= 'Z') goto yy167;
+ if (yych <= '`') goto yy357;
+ if (yych <= 'z') goto yy167;
}
-yy332:
-#line 1540 "ext/date/lib/parse_date.re"
+yy357:
+#line 1583 "ext/date/lib/parse_date.re"
{
DEBUG_OUTPUT("ago");
TIMELIB_INIT;
@@ -5941,1182 +6317,1182 @@ yy332:
TIMELIB_DEINIT;
return TIMELIB_AGO;
}
-#line 5945 "ext/date/lib/parse_date.c"
-yy333:
+#line 6321 "ext/date/lib/parse_date.c"
+yy358:
yyaccept = 5;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '/') {
if (yych <= '(') {
if (yych <= '\t') {
- if (yych <= 0x08) goto yy193;
- goto yy195;
+ if (yych <= 0x08) goto yy218;
+ goto yy220;
} else {
- if (yych == ' ') goto yy195;
- goto yy193;
+ if (yych == ' ') goto yy220;
+ goto yy218;
}
} else {
if (yych <= ',') {
- if (yych <= ')') goto yy139;
- goto yy193;
+ if (yych <= ')') goto yy164;
+ goto yy218;
} else {
- if (yych <= '-') goto yy196;
- if (yych <= '.') goto yy195;
- goto yy193;
+ if (yych <= '-') goto yy221;
+ if (yych <= '.') goto yy220;
+ goto yy218;
}
}
} else {
if (yych <= 'Z') {
if (yych <= '@') {
- if (yych <= '9') goto yy195;
- goto yy193;
+ if (yych <= '9') goto yy220;
+ goto yy218;
} else {
- if (yych != 'I') goto yy142;
+ if (yych != 'I') goto yy167;
}
} else {
if (yych <= 'h') {
- if (yych <= '`') goto yy193;
- goto yy142;
+ if (yych <= '`') goto yy218;
+ goto yy167;
} else {
- if (yych <= 'i') goto yy334;
- if (yych <= 'z') goto yy142;
- goto yy193;
+ if (yych <= 'i') goto yy359;
+ if (yych <= 'z') goto yy167;
+ goto yy218;
}
}
}
-yy334:
+yy359:
yych = *++YYCURSOR;
if (yych <= 'L') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'K') goto yy143;
+ if (yych <= 'K') goto yy168;
}
} else {
if (yych <= 'k') {
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'Z') goto yy168;
if (yych <= '`') goto yy3;
- goto yy143;
+ goto yy168;
} else {
- if (yych <= 'l') goto yy335;
- if (yych <= 'z') goto yy143;
+ if (yych <= 'l') goto yy360;
+ if (yych <= 'z') goto yy168;
goto yy3;
}
}
-yy335:
+yy360:
yyaccept = 5;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= ',') {
if (yych <= 0x1F) {
- if (yych == '\t') goto yy195;
- goto yy193;
+ if (yych == '\t') goto yy220;
+ goto yy218;
} else {
- if (yych <= ' ') goto yy195;
- if (yych == ')') goto yy139;
- goto yy193;
+ if (yych <= ' ') goto yy220;
+ if (yych == ')') goto yy164;
+ goto yy218;
}
} else {
if (yych <= '@') {
- if (yych == '/') goto yy193;
- if (yych <= '9') goto yy195;
- goto yy193;
+ if (yych == '/') goto yy218;
+ if (yych <= '9') goto yy220;
+ goto yy218;
} else {
- if (yych <= 'Z') goto yy144;
- if (yych <= '`') goto yy193;
- if (yych <= 'z') goto yy144;
- goto yy193;
+ if (yych <= 'Z') goto yy169;
+ if (yych <= '`') goto yy218;
+ if (yych <= 'z') goto yy169;
+ goto yy218;
}
}
-yy336:
+yy361:
yyaccept = 5;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '/') {
if (yych <= '(') {
if (yych <= '\t') {
- if (yych <= 0x08) goto yy193;
- goto yy195;
+ if (yych <= 0x08) goto yy218;
+ goto yy220;
} else {
- if (yych == ' ') goto yy195;
- goto yy193;
+ if (yych == ' ') goto yy220;
+ goto yy218;
}
} else {
if (yych <= ',') {
- if (yych <= ')') goto yy139;
- goto yy193;
+ if (yych <= ')') goto yy164;
+ goto yy218;
} else {
- if (yych <= '-') goto yy196;
- if (yych <= '.') goto yy195;
- goto yy193;
+ if (yych <= '-') goto yy221;
+ if (yych <= '.') goto yy220;
+ goto yy218;
}
}
} else {
if (yych <= 'Z') {
if (yych <= '@') {
- if (yych <= '9') goto yy195;
- goto yy193;
+ if (yych <= '9') goto yy220;
+ goto yy218;
} else {
- if (yych != 'U') goto yy142;
+ if (yych != 'U') goto yy167;
}
} else {
if (yych <= 't') {
- if (yych <= '`') goto yy193;
- goto yy142;
+ if (yych <= '`') goto yy218;
+ goto yy167;
} else {
- if (yych <= 'u') goto yy337;
- if (yych <= 'z') goto yy142;
- goto yy193;
+ if (yych <= 'u') goto yy362;
+ if (yych <= 'z') goto yy167;
+ goto yy218;
}
}
}
-yy337:
+yy362:
yych = *++YYCURSOR;
if (yych <= 'S') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'R') goto yy143;
+ if (yych <= 'R') goto yy168;
}
} else {
if (yych <= 'r') {
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'Z') goto yy168;
if (yych <= '`') goto yy3;
- goto yy143;
+ goto yy168;
} else {
- if (yych <= 's') goto yy338;
- if (yych <= 'z') goto yy143;
+ if (yych <= 's') goto yy363;
+ if (yych <= 'z') goto yy168;
goto yy3;
}
}
-yy338:
+yy363:
yych = *++YYCURSOR;
if (yych <= 'T') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'S') goto yy144;
+ if (yych <= 'S') goto yy169;
}
} else {
if (yych <= 's') {
- if (yych <= 'Z') goto yy144;
+ if (yych <= 'Z') goto yy169;
if (yych <= '`') goto yy3;
- goto yy144;
+ goto yy169;
} else {
- if (yych <= 't') goto yy339;
- if (yych <= 'z') goto yy144;
+ if (yych <= 't') goto yy364;
+ if (yych <= 'z') goto yy169;
goto yy3;
}
}
-yy339:
+yy364:
yyaccept = 5;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '(') {
if (yych <= '\t') {
- if (yych <= 0x08) goto yy193;
- goto yy195;
+ if (yych <= 0x08) goto yy218;
+ goto yy220;
} else {
- if (yych == ' ') goto yy195;
- goto yy193;
+ if (yych == ' ') goto yy220;
+ goto yy218;
}
} else {
if (yych <= '.') {
- if (yych <= ')') goto yy139;
- if (yych <= ',') goto yy193;
- goto yy195;
+ if (yych <= ')') goto yy164;
+ if (yych <= ',') goto yy218;
+ goto yy220;
} else {
- if (yych <= '/') goto yy193;
- if (yych <= '9') goto yy195;
- goto yy193;
+ if (yych <= '/') goto yy218;
+ if (yych <= '9') goto yy220;
+ goto yy218;
}
}
-yy340:
+yy365:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'F') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy141;
+ goto yy166;
}
} else {
if (yych <= '_') {
- if (yych <= 'G') goto yy336;
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'G') goto yy361;
+ if (yych <= 'Z') goto yy166;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'f') {
if (yych <= '`') goto yy3;
- goto yy146;
+ goto yy171;
} else {
- if (yych <= 'g') goto yy347;
- if (yych <= 'z') goto yy146;
+ if (yych <= 'g') goto yy372;
+ if (yych <= 'z') goto yy171;
goto yy3;
}
}
}
-yy341:
+yy366:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'Q') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy141;
+ goto yy166;
}
} else {
if (yych <= '_') {
- if (yych <= 'R') goto yy333;
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'R') goto yy358;
+ if (yych <= 'Z') goto yy166;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'q') {
if (yych <= '`') goto yy3;
- goto yy146;
+ goto yy171;
} else {
- if (yych <= 'r') goto yy344;
- if (yych <= 'z') goto yy146;
+ if (yych <= 'r') goto yy369;
+ if (yych <= 'z') goto yy171;
goto yy3;
}
}
}
-yy342:
+yy367:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'N') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy141;
+ goto yy166;
}
} else {
if (yych <= '_') {
- if (yych <= 'O') goto yy331;
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'O') goto yy356;
+ if (yych <= 'Z') goto yy166;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'n') {
if (yych <= '`') goto yy3;
- goto yy146;
+ goto yy171;
} else {
- if (yych <= 'o') goto yy343;
- if (yych <= 'z') goto yy146;
+ if (yych <= 'o') goto yy368;
+ if (yych <= 'z') goto yy171;
goto yy3;
}
}
}
-yy343:
+yy368:
yyaccept = 8;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '/') {
if (yych <= ',') {
- if (yych == ')') goto yy139;
- goto yy332;
+ if (yych == ')') goto yy164;
+ goto yy357;
} else {
- if (yych == '.') goto yy332;
- goto yy147;
+ if (yych == '.') goto yy357;
+ goto yy172;
}
} else {
if (yych <= '^') {
- if (yych <= '@') goto yy332;
- if (yych <= 'Z') goto yy142;
- goto yy332;
+ if (yych <= '@') goto yy357;
+ if (yych <= 'Z') goto yy167;
+ goto yy357;
} else {
- if (yych <= '_') goto yy147;
- if (yych <= '`') goto yy332;
- if (yych <= 'z') goto yy150;
- goto yy332;
+ if (yych <= '_') goto yy172;
+ if (yych <= '`') goto yy357;
+ if (yych <= 'z') goto yy175;
+ goto yy357;
}
}
-yy344:
+yy369:
yyaccept = 5;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '9') {
if (yych <= '(') {
if (yych <= '\t') {
- if (yych <= 0x08) goto yy193;
- goto yy195;
+ if (yych <= 0x08) goto yy218;
+ goto yy220;
} else {
- if (yych == ' ') goto yy195;
- goto yy193;
+ if (yych == ' ') goto yy220;
+ goto yy218;
}
} else {
if (yych <= '-') {
- if (yych <= ')') goto yy139;
- if (yych <= ',') goto yy193;
- goto yy311;
+ if (yych <= ')') goto yy164;
+ if (yych <= ',') goto yy218;
+ goto yy336;
} else {
- if (yych == '/') goto yy147;
- goto yy195;
+ if (yych == '/') goto yy172;
+ goto yy220;
}
}
} else {
if (yych <= '^') {
if (yych <= 'H') {
- if (yych <= '@') goto yy193;
- goto yy142;
+ if (yych <= '@') goto yy218;
+ goto yy167;
} else {
- if (yych <= 'I') goto yy334;
- if (yych <= 'Z') goto yy142;
- goto yy193;
+ if (yych <= 'I') goto yy359;
+ if (yych <= 'Z') goto yy167;
+ goto yy218;
}
} else {
if (yych <= 'h') {
- if (yych <= '_') goto yy147;
- if (yych <= '`') goto yy193;
- goto yy150;
+ if (yych <= '_') goto yy172;
+ if (yych <= '`') goto yy218;
+ goto yy175;
} else {
- if (yych <= 'i') goto yy345;
- if (yych <= 'z') goto yy150;
- goto yy193;
+ if (yych <= 'i') goto yy370;
+ if (yych <= 'z') goto yy175;
+ goto yy218;
}
}
}
-yy345:
+yy370:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'K') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy143;
+ goto yy168;
}
} else {
if (yych <= '_') {
- if (yych <= 'L') goto yy335;
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'L') goto yy360;
+ if (yych <= 'Z') goto yy168;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'k') {
if (yych <= '`') goto yy3;
- goto yy151;
+ goto yy176;
} else {
- if (yych <= 'l') goto yy346;
- if (yych <= 'z') goto yy151;
+ if (yych <= 'l') goto yy371;
+ if (yych <= 'z') goto yy176;
goto yy3;
}
}
}
-yy346:
+yy371:
yyaccept = 5;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '.') {
if (yych <= ' ') {
- if (yych == '\t') goto yy195;
- if (yych <= 0x1F) goto yy193;
- goto yy195;
+ if (yych == '\t') goto yy220;
+ if (yych <= 0x1F) goto yy218;
+ goto yy220;
} else {
if (yych <= ')') {
- if (yych <= '(') goto yy193;
- goto yy139;
+ if (yych <= '(') goto yy218;
+ goto yy164;
} else {
- if (yych <= ',') goto yy193;
- if (yych <= '-') goto yy317;
- goto yy195;
+ if (yych <= ',') goto yy218;
+ if (yych <= '-') goto yy342;
+ goto yy220;
}
}
} else {
if (yych <= 'Z') {
- if (yych <= '/') goto yy147;
- if (yych <= '9') goto yy195;
- if (yych <= '@') goto yy193;
- goto yy144;
+ if (yych <= '/') goto yy172;
+ if (yych <= '9') goto yy220;
+ if (yych <= '@') goto yy218;
+ goto yy169;
} else {
if (yych <= '_') {
- if (yych <= '^') goto yy193;
- goto yy147;
+ if (yych <= '^') goto yy218;
+ goto yy172;
} else {
- if (yych <= '`') goto yy193;
- if (yych <= 'z') goto yy152;
- goto yy193;
+ if (yych <= '`') goto yy218;
+ if (yych <= 'z') goto yy177;
+ goto yy218;
}
}
}
-yy347:
+yy372:
yyaccept = 5;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '9') {
if (yych <= '(') {
if (yych <= '\t') {
- if (yych <= 0x08) goto yy193;
- goto yy195;
+ if (yych <= 0x08) goto yy218;
+ goto yy220;
} else {
- if (yych == ' ') goto yy195;
- goto yy193;
+ if (yych == ' ') goto yy220;
+ goto yy218;
}
} else {
if (yych <= '-') {
- if (yych <= ')') goto yy139;
- if (yych <= ',') goto yy193;
- goto yy311;
+ if (yych <= ')') goto yy164;
+ if (yych <= ',') goto yy218;
+ goto yy336;
} else {
- if (yych == '/') goto yy147;
- goto yy195;
+ if (yych == '/') goto yy172;
+ goto yy220;
}
}
} else {
if (yych <= '^') {
if (yych <= 'T') {
- if (yych <= '@') goto yy193;
- goto yy142;
+ if (yych <= '@') goto yy218;
+ goto yy167;
} else {
- if (yych <= 'U') goto yy337;
- if (yych <= 'Z') goto yy142;
- goto yy193;
+ if (yych <= 'U') goto yy362;
+ if (yych <= 'Z') goto yy167;
+ goto yy218;
}
} else {
if (yych <= 't') {
- if (yych <= '_') goto yy147;
- if (yych <= '`') goto yy193;
- goto yy150;
+ if (yych <= '_') goto yy172;
+ if (yych <= '`') goto yy218;
+ goto yy175;
} else {
- if (yych <= 'u') goto yy348;
- if (yych <= 'z') goto yy150;
- goto yy193;
+ if (yych <= 'u') goto yy373;
+ if (yych <= 'z') goto yy175;
+ goto yy218;
}
}
}
-yy348:
+yy373:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'R') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy143;
+ goto yy168;
}
} else {
if (yych <= '_') {
- if (yych <= 'S') goto yy338;
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'S') goto yy363;
+ if (yych <= 'Z') goto yy168;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'r') {
if (yych <= '`') goto yy3;
- goto yy151;
+ goto yy176;
} else {
- if (yych <= 's') goto yy349;
- if (yych <= 'z') goto yy151;
+ if (yych <= 's') goto yy374;
+ if (yych <= 'z') goto yy176;
goto yy3;
}
}
}
-yy349:
+yy374:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'S') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy144;
+ goto yy169;
}
} else {
if (yych <= '_') {
- if (yych <= 'T') goto yy339;
- if (yych <= 'Z') goto yy144;
+ if (yych <= 'T') goto yy364;
+ if (yych <= 'Z') goto yy169;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 's') {
if (yych <= '`') goto yy3;
- goto yy152;
+ goto yy177;
} else {
- if (yych <= 't') goto yy350;
- if (yych <= 'z') goto yy152;
+ if (yych <= 't') goto yy375;
+ if (yych <= 'z') goto yy177;
goto yy3;
}
}
}
-yy350:
+yy375:
yyaccept = 5;
yych = *(YYMARKER = ++YYCURSOR);
if (yybm[0+yych] & 16) {
- goto yy153;
+ goto yy178;
}
if (yych <= ',') {
if (yych <= 0x1F) {
- if (yych == '\t') goto yy195;
- goto yy193;
+ if (yych == '\t') goto yy220;
+ goto yy218;
} else {
- if (yych <= ' ') goto yy195;
- if (yych == ')') goto yy139;
- goto yy193;
+ if (yych <= ' ') goto yy220;
+ if (yych == ')') goto yy164;
+ goto yy218;
}
} else {
if (yych <= '/') {
- if (yych <= '-') goto yy317;
- if (yych <= '.') goto yy195;
- goto yy147;
+ if (yych <= '-') goto yy342;
+ if (yych <= '.') goto yy220;
+ goto yy172;
} else {
- if (yych <= '9') goto yy195;
- if (yych == '_') goto yy147;
- goto yy193;
+ if (yych <= '9') goto yy220;
+ if (yych == '_') goto yy172;
+ goto yy218;
}
}
-yy351:
+yy376:
yych = *++YYCURSOR;
if (yych <= 'N') {
if (yych <= '@') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
- if (yych == 'L') goto yy358;
- if (yych <= 'M') goto yy141;
- goto yy357;
+ if (yych == 'L') goto yy383;
+ if (yych <= 'M') goto yy166;
+ goto yy382;
}
} else {
if (yych <= 'l') {
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'Z') goto yy166;
if (yych <= '`') goto yy3;
- if (yych <= 'k') goto yy141;
- goto yy358;
+ if (yych <= 'k') goto yy166;
+ goto yy383;
} else {
- if (yych == 'n') goto yy357;
- if (yych <= 'z') goto yy141;
+ if (yych == 'n') goto yy382;
+ if (yych <= 'z') goto yy166;
goto yy3;
}
}
-yy352:
+yy377:
yych = *++YYCURSOR;
if (yych <= 'N') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'M') goto yy141;
+ if (yych <= 'M') goto yy166;
}
} else {
if (yych <= 'm') {
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'Z') goto yy166;
if (yych <= '`') goto yy3;
- goto yy141;
+ goto yy166;
} else {
- if (yych <= 'n') goto yy353;
- if (yych <= 'z') goto yy141;
+ if (yych <= 'n') goto yy378;
+ if (yych <= 'z') goto yy166;
goto yy3;
}
}
-yy353:
+yy378:
yyaccept = 5;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '/') {
if (yych <= '(') {
if (yych <= '\t') {
- if (yych <= 0x08) goto yy193;
- goto yy195;
+ if (yych <= 0x08) goto yy218;
+ goto yy220;
} else {
- if (yych == ' ') goto yy195;
- goto yy193;
+ if (yych == ' ') goto yy220;
+ goto yy218;
}
} else {
if (yych <= ',') {
- if (yych <= ')') goto yy139;
- goto yy193;
+ if (yych <= ')') goto yy164;
+ goto yy218;
} else {
- if (yych <= '-') goto yy196;
- if (yych <= '.') goto yy195;
- goto yy193;
+ if (yych <= '-') goto yy221;
+ if (yych <= '.') goto yy220;
+ goto yy218;
}
}
} else {
if (yych <= 'Z') {
if (yych <= '@') {
- if (yych <= '9') goto yy195;
- goto yy193;
+ if (yych <= '9') goto yy220;
+ goto yy218;
} else {
- if (yych != 'U') goto yy142;
+ if (yych != 'U') goto yy167;
}
} else {
if (yych <= 't') {
- if (yych <= '`') goto yy193;
- goto yy142;
+ if (yych <= '`') goto yy218;
+ goto yy167;
} else {
- if (yych <= 'u') goto yy354;
- if (yych <= 'z') goto yy142;
- goto yy193;
+ if (yych <= 'u') goto yy379;
+ if (yych <= 'z') goto yy167;
+ goto yy218;
}
}
}
-yy354:
+yy379:
yych = *++YYCURSOR;
if (yych <= 'A') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= '@') goto yy3;
} else {
if (yych <= '`') {
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'Z') goto yy168;
goto yy3;
} else {
- if (yych <= 'a') goto yy355;
- if (yych <= 'z') goto yy143;
+ if (yych <= 'a') goto yy380;
+ if (yych <= 'z') goto yy168;
goto yy3;
}
}
-yy355:
+yy380:
yych = *++YYCURSOR;
if (yych <= 'R') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'Q') goto yy144;
+ if (yych <= 'Q') goto yy169;
}
} else {
if (yych <= 'q') {
- if (yych <= 'Z') goto yy144;
+ if (yych <= 'Z') goto yy169;
if (yych <= '`') goto yy3;
- goto yy144;
+ goto yy169;
} else {
- if (yych <= 'r') goto yy356;
- if (yych <= 'z') goto yy144;
+ if (yych <= 'r') goto yy381;
+ if (yych <= 'z') goto yy169;
goto yy3;
}
}
-yy356:
+yy381:
yych = *++YYCURSOR;
if (yych <= 'X') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
- if (yych <= 'Y') goto yy204;
- if (yych == 'y') goto yy204;
+ if (yych <= 'Y') goto yy229;
+ if (yych == 'y') goto yy229;
goto yy3;
}
-yy357:
+yy382:
yyaccept = 5;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '/') {
if (yych <= '(') {
if (yych <= '\t') {
- if (yych <= 0x08) goto yy193;
- goto yy195;
+ if (yych <= 0x08) goto yy218;
+ goto yy220;
} else {
- if (yych == ' ') goto yy195;
- goto yy193;
+ if (yych == ' ') goto yy220;
+ goto yy218;
}
} else {
if (yych <= ',') {
- if (yych <= ')') goto yy139;
- goto yy193;
+ if (yych <= ')') goto yy164;
+ goto yy218;
} else {
- if (yych <= '-') goto yy196;
- if (yych <= '.') goto yy195;
- goto yy193;
+ if (yych <= '-') goto yy221;
+ if (yych <= '.') goto yy220;
+ goto yy218;
}
}
} else {
if (yych <= 'Z') {
if (yych <= '@') {
- if (yych <= '9') goto yy195;
- goto yy193;
+ if (yych <= '9') goto yy220;
+ goto yy218;
} else {
- if (yych == 'E') goto yy359;
- goto yy142;
+ if (yych == 'E') goto yy384;
+ goto yy167;
}
} else {
if (yych <= 'd') {
- if (yych <= '`') goto yy193;
- goto yy142;
+ if (yych <= '`') goto yy218;
+ goto yy167;
} else {
- if (yych <= 'e') goto yy359;
- if (yych <= 'z') goto yy142;
- goto yy193;
+ if (yych <= 'e') goto yy384;
+ if (yych <= 'z') goto yy167;
+ goto yy218;
}
}
}
-yy358:
+yy383:
yyaccept = 5;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '/') {
if (yych <= '(') {
if (yych <= '\t') {
- if (yych <= 0x08) goto yy193;
- goto yy195;
+ if (yych <= 0x08) goto yy218;
+ goto yy220;
} else {
- if (yych == ' ') goto yy195;
- goto yy193;
+ if (yych == ' ') goto yy220;
+ goto yy218;
}
} else {
if (yych <= ',') {
- if (yych <= ')') goto yy139;
- goto yy193;
+ if (yych <= ')') goto yy164;
+ goto yy218;
} else {
- if (yych <= '-') goto yy196;
- if (yych <= '.') goto yy195;
- goto yy193;
+ if (yych <= '-') goto yy221;
+ if (yych <= '.') goto yy220;
+ goto yy218;
}
}
} else {
if (yych <= 'Z') {
if (yych <= '@') {
- if (yych <= '9') goto yy195;
- goto yy193;
+ if (yych <= '9') goto yy220;
+ goto yy218;
} else {
- if (yych != 'Y') goto yy142;
+ if (yych != 'Y') goto yy167;
}
} else {
if (yych <= 'x') {
- if (yych <= '`') goto yy193;
- goto yy142;
+ if (yych <= '`') goto yy218;
+ goto yy167;
} else {
- if (yych <= 'y') goto yy359;
- if (yych <= 'z') goto yy142;
- goto yy193;
+ if (yych <= 'y') goto yy384;
+ if (yych <= 'z') goto yy167;
+ goto yy218;
}
}
}
-yy359:
+yy384:
yyaccept = 5;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= ',') {
if (yych <= 0x1F) {
- if (yych == '\t') goto yy195;
- goto yy193;
+ if (yych == '\t') goto yy220;
+ goto yy218;
} else {
- if (yych <= ' ') goto yy195;
- if (yych == ')') goto yy139;
- goto yy193;
+ if (yych <= ' ') goto yy220;
+ if (yych == ')') goto yy164;
+ goto yy218;
}
} else {
if (yych <= '@') {
- if (yych == '/') goto yy193;
- if (yych <= '9') goto yy195;
- goto yy193;
+ if (yych == '/') goto yy218;
+ if (yych <= '9') goto yy220;
+ goto yy218;
} else {
- if (yych <= 'Z') goto yy143;
- if (yych <= '`') goto yy193;
- if (yych <= 'z') goto yy143;
- goto yy193;
+ if (yych <= 'Z') goto yy168;
+ if (yych <= '`') goto yy218;
+ if (yych <= 'z') goto yy168;
+ goto yy218;
}
}
-yy360:
+yy385:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'M') {
if (yych <= '.') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
- if (yych == '-') goto yy147;
+ if (yych == '-') goto yy172;
goto yy3;
}
} else {
if (yych <= '@') {
- if (yych <= '/') goto yy147;
+ if (yych <= '/') goto yy172;
goto yy3;
} else {
- if (yych == 'L') goto yy358;
- goto yy141;
+ if (yych == 'L') goto yy383;
+ goto yy166;
}
}
} else {
if (yych <= '`') {
if (yych <= 'Z') {
- if (yych <= 'N') goto yy357;
- goto yy141;
+ if (yych <= 'N') goto yy382;
+ goto yy166;
} else {
- if (yych == '_') goto yy147;
+ if (yych == '_') goto yy172;
goto yy3;
}
} else {
if (yych <= 'm') {
- if (yych == 'l') goto yy367;
- goto yy146;
+ if (yych == 'l') goto yy392;
+ goto yy171;
} else {
- if (yych <= 'n') goto yy366;
- if (yych <= 'z') goto yy146;
+ if (yych <= 'n') goto yy391;
+ if (yych <= 'z') goto yy171;
goto yy3;
}
}
}
-yy361:
+yy386:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'M') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy141;
+ goto yy166;
}
} else {
if (yych <= '_') {
- if (yych <= 'N') goto yy353;
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'N') goto yy378;
+ if (yych <= 'Z') goto yy166;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'm') {
if (yych <= '`') goto yy3;
- goto yy146;
+ goto yy171;
} else {
- if (yych <= 'n') goto yy362;
- if (yych <= 'z') goto yy146;
+ if (yych <= 'n') goto yy387;
+ if (yych <= 'z') goto yy171;
goto yy3;
}
}
}
-yy362:
+yy387:
yyaccept = 5;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '9') {
if (yych <= '(') {
if (yych <= '\t') {
- if (yych <= 0x08) goto yy193;
- goto yy195;
+ if (yych <= 0x08) goto yy218;
+ goto yy220;
} else {
- if (yych == ' ') goto yy195;
- goto yy193;
+ if (yych == ' ') goto yy220;
+ goto yy218;
}
} else {
if (yych <= '-') {
- if (yych <= ')') goto yy139;
- if (yych <= ',') goto yy193;
- goto yy311;
+ if (yych <= ')') goto yy164;
+ if (yych <= ',') goto yy218;
+ goto yy336;
} else {
- if (yych == '/') goto yy147;
- goto yy195;
+ if (yych == '/') goto yy172;
+ goto yy220;
}
}
} else {
if (yych <= '^') {
if (yych <= 'T') {
- if (yych <= '@') goto yy193;
- goto yy142;
+ if (yych <= '@') goto yy218;
+ goto yy167;
} else {
- if (yych <= 'U') goto yy354;
- if (yych <= 'Z') goto yy142;
- goto yy193;
+ if (yych <= 'U') goto yy379;
+ if (yych <= 'Z') goto yy167;
+ goto yy218;
}
} else {
if (yych <= 't') {
- if (yych <= '_') goto yy147;
- if (yych <= '`') goto yy193;
- goto yy150;
+ if (yych <= '_') goto yy172;
+ if (yych <= '`') goto yy218;
+ goto yy175;
} else {
- if (yych <= 'u') goto yy363;
- if (yych <= 'z') goto yy150;
- goto yy193;
+ if (yych <= 'u') goto yy388;
+ if (yych <= 'z') goto yy175;
+ goto yy218;
}
}
}
-yy363:
+yy388:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '@') {
if (yych <= ',') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
if (yych == '.') goto yy3;
- if (yych <= '/') goto yy147;
+ if (yych <= '/') goto yy172;
goto yy3;
}
} else {
if (yych <= '_') {
- if (yych <= 'A') goto yy355;
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'A') goto yy380;
+ if (yych <= 'Z') goto yy168;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= '`') goto yy3;
- if (yych <= 'a') goto yy364;
- if (yych <= 'z') goto yy151;
+ if (yych <= 'a') goto yy389;
+ if (yych <= 'z') goto yy176;
goto yy3;
}
}
-yy364:
+yy389:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'Q') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy144;
+ goto yy169;
}
} else {
if (yych <= '_') {
- if (yych <= 'R') goto yy356;
- if (yych <= 'Z') goto yy144;
+ if (yych <= 'R') goto yy381;
+ if (yych <= 'Z') goto yy169;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'q') {
if (yych <= '`') goto yy3;
- goto yy152;
+ goto yy177;
} else {
- if (yych <= 'r') goto yy365;
- if (yych <= 'z') goto yy152;
+ if (yych <= 'r') goto yy390;
+ if (yych <= 'z') goto yy177;
goto yy3;
}
}
}
-yy365:
+yy390:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'X') {
if (yych <= ',') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
if (yych == '.') goto yy3;
- if (yych <= '/') goto yy147;
+ if (yych <= '/') goto yy172;
goto yy3;
}
} else {
if (yych <= '`') {
- if (yych <= 'Y') goto yy204;
- if (yych == '_') goto yy147;
+ if (yych <= 'Y') goto yy229;
+ if (yych == '_') goto yy172;
goto yy3;
} else {
- if (yych == 'y') goto yy316;
- if (yych <= 'z') goto yy153;
+ if (yych == 'y') goto yy341;
+ if (yych <= 'z') goto yy178;
goto yy3;
}
}
-yy366:
+yy391:
yyaccept = 5;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '9') {
if (yych <= '(') {
if (yych <= '\t') {
- if (yych <= 0x08) goto yy193;
- goto yy195;
+ if (yych <= 0x08) goto yy218;
+ goto yy220;
} else {
- if (yych == ' ') goto yy195;
- goto yy193;
+ if (yych == ' ') goto yy220;
+ goto yy218;
}
} else {
if (yych <= '-') {
- if (yych <= ')') goto yy139;
- if (yych <= ',') goto yy193;
- goto yy311;
+ if (yych <= ')') goto yy164;
+ if (yych <= ',') goto yy218;
+ goto yy336;
} else {
- if (yych == '/') goto yy147;
- goto yy195;
+ if (yych == '/') goto yy172;
+ goto yy220;
}
}
} else {
if (yych <= '^') {
if (yych <= 'D') {
- if (yych <= '@') goto yy193;
- goto yy142;
+ if (yych <= '@') goto yy218;
+ goto yy167;
} else {
- if (yych <= 'E') goto yy359;
- if (yych <= 'Z') goto yy142;
- goto yy193;
+ if (yych <= 'E') goto yy384;
+ if (yych <= 'Z') goto yy167;
+ goto yy218;
}
} else {
if (yych <= 'd') {
- if (yych <= '_') goto yy147;
- if (yych <= '`') goto yy193;
- goto yy150;
+ if (yych <= '_') goto yy172;
+ if (yych <= '`') goto yy218;
+ goto yy175;
} else {
- if (yych <= 'e') goto yy368;
- if (yych <= 'z') goto yy150;
- goto yy193;
+ if (yych <= 'e') goto yy393;
+ if (yych <= 'z') goto yy175;
+ goto yy218;
}
}
}
-yy367:
+yy392:
yyaccept = 5;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '9') {
if (yych <= '(') {
if (yych <= '\t') {
- if (yych <= 0x08) goto yy193;
- goto yy195;
+ if (yych <= 0x08) goto yy218;
+ goto yy220;
} else {
- if (yych == ' ') goto yy195;
- goto yy193;
+ if (yych == ' ') goto yy220;
+ goto yy218;
}
} else {
if (yych <= '-') {
- if (yych <= ')') goto yy139;
- if (yych <= ',') goto yy193;
- goto yy311;
+ if (yych <= ')') goto yy164;
+ if (yych <= ',') goto yy218;
+ goto yy336;
} else {
- if (yych == '/') goto yy147;
- goto yy195;
+ if (yych == '/') goto yy172;
+ goto yy220;
}
}
} else {
if (yych <= '^') {
if (yych <= 'X') {
- if (yych <= '@') goto yy193;
- goto yy142;
+ if (yych <= '@') goto yy218;
+ goto yy167;
} else {
- if (yych <= 'Y') goto yy359;
- if (yych <= 'Z') goto yy142;
- goto yy193;
+ if (yych <= 'Y') goto yy384;
+ if (yych <= 'Z') goto yy167;
+ goto yy218;
}
} else {
if (yych <= 'x') {
- if (yych <= '_') goto yy147;
- if (yych <= '`') goto yy193;
- goto yy150;
+ if (yych <= '_') goto yy172;
+ if (yych <= '`') goto yy218;
+ goto yy175;
} else {
- if (yych <= 'y') goto yy368;
- if (yych <= 'z') goto yy150;
- goto yy193;
+ if (yych <= 'y') goto yy393;
+ if (yych <= 'z') goto yy175;
+ goto yy218;
}
}
}
-yy368:
+yy393:
yyaccept = 5;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '.') {
if (yych <= ' ') {
- if (yych == '\t') goto yy195;
- if (yych <= 0x1F) goto yy193;
- goto yy195;
+ if (yych == '\t') goto yy220;
+ if (yych <= 0x1F) goto yy218;
+ goto yy220;
} else {
if (yych <= ')') {
- if (yych <= '(') goto yy193;
- goto yy139;
+ if (yych <= '(') goto yy218;
+ goto yy164;
} else {
- if (yych <= ',') goto yy193;
- if (yych <= '-') goto yy317;
- goto yy195;
+ if (yych <= ',') goto yy218;
+ if (yych <= '-') goto yy342;
+ goto yy220;
}
}
} else {
if (yych <= 'Z') {
- if (yych <= '/') goto yy147;
- if (yych <= '9') goto yy195;
- if (yych <= '@') goto yy193;
- goto yy143;
+ if (yych <= '/') goto yy172;
+ if (yych <= '9') goto yy220;
+ if (yych <= '@') goto yy218;
+ goto yy168;
} else {
if (yych <= '_') {
- if (yych <= '^') goto yy193;
- goto yy147;
+ if (yych <= '^') goto yy218;
+ goto yy172;
} else {
- if (yych <= '`') goto yy193;
- if (yych <= 'z') goto yy151;
- goto yy193;
+ if (yych <= '`') goto yy218;
+ if (yych <= 'z') goto yy176;
+ goto yy218;
}
}
}
-yy369:
+yy394:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '.') {
if (yych <= ' ') {
- if (yych == '\t') goto yy195;
+ if (yych == '\t') goto yy220;
if (yych <= 0x1F) goto yy3;
- goto yy195;
+ goto yy220;
} else {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy195;
+ goto yy220;
}
} else {
if (yych <= 'H') {
if (yych <= '/') goto yy3;
- if (yych <= '9') goto yy195;
+ if (yych <= '9') goto yy220;
if (yych <= '@') goto yy3;
- goto yy141;
+ goto yy166;
} else {
if (yych <= 'Z') {
- if (yych >= 'J') goto yy141;
+ if (yych >= 'J') goto yy166;
} else {
if (yych <= '`') goto yy3;
- if (yych <= 'z') goto yy141;
+ if (yych <= 'z') goto yy166;
goto yy3;
}
}
}
-yy370:
+yy395:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= ',') {
if (yych <= 0x1F) {
- if (yych == '\t') goto yy195;
+ if (yych == '\t') goto yy220;
goto yy3;
} else {
- if (yych <= ' ') goto yy195;
- if (yych == ')') goto yy139;
+ if (yych <= ' ') goto yy220;
+ if (yych == ')') goto yy164;
goto yy3;
}
} else {
if (yych <= '@') {
if (yych == '/') goto yy3;
- if (yych <= '9') goto yy195;
+ if (yych <= '9') goto yy220;
goto yy3;
} else {
- if (yych <= 'Z') goto yy142;
+ if (yych <= 'Z') goto yy167;
if (yych <= '`') goto yy3;
- if (yych <= 'z') goto yy142;
+ if (yych <= 'z') goto yy167;
goto yy3;
}
}
-yy371:
+yy396:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '.') {
if (yych <= ' ') {
- if (yych == '\t') goto yy195;
+ if (yych == '\t') goto yy220;
if (yych <= 0x1F) goto yy3;
- goto yy195;
+ goto yy220;
} else {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy195;
+ goto yy220;
}
} else {
if (yych <= 'H') {
if (yych <= '/') goto yy3;
- if (yych <= '9') goto yy195;
+ if (yych <= '9') goto yy220;
if (yych <= '@') goto yy3;
- goto yy141;
+ goto yy166;
} else {
if (yych <= 'Z') {
- if (yych >= 'J') goto yy141;
+ if (yych >= 'J') goto yy166;
} else {
if (yych <= '`') goto yy3;
- if (yych <= 'z') goto yy141;
+ if (yych <= 'z') goto yy166;
goto yy3;
}
}
@@ -7125,26 +7501,26 @@ yy371:
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '.') {
if (yych <= ' ') {
- if (yych == '\t') goto yy195;
+ if (yych == '\t') goto yy220;
if (yych <= 0x1F) goto yy3;
- goto yy195;
+ goto yy220;
} else {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy195;
+ goto yy220;
}
} else {
if (yych <= 'H') {
if (yych <= '/') goto yy3;
- if (yych <= '9') goto yy195;
+ if (yych <= '9') goto yy220;
if (yych <= '@') goto yy3;
- goto yy142;
+ goto yy167;
} else {
if (yych <= 'Z') {
- if (yych >= 'J') goto yy142;
+ if (yych >= 'J') goto yy167;
} else {
if (yych <= '`') goto yy3;
- if (yych <= 'z') goto yy142;
+ if (yych <= 'z') goto yy167;
goto yy3;
}
}
@@ -7153,83 +7529,83 @@ yy371:
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= ',') {
if (yych <= 0x1F) {
- if (yych == '\t') goto yy195;
+ if (yych == '\t') goto yy220;
goto yy3;
} else {
- if (yych <= ' ') goto yy195;
- if (yych == ')') goto yy139;
+ if (yych <= ' ') goto yy220;
+ if (yych == ')') goto yy164;
goto yy3;
}
} else {
if (yych <= '@') {
if (yych == '/') goto yy3;
- if (yych <= '9') goto yy195;
+ if (yych <= '9') goto yy220;
goto yy3;
} else {
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'Z') goto yy168;
if (yych <= '`') goto yy3;
- if (yych <= 'z') goto yy143;
+ if (yych <= 'z') goto yy168;
goto yy3;
}
}
-yy374:
+yy399:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= ',') {
if (yych <= 0x1F) {
- if (yych == '\t') goto yy195;
+ if (yych == '\t') goto yy220;
goto yy3;
} else {
- if (yych <= ' ') goto yy195;
- if (yych == ')') goto yy139;
+ if (yych <= ' ') goto yy220;
+ if (yych == ')') goto yy164;
goto yy3;
}
} else {
if (yych <= '@') {
if (yych == '/') goto yy3;
- if (yych <= '9') goto yy195;
+ if (yych <= '9') goto yy220;
goto yy3;
} else {
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'Z') goto yy166;
if (yych <= '`') goto yy3;
- if (yych <= 'z') goto yy141;
+ if (yych <= 'z') goto yy166;
goto yy3;
}
}
-yy375:
+yy400:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '.') {
if (yych <= ' ') {
- if (yych == '\t') goto yy195;
+ if (yych == '\t') goto yy220;
if (yych <= 0x1F) goto yy3;
- goto yy195;
+ goto yy220;
} else {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy195;
+ goto yy220;
}
} else {
if (yych <= 'H') {
if (yych <= '/') goto yy3;
- if (yych <= '9') goto yy195;
+ if (yych <= '9') goto yy220;
if (yych <= '@') goto yy3;
- goto yy141;
+ goto yy166;
} else {
if (yych <= 'Z') {
- if (yych <= 'I') goto yy370;
- goto yy141;
+ if (yych <= 'I') goto yy395;
+ goto yy166;
} else {
if (yych <= '`') goto yy3;
- if (yych <= 'z') goto yy141;
+ if (yych <= 'z') goto yy166;
goto yy3;
}
}
}
-yy376:
+yy401:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych <= 'V') {
+ if (yych <= 'W') {
if (yych <= 'D') {
if (yych <= ' ') {
if (yych == '\t') goto yy60;
@@ -7238,139 +7614,154 @@ yy376:
} else {
if (yych <= '9') {
if (yych <= '/') goto yy3;
- goto yy396;
+ goto yy421;
} else {
- if (yych <= ':') goto yy162;
+ if (yych <= ':') goto yy187;
if (yych <= 'C') goto yy3;
goto yy60;
}
}
} else {
- if (yych <= 'H') {
- if (yych == 'F') goto yy60;
- if (yych <= 'G') goto yy3;
- goto yy60;
- } else {
- if (yych <= 'M') {
- if (yych <= 'L') goto yy3;
+ if (yych <= 'L') {
+ if (yych <= 'F') {
+ if (yych <= 'E') goto yy3;
goto yy60;
} else {
- if (yych <= 'R') goto yy3;
- if (yych <= 'T') goto yy60;
+ if (yych == 'H') goto yy60;
+ goto yy3;
+ }
+ } else {
+ if (yych <= 'R') {
+ if (yych <= 'M') goto yy60;
goto yy3;
+ } else {
+ if (yych == 'V') goto yy3;
+ goto yy60;
}
}
}
} else {
- if (yych <= 'h') {
- if (yych <= 'c') {
- if (yych == 'X') goto yy3;
- if (yych <= 'Y') goto yy60;
- goto yy3;
+ if (yych <= 'l') {
+ if (yych <= 'd') {
+ if (yych == 'Y') goto yy60;
+ if (yych <= 'c') goto yy3;
+ goto yy60;
} else {
- if (yych <= 'e') {
- if (yych <= 'd') goto yy60;
- goto yy3;
- } else {
- if (yych == 'g') goto yy3;
+ if (yych <= 'f') {
+ if (yych <= 'e') goto yy3;
goto yy60;
+ } else {
+ if (yych == 'h') goto yy60;
+ goto yy3;
}
}
} else {
- if (yych <= 't') {
- if (yych == 'm') goto yy60;
- if (yych <= 'r') goto yy3;
- goto yy60;
+ if (yych <= 'w') {
+ if (yych <= 'r') {
+ if (yych <= 'm') goto yy60;
+ goto yy3;
+ } else {
+ if (yych == 'v') goto yy3;
+ goto yy60;
+ }
} else {
- if (yych <= 'w') {
- if (yych <= 'v') goto yy3;
+ if (yych <= 'y') {
+ if (yych <= 'x') goto yy3;
goto yy60;
} else {
- if (yych == 'y') goto yy60;
+ if (yych == 0xC2) goto yy60;
goto yy3;
}
}
}
}
-yy377:
+yy402:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych <= 'T') {
- if (yych <= ':') {
- if (yych <= ' ') {
- if (yych == '\t') goto yy60;
- if (yych <= 0x1F) goto yy3;
- goto yy60;
+ if (yych <= 'V') {
+ if (yych <= 'C') {
+ if (yych <= '/') {
+ if (yych <= '\t') {
+ if (yych <= 0x08) goto yy3;
+ goto yy60;
+ } else {
+ if (yych == ' ') goto yy60;
+ goto yy3;
+ }
} else {
- if (yych <= '4') {
- if (yych <= '/') goto yy3;
- goto yy396;
+ if (yych <= '5') {
+ if (yych <= '4') goto yy421;
+ goto yy406;
} else {
- if (yych <= '5') goto yy381;
- if (yych <= '9') goto yy382;
- goto yy162;
+ if (yych <= '9') goto yy407;
+ if (yych <= ':') goto yy187;
+ goto yy3;
}
}
} else {
- if (yych <= 'G') {
- if (yych <= 'D') {
- if (yych <= 'C') goto yy3;
- goto yy60;
- } else {
- if (yych == 'F') goto yy60;
+ if (yych <= 'H') {
+ if (yych <= 'E') {
+ if (yych <= 'D') goto yy60;
goto yy3;
+ } else {
+ if (yych == 'G') goto yy3;
+ goto yy60;
}
} else {
- if (yych <= 'L') {
- if (yych <= 'H') goto yy60;
- goto yy3;
+ if (yych <= 'M') {
+ if (yych <= 'L') goto yy3;
+ goto yy60;
} else {
- if (yych <= 'M') goto yy60;
if (yych <= 'R') goto yy3;
- goto yy60;
+ if (yych <= 'U') goto yy60;
+ goto yy3;
}
}
}
} else {
- if (yych <= 'g') {
- if (yych <= 'Y') {
- if (yych == 'W') goto yy60;
- if (yych <= 'X') goto yy3;
- goto yy60;
- } else {
- if (yych <= 'd') {
+ if (yych <= 'l') {
+ if (yych <= 'd') {
+ if (yych <= 'X') {
+ if (yych <= 'W') goto yy60;
+ goto yy3;
+ } else {
+ if (yych <= 'Y') goto yy60;
if (yych <= 'c') goto yy3;
goto yy60;
+ }
+ } else {
+ if (yych <= 'f') {
+ if (yych <= 'e') goto yy3;
+ goto yy60;
} else {
- if (yych == 'f') goto yy60;
+ if (yych == 'h') goto yy60;
goto yy3;
}
}
} else {
- if (yych <= 't') {
- if (yych <= 'l') {
- if (yych <= 'h') goto yy60;
+ if (yych <= 'w') {
+ if (yych <= 'r') {
+ if (yych <= 'm') goto yy60;
goto yy3;
} else {
- if (yych <= 'm') goto yy60;
- if (yych <= 'r') goto yy3;
+ if (yych == 'v') goto yy3;
goto yy60;
}
} else {
- if (yych <= 'w') {
- if (yych <= 'v') goto yy3;
+ if (yych <= 'y') {
+ if (yych <= 'x') goto yy3;
goto yy60;
} else {
- if (yych == 'y') goto yy60;
+ if (yych == 0xC2) goto yy60;
goto yy3;
}
}
}
}
-yy378:
+yy403:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych <= 'T') {
+ if (yych <= 'V') {
if (yych <= 'C') {
if (yych <= ' ') {
if (yych == '\t') goto yy60;
@@ -7379,66 +7770,74 @@ yy378:
} else {
if (yych <= '5') {
if (yych <= '/') goto yy3;
- goto yy381;
+ goto yy406;
} else {
- if (yych <= '9') goto yy382;
- if (yych <= ':') goto yy162;
+ if (yych <= '9') goto yy407;
+ if (yych <= ':') goto yy187;
goto yy3;
}
}
} else {
- if (yych <= 'G') {
- if (yych == 'E') goto yy3;
- if (yych <= 'F') goto yy60;
- goto yy3;
- } else {
- if (yych <= 'L') {
- if (yych <= 'H') goto yy60;
+ if (yych <= 'H') {
+ if (yych <= 'E') {
+ if (yych <= 'D') goto yy60;
goto yy3;
} else {
- if (yych <= 'M') goto yy60;
- if (yych <= 'R') goto yy3;
+ if (yych == 'G') goto yy3;
goto yy60;
}
+ } else {
+ if (yych <= 'M') {
+ if (yych <= 'L') goto yy3;
+ goto yy60;
+ } else {
+ if (yych <= 'R') goto yy3;
+ if (yych <= 'U') goto yy60;
+ goto yy3;
+ }
}
}
} else {
- if (yych <= 'g') {
- if (yych <= 'Y') {
- if (yych == 'W') goto yy60;
- if (yych <= 'X') goto yy3;
- goto yy60;
- } else {
- if (yych <= 'd') {
+ if (yych <= 'l') {
+ if (yych <= 'd') {
+ if (yych <= 'X') {
+ if (yych <= 'W') goto yy60;
+ goto yy3;
+ } else {
+ if (yych <= 'Y') goto yy60;
if (yych <= 'c') goto yy3;
goto yy60;
+ }
+ } else {
+ if (yych <= 'f') {
+ if (yych <= 'e') goto yy3;
+ goto yy60;
} else {
- if (yych == 'f') goto yy60;
+ if (yych == 'h') goto yy60;
goto yy3;
}
}
} else {
- if (yych <= 't') {
- if (yych <= 'l') {
- if (yych <= 'h') goto yy60;
+ if (yych <= 'w') {
+ if (yych <= 'r') {
+ if (yych <= 'm') goto yy60;
goto yy3;
} else {
- if (yych <= 'm') goto yy60;
- if (yych <= 'r') goto yy3;
+ if (yych == 'v') goto yy3;
goto yy60;
}
} else {
- if (yych <= 'w') {
- if (yych <= 'v') goto yy3;
+ if (yych <= 'y') {
+ if (yych <= 'x') goto yy3;
goto yy60;
} else {
- if (yych == 'y') goto yy60;
+ if (yych == 0xC2) goto yy60;
goto yy3;
}
}
}
}
-yy379:
+yy404:
++YYCURSOR;
if (YYLIMIT <= YYCURSOR) YYFILL(1);
yych = *YYCURSOR;
@@ -7446,190 +7845,200 @@ yy379:
goto yy57;
}
if (yych <= ',') {
- if (yych == '+') goto yy379;
+ if (yych == '+') goto yy404;
goto yy56;
} else {
- if (yych <= '-') goto yy379;
+ if (yych <= '-') goto yy404;
if (yych <= '/') goto yy56;
if (yych <= '9') goto yy54;
goto yy56;
}
-yy381:
+yy406:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych <= 'V') {
- if (yych <= 'D') {
+ if (yych <= 'W') {
+ if (yych <= 'E') {
if (yych <= ' ') {
if (yych == '\t') goto yy60;
if (yych <= 0x1F) goto yy3;
goto yy60;
} else {
- if (yych <= '/') goto yy3;
- if (yych <= '9') goto yy395;
- if (yych <= 'C') goto yy3;
- goto yy60;
+ if (yych <= '9') {
+ if (yych <= '/') goto yy3;
+ goto yy420;
+ } else {
+ if (yych == 'D') goto yy60;
+ goto yy3;
+ }
}
} else {
- if (yych <= 'H') {
- if (yych == 'F') goto yy60;
- if (yych <= 'G') goto yy3;
- goto yy60;
+ if (yych <= 'L') {
+ if (yych == 'G') goto yy3;
+ if (yych <= 'H') goto yy60;
+ goto yy3;
} else {
- if (yych <= 'M') {
- if (yych <= 'L') goto yy3;
- goto yy60;
- } else {
- if (yych <= 'R') goto yy3;
- if (yych <= 'T') goto yy60;
+ if (yych <= 'R') {
+ if (yych <= 'M') goto yy60;
goto yy3;
+ } else {
+ if (yych == 'V') goto yy3;
+ goto yy60;
}
}
}
} else {
- if (yych <= 'h') {
- if (yych <= 'c') {
- if (yych == 'X') goto yy3;
- if (yych <= 'Y') goto yy60;
- goto yy3;
+ if (yych <= 'l') {
+ if (yych <= 'd') {
+ if (yych == 'Y') goto yy60;
+ if (yych <= 'c') goto yy3;
+ goto yy60;
} else {
- if (yych <= 'e') {
- if (yych <= 'd') goto yy60;
- goto yy3;
- } else {
- if (yych == 'g') goto yy3;
+ if (yych <= 'f') {
+ if (yych <= 'e') goto yy3;
goto yy60;
+ } else {
+ if (yych == 'h') goto yy60;
+ goto yy3;
}
}
} else {
- if (yych <= 't') {
- if (yych == 'm') goto yy60;
- if (yych <= 'r') goto yy3;
- goto yy60;
+ if (yych <= 'w') {
+ if (yych <= 'r') {
+ if (yych <= 'm') goto yy60;
+ goto yy3;
+ } else {
+ if (yych == 'v') goto yy3;
+ goto yy60;
+ }
} else {
- if (yych <= 'w') {
- if (yych <= 'v') goto yy3;
+ if (yych <= 'y') {
+ if (yych <= 'x') goto yy3;
goto yy60;
} else {
- if (yych == 'y') goto yy60;
+ if (yych == 0xC2) goto yy60;
goto yy3;
}
}
}
}
-yy382:
+yy407:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych <= 'V') {
- if (yych <= 'D') {
+ if (yych <= 'W') {
+ if (yych <= 'E') {
if (yych <= ' ') {
if (yych == '\t') goto yy60;
if (yych <= 0x1F) goto yy3;
goto yy60;
} else {
- if (yych <= '/') goto yy3;
- if (yych <= '9') goto yy383;
- if (yych <= 'C') goto yy3;
- goto yy60;
+ if (yych <= '9') {
+ if (yych <= '/') goto yy3;
+ } else {
+ if (yych == 'D') goto yy60;
+ goto yy3;
+ }
}
} else {
- if (yych <= 'H') {
- if (yych == 'F') goto yy60;
- if (yych <= 'G') goto yy3;
- goto yy60;
+ if (yych <= 'L') {
+ if (yych == 'G') goto yy3;
+ if (yych <= 'H') goto yy60;
+ goto yy3;
} else {
- if (yych <= 'M') {
- if (yych <= 'L') goto yy3;
- goto yy60;
- } else {
- if (yych <= 'R') goto yy3;
- if (yych <= 'T') goto yy60;
+ if (yych <= 'R') {
+ if (yych <= 'M') goto yy60;
goto yy3;
+ } else {
+ if (yych == 'V') goto yy3;
+ goto yy60;
}
}
}
} else {
- if (yych <= 'h') {
- if (yych <= 'c') {
- if (yych == 'X') goto yy3;
- if (yych <= 'Y') goto yy60;
- goto yy3;
+ if (yych <= 'l') {
+ if (yych <= 'd') {
+ if (yych == 'Y') goto yy60;
+ if (yych <= 'c') goto yy3;
+ goto yy60;
} else {
- if (yych <= 'e') {
- if (yych <= 'd') goto yy60;
- goto yy3;
- } else {
- if (yych == 'g') goto yy3;
+ if (yych <= 'f') {
+ if (yych <= 'e') goto yy3;
goto yy60;
+ } else {
+ if (yych == 'h') goto yy60;
+ goto yy3;
}
}
} else {
- if (yych <= 't') {
- if (yych == 'm') goto yy60;
- if (yych <= 'r') goto yy3;
- goto yy60;
+ if (yych <= 'w') {
+ if (yych <= 'r') {
+ if (yych <= 'm') goto yy60;
+ goto yy3;
+ } else {
+ if (yych == 'v') goto yy3;
+ goto yy60;
+ }
} else {
- if (yych <= 'w') {
- if (yych <= 'v') goto yy3;
+ if (yych <= 'y') {
+ if (yych <= 'x') goto yy3;
goto yy60;
} else {
- if (yych == 'y') goto yy60;
+ if (yych == 0xC2) goto yy60;
goto yy3;
}
}
}
}
-yy383:
yych = *++YYCURSOR;
if (yych <= '/') goto yy60;
if (yych >= ':') goto yy60;
-yy384:
+yy409:
yych = *++YYCURSOR;
if (yybm[0+yych] & 2) {
goto yy54;
}
if (yych != '-') goto yy60;
-yy385:
+yy410:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '0') goto yy386;
- if (yych <= '1') goto yy387;
+ if (yych <= '0') goto yy411;
+ if (yych <= '1') goto yy412;
goto yy56;
-yy386:
+yy411:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '9') goto yy388;
+ if (yych <= '9') goto yy413;
goto yy56;
-yy387:
+yy412:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
if (yych >= '3') goto yy56;
-yy388:
+yy413:
yych = *++YYCURSOR;
if (yych != '-') goto yy56;
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '0') goto yy390;
- if (yych <= '2') goto yy391;
- if (yych <= '3') goto yy392;
+ if (yych <= '0') goto yy415;
+ if (yych <= '2') goto yy416;
+ if (yych <= '3') goto yy417;
goto yy56;
-yy390:
+yy415:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '9') goto yy393;
+ if (yych <= '9') goto yy418;
goto yy56;
-yy391:
+yy416:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '9') goto yy393;
+ if (yych <= '9') goto yy418;
goto yy56;
-yy392:
+yy417:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
if (yych >= '2') goto yy56;
-yy393:
+yy418:
++YYCURSOR;
-yy394:
-#line 1243 "ext/date/lib/parse_date.re"
+yy419:
+#line 1286 "ext/date/lib/parse_date.re"
{
DEBUG_OUTPUT("iso8601date4 | iso8601date2 | iso8601dateslash | dateslash");
TIMELIB_INIT;
@@ -7640,73 +8049,79 @@ yy394:
TIMELIB_DEINIT;
return TIMELIB_ISO_DATE;
}
-#line 7644 "ext/date/lib/parse_date.c"
-yy395:
+#line 8053 "ext/date/lib/parse_date.c"
+yy420:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych <= 'V') {
- if (yych <= 'D') {
+ if (yych <= 'W') {
+ if (yych <= 'E') {
if (yych <= ' ') {
if (yych == '\t') goto yy60;
if (yych <= 0x1F) goto yy3;
goto yy60;
} else {
- if (yych <= '/') goto yy3;
- if (yych <= '9') goto yy384;
- if (yych <= 'C') goto yy3;
- goto yy60;
+ if (yych <= '9') {
+ if (yych <= '/') goto yy3;
+ goto yy409;
+ } else {
+ if (yych == 'D') goto yy60;
+ goto yy3;
+ }
}
} else {
- if (yych <= 'H') {
- if (yych == 'F') goto yy60;
- if (yych <= 'G') goto yy3;
- goto yy60;
+ if (yych <= 'L') {
+ if (yych == 'G') goto yy3;
+ if (yych <= 'H') goto yy60;
+ goto yy3;
} else {
- if (yych <= 'M') {
- if (yych <= 'L') goto yy3;
- goto yy60;
- } else {
- if (yych <= 'R') goto yy3;
- if (yych <= 'T') goto yy60;
+ if (yych <= 'R') {
+ if (yych <= 'M') goto yy60;
goto yy3;
+ } else {
+ if (yych == 'V') goto yy3;
+ goto yy60;
}
}
}
} else {
- if (yych <= 'h') {
- if (yych <= 'c') {
- if (yych == 'X') goto yy3;
- if (yych <= 'Y') goto yy60;
- goto yy3;
+ if (yych <= 'l') {
+ if (yych <= 'd') {
+ if (yych == 'Y') goto yy60;
+ if (yych <= 'c') goto yy3;
+ goto yy60;
} else {
- if (yych <= 'e') {
- if (yych <= 'd') goto yy60;
- goto yy3;
- } else {
- if (yych == 'g') goto yy3;
+ if (yych <= 'f') {
+ if (yych <= 'e') goto yy3;
goto yy60;
+ } else {
+ if (yych == 'h') goto yy60;
+ goto yy3;
}
}
} else {
- if (yych <= 't') {
- if (yych == 'm') goto yy60;
- if (yych <= 'r') goto yy3;
- goto yy60;
+ if (yych <= 'w') {
+ if (yych <= 'r') {
+ if (yych <= 'm') goto yy60;
+ goto yy3;
+ } else {
+ if (yych == 'v') goto yy3;
+ goto yy60;
+ }
} else {
- if (yych <= 'w') {
- if (yych <= 'v') goto yy3;
+ if (yych <= 'y') {
+ if (yych <= 'x') goto yy3;
goto yy60;
} else {
- if (yych == 'y') goto yy60;
+ if (yych == 0xC2) goto yy60;
goto yy3;
}
}
}
}
-yy396:
+yy421:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych <= 'T') {
+ if (yych <= 'V') {
if (yych <= 'C') {
if (yych <= ' ') {
if (yych == '\t') goto yy60;
@@ -7716,58 +8131,66 @@ yy396:
if (yych <= '5') {
if (yych <= '/') goto yy3;
} else {
- if (yych <= '9') goto yy395;
- if (yych <= ':') goto yy162;
+ if (yych <= '9') goto yy420;
+ if (yych <= ':') goto yy187;
goto yy3;
}
}
} else {
- if (yych <= 'G') {
- if (yych == 'E') goto yy3;
- if (yych <= 'F') goto yy60;
- goto yy3;
- } else {
- if (yych <= 'L') {
- if (yych <= 'H') goto yy60;
+ if (yych <= 'H') {
+ if (yych <= 'E') {
+ if (yych <= 'D') goto yy60;
goto yy3;
} else {
- if (yych <= 'M') goto yy60;
- if (yych <= 'R') goto yy3;
+ if (yych == 'G') goto yy3;
+ goto yy60;
+ }
+ } else {
+ if (yych <= 'M') {
+ if (yych <= 'L') goto yy3;
goto yy60;
+ } else {
+ if (yych <= 'R') goto yy3;
+ if (yych <= 'U') goto yy60;
+ goto yy3;
}
}
}
} else {
- if (yych <= 'g') {
- if (yych <= 'Y') {
- if (yych == 'W') goto yy60;
- if (yych <= 'X') goto yy3;
- goto yy60;
- } else {
- if (yych <= 'd') {
+ if (yych <= 'l') {
+ if (yych <= 'd') {
+ if (yych <= 'X') {
+ if (yych <= 'W') goto yy60;
+ goto yy3;
+ } else {
+ if (yych <= 'Y') goto yy60;
if (yych <= 'c') goto yy3;
goto yy60;
+ }
+ } else {
+ if (yych <= 'f') {
+ if (yych <= 'e') goto yy3;
+ goto yy60;
} else {
- if (yych == 'f') goto yy60;
+ if (yych == 'h') goto yy60;
goto yy3;
}
}
} else {
- if (yych <= 't') {
- if (yych <= 'l') {
- if (yych <= 'h') goto yy60;
+ if (yych <= 'w') {
+ if (yych <= 'r') {
+ if (yych <= 'm') goto yy60;
goto yy3;
} else {
- if (yych <= 'm') goto yy60;
- if (yych <= 'r') goto yy3;
+ if (yych == 'v') goto yy3;
goto yy60;
}
} else {
- if (yych <= 'w') {
- if (yych <= 'v') goto yy3;
+ if (yych <= 'y') {
+ if (yych <= 'x') goto yy3;
goto yy60;
} else {
- if (yych == 'y') goto yy60;
+ if (yych == 0xC2) goto yy60;
goto yy3;
}
}
@@ -7775,349 +8198,422 @@ yy396:
}
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych <= 'V') {
- if (yych <= 'D') {
+ if (yych <= 'W') {
+ if (yych <= 'E') {
if (yych <= ' ') {
if (yych == '\t') goto yy60;
if (yych <= 0x1F) goto yy3;
goto yy60;
} else {
- if (yych <= '/') goto yy3;
- if (yych <= '9') goto yy398;
- if (yych <= 'C') goto yy3;
- goto yy60;
+ if (yych <= '9') {
+ if (yych <= '/') goto yy3;
+ } else {
+ if (yych == 'D') goto yy60;
+ goto yy3;
+ }
}
} else {
- if (yych <= 'H') {
- if (yych == 'F') goto yy60;
- if (yych <= 'G') goto yy3;
- goto yy60;
+ if (yych <= 'L') {
+ if (yych == 'G') goto yy3;
+ if (yych <= 'H') goto yy60;
+ goto yy3;
} else {
- if (yych <= 'M') {
- if (yych <= 'L') goto yy3;
- goto yy60;
- } else {
- if (yych <= 'R') goto yy3;
- if (yych <= 'T') goto yy60;
+ if (yych <= 'R') {
+ if (yych <= 'M') goto yy60;
goto yy3;
+ } else {
+ if (yych == 'V') goto yy3;
+ goto yy60;
}
}
}
} else {
- if (yych <= 'h') {
- if (yych <= 'c') {
- if (yych == 'X') goto yy3;
- if (yych <= 'Y') goto yy60;
- goto yy3;
+ if (yych <= 'l') {
+ if (yych <= 'd') {
+ if (yych == 'Y') goto yy60;
+ if (yych <= 'c') goto yy3;
+ goto yy60;
} else {
- if (yych <= 'e') {
- if (yych <= 'd') goto yy60;
- goto yy3;
- } else {
- if (yych == 'g') goto yy3;
+ if (yych <= 'f') {
+ if (yych <= 'e') goto yy3;
goto yy60;
+ } else {
+ if (yych == 'h') goto yy60;
+ goto yy3;
}
}
} else {
- if (yych <= 't') {
- if (yych == 'm') goto yy60;
- if (yych <= 'r') goto yy3;
- goto yy60;
+ if (yych <= 'w') {
+ if (yych <= 'r') {
+ if (yych <= 'm') goto yy60;
+ goto yy3;
+ } else {
+ if (yych == 'v') goto yy3;
+ goto yy60;
+ }
} else {
- if (yych <= 'w') {
- if (yych <= 'v') goto yy3;
+ if (yych <= 'y') {
+ if (yych <= 'x') goto yy3;
goto yy60;
} else {
- if (yych == 'y') goto yy60;
+ if (yych == 0xC2) goto yy60;
goto yy3;
}
}
}
}
-yy398:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yybm[0+yych] & 2) {
goto yy54;
}
- if (yych <= 'V') {
- if (yych <= 'D') {
+ if (yych <= 'W') {
+ if (yych <= 'E') {
if (yych <= ' ') {
if (yych == '\t') goto yy60;
if (yych <= 0x1F) goto yy3;
goto yy60;
} else {
- if (yych == '-') goto yy385;
- if (yych <= 'C') goto yy3;
- goto yy60;
+ if (yych <= '-') {
+ if (yych <= ',') goto yy3;
+ goto yy410;
+ } else {
+ if (yych == 'D') goto yy60;
+ goto yy3;
+ }
}
} else {
- if (yych <= 'H') {
- if (yych == 'F') goto yy60;
- if (yych <= 'G') goto yy3;
- goto yy60;
+ if (yych <= 'L') {
+ if (yych == 'G') goto yy3;
+ if (yych <= 'H') goto yy60;
+ goto yy3;
} else {
- if (yych <= 'M') {
- if (yych <= 'L') goto yy3;
- goto yy60;
- } else {
- if (yych <= 'R') goto yy3;
- if (yych <= 'T') goto yy60;
+ if (yych <= 'R') {
+ if (yych <= 'M') goto yy60;
goto yy3;
+ } else {
+ if (yych == 'V') goto yy3;
+ goto yy60;
}
}
}
} else {
- if (yych <= 'h') {
- if (yych <= 'c') {
- if (yych == 'X') goto yy3;
- if (yych <= 'Y') goto yy60;
- goto yy3;
+ if (yych <= 'l') {
+ if (yych <= 'd') {
+ if (yych == 'Y') goto yy60;
+ if (yych <= 'c') goto yy3;
+ goto yy60;
} else {
- if (yych <= 'e') {
- if (yych <= 'd') goto yy60;
- goto yy3;
- } else {
- if (yych == 'g') goto yy3;
+ if (yych <= 'f') {
+ if (yych <= 'e') goto yy3;
goto yy60;
+ } else {
+ if (yych == 'h') goto yy60;
+ goto yy3;
}
}
} else {
- if (yych <= 't') {
- if (yych == 'm') goto yy60;
- if (yych <= 'r') goto yy3;
- goto yy60;
+ if (yych <= 'w') {
+ if (yych <= 'r') {
+ if (yych <= 'm') goto yy60;
+ goto yy3;
+ } else {
+ if (yych == 'v') goto yy3;
+ goto yy60;
+ }
} else {
- if (yych <= 'w') {
- if (yych <= 'v') goto yy3;
+ if (yych <= 'y') {
+ if (yych <= 'x') goto yy3;
goto yy60;
} else {
- if (yych == 'y') goto yy60;
+ if (yych == 0xC2) goto yy60;
goto yy3;
}
}
}
}
-yy399:
+yy424:
yych = *++YYCURSOR;
- if (yych <= '/') goto yy401;
- if (yych <= '0') goto yy675;
- if (yych <= '1') goto yy676;
- if (yych <= '9') goto yy677;
- goto yy401;
-yy400:
+ if (yych <= '/') goto yy426;
+ if (yych <= '0') goto yy700;
+ if (yych <= '1') goto yy701;
+ if (yych <= '9') goto yy702;
+ goto yy426;
+yy425:
++YYCURSOR;
if ((YYLIMIT - YYCURSOR) < 13) YYFILL(13);
yych = *YYCURSOR;
-yy401:
- switch (yych) {
- case '\t':
- case ' ': goto yy400;
- case '-':
- case '.': goto yy516;
- case 'A':
- case 'a': goto yy419;
- case 'D':
- case 'd': goto yy405;
- case 'F':
- case 'f': goto yy406;
- case 'H':
- case 'h': goto yy63;
- case 'I': goto yy414;
- case 'J':
- case 'j': goto yy418;
- case 'M':
- case 'm': goto yy404;
- case 'N':
- case 'n': goto yy421;
- case 'O':
- case 'o': goto yy420;
- case 'P':
- case 'p': goto yy423;
- case 'S':
- case 's': goto yy402;
- case 'T':
- case 't': goto yy68;
- case 'V': goto yy416;
- case 'W':
- case 'w': goto yy67;
- case 'X': goto yy417;
- case 'Y':
- case 'y': goto yy66;
- default: goto yy56;
+yy426:
+ if (yych <= 'W') {
+ if (yych <= 'G') {
+ if (yych <= '.') {
+ if (yych <= 0x1F) {
+ if (yych == '\t') goto yy425;
+ goto yy56;
+ } else {
+ if (yych <= ' ') goto yy425;
+ if (yych <= ',') goto yy56;
+ goto yy541;
+ }
+ } else {
+ if (yych <= 'C') {
+ if (yych == 'A') goto yy444;
+ goto yy56;
+ } else {
+ if (yych <= 'D') goto yy430;
+ if (yych == 'F') goto yy431;
+ goto yy56;
+ }
+ }
+ } else {
+ if (yych <= 'O') {
+ if (yych <= 'J') {
+ if (yych <= 'H') goto yy65;
+ if (yych <= 'I') goto yy439;
+ goto yy443;
+ } else {
+ if (yych <= 'L') goto yy56;
+ if (yych <= 'M') goto yy427;
+ if (yych <= 'N') goto yy446;
+ goto yy445;
+ }
+ } else {
+ if (yych <= 'S') {
+ if (yych <= 'P') goto yy448;
+ if (yych <= 'R') goto yy56;
+ goto yy428;
+ } else {
+ if (yych <= 'T') goto yy70;
+ if (yych <= 'U') goto yy63;
+ if (yych <= 'V') goto yy441;
+ goto yy69;
+ }
+ }
+ }
+ } else {
+ if (yych <= 'm') {
+ if (yych <= 'e') {
+ if (yych <= '`') {
+ if (yych <= 'X') goto yy442;
+ if (yych <= 'Y') goto yy68;
+ goto yy56;
+ } else {
+ if (yych <= 'a') goto yy444;
+ if (yych == 'd') goto yy430;
+ goto yy56;
+ }
+ } else {
+ if (yych <= 'h') {
+ if (yych <= 'f') goto yy431;
+ if (yych <= 'g') goto yy56;
+ goto yy65;
+ } else {
+ if (yych == 'j') goto yy443;
+ if (yych <= 'l') goto yy56;
+ }
+ }
+ } else {
+ if (yych <= 'u') {
+ if (yych <= 'p') {
+ if (yych <= 'n') goto yy446;
+ if (yych <= 'o') goto yy445;
+ goto yy448;
+ } else {
+ if (yych <= 'r') goto yy56;
+ if (yych <= 's') goto yy428;
+ if (yych <= 't') goto yy70;
+ goto yy63;
+ }
+ } else {
+ if (yych <= 'x') {
+ if (yych == 'w') goto yy69;
+ goto yy56;
+ } else {
+ if (yych <= 'y') goto yy68;
+ if (yych == 0xC2) goto yy62;
+ goto yy56;
+ }
+ }
+ }
}
-yy402:
+yy427:
+ yych = *++YYCURSOR;
+ if (yych <= 'S') {
+ if (yych <= 'I') {
+ if (yych == 'A') goto yy556;
+ if (yych <= 'H') goto yy56;
+ goto yy138;
+ } else {
+ if (yych == 'O') goto yy137;
+ if (yych <= 'R') goto yy56;
+ goto yy139;
+ }
+ } else {
+ if (yych <= 'i') {
+ if (yych == 'a') goto yy556;
+ if (yych <= 'h') goto yy56;
+ goto yy138;
+ } else {
+ if (yych <= 'o') {
+ if (yych <= 'n') goto yy56;
+ goto yy137;
+ } else {
+ if (yych == 's') goto yy139;
+ goto yy56;
+ }
+ }
+ }
+yy428:
yych = *++YYCURSOR;
if (yych <= 'U') {
if (yych <= 'D') {
- if (yych == 'A') goto yy126;
+ if (yych == 'A') goto yy119;
goto yy56;
} else {
- if (yych <= 'E') goto yy988;
+ if (yych <= 'E') goto yy1013;
if (yych <= 'T') goto yy56;
- goto yy125;
+ goto yy118;
}
} else {
if (yych <= 'd') {
- if (yych == 'a') goto yy126;
+ if (yych == 'a') goto yy119;
goto yy56;
} else {
- if (yych <= 'e') goto yy988;
- if (yych == 'u') goto yy125;
+ if (yych <= 'e') goto yy1013;
+ if (yych == 'u') goto yy118;
goto yy56;
}
}
-yy403:
+yy429:
yych = *++YYCURSOR;
if (yych <= '`') {
if (yych <= 'D') {
- if (yych == 'A') goto yy126;
+ if (yych == 'A') goto yy119;
goto yy56;
} else {
- if (yych <= 'E') goto yy988;
- if (yych == 'U') goto yy125;
+ if (yych <= 'E') goto yy1013;
+ if (yych == 'U') goto yy118;
goto yy56;
}
} else {
if (yych <= 'e') {
- if (yych <= 'a') goto yy126;
+ if (yych <= 'a') goto yy119;
if (yych <= 'd') goto yy56;
- goto yy988;
+ goto yy1013;
} else {
if (yych <= 's') goto yy56;
- if (yych <= 't') goto yy668;
- if (yych <= 'u') goto yy125;
- goto yy56;
- }
- }
-yy404:
- yych = *++YYCURSOR;
- if (yych <= 'O') {
- if (yych <= 'H') {
- if (yych == 'A') goto yy531;
- goto yy56;
- } else {
- if (yych <= 'I') goto yy117;
- if (yych <= 'N') goto yy56;
- goto yy116;
- }
- } else {
- if (yych <= 'h') {
- if (yych == 'a') goto yy531;
- goto yy56;
- } else {
- if (yych <= 'i') goto yy117;
- if (yych == 'o') goto yy116;
+ if (yych <= 't') goto yy693;
+ if (yych <= 'u') goto yy118;
goto yy56;
}
}
-yy405:
+yy430:
yych = *++YYCURSOR;
if (yych <= 'E') {
- if (yych == 'A') goto yy113;
+ if (yych == 'A') goto yy115;
if (yych <= 'D') goto yy56;
- goto yy518;
+ goto yy543;
} else {
if (yych <= 'a') {
if (yych <= '`') goto yy56;
- goto yy113;
+ goto yy115;
} else {
- if (yych == 'e') goto yy518;
+ if (yych == 'e') goto yy543;
goto yy56;
}
}
-yy406:
+yy431:
yych = *++YYCURSOR;
if (yych <= 'R') {
if (yych <= 'N') {
- if (yych == 'E') goto yy534;
+ if (yych == 'E') goto yy559;
goto yy56;
} else {
- if (yych <= 'O') goto yy98;
+ if (yych <= 'O') goto yy100;
if (yych <= 'Q') goto yy56;
- goto yy97;
+ goto yy99;
}
} else {
if (yych <= 'n') {
- if (yych == 'e') goto yy534;
+ if (yych == 'e') goto yy559;
goto yy56;
} else {
- if (yych <= 'o') goto yy98;
- if (yych == 'r') goto yy97;
+ if (yych <= 'o') goto yy100;
+ if (yych == 'r') goto yy99;
goto yy56;
}
}
-yy407:
+yy432:
yych = *++YYCURSOR;
if (yych <= 'U') {
- if (yych == 'H') goto yy69;
+ if (yych == 'H') goto yy71;
if (yych <= 'T') goto yy56;
- goto yy70;
+ goto yy72;
} else {
if (yych <= 'h') {
if (yych <= 'g') goto yy56;
- goto yy987;
+ goto yy1012;
} else {
- if (yych == 'u') goto yy70;
+ if (yych == 'u') goto yy72;
goto yy56;
}
}
-yy408:
+yy433:
yych = *++YYCURSOR;
- if (yych == '-') goto yy681;
+ if (yych == '-') goto yy706;
if (yych <= '/') goto yy60;
- if (yych <= '9') goto yy680;
+ if (yych <= '9') goto yy705;
goto yy60;
-yy409:
+yy434:
yych = *++YYCURSOR;
if (yych <= 'c') {
- if (yych == 'O') goto yy469;
+ if (yych == 'O') goto yy494;
goto yy56;
} else {
- if (yych <= 'd') goto yy668;
- if (yych == 'o') goto yy469;
+ if (yych <= 'd') goto yy693;
+ if (yych == 'o') goto yy494;
goto yy56;
}
-yy410:
+yy435:
yych = *++YYCURSOR;
- if (yych == 'd') goto yy668;
+ if (yych == 'd') goto yy693;
goto yy56;
-yy411:
+yy436:
yych = *++YYCURSOR;
switch (yych) {
case '0':
case '1':
- case '2': goto yy605;
- case '3': goto yy607;
+ case '2': goto yy630;
+ case '3': goto yy632;
case '4':
case '5':
case '6':
case '7':
case '8':
- case '9': goto yy608;
+ case '9': goto yy633;
case 'A':
- case 'a': goto yy612;
+ case 'a': goto yy637;
case 'D':
- case 'd': goto yy616;
+ case 'd': goto yy641;
case 'F':
- case 'f': goto yy610;
+ case 'f': goto yy635;
case 'J':
- case 'j': goto yy609;
+ case 'j': goto yy634;
case 'M':
- case 'm': goto yy611;
+ case 'm': goto yy636;
case 'N':
- case 'n': goto yy615;
+ case 'n': goto yy640;
case 'O':
- case 'o': goto yy614;
+ case 'o': goto yy639;
case 'S':
- case 's': goto yy613;
+ case 's': goto yy638;
default: goto yy56;
}
-yy412:
+yy437:
yych = *++YYCURSOR;
switch (yych) {
- case '0': goto yy555;
- case '1': goto yy556;
+ case '0': goto yy580;
+ case '1': goto yy581;
case '2':
case '3':
case '4':
@@ -8125,58 +8621,58 @@ yy412:
case '6':
case '7':
case '8':
- case '9': goto yy557;
+ case '9': goto yy582;
case 'A':
- case 'a': goto yy561;
+ case 'a': goto yy586;
case 'D':
- case 'd': goto yy565;
+ case 'd': goto yy590;
case 'F':
- case 'f': goto yy559;
+ case 'f': goto yy584;
case 'J':
- case 'j': goto yy558;
+ case 'j': goto yy583;
case 'M':
- case 'm': goto yy560;
+ case 'm': goto yy585;
case 'N':
- case 'n': goto yy564;
+ case 'n': goto yy589;
case 'O':
- case 'o': goto yy563;
+ case 'o': goto yy588;
case 'S':
- case 's': goto yy562;
- default: goto yy517;
+ case 's': goto yy587;
+ default: goto yy542;
}
-yy413:
+yy438:
yych = *++YYCURSOR;
if (yych <= '1') {
- if (yych <= '/') goto yy517;
- if (yych <= '0') goto yy507;
- goto yy508;
+ if (yych <= '/') goto yy542;
+ if (yych <= '0') goto yy532;
+ goto yy533;
} else {
- if (yych <= '5') goto yy509;
- if (yych <= '9') goto yy510;
- goto yy517;
+ if (yych <= '5') goto yy534;
+ if (yych <= '9') goto yy535;
+ goto yy542;
}
-yy414:
+yy439:
yyaccept = 9;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '/') {
if (yych <= 0x1F) {
- if (yych == '\t') goto yy471;
+ if (yych == '\t') goto yy496;
} else {
- if (yych <= ' ') goto yy471;
- if (yych <= ',') goto yy415;
- if (yych <= '.') goto yy471;
+ if (yych <= ' ') goto yy496;
+ if (yych <= ',') goto yy440;
+ if (yych <= '.') goto yy496;
}
} else {
if (yych <= 'U') {
- if (yych <= '9') goto yy473;
- if (yych == 'I') goto yy506;
+ if (yych <= '9') goto yy498;
+ if (yych == 'I') goto yy531;
} else {
- if (yych == 'W') goto yy415;
- if (yych <= 'X') goto yy479;
+ if (yych == 'W') goto yy440;
+ if (yych <= 'X') goto yy504;
}
}
-yy415:
-#line 1380 "ext/date/lib/parse_date.re"
+yy440:
+#line 1423 "ext/date/lib/parse_date.re"
{
DEBUG_OUTPUT("datenoyearrev");
TIMELIB_INIT;
@@ -8187,135 +8683,135 @@ yy415:
TIMELIB_DEINIT;
return TIMELIB_DATE_TEXT;
}
-#line 8191 "ext/date/lib/parse_date.c"
-yy416:
+#line 8687 "ext/date/lib/parse_date.c"
+yy441:
yyaccept = 9;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= ',') {
if (yych <= '\t') {
- if (yych <= 0x08) goto yy415;
- goto yy471;
+ if (yych <= 0x08) goto yy440;
+ goto yy496;
} else {
- if (yych == ' ') goto yy471;
- goto yy415;
+ if (yych == ' ') goto yy496;
+ goto yy440;
}
} else {
if (yych <= '9') {
- if (yych <= '.') goto yy471;
- if (yych <= '/') goto yy415;
- goto yy473;
+ if (yych <= '.') goto yy496;
+ if (yych <= '/') goto yy440;
+ goto yy498;
} else {
- if (yych == 'I') goto yy504;
- goto yy415;
+ if (yych == 'I') goto yy529;
+ goto yy440;
}
}
-yy417:
+yy442:
yyaccept = 9;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= ',') {
if (yych <= '\t') {
- if (yych <= 0x08) goto yy415;
- goto yy471;
+ if (yych <= 0x08) goto yy440;
+ goto yy496;
} else {
- if (yych == ' ') goto yy471;
- goto yy415;
+ if (yych == ' ') goto yy496;
+ goto yy440;
}
} else {
if (yych <= '9') {
- if (yych <= '.') goto yy471;
- if (yych <= '/') goto yy415;
- goto yy473;
+ if (yych <= '.') goto yy496;
+ if (yych <= '/') goto yy440;
+ goto yy498;
} else {
- if (yych == 'I') goto yy503;
- goto yy415;
+ if (yych == 'I') goto yy528;
+ goto yy440;
}
}
-yy418:
+yy443:
yych = *++YYCURSOR;
if (yych <= 'U') {
- if (yych == 'A') goto yy496;
+ if (yych == 'A') goto yy521;
if (yych <= 'T') goto yy56;
- goto yy495;
+ goto yy520;
} else {
if (yych <= 'a') {
if (yych <= '`') goto yy56;
- goto yy496;
+ goto yy521;
} else {
- if (yych == 'u') goto yy495;
+ if (yych == 'u') goto yy520;
goto yy56;
}
}
-yy419:
+yy444:
yych = *++YYCURSOR;
if (yych <= 'T') {
if (yych <= 'L') {
- if (yych == '.') goto yy424;
+ if (yych == '.') goto yy449;
goto yy56;
} else {
- if (yych <= 'M') goto yy425;
- if (yych == 'P') goto yy489;
+ if (yych <= 'M') goto yy450;
+ if (yych == 'P') goto yy514;
goto yy56;
}
} else {
if (yych <= 'o') {
- if (yych <= 'U') goto yy488;
- if (yych == 'm') goto yy425;
+ if (yych <= 'U') goto yy513;
+ if (yych == 'm') goto yy450;
goto yy56;
} else {
- if (yych <= 'p') goto yy489;
- if (yych == 'u') goto yy488;
+ if (yych <= 'p') goto yy514;
+ if (yych == 'u') goto yy513;
goto yy56;
}
}
-yy420:
+yy445:
yych = *++YYCURSOR;
- if (yych == 'C') goto yy483;
- if (yych == 'c') goto yy483;
+ if (yych == 'C') goto yy508;
+ if (yych == 'c') goto yy508;
goto yy56;
-yy421:
+yy446:
yych = *++YYCURSOR;
- if (yych == 'O') goto yy469;
- if (yych == 'o') goto yy469;
+ if (yych == 'O') goto yy494;
+ if (yych == 'o') goto yy494;
goto yy56;
-yy422:
+yy447:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '5') goto yy429;
- if (yych <= '9') goto yy431;
+ if (yych <= '5') goto yy454;
+ if (yych <= '9') goto yy456;
goto yy56;
-yy423:
+yy448:
yych = *++YYCURSOR;
if (yych <= 'L') {
if (yych != '.') goto yy56;
} else {
- if (yych <= 'M') goto yy425;
- if (yych == 'm') goto yy425;
+ if (yych <= 'M') goto yy450;
+ if (yych == 'm') goto yy450;
goto yy56;
}
-yy424:
+yy449:
yych = *++YYCURSOR;
- if (yych == 'M') goto yy425;
+ if (yych == 'M') goto yy450;
if (yych != 'm') goto yy56;
-yy425:
+yy450:
yych = *++YYCURSOR;
if (yych <= 0x1F) {
- if (yych <= 0x00) goto yy427;
- if (yych == '\t') goto yy427;
+ if (yych <= 0x00) goto yy452;
+ if (yych == '\t') goto yy452;
goto yy56;
} else {
- if (yych <= ' ') goto yy427;
+ if (yych <= ' ') goto yy452;
if (yych != '.') goto yy56;
}
yych = *++YYCURSOR;
if (yych <= '\t') {
- if (yych <= 0x00) goto yy427;
+ if (yych <= 0x00) goto yy452;
if (yych <= 0x08) goto yy56;
} else {
if (yych != ' ') goto yy56;
}
-yy427:
+yy452:
++YYCURSOR;
-#line 1098 "ext/date/lib/parse_date.re"
+#line 1141 "ext/date/lib/parse_date.re"
{
DEBUG_OUTPUT("timetiny12 | timeshort12 | timelong12");
TIMELIB_INIT;
@@ -8331,18 +8827,18 @@ yy427:
TIMELIB_DEINIT;
return TIMELIB_TIME12;
}
-#line 8335 "ext/date/lib/parse_date.c"
-yy429:
+#line 8831 "ext/date/lib/parse_date.c"
+yy454:
yyaccept = 10;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '/') {
- if (yych == '.') goto yy432;
+ if (yych == '.') goto yy457;
} else {
- if (yych <= '9') goto yy446;
- if (yych <= ':') goto yy432;
+ if (yych <= '9') goto yy471;
+ if (yych <= ':') goto yy457;
}
-yy430:
-#line 1135 "ext/date/lib/parse_date.re"
+yy455:
+#line 1178 "ext/date/lib/parse_date.re"
{
int tz_not_found;
DEBUG_OUTPUT("timeshort24 | timelong24 | iso8601long");
@@ -8367,284 +8863,284 @@ yy430:
TIMELIB_DEINIT;
return TIMELIB_TIME24_WITH_ZONE;
}
-#line 8371 "ext/date/lib/parse_date.c"
-yy431:
+#line 8867 "ext/date/lib/parse_date.c"
+yy456:
yyaccept = 10;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych == '.') goto yy432;
- if (yych != ':') goto yy430;
-yy432:
+ if (yych == '.') goto yy457;
+ if (yych != ':') goto yy455;
+yy457:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '5') goto yy433;
- if (yych <= '6') goto yy434;
- if (yych <= '9') goto yy435;
+ if (yych <= '5') goto yy458;
+ if (yych <= '6') goto yy459;
+ if (yych <= '9') goto yy460;
goto yy56;
-yy433:
+yy458:
yyaccept = 10;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych == '.') goto yy436;
- if (yych <= '/') goto yy430;
- if (yych <= '9') goto yy439;
- goto yy430;
-yy434:
+ if (yych == '.') goto yy461;
+ if (yych <= '/') goto yy455;
+ if (yych <= '9') goto yy464;
+ goto yy455;
+yy459:
yyaccept = 10;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych == '.') goto yy436;
- if (yych == '0') goto yy439;
- goto yy430;
-yy435:
+ if (yych == '.') goto yy461;
+ if (yych == '0') goto yy464;
+ goto yy455;
+yy460:
yyaccept = 10;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych != '.') goto yy430;
-yy436:
+ if (yych != '.') goto yy455;
+yy461:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
if (yych >= ':') goto yy56;
-yy437:
+yy462:
++YYCURSOR;
if (YYLIMIT <= YYCURSOR) YYFILL(1);
yych = *YYCURSOR;
- if (yych <= '/') goto yy430;
- if (yych <= '9') goto yy437;
- goto yy430;
-yy439:
+ if (yych <= '/') goto yy455;
+ if (yych <= '9') goto yy462;
+ goto yy455;
+yy464:
yyaccept = 10;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '@') {
if (yych <= 0x1F) {
- if (yych != '\t') goto yy430;
+ if (yych != '\t') goto yy455;
} else {
- if (yych <= ' ') goto yy440;
- if (yych == '.') goto yy436;
- goto yy430;
+ if (yych <= ' ') goto yy465;
+ if (yych == '.') goto yy461;
+ goto yy455;
}
} else {
if (yych <= '`') {
- if (yych <= 'A') goto yy442;
- if (yych == 'P') goto yy442;
- goto yy430;
+ if (yych <= 'A') goto yy467;
+ if (yych == 'P') goto yy467;
+ goto yy455;
} else {
- if (yych <= 'a') goto yy442;
- if (yych == 'p') goto yy442;
- goto yy430;
+ if (yych <= 'a') goto yy467;
+ if (yych == 'p') goto yy467;
+ goto yy455;
}
}
-yy440:
+yy465:
++YYCURSOR;
if ((YYLIMIT - YYCURSOR) < 5) YYFILL(5);
yych = *YYCURSOR;
if (yych <= 'A') {
if (yych <= 0x1F) {
- if (yych == '\t') goto yy440;
+ if (yych == '\t') goto yy465;
goto yy56;
} else {
- if (yych <= ' ') goto yy440;
+ if (yych <= ' ') goto yy465;
if (yych <= '@') goto yy56;
}
} else {
if (yych <= '`') {
if (yych != 'P') goto yy56;
} else {
- if (yych <= 'a') goto yy442;
+ if (yych <= 'a') goto yy467;
if (yych != 'p') goto yy56;
}
}
-yy442:
+yy467:
yych = *++YYCURSOR;
if (yych <= 'L') {
if (yych != '.') goto yy56;
} else {
- if (yych <= 'M') goto yy444;
- if (yych == 'm') goto yy444;
+ if (yych <= 'M') goto yy469;
+ if (yych == 'm') goto yy469;
goto yy56;
}
yych = *++YYCURSOR;
- if (yych == 'M') goto yy444;
+ if (yych == 'M') goto yy469;
if (yych != 'm') goto yy56;
-yy444:
+yy469:
yych = *++YYCURSOR;
if (yych <= 0x1F) {
- if (yych <= 0x00) goto yy427;
- if (yych == '\t') goto yy427;
+ if (yych <= 0x00) goto yy452;
+ if (yych == '\t') goto yy452;
goto yy56;
} else {
- if (yych <= ' ') goto yy427;
+ if (yych <= ' ') goto yy452;
if (yych != '.') goto yy56;
}
yych = *++YYCURSOR;
if (yych <= '\t') {
- if (yych <= 0x00) goto yy427;
+ if (yych <= 0x00) goto yy452;
if (yych <= 0x08) goto yy56;
- goto yy427;
+ goto yy452;
} else {
- if (yych == ' ') goto yy427;
+ if (yych == ' ') goto yy452;
goto yy56;
}
-yy446:
+yy471:
yyaccept = 10;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= ':') {
if (yych <= ' ') {
- if (yych == '\t') goto yy447;
- if (yych <= 0x1F) goto yy430;
+ if (yych == '\t') goto yy472;
+ if (yych <= 0x1F) goto yy455;
} else {
- if (yych == '.') goto yy432;
- if (yych <= '9') goto yy430;
- goto yy450;
+ if (yych == '.') goto yy457;
+ if (yych <= '9') goto yy455;
+ goto yy475;
}
} else {
if (yych <= 'P') {
- if (yych == 'A') goto yy449;
- if (yych <= 'O') goto yy430;
- goto yy449;
+ if (yych == 'A') goto yy474;
+ if (yych <= 'O') goto yy455;
+ goto yy474;
} else {
if (yych <= 'a') {
- if (yych <= '`') goto yy430;
- goto yy449;
+ if (yych <= '`') goto yy455;
+ goto yy474;
} else {
- if (yych == 'p') goto yy449;
- goto yy430;
+ if (yych == 'p') goto yy474;
+ goto yy455;
}
}
}
-yy447:
+yy472:
++YYCURSOR;
if ((YYLIMIT - YYCURSOR) < 5) YYFILL(5);
yych = *YYCURSOR;
if (yych <= 'A') {
if (yych <= 0x1F) {
- if (yych == '\t') goto yy447;
+ if (yych == '\t') goto yy472;
goto yy56;
} else {
- if (yych <= ' ') goto yy447;
+ if (yych <= ' ') goto yy472;
if (yych <= '@') goto yy56;
}
} else {
if (yych <= '`') {
if (yych != 'P') goto yy56;
} else {
- if (yych <= 'a') goto yy449;
+ if (yych <= 'a') goto yy474;
if (yych != 'p') goto yy56;
}
}
-yy449:
+yy474:
yych = *++YYCURSOR;
if (yych <= 'L') {
- if (yych == '.') goto yy466;
+ if (yych == '.') goto yy491;
goto yy56;
} else {
- if (yych <= 'M') goto yy467;
- if (yych == 'm') goto yy467;
+ if (yych <= 'M') goto yy492;
+ if (yych == 'm') goto yy492;
goto yy56;
}
-yy450:
+yy475:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '5') goto yy451;
- if (yych <= '6') goto yy452;
- if (yych <= '9') goto yy435;
+ if (yych <= '5') goto yy476;
+ if (yych <= '6') goto yy477;
+ if (yych <= '9') goto yy460;
goto yy56;
-yy451:
+yy476:
yyaccept = 10;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych == '.') goto yy436;
- if (yych <= '/') goto yy430;
- if (yych <= '9') goto yy453;
- goto yy430;
-yy452:
+ if (yych == '.') goto yy461;
+ if (yych <= '/') goto yy455;
+ if (yych <= '9') goto yy478;
+ goto yy455;
+yy477:
yyaccept = 10;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych == '.') goto yy436;
- if (yych != '0') goto yy430;
-yy453:
+ if (yych == '.') goto yy461;
+ if (yych != '0') goto yy455;
+yy478:
yyaccept = 10;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= ':') {
if (yych <= ' ') {
- if (yych == '\t') goto yy440;
- if (yych <= 0x1F) goto yy430;
- goto yy440;
+ if (yych == '\t') goto yy465;
+ if (yych <= 0x1F) goto yy455;
+ goto yy465;
} else {
- if (yych == '.') goto yy454;
- if (yych <= '9') goto yy430;
- goto yy455;
+ if (yych == '.') goto yy479;
+ if (yych <= '9') goto yy455;
+ goto yy480;
}
} else {
if (yych <= 'P') {
- if (yych == 'A') goto yy442;
- if (yych <= 'O') goto yy430;
- goto yy442;
+ if (yych == 'A') goto yy467;
+ if (yych <= 'O') goto yy455;
+ goto yy467;
} else {
if (yych <= 'a') {
- if (yych <= '`') goto yy430;
- goto yy442;
+ if (yych <= '`') goto yy455;
+ goto yy467;
} else {
- if (yych == 'p') goto yy442;
- goto yy430;
+ if (yych == 'p') goto yy467;
+ goto yy455;
}
}
}
-yy454:
+yy479:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '9') goto yy464;
+ if (yych <= '9') goto yy489;
goto yy56;
-yy455:
+yy480:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
if (yych >= ':') goto yy56;
-yy456:
+yy481:
++YYCURSOR;
if ((YYLIMIT - YYCURSOR) < 5) YYFILL(5);
yych = *YYCURSOR;
if (yych <= 'O') {
if (yych <= '9') {
if (yych <= '/') goto yy56;
- goto yy456;
+ goto yy481;
} else {
if (yych != 'A') goto yy56;
}
} else {
if (yych <= 'a') {
- if (yych <= 'P') goto yy458;
+ if (yych <= 'P') goto yy483;
if (yych <= '`') goto yy56;
} else {
if (yych != 'p') goto yy56;
}
}
-yy458:
+yy483:
yych = *++YYCURSOR;
if (yych <= 'L') {
if (yych != '.') goto yy56;
} else {
- if (yych <= 'M') goto yy460;
- if (yych == 'm') goto yy460;
+ if (yych <= 'M') goto yy485;
+ if (yych == 'm') goto yy485;
goto yy56;
}
yych = *++YYCURSOR;
- if (yych == 'M') goto yy460;
+ if (yych == 'M') goto yy485;
if (yych != 'm') goto yy56;
-yy460:
+yy485:
yych = *++YYCURSOR;
if (yych <= 0x1F) {
- if (yych <= 0x00) goto yy462;
- if (yych == '\t') goto yy462;
+ if (yych <= 0x00) goto yy487;
+ if (yych == '\t') goto yy487;
goto yy56;
} else {
- if (yych <= ' ') goto yy462;
+ if (yych <= ' ') goto yy487;
if (yych != '.') goto yy56;
}
yych = *++YYCURSOR;
if (yych <= '\t') {
- if (yych <= 0x00) goto yy462;
+ if (yych <= 0x00) goto yy487;
if (yych <= 0x08) goto yy56;
} else {
if (yych != ' ') goto yy56;
}
-yy462:
+yy487:
++YYCURSOR;
-#line 1115 "ext/date/lib/parse_date.re"
+#line 1158 "ext/date/lib/parse_date.re"
{
DEBUG_OUTPUT("mssqltime");
TIMELIB_INIT;
@@ -8663,102 +9159,102 @@ yy462:
TIMELIB_DEINIT;
return TIMELIB_TIME24_WITH_ZONE;
}
-#line 8667 "ext/date/lib/parse_date.c"
-yy464:
+#line 9163 "ext/date/lib/parse_date.c"
+yy489:
yyaccept = 10;
YYMARKER = ++YYCURSOR;
if ((YYLIMIT - YYCURSOR) < 5) YYFILL(5);
yych = *YYCURSOR;
if (yych <= 'O') {
if (yych <= '9') {
- if (yych <= '/') goto yy430;
- goto yy464;
+ if (yych <= '/') goto yy455;
+ goto yy489;
} else {
- if (yych == 'A') goto yy458;
- goto yy430;
+ if (yych == 'A') goto yy483;
+ goto yy455;
}
} else {
if (yych <= 'a') {
- if (yych <= 'P') goto yy458;
- if (yych <= '`') goto yy430;
- goto yy458;
+ if (yych <= 'P') goto yy483;
+ if (yych <= '`') goto yy455;
+ goto yy483;
} else {
- if (yych == 'p') goto yy458;
- goto yy430;
+ if (yych == 'p') goto yy483;
+ goto yy455;
}
}
-yy466:
+yy491:
yych = *++YYCURSOR;
- if (yych == 'M') goto yy467;
+ if (yych == 'M') goto yy492;
if (yych != 'm') goto yy56;
-yy467:
+yy492:
yych = *++YYCURSOR;
if (yych <= 0x1F) {
- if (yych <= 0x00) goto yy427;
- if (yych == '\t') goto yy427;
+ if (yych <= 0x00) goto yy452;
+ if (yych == '\t') goto yy452;
goto yy56;
} else {
- if (yych <= ' ') goto yy427;
+ if (yych <= ' ') goto yy452;
if (yych != '.') goto yy56;
}
yych = *++YYCURSOR;
if (yych <= '\t') {
- if (yych <= 0x00) goto yy427;
+ if (yych <= 0x00) goto yy452;
if (yych <= 0x08) goto yy56;
- goto yy427;
+ goto yy452;
} else {
- if (yych == ' ') goto yy427;
+ if (yych == ' ') goto yy452;
goto yy56;
}
-yy469:
+yy494:
yych = *++YYCURSOR;
- if (yych == 'V') goto yy470;
+ if (yych == 'V') goto yy495;
if (yych != 'v') goto yy56;
-yy470:
+yy495:
yyaccept = 9;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '.') {
if (yych <= 0x1F) {
- if (yych != '\t') goto yy415;
+ if (yych != '\t') goto yy440;
} else {
- if (yych <= ' ') goto yy471;
- if (yych <= ',') goto yy415;
+ if (yych <= ' ') goto yy496;
+ if (yych <= ',') goto yy440;
}
} else {
if (yych <= 'D') {
- if (yych <= '/') goto yy415;
- if (yych <= '9') goto yy473;
- goto yy415;
+ if (yych <= '/') goto yy440;
+ if (yych <= '9') goto yy498;
+ goto yy440;
} else {
- if (yych <= 'E') goto yy475;
- if (yych == 'e') goto yy475;
- goto yy415;
+ if (yych <= 'E') goto yy500;
+ if (yych == 'e') goto yy500;
+ goto yy440;
}
}
-yy471:
+yy496:
++YYCURSOR;
if ((YYLIMIT - YYCURSOR) < 4) YYFILL(4);
yych = *YYCURSOR;
-yy472:
+yy497:
if (yych <= ' ') {
- if (yych == '\t') goto yy471;
+ if (yych == '\t') goto yy496;
if (yych <= 0x1F) goto yy56;
- goto yy471;
+ goto yy496;
} else {
if (yych <= '.') {
if (yych <= ',') goto yy56;
- goto yy471;
+ goto yy496;
} else {
if (yych <= '/') goto yy56;
if (yych >= ':') goto yy56;
}
}
-yy473:
+yy498:
++YYCURSOR;
- if ((yych = *YYCURSOR) <= '/') goto yy474;
- if (yych <= '9') goto yy480;
-yy474:
-#line 1297 "ext/date/lib/parse_date.re"
+ if ((yych = *YYCURSOR) <= '/') goto yy499;
+ if (yych <= '9') goto yy505;
+yy499:
+#line 1340 "ext/date/lib/parse_date.re"
{
int length = 0;
DEBUG_OUTPUT("datefull");
@@ -8772,671 +9268,671 @@ yy474:
TIMELIB_DEINIT;
return TIMELIB_DATE_FULL;
}
-#line 8776 "ext/date/lib/parse_date.c"
-yy475:
+#line 9272 "ext/date/lib/parse_date.c"
+yy500:
yych = *++YYCURSOR;
- if (yych == 'M') goto yy476;
+ if (yych == 'M') goto yy501;
if (yych != 'm') goto yy56;
-yy476:
+yy501:
yych = *++YYCURSOR;
- if (yych == 'B') goto yy477;
+ if (yych == 'B') goto yy502;
if (yych != 'b') goto yy56;
-yy477:
+yy502:
yych = *++YYCURSOR;
- if (yych == 'E') goto yy478;
+ if (yych == 'E') goto yy503;
if (yych != 'e') goto yy56;
-yy478:
+yy503:
yych = *++YYCURSOR;
- if (yych == 'R') goto yy479;
+ if (yych == 'R') goto yy504;
if (yych != 'r') goto yy56;
-yy479:
+yy504:
yyaccept = 9;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= ' ') {
- if (yych == '\t') goto yy471;
- if (yych <= 0x1F) goto yy415;
- goto yy471;
+ if (yych == '\t') goto yy496;
+ if (yych <= 0x1F) goto yy440;
+ goto yy496;
} else {
if (yych <= '.') {
- if (yych <= ',') goto yy415;
- goto yy471;
+ if (yych <= ',') goto yy440;
+ goto yy496;
} else {
- if (yych <= '/') goto yy415;
- if (yych <= '9') goto yy473;
- goto yy415;
+ if (yych <= '/') goto yy440;
+ if (yych <= '9') goto yy498;
+ goto yy440;
}
}
-yy480:
+yy505:
yych = *++YYCURSOR;
- if (yych <= '/') goto yy474;
- if (yych >= ':') goto yy474;
-yy481:
+ if (yych <= '/') goto yy499;
+ if (yych >= ':') goto yy499;
+yy506:
yych = *++YYCURSOR;
- if (yych <= '/') goto yy474;
- if (yych >= ':') goto yy474;
+ if (yych <= '/') goto yy499;
+ if (yych >= ':') goto yy499;
yych = *++YYCURSOR;
- goto yy474;
-yy483:
+ goto yy499;
+yy508:
yych = *++YYCURSOR;
- if (yych == 'T') goto yy484;
+ if (yych == 'T') goto yy509;
if (yych != 't') goto yy56;
-yy484:
+yy509:
yyaccept = 9;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '.') {
if (yych <= 0x1F) {
- if (yych == '\t') goto yy471;
- goto yy415;
+ if (yych == '\t') goto yy496;
+ goto yy440;
} else {
- if (yych <= ' ') goto yy471;
- if (yych <= ',') goto yy415;
- goto yy471;
+ if (yych <= ' ') goto yy496;
+ if (yych <= ',') goto yy440;
+ goto yy496;
}
} else {
if (yych <= 'N') {
- if (yych <= '/') goto yy415;
- if (yych <= '9') goto yy473;
- goto yy415;
+ if (yych <= '/') goto yy440;
+ if (yych <= '9') goto yy498;
+ goto yy440;
} else {
- if (yych <= 'O') goto yy485;
- if (yych != 'o') goto yy415;
+ if (yych <= 'O') goto yy510;
+ if (yych != 'o') goto yy440;
}
}
-yy485:
+yy510:
yych = *++YYCURSOR;
- if (yych == 'B') goto yy486;
+ if (yych == 'B') goto yy511;
if (yych != 'b') goto yy56;
-yy486:
+yy511:
yych = *++YYCURSOR;
- if (yych == 'E') goto yy487;
+ if (yych == 'E') goto yy512;
if (yych != 'e') goto yy56;
-yy487:
+yy512:
yych = *++YYCURSOR;
- if (yych == 'R') goto yy479;
- if (yych == 'r') goto yy479;
+ if (yych == 'R') goto yy504;
+ if (yych == 'r') goto yy504;
goto yy56;
-yy488:
+yy513:
yych = *++YYCURSOR;
- if (yych == 'G') goto yy492;
- if (yych == 'g') goto yy492;
+ if (yych == 'G') goto yy517;
+ if (yych == 'g') goto yy517;
goto yy56;
-yy489:
+yy514:
yych = *++YYCURSOR;
- if (yych == 'R') goto yy490;
+ if (yych == 'R') goto yy515;
if (yych != 'r') goto yy56;
-yy490:
+yy515:
yyaccept = 9;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '.') {
if (yych <= 0x1F) {
- if (yych == '\t') goto yy471;
- goto yy415;
+ if (yych == '\t') goto yy496;
+ goto yy440;
} else {
- if (yych <= ' ') goto yy471;
- if (yych <= ',') goto yy415;
- goto yy471;
+ if (yych <= ' ') goto yy496;
+ if (yych <= ',') goto yy440;
+ goto yy496;
}
} else {
if (yych <= 'H') {
- if (yych <= '/') goto yy415;
- if (yych <= '9') goto yy473;
- goto yy415;
+ if (yych <= '/') goto yy440;
+ if (yych <= '9') goto yy498;
+ goto yy440;
} else {
- if (yych <= 'I') goto yy491;
- if (yych != 'i') goto yy415;
+ if (yych <= 'I') goto yy516;
+ if (yych != 'i') goto yy440;
}
}
-yy491:
+yy516:
yych = *++YYCURSOR;
- if (yych == 'L') goto yy479;
- if (yych == 'l') goto yy479;
+ if (yych == 'L') goto yy504;
+ if (yych == 'l') goto yy504;
goto yy56;
-yy492:
+yy517:
yyaccept = 9;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '.') {
if (yych <= 0x1F) {
- if (yych == '\t') goto yy471;
- goto yy415;
+ if (yych == '\t') goto yy496;
+ goto yy440;
} else {
- if (yych <= ' ') goto yy471;
- if (yych <= ',') goto yy415;
- goto yy471;
+ if (yych <= ' ') goto yy496;
+ if (yych <= ',') goto yy440;
+ goto yy496;
}
} else {
if (yych <= 'T') {
- if (yych <= '/') goto yy415;
- if (yych <= '9') goto yy473;
- goto yy415;
+ if (yych <= '/') goto yy440;
+ if (yych <= '9') goto yy498;
+ goto yy440;
} else {
- if (yych <= 'U') goto yy493;
- if (yych != 'u') goto yy415;
+ if (yych <= 'U') goto yy518;
+ if (yych != 'u') goto yy440;
}
}
-yy493:
+yy518:
yych = *++YYCURSOR;
- if (yych == 'S') goto yy494;
+ if (yych == 'S') goto yy519;
if (yych != 's') goto yy56;
-yy494:
+yy519:
yych = *++YYCURSOR;
- if (yych == 'T') goto yy479;
- if (yych == 't') goto yy479;
+ if (yych == 'T') goto yy504;
+ if (yych == 't') goto yy504;
goto yy56;
-yy495:
+yy520:
yych = *++YYCURSOR;
if (yych <= 'N') {
- if (yych == 'L') goto yy502;
+ if (yych == 'L') goto yy527;
if (yych <= 'M') goto yy56;
- goto yy501;
+ goto yy526;
} else {
if (yych <= 'l') {
if (yych <= 'k') goto yy56;
- goto yy502;
+ goto yy527;
} else {
- if (yych == 'n') goto yy501;
+ if (yych == 'n') goto yy526;
goto yy56;
}
}
-yy496:
+yy521:
yych = *++YYCURSOR;
- if (yych == 'N') goto yy497;
+ if (yych == 'N') goto yy522;
if (yych != 'n') goto yy56;
-yy497:
+yy522:
yyaccept = 9;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '.') {
if (yych <= 0x1F) {
- if (yych == '\t') goto yy471;
- goto yy415;
+ if (yych == '\t') goto yy496;
+ goto yy440;
} else {
- if (yych <= ' ') goto yy471;
- if (yych <= ',') goto yy415;
- goto yy471;
+ if (yych <= ' ') goto yy496;
+ if (yych <= ',') goto yy440;
+ goto yy496;
}
} else {
if (yych <= 'T') {
- if (yych <= '/') goto yy415;
- if (yych <= '9') goto yy473;
- goto yy415;
+ if (yych <= '/') goto yy440;
+ if (yych <= '9') goto yy498;
+ goto yy440;
} else {
- if (yych <= 'U') goto yy498;
- if (yych != 'u') goto yy415;
+ if (yych <= 'U') goto yy523;
+ if (yych != 'u') goto yy440;
}
}
-yy498:
+yy523:
yych = *++YYCURSOR;
- if (yych == 'A') goto yy499;
+ if (yych == 'A') goto yy524;
if (yych != 'a') goto yy56;
-yy499:
+yy524:
yych = *++YYCURSOR;
- if (yych == 'R') goto yy500;
+ if (yych == 'R') goto yy525;
if (yych != 'r') goto yy56;
-yy500:
+yy525:
yych = *++YYCURSOR;
- if (yych == 'Y') goto yy479;
- if (yych == 'y') goto yy479;
+ if (yych == 'Y') goto yy504;
+ if (yych == 'y') goto yy504;
goto yy56;
-yy501:
+yy526:
yyaccept = 9;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '.') {
if (yych <= 0x1F) {
- if (yych == '\t') goto yy471;
- goto yy415;
+ if (yych == '\t') goto yy496;
+ goto yy440;
} else {
- if (yych <= ' ') goto yy471;
- if (yych <= ',') goto yy415;
- goto yy471;
+ if (yych <= ' ') goto yy496;
+ if (yych <= ',') goto yy440;
+ goto yy496;
}
} else {
if (yych <= 'D') {
- if (yych <= '/') goto yy415;
- if (yych <= '9') goto yy473;
- goto yy415;
+ if (yych <= '/') goto yy440;
+ if (yych <= '9') goto yy498;
+ goto yy440;
} else {
- if (yych <= 'E') goto yy479;
- if (yych == 'e') goto yy479;
- goto yy415;
+ if (yych <= 'E') goto yy504;
+ if (yych == 'e') goto yy504;
+ goto yy440;
}
}
-yy502:
+yy527:
yyaccept = 9;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '.') {
if (yych <= 0x1F) {
- if (yych == '\t') goto yy471;
- goto yy415;
+ if (yych == '\t') goto yy496;
+ goto yy440;
} else {
- if (yych <= ' ') goto yy471;
- if (yych <= ',') goto yy415;
- goto yy471;
+ if (yych <= ' ') goto yy496;
+ if (yych <= ',') goto yy440;
+ goto yy496;
}
} else {
if (yych <= 'X') {
- if (yych <= '/') goto yy415;
- if (yych <= '9') goto yy473;
- goto yy415;
+ if (yych <= '/') goto yy440;
+ if (yych <= '9') goto yy498;
+ goto yy440;
} else {
- if (yych <= 'Y') goto yy479;
- if (yych == 'y') goto yy479;
- goto yy415;
+ if (yych <= 'Y') goto yy504;
+ if (yych == 'y') goto yy504;
+ goto yy440;
}
}
-yy503:
+yy528:
yyaccept = 9;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= ',') {
if (yych <= '\t') {
- if (yych <= 0x08) goto yy415;
- goto yy471;
+ if (yych <= 0x08) goto yy440;
+ goto yy496;
} else {
- if (yych == ' ') goto yy471;
- goto yy415;
+ if (yych == ' ') goto yy496;
+ goto yy440;
}
} else {
if (yych <= '9') {
- if (yych <= '.') goto yy471;
- if (yych <= '/') goto yy415;
- goto yy473;
+ if (yych <= '.') goto yy496;
+ if (yych <= '/') goto yy440;
+ goto yy498;
} else {
- if (yych == 'I') goto yy479;
- goto yy415;
+ if (yych == 'I') goto yy504;
+ goto yy440;
}
}
-yy504:
+yy529:
yyaccept = 9;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= ',') {
if (yych <= '\t') {
- if (yych <= 0x08) goto yy415;
- goto yy471;
+ if (yych <= 0x08) goto yy440;
+ goto yy496;
} else {
- if (yych == ' ') goto yy471;
- goto yy415;
+ if (yych == ' ') goto yy496;
+ goto yy440;
}
} else {
if (yych <= '9') {
- if (yych <= '.') goto yy471;
- if (yych <= '/') goto yy415;
- goto yy473;
+ if (yych <= '.') goto yy496;
+ if (yych <= '/') goto yy440;
+ goto yy498;
} else {
- if (yych != 'I') goto yy415;
+ if (yych != 'I') goto yy440;
}
}
yyaccept = 9;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= ',') {
if (yych <= '\t') {
- if (yych <= 0x08) goto yy415;
- goto yy471;
+ if (yych <= 0x08) goto yy440;
+ goto yy496;
} else {
- if (yych == ' ') goto yy471;
- goto yy415;
+ if (yych == ' ') goto yy496;
+ goto yy440;
}
} else {
if (yych <= '9') {
- if (yych <= '.') goto yy471;
- if (yych <= '/') goto yy415;
- goto yy473;
+ if (yych <= '.') goto yy496;
+ if (yych <= '/') goto yy440;
+ goto yy498;
} else {
- if (yych == 'I') goto yy479;
- goto yy415;
+ if (yych == 'I') goto yy504;
+ goto yy440;
}
}
-yy506:
+yy531:
yyaccept = 9;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= ',') {
if (yych <= '\t') {
- if (yych <= 0x08) goto yy415;
- goto yy471;
+ if (yych <= 0x08) goto yy440;
+ goto yy496;
} else {
- if (yych == ' ') goto yy471;
- goto yy415;
+ if (yych == ' ') goto yy496;
+ goto yy440;
}
} else {
if (yych <= '9') {
- if (yych <= '.') goto yy471;
- if (yych <= '/') goto yy415;
- goto yy473;
+ if (yych <= '.') goto yy496;
+ if (yych <= '/') goto yy440;
+ goto yy498;
} else {
- if (yych == 'I') goto yy479;
- goto yy415;
+ if (yych == 'I') goto yy504;
+ goto yy440;
}
}
-yy507:
+yy532:
yyaccept = 10;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '.') {
- if (yych <= ',') goto yy430;
- if (yych <= '-') goto yy541;
- goto yy540;
+ if (yych <= ',') goto yy455;
+ if (yych <= '-') goto yy566;
+ goto yy565;
} else {
- if (yych <= '/') goto yy430;
- if (yych <= '9') goto yy554;
- if (yych <= ':') goto yy432;
- goto yy430;
+ if (yych <= '/') goto yy455;
+ if (yych <= '9') goto yy579;
+ if (yych <= ':') goto yy457;
+ goto yy455;
}
-yy508:
+yy533:
yyaccept = 10;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '/') {
- if (yych <= ',') goto yy430;
- if (yych <= '-') goto yy541;
- if (yych <= '.') goto yy540;
- goto yy430;
+ if (yych <= ',') goto yy455;
+ if (yych <= '-') goto yy566;
+ if (yych <= '.') goto yy565;
+ goto yy455;
} else {
- if (yych <= '2') goto yy554;
- if (yych <= '9') goto yy553;
- if (yych <= ':') goto yy432;
- goto yy430;
+ if (yych <= '2') goto yy579;
+ if (yych <= '9') goto yy578;
+ if (yych <= ':') goto yy457;
+ goto yy455;
}
-yy509:
+yy534:
yyaccept = 10;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '.') {
- if (yych <= ',') goto yy430;
- if (yych <= '-') goto yy541;
- goto yy540;
+ if (yych <= ',') goto yy455;
+ if (yych <= '-') goto yy566;
+ goto yy565;
} else {
- if (yych <= '/') goto yy430;
- if (yych <= '9') goto yy553;
- if (yych <= ':') goto yy432;
- goto yy430;
+ if (yych <= '/') goto yy455;
+ if (yych <= '9') goto yy578;
+ if (yych <= ':') goto yy457;
+ goto yy455;
}
-yy510:
+yy535:
yyaccept = 10;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '.') {
- if (yych <= ',') goto yy430;
- if (yych <= '-') goto yy541;
- goto yy540;
+ if (yych <= ',') goto yy455;
+ if (yych <= '-') goto yy566;
+ goto yy565;
} else {
- if (yych == ':') goto yy432;
- goto yy430;
+ if (yych == ':') goto yy457;
+ goto yy455;
}
-yy511:
+yy536:
yych = *++YYCURSOR;
- if (yych == 'E') goto yy534;
- if (yych == 'e') goto yy534;
+ if (yych == 'E') goto yy559;
+ if (yych == 'e') goto yy559;
goto yy56;
-yy512:
+yy537:
yych = *++YYCURSOR;
- if (yych == 'A') goto yy531;
- if (yych == 'a') goto yy531;
+ if (yych == 'A') goto yy556;
+ if (yych == 'a') goto yy556;
goto yy56;
-yy513:
+yy538:
yych = *++YYCURSOR;
if (yych <= 'U') {
- if (yych == 'P') goto yy489;
+ if (yych == 'P') goto yy514;
if (yych <= 'T') goto yy56;
- goto yy488;
+ goto yy513;
} else {
if (yych <= 'p') {
if (yych <= 'o') goto yy56;
- goto yy489;
+ goto yy514;
} else {
- if (yych == 'u') goto yy488;
+ if (yych == 'u') goto yy513;
goto yy56;
}
}
-yy514:
+yy539:
yych = *++YYCURSOR;
- if (yych == 'E') goto yy524;
- if (yych == 'e') goto yy524;
+ if (yych == 'E') goto yy549;
+ if (yych == 'e') goto yy549;
goto yy56;
-yy515:
+yy540:
yych = *++YYCURSOR;
- if (yych == 'E') goto yy518;
- if (yych == 'e') goto yy518;
+ if (yych == 'E') goto yy543;
+ if (yych == 'e') goto yy543;
goto yy56;
-yy516:
+yy541:
++YYCURSOR;
if ((YYLIMIT - YYCURSOR) < 13) YYFILL(13);
yych = *YYCURSOR;
-yy517:
+yy542:
switch (yych) {
case '\t':
case ' ':
case '-':
- case '.': goto yy516;
+ case '.': goto yy541;
case 'A':
- case 'a': goto yy513;
+ case 'a': goto yy538;
case 'D':
- case 'd': goto yy515;
+ case 'd': goto yy540;
case 'F':
- case 'f': goto yy511;
- case 'I': goto yy414;
+ case 'f': goto yy536;
+ case 'I': goto yy439;
case 'J':
- case 'j': goto yy418;
+ case 'j': goto yy443;
case 'M':
- case 'm': goto yy512;
+ case 'm': goto yy537;
case 'N':
- case 'n': goto yy421;
+ case 'n': goto yy446;
case 'O':
- case 'o': goto yy420;
+ case 'o': goto yy445;
case 'S':
- case 's': goto yy514;
- case 'V': goto yy416;
- case 'X': goto yy417;
+ case 's': goto yy539;
+ case 'V': goto yy441;
+ case 'X': goto yy442;
default: goto yy56;
}
-yy518:
+yy543:
yych = *++YYCURSOR;
- if (yych == 'C') goto yy519;
+ if (yych == 'C') goto yy544;
if (yych != 'c') goto yy56;
-yy519:
+yy544:
yyaccept = 9;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '.') {
if (yych <= 0x1F) {
- if (yych == '\t') goto yy471;
- goto yy415;
+ if (yych == '\t') goto yy496;
+ goto yy440;
} else {
- if (yych <= ' ') goto yy471;
- if (yych <= ',') goto yy415;
- goto yy471;
+ if (yych <= ' ') goto yy496;
+ if (yych <= ',') goto yy440;
+ goto yy496;
}
} else {
if (yych <= 'D') {
- if (yych <= '/') goto yy415;
- if (yych <= '9') goto yy473;
- goto yy415;
+ if (yych <= '/') goto yy440;
+ if (yych <= '9') goto yy498;
+ goto yy440;
} else {
- if (yych <= 'E') goto yy520;
- if (yych != 'e') goto yy415;
+ if (yych <= 'E') goto yy545;
+ if (yych != 'e') goto yy440;
}
}
-yy520:
+yy545:
yych = *++YYCURSOR;
- if (yych == 'M') goto yy521;
+ if (yych == 'M') goto yy546;
if (yych != 'm') goto yy56;
-yy521:
+yy546:
yych = *++YYCURSOR;
- if (yych == 'B') goto yy522;
+ if (yych == 'B') goto yy547;
if (yych != 'b') goto yy56;
-yy522:
+yy547:
yych = *++YYCURSOR;
- if (yych == 'E') goto yy523;
+ if (yych == 'E') goto yy548;
if (yych != 'e') goto yy56;
-yy523:
+yy548:
yych = *++YYCURSOR;
- if (yych == 'R') goto yy479;
- if (yych == 'r') goto yy479;
+ if (yych == 'R') goto yy504;
+ if (yych == 'r') goto yy504;
goto yy56;
-yy524:
+yy549:
yych = *++YYCURSOR;
- if (yych == 'P') goto yy525;
+ if (yych == 'P') goto yy550;
if (yych != 'p') goto yy56;
-yy525:
+yy550:
yyaccept = 9;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '.') {
if (yych <= 0x1F) {
- if (yych == '\t') goto yy471;
- goto yy415;
+ if (yych == '\t') goto yy496;
+ goto yy440;
} else {
- if (yych <= ' ') goto yy471;
- if (yych <= ',') goto yy415;
- goto yy471;
+ if (yych <= ' ') goto yy496;
+ if (yych <= ',') goto yy440;
+ goto yy496;
}
} else {
if (yych <= 'S') {
- if (yych <= '/') goto yy415;
- if (yych <= '9') goto yy473;
- goto yy415;
+ if (yych <= '/') goto yy440;
+ if (yych <= '9') goto yy498;
+ goto yy440;
} else {
- if (yych <= 'T') goto yy526;
- if (yych != 't') goto yy415;
+ if (yych <= 'T') goto yy551;
+ if (yych != 't') goto yy440;
}
}
-yy526:
+yy551:
yyaccept = 9;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '.') {
if (yych <= 0x1F) {
- if (yych == '\t') goto yy471;
- goto yy415;
+ if (yych == '\t') goto yy496;
+ goto yy440;
} else {
- if (yych <= ' ') goto yy471;
- if (yych <= ',') goto yy415;
- goto yy471;
+ if (yych <= ' ') goto yy496;
+ if (yych <= ',') goto yy440;
+ goto yy496;
}
} else {
if (yych <= 'D') {
- if (yych <= '/') goto yy415;
- if (yych <= '9') goto yy473;
- goto yy415;
+ if (yych <= '/') goto yy440;
+ if (yych <= '9') goto yy498;
+ goto yy440;
} else {
- if (yych <= 'E') goto yy527;
- if (yych != 'e') goto yy415;
+ if (yych <= 'E') goto yy552;
+ if (yych != 'e') goto yy440;
}
}
-yy527:
+yy552:
yych = *++YYCURSOR;
- if (yych == 'M') goto yy528;
+ if (yych == 'M') goto yy553;
if (yych != 'm') goto yy56;
-yy528:
+yy553:
yych = *++YYCURSOR;
- if (yych == 'B') goto yy529;
+ if (yych == 'B') goto yy554;
if (yych != 'b') goto yy56;
-yy529:
+yy554:
yych = *++YYCURSOR;
- if (yych == 'E') goto yy530;
+ if (yych == 'E') goto yy555;
if (yych != 'e') goto yy56;
-yy530:
+yy555:
yych = *++YYCURSOR;
- if (yych == 'R') goto yy479;
- if (yych == 'r') goto yy479;
+ if (yych == 'R') goto yy504;
+ if (yych == 'r') goto yy504;
goto yy56;
-yy531:
+yy556:
yych = *++YYCURSOR;
if (yych <= 'Y') {
- if (yych == 'R') goto yy532;
+ if (yych == 'R') goto yy557;
if (yych <= 'X') goto yy56;
- goto yy479;
+ goto yy504;
} else {
if (yych <= 'r') {
if (yych <= 'q') goto yy56;
} else {
- if (yych == 'y') goto yy479;
+ if (yych == 'y') goto yy504;
goto yy56;
}
}
-yy532:
+yy557:
yyaccept = 9;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '.') {
if (yych <= 0x1F) {
- if (yych == '\t') goto yy471;
- goto yy415;
+ if (yych == '\t') goto yy496;
+ goto yy440;
} else {
- if (yych <= ' ') goto yy471;
- if (yych <= ',') goto yy415;
- goto yy471;
+ if (yych <= ' ') goto yy496;
+ if (yych <= ',') goto yy440;
+ goto yy496;
}
} else {
if (yych <= 'B') {
- if (yych <= '/') goto yy415;
- if (yych <= '9') goto yy473;
- goto yy415;
+ if (yych <= '/') goto yy440;
+ if (yych <= '9') goto yy498;
+ goto yy440;
} else {
- if (yych <= 'C') goto yy533;
- if (yych != 'c') goto yy415;
+ if (yych <= 'C') goto yy558;
+ if (yych != 'c') goto yy440;
}
}
-yy533:
+yy558:
yych = *++YYCURSOR;
- if (yych == 'H') goto yy479;
- if (yych == 'h') goto yy479;
+ if (yych == 'H') goto yy504;
+ if (yych == 'h') goto yy504;
goto yy56;
-yy534:
+yy559:
yych = *++YYCURSOR;
- if (yych == 'B') goto yy535;
+ if (yych == 'B') goto yy560;
if (yych != 'b') goto yy56;
-yy535:
+yy560:
yyaccept = 9;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '.') {
if (yych <= 0x1F) {
- if (yych == '\t') goto yy471;
- goto yy415;
+ if (yych == '\t') goto yy496;
+ goto yy440;
} else {
- if (yych <= ' ') goto yy471;
- if (yych <= ',') goto yy415;
- goto yy471;
+ if (yych <= ' ') goto yy496;
+ if (yych <= ',') goto yy440;
+ goto yy496;
}
} else {
if (yych <= 'Q') {
- if (yych <= '/') goto yy415;
- if (yych <= '9') goto yy473;
- goto yy415;
+ if (yych <= '/') goto yy440;
+ if (yych <= '9') goto yy498;
+ goto yy440;
} else {
- if (yych <= 'R') goto yy536;
- if (yych != 'r') goto yy415;
+ if (yych <= 'R') goto yy561;
+ if (yych != 'r') goto yy440;
}
}
-yy536:
+yy561:
yych = *++YYCURSOR;
- if (yych == 'U') goto yy537;
+ if (yych == 'U') goto yy562;
if (yych != 'u') goto yy56;
-yy537:
+yy562:
yych = *++YYCURSOR;
- if (yych == 'A') goto yy538;
+ if (yych == 'A') goto yy563;
if (yych != 'a') goto yy56;
-yy538:
+yy563:
yych = *++YYCURSOR;
- if (yych == 'R') goto yy539;
+ if (yych == 'R') goto yy564;
if (yych != 'r') goto yy56;
-yy539:
+yy564:
yych = *++YYCURSOR;
- if (yych == 'Y') goto yy479;
- if (yych == 'y') goto yy479;
+ if (yych == 'Y') goto yy504;
+ if (yych == 'y') goto yy504;
goto yy56;
-yy540:
+yy565:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '5') goto yy547;
- if (yych <= '6') goto yy548;
- if (yych <= '9') goto yy549;
+ if (yych <= '5') goto yy572;
+ if (yych <= '6') goto yy573;
+ if (yych <= '9') goto yy574;
goto yy56;
-yy541:
+yy566:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
if (yych >= ':') goto yy56;
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
if (yych >= ':') goto yy56;
-yy543:
+yy568:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
if (yych >= ':') goto yy56;
-yy544:
+yy569:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
if (yych >= ':') goto yy56;
++YYCURSOR;
-#line 1312 "ext/date/lib/parse_date.re"
+#line 1355 "ext/date/lib/parse_date.re"
{
DEBUG_OUTPUT("pointed date YYYY");
TIMELIB_INIT;
@@ -9447,38 +9943,38 @@ yy544:
TIMELIB_DEINIT;
return TIMELIB_DATE_FULL_POINTED;
}
-#line 9451 "ext/date/lib/parse_date.c"
-yy547:
+#line 9947 "ext/date/lib/parse_date.c"
+yy572:
yyaccept = 10;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych == '.') goto yy436;
- if (yych <= '/') goto yy430;
- if (yych <= '9') goto yy552;
- goto yy430;
-yy548:
+ if (yych == '.') goto yy461;
+ if (yych <= '/') goto yy455;
+ if (yych <= '9') goto yy577;
+ goto yy455;
+yy573:
yyaccept = 10;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '/') {
- if (yych == '.') goto yy436;
- goto yy430;
+ if (yych == '.') goto yy461;
+ goto yy455;
} else {
- if (yych <= '0') goto yy552;
- if (yych <= '9') goto yy550;
- goto yy430;
+ if (yych <= '0') goto yy577;
+ if (yych <= '9') goto yy575;
+ goto yy455;
}
-yy549:
+yy574:
yyaccept = 10;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych == '.') goto yy436;
- if (yych <= '/') goto yy430;
- if (yych >= ':') goto yy430;
-yy550:
+ if (yych == '.') goto yy461;
+ if (yych <= '/') goto yy455;
+ if (yych >= ':') goto yy455;
+yy575:
yyaccept = 11;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych <= '/') goto yy551;
- if (yych <= '9') goto yy544;
-yy551:
-#line 1324 "ext/date/lib/parse_date.re"
+ if (yych <= '/') goto yy576;
+ if (yych <= '9') goto yy569;
+yy576:
+#line 1367 "ext/date/lib/parse_date.re"
{
int length = 0;
DEBUG_OUTPUT("pointed date YY");
@@ -9491,603 +9987,603 @@ yy551:
TIMELIB_DEINIT;
return TIMELIB_DATE_FULL_POINTED;
}
-#line 9495 "ext/date/lib/parse_date.c"
-yy552:
+#line 9991 "ext/date/lib/parse_date.c"
+yy577:
yyaccept = 10;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '9') {
if (yych <= ' ') {
- if (yych == '\t') goto yy440;
- if (yych <= 0x1F) goto yy430;
- goto yy440;
+ if (yych == '\t') goto yy465;
+ if (yych <= 0x1F) goto yy455;
+ goto yy465;
} else {
- if (yych == '.') goto yy436;
- if (yych <= '/') goto yy430;
- goto yy544;
+ if (yych == '.') goto yy461;
+ if (yych <= '/') goto yy455;
+ goto yy569;
}
} else {
if (yych <= 'P') {
- if (yych == 'A') goto yy442;
- if (yych <= 'O') goto yy430;
- goto yy442;
+ if (yych == 'A') goto yy467;
+ if (yych <= 'O') goto yy455;
+ goto yy467;
} else {
if (yych <= 'a') {
- if (yych <= '`') goto yy430;
- goto yy442;
+ if (yych <= '`') goto yy455;
+ goto yy467;
} else {
- if (yych == 'p') goto yy442;
- goto yy430;
+ if (yych == 'p') goto yy467;
+ goto yy455;
}
}
}
-yy553:
+yy578:
yyaccept = 10;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= ':') {
if (yych <= ' ') {
- if (yych == '\t') goto yy447;
- if (yych <= 0x1F) goto yy430;
- goto yy447;
+ if (yych == '\t') goto yy472;
+ if (yych <= 0x1F) goto yy455;
+ goto yy472;
} else {
- if (yych == '.') goto yy432;
- if (yych <= '9') goto yy430;
- goto yy432;
+ if (yych == '.') goto yy457;
+ if (yych <= '9') goto yy455;
+ goto yy457;
}
} else {
if (yych <= 'P') {
- if (yych == 'A') goto yy449;
- if (yych <= 'O') goto yy430;
- goto yy449;
+ if (yych == 'A') goto yy474;
+ if (yych <= 'O') goto yy455;
+ goto yy474;
} else {
if (yych <= 'a') {
- if (yych <= '`') goto yy430;
- goto yy449;
+ if (yych <= '`') goto yy455;
+ goto yy474;
} else {
- if (yych == 'p') goto yy449;
- goto yy430;
+ if (yych == 'p') goto yy474;
+ goto yy455;
}
}
}
-yy554:
+yy579:
yyaccept = 10;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= ':') {
if (yych <= ' ') {
- if (yych == '\t') goto yy447;
- if (yych <= 0x1F) goto yy430;
- goto yy447;
+ if (yych == '\t') goto yy472;
+ if (yych <= 0x1F) goto yy455;
+ goto yy472;
} else {
if (yych <= '-') {
- if (yych <= ',') goto yy430;
- goto yy541;
+ if (yych <= ',') goto yy455;
+ goto yy566;
} else {
- if (yych <= '.') goto yy540;
- if (yych <= '9') goto yy430;
- goto yy432;
+ if (yych <= '.') goto yy565;
+ if (yych <= '9') goto yy455;
+ goto yy457;
}
}
} else {
if (yych <= 'P') {
- if (yych == 'A') goto yy449;
- if (yych <= 'O') goto yy430;
- goto yy449;
+ if (yych == 'A') goto yy474;
+ if (yych <= 'O') goto yy455;
+ goto yy474;
} else {
if (yych <= 'a') {
- if (yych <= '`') goto yy430;
- goto yy449;
+ if (yych <= '`') goto yy455;
+ goto yy474;
} else {
- if (yych == 'p') goto yy449;
- goto yy430;
+ if (yych == 'p') goto yy474;
+ goto yy455;
}
}
}
-yy555:
+yy580:
yych = *++YYCURSOR;
if (yych <= '.') {
if (yych <= ',') goto yy56;
- if (yych <= '-') goto yy594;
- goto yy541;
+ if (yych <= '-') goto yy619;
+ goto yy566;
} else {
if (yych <= '/') goto yy56;
- if (yych <= '9') goto yy557;
+ if (yych <= '9') goto yy582;
goto yy56;
}
-yy556:
+yy581:
yych = *++YYCURSOR;
if (yych <= '.') {
if (yych <= ',') goto yy56;
- if (yych <= '-') goto yy594;
- goto yy541;
+ if (yych <= '-') goto yy619;
+ goto yy566;
} else {
if (yych <= '/') goto yy56;
if (yych >= '3') goto yy56;
}
-yy557:
+yy582:
yych = *++YYCURSOR;
if (yych <= ',') goto yy56;
- if (yych <= '-') goto yy594;
- if (yych <= '.') goto yy541;
+ if (yych <= '-') goto yy619;
+ if (yych <= '.') goto yy566;
goto yy56;
-yy558:
+yy583:
yych = *++YYCURSOR;
if (yych <= 'U') {
- if (yych == 'A') goto yy590;
+ if (yych == 'A') goto yy615;
if (yych <= 'T') goto yy56;
- goto yy589;
+ goto yy614;
} else {
if (yych <= 'a') {
if (yych <= '`') goto yy56;
- goto yy590;
+ goto yy615;
} else {
- if (yych == 'u') goto yy589;
+ if (yych == 'u') goto yy614;
goto yy56;
}
}
-yy559:
+yy584:
yych = *++YYCURSOR;
- if (yych == 'E') goto yy587;
- if (yych == 'e') goto yy587;
+ if (yych == 'E') goto yy612;
+ if (yych == 'e') goto yy612;
goto yy56;
-yy560:
+yy585:
yych = *++YYCURSOR;
- if (yych == 'A') goto yy584;
- if (yych == 'a') goto yy584;
+ if (yych == 'A') goto yy609;
+ if (yych == 'a') goto yy609;
goto yy56;
-yy561:
+yy586:
yych = *++YYCURSOR;
if (yych <= 'U') {
- if (yych == 'P') goto yy581;
+ if (yych == 'P') goto yy606;
if (yych <= 'T') goto yy56;
- goto yy580;
+ goto yy605;
} else {
if (yych <= 'p') {
if (yych <= 'o') goto yy56;
- goto yy581;
+ goto yy606;
} else {
- if (yych == 'u') goto yy580;
+ if (yych == 'u') goto yy605;
goto yy56;
}
}
-yy562:
+yy587:
yych = *++YYCURSOR;
- if (yych == 'E') goto yy577;
- if (yych == 'e') goto yy577;
+ if (yych == 'E') goto yy602;
+ if (yych == 'e') goto yy602;
goto yy56;
-yy563:
+yy588:
yych = *++YYCURSOR;
- if (yych == 'C') goto yy575;
- if (yych == 'c') goto yy575;
+ if (yych == 'C') goto yy600;
+ if (yych == 'c') goto yy600;
goto yy56;
-yy564:
+yy589:
yych = *++YYCURSOR;
- if (yych == 'O') goto yy573;
- if (yych == 'o') goto yy573;
+ if (yych == 'O') goto yy598;
+ if (yych == 'o') goto yy598;
goto yy56;
-yy565:
+yy590:
yych = *++YYCURSOR;
- if (yych == 'E') goto yy566;
+ if (yych == 'E') goto yy591;
if (yych != 'e') goto yy56;
-yy566:
+yy591:
yych = *++YYCURSOR;
- if (yych == 'C') goto yy567;
+ if (yych == 'C') goto yy592;
if (yych != 'c') goto yy56;
-yy567:
+yy592:
yyaccept = 9;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '.') {
if (yych <= 0x1F) {
- if (yych == '\t') goto yy471;
- goto yy415;
+ if (yych == '\t') goto yy496;
+ goto yy440;
} else {
- if (yych <= ' ') goto yy471;
- if (yych <= ',') goto yy415;
- if (yych >= '.') goto yy471;
+ if (yych <= ' ') goto yy496;
+ if (yych <= ',') goto yy440;
+ if (yych >= '.') goto yy496;
}
} else {
if (yych <= 'D') {
- if (yych <= '/') goto yy415;
- if (yych <= '9') goto yy473;
- goto yy415;
+ if (yych <= '/') goto yy440;
+ if (yych <= '9') goto yy498;
+ goto yy440;
} else {
- if (yych <= 'E') goto yy520;
- if (yych == 'e') goto yy520;
- goto yy415;
+ if (yych <= 'E') goto yy545;
+ if (yych == 'e') goto yy545;
+ goto yy440;
}
}
-yy568:
+yy593:
yych = *++YYCURSOR;
- if (yych <= '/') goto yy472;
- if (yych <= '0') goto yy569;
- if (yych <= '2') goto yy570;
- if (yych <= '3') goto yy571;
- goto yy472;
-yy569:
+ if (yych <= '/') goto yy497;
+ if (yych <= '0') goto yy594;
+ if (yych <= '2') goto yy595;
+ if (yych <= '3') goto yy596;
+ goto yy497;
+yy594:
yych = *++YYCURSOR;
- if (yych <= '/') goto yy474;
- if (yych <= '9') goto yy572;
- goto yy474;
-yy570:
+ if (yych <= '/') goto yy499;
+ if (yych <= '9') goto yy597;
+ goto yy499;
+yy595:
yych = *++YYCURSOR;
- if (yych <= '/') goto yy474;
- if (yych <= '9') goto yy572;
- goto yy474;
-yy571:
+ if (yych <= '/') goto yy499;
+ if (yych <= '9') goto yy597;
+ goto yy499;
+yy596:
yych = *++YYCURSOR;
- if (yych <= '/') goto yy474;
- if (yych <= '1') goto yy572;
- if (yych <= '9') goto yy480;
- goto yy474;
-yy572:
+ if (yych <= '/') goto yy499;
+ if (yych <= '1') goto yy597;
+ if (yych <= '9') goto yy505;
+ goto yy499;
+yy597:
yych = *++YYCURSOR;
- if (yych <= '/') goto yy474;
- if (yych <= '9') goto yy481;
- goto yy474;
-yy573:
+ if (yych <= '/') goto yy499;
+ if (yych <= '9') goto yy506;
+ goto yy499;
+yy598:
yych = *++YYCURSOR;
- if (yych == 'V') goto yy574;
+ if (yych == 'V') goto yy599;
if (yych != 'v') goto yy56;
-yy574:
+yy599:
yyaccept = 9;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '.') {
if (yych <= 0x1F) {
- if (yych == '\t') goto yy471;
- goto yy415;
+ if (yych == '\t') goto yy496;
+ goto yy440;
} else {
- if (yych <= ' ') goto yy471;
- if (yych <= ',') goto yy415;
- if (yych <= '-') goto yy568;
- goto yy471;
+ if (yych <= ' ') goto yy496;
+ if (yych <= ',') goto yy440;
+ if (yych <= '-') goto yy593;
+ goto yy496;
}
} else {
if (yych <= 'D') {
- if (yych <= '/') goto yy415;
- if (yych <= '9') goto yy473;
- goto yy415;
+ if (yych <= '/') goto yy440;
+ if (yych <= '9') goto yy498;
+ goto yy440;
} else {
- if (yych <= 'E') goto yy475;
- if (yych == 'e') goto yy475;
- goto yy415;
+ if (yych <= 'E') goto yy500;
+ if (yych == 'e') goto yy500;
+ goto yy440;
}
}
-yy575:
+yy600:
yych = *++YYCURSOR;
- if (yych == 'T') goto yy576;
+ if (yych == 'T') goto yy601;
if (yych != 't') goto yy56;
-yy576:
+yy601:
yyaccept = 9;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '.') {
if (yych <= 0x1F) {
- if (yych == '\t') goto yy471;
- goto yy415;
+ if (yych == '\t') goto yy496;
+ goto yy440;
} else {
- if (yych <= ' ') goto yy471;
- if (yych <= ',') goto yy415;
- if (yych <= '-') goto yy568;
- goto yy471;
+ if (yych <= ' ') goto yy496;
+ if (yych <= ',') goto yy440;
+ if (yych <= '-') goto yy593;
+ goto yy496;
}
} else {
if (yych <= 'N') {
- if (yych <= '/') goto yy415;
- if (yych <= '9') goto yy473;
- goto yy415;
+ if (yych <= '/') goto yy440;
+ if (yych <= '9') goto yy498;
+ goto yy440;
} else {
- if (yych <= 'O') goto yy485;
- if (yych == 'o') goto yy485;
- goto yy415;
+ if (yych <= 'O') goto yy510;
+ if (yych == 'o') goto yy510;
+ goto yy440;
}
}
-yy577:
+yy602:
yych = *++YYCURSOR;
- if (yych == 'P') goto yy578;
+ if (yych == 'P') goto yy603;
if (yych != 'p') goto yy56;
-yy578:
+yy603:
yyaccept = 9;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '.') {
if (yych <= 0x1F) {
- if (yych == '\t') goto yy471;
- goto yy415;
+ if (yych == '\t') goto yy496;
+ goto yy440;
} else {
- if (yych <= ' ') goto yy471;
- if (yych <= ',') goto yy415;
- if (yych <= '-') goto yy568;
- goto yy471;
+ if (yych <= ' ') goto yy496;
+ if (yych <= ',') goto yy440;
+ if (yych <= '-') goto yy593;
+ goto yy496;
}
} else {
if (yych <= 'S') {
- if (yych <= '/') goto yy415;
- if (yych <= '9') goto yy473;
- goto yy415;
+ if (yych <= '/') goto yy440;
+ if (yych <= '9') goto yy498;
+ goto yy440;
} else {
- if (yych <= 'T') goto yy579;
- if (yych != 't') goto yy415;
+ if (yych <= 'T') goto yy604;
+ if (yych != 't') goto yy440;
}
}
-yy579:
+yy604:
yyaccept = 9;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '.') {
if (yych <= 0x1F) {
- if (yych == '\t') goto yy471;
- goto yy415;
+ if (yych == '\t') goto yy496;
+ goto yy440;
} else {
- if (yych <= ' ') goto yy471;
- if (yych <= ',') goto yy415;
- if (yych <= '-') goto yy568;
- goto yy471;
+ if (yych <= ' ') goto yy496;
+ if (yych <= ',') goto yy440;
+ if (yych <= '-') goto yy593;
+ goto yy496;
}
} else {
if (yych <= 'D') {
- if (yych <= '/') goto yy415;
- if (yych <= '9') goto yy473;
- goto yy415;
+ if (yych <= '/') goto yy440;
+ if (yych <= '9') goto yy498;
+ goto yy440;
} else {
- if (yych <= 'E') goto yy527;
- if (yych == 'e') goto yy527;
- goto yy415;
+ if (yych <= 'E') goto yy552;
+ if (yych == 'e') goto yy552;
+ goto yy440;
}
}
-yy580:
+yy605:
yych = *++YYCURSOR;
- if (yych == 'G') goto yy583;
- if (yych == 'g') goto yy583;
+ if (yych == 'G') goto yy608;
+ if (yych == 'g') goto yy608;
goto yy56;
-yy581:
+yy606:
yych = *++YYCURSOR;
- if (yych == 'R') goto yy582;
+ if (yych == 'R') goto yy607;
if (yych != 'r') goto yy56;
-yy582:
+yy607:
yyaccept = 9;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '.') {
if (yych <= 0x1F) {
- if (yych == '\t') goto yy471;
- goto yy415;
+ if (yych == '\t') goto yy496;
+ goto yy440;
} else {
- if (yych <= ' ') goto yy471;
- if (yych <= ',') goto yy415;
- if (yych <= '-') goto yy568;
- goto yy471;
+ if (yych <= ' ') goto yy496;
+ if (yych <= ',') goto yy440;
+ if (yych <= '-') goto yy593;
+ goto yy496;
}
} else {
if (yych <= 'H') {
- if (yych <= '/') goto yy415;
- if (yych <= '9') goto yy473;
- goto yy415;
+ if (yych <= '/') goto yy440;
+ if (yych <= '9') goto yy498;
+ goto yy440;
} else {
- if (yych <= 'I') goto yy491;
- if (yych == 'i') goto yy491;
- goto yy415;
+ if (yych <= 'I') goto yy516;
+ if (yych == 'i') goto yy516;
+ goto yy440;
}
}
-yy583:
+yy608:
yyaccept = 9;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '.') {
if (yych <= 0x1F) {
- if (yych == '\t') goto yy471;
- goto yy415;
+ if (yych == '\t') goto yy496;
+ goto yy440;
} else {
- if (yych <= ' ') goto yy471;
- if (yych <= ',') goto yy415;
- if (yych <= '-') goto yy568;
- goto yy471;
+ if (yych <= ' ') goto yy496;
+ if (yych <= ',') goto yy440;
+ if (yych <= '-') goto yy593;
+ goto yy496;
}
} else {
if (yych <= 'T') {
- if (yych <= '/') goto yy415;
- if (yych <= '9') goto yy473;
- goto yy415;
+ if (yych <= '/') goto yy440;
+ if (yych <= '9') goto yy498;
+ goto yy440;
} else {
- if (yych <= 'U') goto yy493;
- if (yych == 'u') goto yy493;
- goto yy415;
+ if (yych <= 'U') goto yy518;
+ if (yych == 'u') goto yy518;
+ goto yy440;
}
}
-yy584:
+yy609:
yych = *++YYCURSOR;
if (yych <= 'Y') {
- if (yych == 'R') goto yy585;
+ if (yych == 'R') goto yy610;
if (yych <= 'X') goto yy56;
- goto yy586;
+ goto yy611;
} else {
if (yych <= 'r') {
if (yych <= 'q') goto yy56;
} else {
- if (yych == 'y') goto yy586;
+ if (yych == 'y') goto yy611;
goto yy56;
}
}
-yy585:
+yy610:
yyaccept = 9;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '.') {
if (yych <= 0x1F) {
- if (yych == '\t') goto yy471;
- goto yy415;
+ if (yych == '\t') goto yy496;
+ goto yy440;
} else {
- if (yych <= ' ') goto yy471;
- if (yych <= ',') goto yy415;
- if (yych <= '-') goto yy568;
- goto yy471;
+ if (yych <= ' ') goto yy496;
+ if (yych <= ',') goto yy440;
+ if (yych <= '-') goto yy593;
+ goto yy496;
}
} else {
if (yych <= 'B') {
- if (yych <= '/') goto yy415;
- if (yych <= '9') goto yy473;
- goto yy415;
+ if (yych <= '/') goto yy440;
+ if (yych <= '9') goto yy498;
+ goto yy440;
} else {
- if (yych <= 'C') goto yy533;
- if (yych == 'c') goto yy533;
- goto yy415;
+ if (yych <= 'C') goto yy558;
+ if (yych == 'c') goto yy558;
+ goto yy440;
}
}
-yy586:
+yy611:
yyaccept = 9;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= ',') {
if (yych <= '\t') {
- if (yych <= 0x08) goto yy415;
- goto yy471;
+ if (yych <= 0x08) goto yy440;
+ goto yy496;
} else {
- if (yych == ' ') goto yy471;
- goto yy415;
+ if (yych == ' ') goto yy496;
+ goto yy440;
}
} else {
if (yych <= '.') {
- if (yych <= '-') goto yy568;
- goto yy471;
+ if (yych <= '-') goto yy593;
+ goto yy496;
} else {
- if (yych <= '/') goto yy415;
- if (yych <= '9') goto yy473;
- goto yy415;
+ if (yych <= '/') goto yy440;
+ if (yych <= '9') goto yy498;
+ goto yy440;
}
}
-yy587:
+yy612:
yych = *++YYCURSOR;
- if (yych == 'B') goto yy588;
+ if (yych == 'B') goto yy613;
if (yych != 'b') goto yy56;
-yy588:
+yy613:
yyaccept = 9;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '.') {
if (yych <= 0x1F) {
- if (yych == '\t') goto yy471;
- goto yy415;
+ if (yych == '\t') goto yy496;
+ goto yy440;
} else {
- if (yych <= ' ') goto yy471;
- if (yych <= ',') goto yy415;
- if (yych <= '-') goto yy568;
- goto yy471;
+ if (yych <= ' ') goto yy496;
+ if (yych <= ',') goto yy440;
+ if (yych <= '-') goto yy593;
+ goto yy496;
}
} else {
if (yych <= 'Q') {
- if (yych <= '/') goto yy415;
- if (yych <= '9') goto yy473;
- goto yy415;
+ if (yych <= '/') goto yy440;
+ if (yych <= '9') goto yy498;
+ goto yy440;
} else {
- if (yych <= 'R') goto yy536;
- if (yych == 'r') goto yy536;
- goto yy415;
+ if (yych <= 'R') goto yy561;
+ if (yych == 'r') goto yy561;
+ goto yy440;
}
}
-yy589:
+yy614:
yych = *++YYCURSOR;
if (yych <= 'N') {
- if (yych == 'L') goto yy593;
+ if (yych == 'L') goto yy618;
if (yych <= 'M') goto yy56;
- goto yy592;
+ goto yy617;
} else {
if (yych <= 'l') {
if (yych <= 'k') goto yy56;
- goto yy593;
+ goto yy618;
} else {
- if (yych == 'n') goto yy592;
+ if (yych == 'n') goto yy617;
goto yy56;
}
}
-yy590:
+yy615:
yych = *++YYCURSOR;
- if (yych == 'N') goto yy591;
+ if (yych == 'N') goto yy616;
if (yych != 'n') goto yy56;
-yy591:
+yy616:
yyaccept = 9;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '.') {
if (yych <= 0x1F) {
- if (yych == '\t') goto yy471;
- goto yy415;
+ if (yych == '\t') goto yy496;
+ goto yy440;
} else {
- if (yych <= ' ') goto yy471;
- if (yych <= ',') goto yy415;
- if (yych <= '-') goto yy568;
- goto yy471;
+ if (yych <= ' ') goto yy496;
+ if (yych <= ',') goto yy440;
+ if (yych <= '-') goto yy593;
+ goto yy496;
}
} else {
if (yych <= 'T') {
- if (yych <= '/') goto yy415;
- if (yych <= '9') goto yy473;
- goto yy415;
+ if (yych <= '/') goto yy440;
+ if (yych <= '9') goto yy498;
+ goto yy440;
} else {
- if (yych <= 'U') goto yy498;
- if (yych == 'u') goto yy498;
- goto yy415;
+ if (yych <= 'U') goto yy523;
+ if (yych == 'u') goto yy523;
+ goto yy440;
}
}
-yy592:
+yy617:
yyaccept = 9;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '.') {
if (yych <= 0x1F) {
- if (yych == '\t') goto yy471;
- goto yy415;
+ if (yych == '\t') goto yy496;
+ goto yy440;
} else {
- if (yych <= ' ') goto yy471;
- if (yych <= ',') goto yy415;
- if (yych <= '-') goto yy568;
- goto yy471;
+ if (yych <= ' ') goto yy496;
+ if (yych <= ',') goto yy440;
+ if (yych <= '-') goto yy593;
+ goto yy496;
}
} else {
if (yych <= 'D') {
- if (yych <= '/') goto yy415;
- if (yych <= '9') goto yy473;
- goto yy415;
+ if (yych <= '/') goto yy440;
+ if (yych <= '9') goto yy498;
+ goto yy440;
} else {
- if (yych <= 'E') goto yy479;
- if (yych == 'e') goto yy479;
- goto yy415;
+ if (yych <= 'E') goto yy504;
+ if (yych == 'e') goto yy504;
+ goto yy440;
}
}
-yy593:
+yy618:
yyaccept = 9;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '.') {
if (yych <= 0x1F) {
- if (yych == '\t') goto yy471;
- goto yy415;
+ if (yych == '\t') goto yy496;
+ goto yy440;
} else {
- if (yych <= ' ') goto yy471;
- if (yych <= ',') goto yy415;
- if (yych <= '-') goto yy568;
- goto yy471;
+ if (yych <= ' ') goto yy496;
+ if (yych <= ',') goto yy440;
+ if (yych <= '-') goto yy593;
+ goto yy496;
}
} else {
if (yych <= 'X') {
- if (yych <= '/') goto yy415;
- if (yych <= '9') goto yy473;
- goto yy415;
+ if (yych <= '/') goto yy440;
+ if (yych <= '9') goto yy498;
+ goto yy440;
} else {
- if (yych <= 'Y') goto yy479;
- if (yych == 'y') goto yy479;
- goto yy415;
+ if (yych <= 'Y') goto yy504;
+ if (yych == 'y') goto yy504;
+ goto yy440;
}
}
-yy594:
+yy619:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '2') goto yy595;
- if (yych <= '3') goto yy597;
- if (yych <= '9') goto yy598;
+ if (yych <= '2') goto yy620;
+ if (yych <= '3') goto yy622;
+ if (yych <= '9') goto yy623;
goto yy56;
-yy595:
+yy620:
yyaccept = 12;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'n') {
- if (yych <= '/') goto yy596;
- if (yych <= '9') goto yy604;
- if (yych >= 'n') goto yy600;
+ if (yych <= '/') goto yy621;
+ if (yych <= '9') goto yy629;
+ if (yych >= 'n') goto yy625;
} else {
if (yych <= 'r') {
- if (yych >= 'r') goto yy601;
+ if (yych >= 'r') goto yy626;
} else {
- if (yych <= 's') goto yy599;
- if (yych <= 't') goto yy602;
+ if (yych <= 's') goto yy624;
+ if (yych <= 't') goto yy627;
}
}
-yy596:
-#line 1283 "ext/date/lib/parse_date.re"
+yy621:
+#line 1326 "ext/date/lib/parse_date.re"
{
int length = 0;
DEBUG_OUTPUT("gnudateshort");
@@ -10100,103 +10596,103 @@ yy596:
TIMELIB_DEINIT;
return TIMELIB_ISO_DATE;
}
-#line 10104 "ext/date/lib/parse_date.c"
-yy597:
+#line 10600 "ext/date/lib/parse_date.c"
+yy622:
yyaccept = 12;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'n') {
if (yych <= '1') {
- if (yych <= '/') goto yy596;
- goto yy604;
+ if (yych <= '/') goto yy621;
+ goto yy629;
} else {
- if (yych <= '9') goto yy543;
- if (yych <= 'm') goto yy596;
- goto yy600;
+ if (yych <= '9') goto yy568;
+ if (yych <= 'm') goto yy621;
+ goto yy625;
}
} else {
if (yych <= 'r') {
- if (yych <= 'q') goto yy596;
- goto yy601;
+ if (yych <= 'q') goto yy621;
+ goto yy626;
} else {
- if (yych <= 's') goto yy599;
- if (yych <= 't') goto yy602;
- goto yy596;
+ if (yych <= 's') goto yy624;
+ if (yych <= 't') goto yy627;
+ goto yy621;
}
}
-yy598:
+yy623:
yyaccept = 12;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'n') {
- if (yych <= '/') goto yy596;
- if (yych <= '9') goto yy543;
- if (yych <= 'm') goto yy596;
- goto yy600;
+ if (yych <= '/') goto yy621;
+ if (yych <= '9') goto yy568;
+ if (yych <= 'm') goto yy621;
+ goto yy625;
} else {
if (yych <= 'r') {
- if (yych <= 'q') goto yy596;
- goto yy601;
+ if (yych <= 'q') goto yy621;
+ goto yy626;
} else {
- if (yych <= 's') goto yy599;
- if (yych <= 't') goto yy602;
- goto yy596;
+ if (yych <= 's') goto yy624;
+ if (yych <= 't') goto yy627;
+ goto yy621;
}
}
-yy599:
+yy624:
yych = *++YYCURSOR;
- if (yych == 't') goto yy603;
+ if (yych == 't') goto yy628;
goto yy56;
-yy600:
+yy625:
yych = *++YYCURSOR;
- if (yych == 'd') goto yy603;
+ if (yych == 'd') goto yy628;
goto yy56;
-yy601:
+yy626:
yych = *++YYCURSOR;
- if (yych == 'd') goto yy603;
+ if (yych == 'd') goto yy628;
goto yy56;
-yy602:
+yy627:
yych = *++YYCURSOR;
if (yych != 'h') goto yy56;
-yy603:
+yy628:
yych = *++YYCURSOR;
- goto yy596;
-yy604:
+ goto yy621;
+yy629:
yyaccept = 12;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'n') {
- if (yych <= '/') goto yy596;
- if (yych <= '9') goto yy544;
- if (yych <= 'm') goto yy596;
- goto yy600;
+ if (yych <= '/') goto yy621;
+ if (yych <= '9') goto yy569;
+ if (yych <= 'm') goto yy621;
+ goto yy625;
} else {
if (yych <= 'r') {
- if (yych <= 'q') goto yy596;
- goto yy601;
+ if (yych <= 'q') goto yy621;
+ goto yy626;
} else {
- if (yych <= 's') goto yy599;
- if (yych <= 't') goto yy602;
- goto yy596;
+ if (yych <= 's') goto yy624;
+ if (yych <= 't') goto yy627;
+ goto yy621;
}
}
-yy605:
+yy630:
yyaccept = 13;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'n') {
if (yych <= '/') {
- if (yych >= '/') goto yy662;
+ if (yych >= '/') goto yy687;
} else {
- if (yych <= '9') goto yy608;
- if (yych >= 'n') goto yy659;
+ if (yych <= '9') goto yy633;
+ if (yych >= 'n') goto yy684;
}
} else {
if (yych <= 'r') {
- if (yych >= 'r') goto yy660;
+ if (yych >= 'r') goto yy685;
} else {
- if (yych <= 's') goto yy658;
- if (yych <= 't') goto yy661;
+ if (yych <= 's') goto yy683;
+ if (yych <= 't') goto yy686;
}
}
-yy606:
-#line 1227 "ext/date/lib/parse_date.re"
+yy631:
+#line 1270 "ext/date/lib/parse_date.re"
{
int length = 0;
DEBUG_OUTPUT("americanshort | american");
@@ -10211,113 +10707,113 @@ yy606:
TIMELIB_DEINIT;
return TIMELIB_AMERICAN;
}
-#line 10215 "ext/date/lib/parse_date.c"
-yy607:
+#line 10711 "ext/date/lib/parse_date.c"
+yy632:
yyaccept = 13;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'n') {
if (yych <= '/') {
- if (yych <= '.') goto yy606;
- goto yy662;
+ if (yych <= '.') goto yy631;
+ goto yy687;
} else {
- if (yych <= '1') goto yy608;
- if (yych <= 'm') goto yy606;
- goto yy659;
+ if (yych <= '1') goto yy633;
+ if (yych <= 'm') goto yy631;
+ goto yy684;
}
} else {
if (yych <= 'r') {
- if (yych <= 'q') goto yy606;
- goto yy660;
+ if (yych <= 'q') goto yy631;
+ goto yy685;
} else {
- if (yych <= 's') goto yy658;
- if (yych <= 't') goto yy661;
- goto yy606;
+ if (yych <= 's') goto yy683;
+ if (yych <= 't') goto yy686;
+ goto yy631;
}
}
-yy608:
+yy633:
yyaccept = 13;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'n') {
- if (yych == '/') goto yy662;
- if (yych <= 'm') goto yy606;
- goto yy659;
+ if (yych == '/') goto yy687;
+ if (yych <= 'm') goto yy631;
+ goto yy684;
} else {
if (yych <= 'r') {
- if (yych <= 'q') goto yy606;
- goto yy660;
+ if (yych <= 'q') goto yy631;
+ goto yy685;
} else {
- if (yych <= 's') goto yy658;
- if (yych <= 't') goto yy661;
- goto yy606;
+ if (yych <= 's') goto yy683;
+ if (yych <= 't') goto yy686;
+ goto yy631;
}
}
-yy609:
+yy634:
yych = *++YYCURSOR;
if (yych <= 'U') {
- if (yych == 'A') goto yy657;
+ if (yych == 'A') goto yy682;
if (yych <= 'T') goto yy56;
- goto yy656;
+ goto yy681;
} else {
if (yych <= 'a') {
if (yych <= '`') goto yy56;
- goto yy657;
+ goto yy682;
} else {
- if (yych == 'u') goto yy656;
+ if (yych == 'u') goto yy681;
goto yy56;
}
}
-yy610:
+yy635:
yych = *++YYCURSOR;
- if (yych == 'E') goto yy655;
- if (yych == 'e') goto yy655;
+ if (yych == 'E') goto yy680;
+ if (yych == 'e') goto yy680;
goto yy56;
-yy611:
+yy636:
yych = *++YYCURSOR;
- if (yych == 'A') goto yy654;
- if (yych == 'a') goto yy654;
+ if (yych == 'A') goto yy679;
+ if (yych == 'a') goto yy679;
goto yy56;
-yy612:
+yy637:
yych = *++YYCURSOR;
if (yych <= 'U') {
- if (yych == 'P') goto yy653;
+ if (yych == 'P') goto yy678;
if (yych <= 'T') goto yy56;
- goto yy652;
+ goto yy677;
} else {
if (yych <= 'p') {
if (yych <= 'o') goto yy56;
- goto yy653;
+ goto yy678;
} else {
- if (yych == 'u') goto yy652;
+ if (yych == 'u') goto yy677;
goto yy56;
}
}
-yy613:
+yy638:
yych = *++YYCURSOR;
- if (yych == 'E') goto yy650;
- if (yych == 'e') goto yy650;
+ if (yych == 'E') goto yy675;
+ if (yych == 'e') goto yy675;
goto yy56;
-yy614:
+yy639:
yych = *++YYCURSOR;
- if (yych == 'C') goto yy649;
- if (yych == 'c') goto yy649;
+ if (yych == 'C') goto yy674;
+ if (yych == 'c') goto yy674;
goto yy56;
-yy615:
+yy640:
yych = *++YYCURSOR;
- if (yych == 'O') goto yy648;
- if (yych == 'o') goto yy648;
+ if (yych == 'O') goto yy673;
+ if (yych == 'o') goto yy673;
goto yy56;
-yy616:
+yy641:
yych = *++YYCURSOR;
- if (yych == 'E') goto yy617;
+ if (yych == 'E') goto yy642;
if (yych != 'e') goto yy56;
-yy617:
+yy642:
yych = *++YYCURSOR;
- if (yych == 'C') goto yy618;
+ if (yych == 'C') goto yy643;
if (yych != 'c') goto yy56;
-yy618:
+yy643:
yych = *++YYCURSOR;
if (yych != '/') goto yy56;
-yy619:
+yy644:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
if (yych >= ':') goto yy56;
@@ -10334,19 +10830,19 @@ yy619:
if (yych != ':') goto yy56;
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '1') goto yy625;
- if (yych <= '2') goto yy626;
+ if (yych <= '1') goto yy650;
+ if (yych <= '2') goto yy651;
goto yy56;
-yy625:
+yy650:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '9') goto yy627;
+ if (yych <= '9') goto yy652;
goto yy56;
-yy626:
+yy651:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
if (yych >= '5') goto yy56;
-yy627:
+yy652:
yych = *++YYCURSOR;
if (yych != ':') goto yy56;
yych = *++YYCURSOR;
@@ -10359,58 +10855,58 @@ yy627:
if (yych != ':') goto yy56;
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '5') goto yy632;
- if (yych <= '6') goto yy633;
+ if (yych <= '5') goto yy657;
+ if (yych <= '6') goto yy658;
goto yy56;
-yy632:
+yy657:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '9') goto yy634;
+ if (yych <= '9') goto yy659;
goto yy56;
-yy633:
+yy658:
yych = *++YYCURSOR;
if (yych != '0') goto yy56;
-yy634:
+yy659:
yych = *++YYCURSOR;
- if (yych == '\t') goto yy635;
+ if (yych == '\t') goto yy660;
if (yych != ' ') goto yy56;
-yy635:
+yy660:
++YYCURSOR;
if ((YYLIMIT - YYCURSOR) < 9) YYFILL(9);
yych = *YYCURSOR;
if (yych <= '*') {
if (yych <= '\t') {
if (yych <= 0x08) goto yy56;
- goto yy635;
+ goto yy660;
} else {
- if (yych == ' ') goto yy635;
+ if (yych == ' ') goto yy660;
goto yy56;
}
} else {
if (yych <= '-') {
if (yych == ',') goto yy56;
- goto yy638;
+ goto yy663;
} else {
if (yych != 'G') goto yy56;
}
}
yych = *++YYCURSOR;
- if (yych == 'M') goto yy646;
+ if (yych == 'M') goto yy671;
goto yy56;
-yy638:
+yy663:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '1') goto yy639;
- if (yych <= '2') goto yy641;
- if (yych <= '9') goto yy642;
+ if (yych <= '1') goto yy664;
+ if (yych <= '2') goto yy666;
+ if (yych <= '9') goto yy667;
goto yy56;
-yy639:
+yy664:
++YYCURSOR;
- if ((yych = *YYCURSOR) <= '/') goto yy640;
- if (yych <= '9') goto yy642;
- if (yych <= ':') goto yy643;
-yy640:
-#line 1510 "ext/date/lib/parse_date.re"
+ if ((yych = *YYCURSOR) <= '/') goto yy665;
+ if (yych <= '9') goto yy667;
+ if (yych <= ':') goto yy668;
+yy665:
+#line 1553 "ext/date/lib/parse_date.re"
{
int tz_not_found;
DEBUG_OUTPUT("clf");
@@ -10430,261 +10926,261 @@ yy640:
TIMELIB_DEINIT;
return TIMELIB_CLF;
}
-#line 10434 "ext/date/lib/parse_date.c"
-yy641:
+#line 10930 "ext/date/lib/parse_date.c"
+yy666:
yych = *++YYCURSOR;
if (yych <= '5') {
- if (yych <= '/') goto yy640;
- if (yych >= '5') goto yy644;
+ if (yych <= '/') goto yy665;
+ if (yych >= '5') goto yy669;
} else {
- if (yych <= '9') goto yy645;
- if (yych <= ':') goto yy643;
- goto yy640;
+ if (yych <= '9') goto yy670;
+ if (yych <= ':') goto yy668;
+ goto yy665;
}
-yy642:
+yy667:
yych = *++YYCURSOR;
- if (yych <= '/') goto yy640;
- if (yych <= '5') goto yy644;
- if (yych <= '9') goto yy645;
- if (yych >= ';') goto yy640;
-yy643:
+ if (yych <= '/') goto yy665;
+ if (yych <= '5') goto yy669;
+ if (yych <= '9') goto yy670;
+ if (yych >= ';') goto yy665;
+yy668:
yych = *++YYCURSOR;
- if (yych <= '/') goto yy640;
- if (yych <= '5') goto yy644;
- if (yych <= '9') goto yy645;
- goto yy640;
-yy644:
+ if (yych <= '/') goto yy665;
+ if (yych <= '5') goto yy669;
+ if (yych <= '9') goto yy670;
+ goto yy665;
+yy669:
yych = *++YYCURSOR;
- if (yych <= '/') goto yy640;
- if (yych >= ':') goto yy640;
-yy645:
+ if (yych <= '/') goto yy665;
+ if (yych >= ':') goto yy665;
+yy670:
yych = *++YYCURSOR;
- goto yy640;
-yy646:
+ goto yy665;
+yy671:
yych = *++YYCURSOR;
if (yych != 'T') goto yy56;
yych = *++YYCURSOR;
- if (yych == '+') goto yy638;
- if (yych == '-') goto yy638;
+ if (yych == '+') goto yy663;
+ if (yych == '-') goto yy663;
goto yy56;
-yy648:
+yy673:
yych = *++YYCURSOR;
- if (yych == 'V') goto yy618;
- if (yych == 'v') goto yy618;
+ if (yych == 'V') goto yy643;
+ if (yych == 'v') goto yy643;
goto yy56;
-yy649:
+yy674:
yych = *++YYCURSOR;
- if (yych == 'T') goto yy618;
- if (yych == 't') goto yy618;
+ if (yych == 'T') goto yy643;
+ if (yych == 't') goto yy643;
goto yy56;
-yy650:
+yy675:
yych = *++YYCURSOR;
- if (yych == 'P') goto yy651;
+ if (yych == 'P') goto yy676;
if (yych != 'p') goto yy56;
-yy651:
+yy676:
yych = *++YYCURSOR;
if (yych <= 'S') {
- if (yych == '/') goto yy619;
+ if (yych == '/') goto yy644;
goto yy56;
} else {
- if (yych <= 'T') goto yy618;
- if (yych == 't') goto yy618;
+ if (yych <= 'T') goto yy643;
+ if (yych == 't') goto yy643;
goto yy56;
}
-yy652:
+yy677:
yych = *++YYCURSOR;
- if (yych == 'G') goto yy618;
- if (yych == 'g') goto yy618;
+ if (yych == 'G') goto yy643;
+ if (yych == 'g') goto yy643;
goto yy56;
-yy653:
+yy678:
yych = *++YYCURSOR;
- if (yych == 'R') goto yy618;
- if (yych == 'r') goto yy618;
+ if (yych == 'R') goto yy643;
+ if (yych == 'r') goto yy643;
goto yy56;
-yy654:
+yy679:
yych = *++YYCURSOR;
if (yych <= 'Y') {
- if (yych == 'R') goto yy618;
+ if (yych == 'R') goto yy643;
if (yych <= 'X') goto yy56;
- goto yy618;
+ goto yy643;
} else {
if (yych <= 'r') {
if (yych <= 'q') goto yy56;
- goto yy618;
+ goto yy643;
} else {
- if (yych == 'y') goto yy618;
+ if (yych == 'y') goto yy643;
goto yy56;
}
}
-yy655:
+yy680:
yych = *++YYCURSOR;
- if (yych == 'B') goto yy618;
- if (yych == 'b') goto yy618;
+ if (yych == 'B') goto yy643;
+ if (yych == 'b') goto yy643;
goto yy56;
-yy656:
+yy681:
yych = *++YYCURSOR;
if (yych <= 'N') {
- if (yych == 'L') goto yy618;
+ if (yych == 'L') goto yy643;
if (yych <= 'M') goto yy56;
- goto yy618;
+ goto yy643;
} else {
if (yych <= 'l') {
if (yych <= 'k') goto yy56;
- goto yy618;
+ goto yy643;
} else {
- if (yych == 'n') goto yy618;
+ if (yych == 'n') goto yy643;
goto yy56;
}
}
-yy657:
+yy682:
yych = *++YYCURSOR;
- if (yych == 'N') goto yy618;
- if (yych == 'n') goto yy618;
+ if (yych == 'N') goto yy643;
+ if (yych == 'n') goto yy643;
goto yy56;
-yy658:
+yy683:
yych = *++YYCURSOR;
- if (yych == 't') goto yy667;
+ if (yych == 't') goto yy692;
goto yy56;
-yy659:
+yy684:
yych = *++YYCURSOR;
- if (yych == 'd') goto yy667;
+ if (yych == 'd') goto yy692;
goto yy56;
-yy660:
+yy685:
yych = *++YYCURSOR;
- if (yych == 'd') goto yy667;
+ if (yych == 'd') goto yy692;
goto yy56;
-yy661:
+yy686:
yych = *++YYCURSOR;
- if (yych == 'h') goto yy667;
+ if (yych == 'h') goto yy692;
goto yy56;
-yy662:
+yy687:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
if (yych >= ':') goto yy56;
yych = *++YYCURSOR;
- if (yych <= '/') goto yy606;
- if (yych >= ':') goto yy606;
+ if (yych <= '/') goto yy631;
+ if (yych >= ':') goto yy631;
yych = *++YYCURSOR;
- if (yych <= '/') goto yy606;
- if (yych >= ':') goto yy606;
+ if (yych <= '/') goto yy631;
+ if (yych >= ':') goto yy631;
yych = *++YYCURSOR;
- if (yych <= '/') goto yy606;
- if (yych >= ':') goto yy606;
+ if (yych <= '/') goto yy631;
+ if (yych >= ':') goto yy631;
yych = *++YYCURSOR;
- goto yy606;
-yy667:
+ goto yy631;
+yy692:
yyaccept = 13;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych == '/') goto yy662;
- goto yy606;
-yy668:
+ if (yych == '/') goto yy687;
+ goto yy631;
+yy693:
yych = *++YYCURSOR;
if (yych <= ',') {
- if (yych == '\t') goto yy670;
- goto yy517;
+ if (yych == '\t') goto yy695;
+ goto yy542;
} else {
- if (yych <= '-') goto yy671;
- if (yych <= '.') goto yy670;
- if (yych >= '0') goto yy517;
+ if (yych <= '-') goto yy696;
+ if (yych <= '.') goto yy695;
+ if (yych >= '0') goto yy542;
}
-yy669:
+yy694:
yych = *++YYCURSOR;
switch (yych) {
case 'A':
- case 'a': goto yy612;
+ case 'a': goto yy637;
case 'D':
- case 'd': goto yy616;
+ case 'd': goto yy641;
case 'F':
- case 'f': goto yy610;
+ case 'f': goto yy635;
case 'J':
- case 'j': goto yy609;
+ case 'j': goto yy634;
case 'M':
- case 'm': goto yy611;
+ case 'm': goto yy636;
case 'N':
- case 'n': goto yy615;
+ case 'n': goto yy640;
case 'O':
- case 'o': goto yy614;
+ case 'o': goto yy639;
case 'S':
- case 's': goto yy613;
+ case 's': goto yy638;
default: goto yy56;
}
-yy670:
+yy695:
yych = *++YYCURSOR;
- if (yych <= '/') goto yy517;
- if (yych <= '0') goto yy675;
- if (yych <= '1') goto yy676;
- if (yych <= '9') goto yy677;
- goto yy517;
-yy671:
+ if (yych <= '/') goto yy542;
+ if (yych <= '0') goto yy700;
+ if (yych <= '1') goto yy701;
+ if (yych <= '9') goto yy702;
+ goto yy542;
+yy696:
yych = *++YYCURSOR;
- if (yych <= '/') goto yy517;
- if (yych <= '0') goto yy672;
- if (yych <= '1') goto yy673;
- if (yych <= '9') goto yy674;
- goto yy517;
-yy672:
+ if (yych <= '/') goto yy542;
+ if (yych <= '0') goto yy697;
+ if (yych <= '1') goto yy698;
+ if (yych <= '9') goto yy699;
+ goto yy542;
+yy697:
yych = *++YYCURSOR;
if (yych <= ',') goto yy56;
- if (yych <= '.') goto yy541;
+ if (yych <= '.') goto yy566;
if (yych <= '/') goto yy56;
- if (yych <= '9') goto yy674;
+ if (yych <= '9') goto yy699;
goto yy56;
-yy673:
+yy698:
yych = *++YYCURSOR;
if (yych <= ',') goto yy56;
- if (yych <= '.') goto yy541;
+ if (yych <= '.') goto yy566;
if (yych <= '/') goto yy56;
if (yych >= '3') goto yy56;
-yy674:
+yy699:
yych = *++YYCURSOR;
if (yych <= ',') goto yy56;
- if (yych <= '.') goto yy541;
+ if (yych <= '.') goto yy566;
goto yy56;
-yy675:
+yy700:
yych = *++YYCURSOR;
if (yych <= '.') {
if (yych <= ',') goto yy56;
- if (yych <= '-') goto yy541;
- goto yy678;
+ if (yych <= '-') goto yy566;
+ goto yy703;
} else {
if (yych <= '/') goto yy56;
- if (yych <= '9') goto yy677;
+ if (yych <= '9') goto yy702;
goto yy56;
}
-yy676:
+yy701:
yych = *++YYCURSOR;
if (yych <= '.') {
if (yych <= ',') goto yy56;
- if (yych <= '-') goto yy541;
- goto yy678;
+ if (yych <= '-') goto yy566;
+ goto yy703;
} else {
if (yych <= '/') goto yy56;
if (yych >= '3') goto yy56;
}
-yy677:
+yy702:
yych = *++YYCURSOR;
if (yych <= ',') goto yy56;
- if (yych <= '-') goto yy541;
+ if (yych <= '-') goto yy566;
if (yych >= '/') goto yy56;
-yy678:
+yy703:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
if (yych >= ':') goto yy56;
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '9') goto yy550;
+ if (yych <= '9') goto yy575;
goto yy56;
-yy680:
+yy705:
yych = *++YYCURSOR;
- if (yych == '-') goto yy724;
+ if (yych == '-') goto yy749;
if (yych <= '/') goto yy60;
- if (yych <= '9') goto yy722;
+ if (yych <= '9') goto yy747;
goto yy60;
-yy681:
+yy706:
yych = *++YYCURSOR;
switch (yych) {
- case '0': goto yy690;
- case '1': goto yy691;
+ case '0': goto yy715;
+ case '1': goto yy716;
case '2':
case '3':
case '4':
@@ -10692,232 +11188,232 @@ yy681:
case '6':
case '7':
case '8':
- case '9': goto yy692;
+ case '9': goto yy717;
case 'A':
- case 'a': goto yy685;
+ case 'a': goto yy710;
case 'D':
- case 'd': goto yy689;
+ case 'd': goto yy714;
case 'F':
- case 'f': goto yy683;
+ case 'f': goto yy708;
case 'J':
- case 'j': goto yy682;
+ case 'j': goto yy707;
case 'M':
- case 'm': goto yy684;
+ case 'm': goto yy709;
case 'N':
- case 'n': goto yy688;
+ case 'n': goto yy713;
case 'O':
- case 'o': goto yy687;
+ case 'o': goto yy712;
case 'S':
- case 's': goto yy686;
+ case 's': goto yy711;
default: goto yy56;
}
-yy682:
+yy707:
yych = *++YYCURSOR;
if (yych <= 'U') {
- if (yych == 'A') goto yy721;
+ if (yych == 'A') goto yy746;
if (yych <= 'T') goto yy56;
- goto yy720;
+ goto yy745;
} else {
if (yych <= 'a') {
if (yych <= '`') goto yy56;
- goto yy721;
+ goto yy746;
} else {
- if (yych == 'u') goto yy720;
+ if (yych == 'u') goto yy745;
goto yy56;
}
}
-yy683:
+yy708:
yych = *++YYCURSOR;
- if (yych == 'E') goto yy719;
- if (yych == 'e') goto yy719;
+ if (yych == 'E') goto yy744;
+ if (yych == 'e') goto yy744;
goto yy56;
-yy684:
+yy709:
yych = *++YYCURSOR;
- if (yych == 'A') goto yy718;
- if (yych == 'a') goto yy718;
+ if (yych == 'A') goto yy743;
+ if (yych == 'a') goto yy743;
goto yy56;
-yy685:
+yy710:
yych = *++YYCURSOR;
if (yych <= 'U') {
- if (yych == 'P') goto yy717;
+ if (yych == 'P') goto yy742;
if (yych <= 'T') goto yy56;
- goto yy716;
+ goto yy741;
} else {
if (yych <= 'p') {
if (yych <= 'o') goto yy56;
- goto yy717;
+ goto yy742;
} else {
- if (yych == 'u') goto yy716;
+ if (yych == 'u') goto yy741;
goto yy56;
}
}
-yy686:
+yy711:
yych = *++YYCURSOR;
- if (yych == 'E') goto yy714;
- if (yych == 'e') goto yy714;
+ if (yych == 'E') goto yy739;
+ if (yych == 'e') goto yy739;
goto yy56;
-yy687:
+yy712:
yych = *++YYCURSOR;
- if (yych == 'C') goto yy713;
- if (yych == 'c') goto yy713;
+ if (yych == 'C') goto yy738;
+ if (yych == 'c') goto yy738;
goto yy56;
-yy688:
+yy713:
yych = *++YYCURSOR;
- if (yych == 'O') goto yy712;
- if (yych == 'o') goto yy712;
+ if (yych == 'O') goto yy737;
+ if (yych == 'o') goto yy737;
goto yy56;
-yy689:
+yy714:
yych = *++YYCURSOR;
- if (yych == 'E') goto yy704;
- if (yych == 'e') goto yy704;
+ if (yych == 'E') goto yy729;
+ if (yych == 'e') goto yy729;
goto yy56;
-yy690:
+yy715:
yych = *++YYCURSOR;
- if (yych == '-') goto yy693;
+ if (yych == '-') goto yy718;
if (yych <= '/') goto yy56;
- if (yych <= '9') goto yy697;
+ if (yych <= '9') goto yy722;
goto yy56;
-yy691:
+yy716:
yych = *++YYCURSOR;
- if (yych == '-') goto yy693;
+ if (yych == '-') goto yy718;
if (yych <= '/') goto yy56;
- if (yych <= '2') goto yy697;
+ if (yych <= '2') goto yy722;
goto yy56;
-yy692:
+yy717:
yych = *++YYCURSOR;
if (yych != '-') goto yy56;
-yy693:
+yy718:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '2') goto yy694;
- if (yych <= '3') goto yy695;
- if (yych <= '9') goto yy696;
+ if (yych <= '2') goto yy719;
+ if (yych <= '3') goto yy720;
+ if (yych <= '9') goto yy721;
goto yy56;
-yy694:
+yy719:
yyaccept = 12;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'n') {
- if (yych <= '/') goto yy596;
- if (yych <= '9') goto yy696;
- if (yych <= 'm') goto yy596;
- goto yy600;
+ if (yych <= '/') goto yy621;
+ if (yych <= '9') goto yy721;
+ if (yych <= 'm') goto yy621;
+ goto yy625;
} else {
if (yych <= 'r') {
- if (yych <= 'q') goto yy596;
- goto yy601;
+ if (yych <= 'q') goto yy621;
+ goto yy626;
} else {
- if (yych <= 's') goto yy599;
- if (yych <= 't') goto yy602;
- goto yy596;
+ if (yych <= 's') goto yy624;
+ if (yych <= 't') goto yy627;
+ goto yy621;
}
}
-yy695:
+yy720:
yyaccept = 12;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'n') {
- if (yych <= '/') goto yy596;
- if (yych <= '1') goto yy696;
- if (yych <= 'm') goto yy596;
- goto yy600;
+ if (yych <= '/') goto yy621;
+ if (yych <= '1') goto yy721;
+ if (yych <= 'm') goto yy621;
+ goto yy625;
} else {
if (yych <= 'r') {
- if (yych <= 'q') goto yy596;
- goto yy601;
+ if (yych <= 'q') goto yy621;
+ goto yy626;
} else {
- if (yych <= 's') goto yy599;
- if (yych <= 't') goto yy602;
- goto yy596;
+ if (yych <= 's') goto yy624;
+ if (yych <= 't') goto yy627;
+ goto yy621;
}
}
-yy696:
+yy721:
yyaccept = 12;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'q') {
- if (yych == 'n') goto yy600;
- goto yy596;
+ if (yych == 'n') goto yy625;
+ goto yy621;
} else {
- if (yych <= 'r') goto yy601;
- if (yych <= 's') goto yy599;
- if (yych <= 't') goto yy602;
- goto yy596;
+ if (yych <= 'r') goto yy626;
+ if (yych <= 's') goto yy624;
+ if (yych <= 't') goto yy627;
+ goto yy621;
}
-yy697:
+yy722:
yych = *++YYCURSOR;
if (yych != '-') goto yy56;
yych = *++YYCURSOR;
if (yych <= '2') {
if (yych <= '/') goto yy56;
- if (yych >= '1') goto yy700;
+ if (yych >= '1') goto yy725;
} else {
- if (yych <= '3') goto yy701;
- if (yych <= '9') goto yy696;
+ if (yych <= '3') goto yy726;
+ if (yych <= '9') goto yy721;
goto yy56;
}
yyaccept = 12;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'n') {
- if (yych <= '/') goto yy596;
- if (yych <= '9') goto yy702;
- if (yych <= 'm') goto yy596;
- goto yy600;
+ if (yych <= '/') goto yy621;
+ if (yych <= '9') goto yy727;
+ if (yych <= 'm') goto yy621;
+ goto yy625;
} else {
if (yych <= 'r') {
- if (yych <= 'q') goto yy596;
- goto yy601;
+ if (yych <= 'q') goto yy621;
+ goto yy626;
} else {
- if (yych <= 's') goto yy599;
- if (yych <= 't') goto yy602;
- goto yy596;
+ if (yych <= 's') goto yy624;
+ if (yych <= 't') goto yy627;
+ goto yy621;
}
}
-yy700:
+yy725:
yyaccept = 12;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'n') {
- if (yych <= '/') goto yy596;
- if (yych <= '9') goto yy702;
- if (yych <= 'm') goto yy596;
- goto yy600;
+ if (yych <= '/') goto yy621;
+ if (yych <= '9') goto yy727;
+ if (yych <= 'm') goto yy621;
+ goto yy625;
} else {
if (yych <= 'r') {
- if (yych <= 'q') goto yy596;
- goto yy601;
+ if (yych <= 'q') goto yy621;
+ goto yy626;
} else {
- if (yych <= 's') goto yy599;
- if (yych <= 't') goto yy602;
- goto yy596;
+ if (yych <= 's') goto yy624;
+ if (yych <= 't') goto yy627;
+ goto yy621;
}
}
-yy701:
+yy726:
yyaccept = 12;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'n') {
- if (yych <= '/') goto yy596;
- if (yych <= '1') goto yy702;
- if (yych <= 'm') goto yy596;
- goto yy600;
+ if (yych <= '/') goto yy621;
+ if (yych <= '1') goto yy727;
+ if (yych <= 'm') goto yy621;
+ goto yy625;
} else {
if (yych <= 'r') {
- if (yych <= 'q') goto yy596;
- goto yy601;
+ if (yych <= 'q') goto yy621;
+ goto yy626;
} else {
- if (yych <= 's') goto yy599;
- if (yych <= 't') goto yy602;
- goto yy596;
+ if (yych <= 's') goto yy624;
+ if (yych <= 't') goto yy627;
+ goto yy621;
}
}
-yy702:
+yy727:
yyaccept = 14;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'q') {
- if (yych == 'n') goto yy600;
+ if (yych == 'n') goto yy625;
} else {
- if (yych <= 'r') goto yy601;
- if (yych <= 's') goto yy599;
- if (yych <= 't') goto yy602;
+ if (yych <= 'r') goto yy626;
+ if (yych <= 's') goto yy624;
+ if (yych <= 't') goto yy627;
}
-yy703:
-#line 1255 "ext/date/lib/parse_date.re"
+yy728:
+#line 1298 "ext/date/lib/parse_date.re"
{
int length = 0;
DEBUG_OUTPUT("iso8601date2");
@@ -10930,38 +11426,38 @@ yy703:
TIMELIB_DEINIT;
return TIMELIB_ISO_DATE;
}
-#line 10934 "ext/date/lib/parse_date.c"
-yy704:
+#line 11430 "ext/date/lib/parse_date.c"
+yy729:
yych = *++YYCURSOR;
- if (yych == 'C') goto yy705;
+ if (yych == 'C') goto yy730;
if (yych != 'c') goto yy56;
-yy705:
+yy730:
yych = *++YYCURSOR;
if (yych != '-') goto yy56;
-yy706:
+yy731:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '0') goto yy707;
- if (yych <= '2') goto yy708;
- if (yych <= '3') goto yy709;
+ if (yych <= '0') goto yy732;
+ if (yych <= '2') goto yy733;
+ if (yych <= '3') goto yy734;
goto yy56;
-yy707:
+yy732:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '9') goto yy710;
+ if (yych <= '9') goto yy735;
goto yy56;
-yy708:
+yy733:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '9') goto yy710;
+ if (yych <= '9') goto yy735;
goto yy56;
-yy709:
+yy734:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
if (yych >= '2') goto yy56;
-yy710:
+yy735:
++YYCURSOR;
-#line 1496 "ext/date/lib/parse_date.re"
+#line 1539 "ext/date/lib/parse_date.re"
{
int length = 0;
DEBUG_OUTPUT("pgtextreverse");
@@ -10974,132 +11470,160 @@ yy710:
TIMELIB_DEINIT;
return TIMELIB_PG_TEXT;
}
-#line 10978 "ext/date/lib/parse_date.c"
-yy712:
+#line 11474 "ext/date/lib/parse_date.c"
+yy737:
yych = *++YYCURSOR;
- if (yych == 'V') goto yy705;
- if (yych == 'v') goto yy705;
+ if (yych == 'V') goto yy730;
+ if (yych == 'v') goto yy730;
goto yy56;
-yy713:
+yy738:
yych = *++YYCURSOR;
- if (yych == 'T') goto yy705;
- if (yych == 't') goto yy705;
+ if (yych == 'T') goto yy730;
+ if (yych == 't') goto yy730;
goto yy56;
-yy714:
+yy739:
yych = *++YYCURSOR;
- if (yych == 'P') goto yy715;
+ if (yych == 'P') goto yy740;
if (yych != 'p') goto yy56;
-yy715:
+yy740:
yych = *++YYCURSOR;
if (yych <= 'S') {
- if (yych == '-') goto yy706;
+ if (yych == '-') goto yy731;
goto yy56;
} else {
- if (yych <= 'T') goto yy705;
- if (yych == 't') goto yy705;
+ if (yych <= 'T') goto yy730;
+ if (yych == 't') goto yy730;
goto yy56;
}
-yy716:
+yy741:
yych = *++YYCURSOR;
- if (yych == 'G') goto yy705;
- if (yych == 'g') goto yy705;
+ if (yych == 'G') goto yy730;
+ if (yych == 'g') goto yy730;
goto yy56;
-yy717:
+yy742:
yych = *++YYCURSOR;
- if (yych == 'R') goto yy705;
- if (yych == 'r') goto yy705;
+ if (yych == 'R') goto yy730;
+ if (yych == 'r') goto yy730;
goto yy56;
-yy718:
+yy743:
yych = *++YYCURSOR;
if (yych <= 'Y') {
- if (yych == 'R') goto yy705;
+ if (yych == 'R') goto yy730;
if (yych <= 'X') goto yy56;
- goto yy705;
+ goto yy730;
} else {
if (yych <= 'r') {
if (yych <= 'q') goto yy56;
- goto yy705;
+ goto yy730;
} else {
- if (yych == 'y') goto yy705;
+ if (yych == 'y') goto yy730;
goto yy56;
}
}
-yy719:
+yy744:
yych = *++YYCURSOR;
- if (yych == 'B') goto yy705;
- if (yych == 'b') goto yy705;
+ if (yych == 'B') goto yy730;
+ if (yych == 'b') goto yy730;
goto yy56;
-yy720:
+yy745:
yych = *++YYCURSOR;
if (yych <= 'N') {
- if (yych == 'L') goto yy705;
+ if (yych == 'L') goto yy730;
if (yych <= 'M') goto yy56;
- goto yy705;
+ goto yy730;
} else {
if (yych <= 'l') {
if (yych <= 'k') goto yy56;
- goto yy705;
+ goto yy730;
} else {
- if (yych == 'n') goto yy705;
+ if (yych == 'n') goto yy730;
goto yy56;
}
}
-yy721:
+yy746:
yych = *++YYCURSOR;
- if (yych == 'N') goto yy705;
- if (yych == 'n') goto yy705;
+ if (yych == 'N') goto yy730;
+ if (yych == 'n') goto yy730;
goto yy56;
-yy722:
+yy747:
yyaccept = 15;
yych = *(YYMARKER = ++YYCURSOR);
- switch (yych) {
- case '\t':
- case ' ':
- case 'A':
- case 'D':
- case 'F':
- case 'H':
- case 'I':
- case 'J':
- case 'M':
- case 'N':
- case 'O':
- case 'S':
- case 'T':
- case 'V':
- case 'X':
- case 'Y':
- case 'a':
- case 'd':
- case 'f':
- case 'h':
- case 'j':
- case 'm':
- case 'n':
- case 'o':
- case 's':
- case 't':
- case 'w':
- case 'y': goto yy730;
- case '-': goto yy727;
- case '.': goto yy731;
- case '/': goto yy728;
- case '0': goto yy744;
- case '1': goto yy745;
- case '2': goto yy747;
- case '3': goto yy748;
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9': goto yy54;
- case ':': goto yy746;
- case 'W': goto yy749;
- default: goto yy723;
+ if (yych <= 'O') {
+ if (yych <= '3') {
+ if (yych <= '-') {
+ if (yych <= 0x1F) {
+ if (yych == '\t') goto yy755;
+ } else {
+ if (yych <= ' ') goto yy755;
+ if (yych >= '-') goto yy752;
+ }
+ } else {
+ if (yych <= '0') {
+ if (yych <= '.') goto yy756;
+ if (yych <= '/') goto yy753;
+ goto yy769;
+ } else {
+ if (yych <= '1') goto yy770;
+ if (yych <= '2') goto yy772;
+ goto yy773;
+ }
+ }
+ } else {
+ if (yych <= 'D') {
+ if (yych <= '@') {
+ if (yych <= '9') goto yy54;
+ if (yych <= ':') goto yy771;
+ } else {
+ if (yych <= 'A') goto yy755;
+ if (yych >= 'D') goto yy755;
+ }
+ } else {
+ if (yych <= 'G') {
+ if (yych == 'F') goto yy755;
+ } else {
+ if (yych <= 'J') goto yy755;
+ if (yych >= 'M') goto yy755;
+ }
+ }
+ }
+ } else {
+ if (yych <= 'h') {
+ if (yych <= 'a') {
+ if (yych <= 'W') {
+ if (yych <= 'R') goto yy748;
+ if (yych <= 'V') goto yy755;
+ goto yy774;
+ } else {
+ if (yych <= 'Y') goto yy755;
+ if (yych >= 'a') goto yy755;
+ }
+ } else {
+ if (yych <= 'e') {
+ if (yych == 'd') goto yy755;
+ } else {
+ if (yych != 'g') goto yy755;
+ }
+ }
+ } else {
+ if (yych <= 'u') {
+ if (yych <= 'l') {
+ if (yych == 'j') goto yy755;
+ } else {
+ if (yych <= 'o') goto yy755;
+ if (yych >= 's') goto yy755;
+ }
+ } else {
+ if (yych <= 'x') {
+ if (yych == 'w') goto yy755;
+ } else {
+ if (yych <= 'y') goto yy755;
+ if (yych == 0xC2) goto yy755;
+ }
+ }
+ }
}
-yy723:
-#line 1531 "ext/date/lib/parse_date.re"
+yy748:
+#line 1574 "ext/date/lib/parse_date.re"
{
DEBUG_OUTPUT("year4");
TIMELIB_INIT;
@@ -11107,12 +11631,12 @@ yy723:
TIMELIB_DEINIT;
return TIMELIB_CLF;
}
-#line 11111 "ext/date/lib/parse_date.c"
-yy724:
+#line 11635 "ext/date/lib/parse_date.c"
+yy749:
yych = *++YYCURSOR;
switch (yych) {
- case '0': goto yy725;
- case '1': goto yy726;
+ case '0': goto yy750;
+ case '1': goto yy751;
case '2':
case '3':
case '4':
@@ -11120,42 +11644,42 @@ yy724:
case '6':
case '7':
case '8':
- case '9': goto yy692;
+ case '9': goto yy717;
case 'A':
- case 'a': goto yy685;
+ case 'a': goto yy710;
case 'D':
- case 'd': goto yy689;
+ case 'd': goto yy714;
case 'F':
- case 'f': goto yy683;
+ case 'f': goto yy708;
case 'J':
- case 'j': goto yy682;
+ case 'j': goto yy707;
case 'M':
- case 'm': goto yy684;
+ case 'm': goto yy709;
case 'N':
- case 'n': goto yy688;
+ case 'n': goto yy713;
case 'O':
- case 'o': goto yy687;
+ case 'o': goto yy712;
case 'S':
- case 's': goto yy686;
+ case 's': goto yy711;
default: goto yy56;
}
-yy725:
+yy750:
yych = *++YYCURSOR;
- if (yych == '-') goto yy693;
+ if (yych == '-') goto yy718;
if (yych <= '/') goto yy56;
- if (yych <= '9') goto yy692;
+ if (yych <= '9') goto yy717;
goto yy56;
-yy726:
+yy751:
yych = *++YYCURSOR;
- if (yych == '-') goto yy693;
+ if (yych == '-') goto yy718;
if (yych <= '/') goto yy56;
- if (yych <= '2') goto yy692;
+ if (yych <= '2') goto yy717;
goto yy56;
-yy727:
+yy752:
yych = *++YYCURSOR;
switch (yych) {
- case '0': goto yy912;
- case '1': goto yy914;
+ case '0': goto yy937;
+ case '1': goto yy939;
case '2':
case '3':
case '4':
@@ -11163,89 +11687,146 @@ yy727:
case '6':
case '7':
case '8':
- case '9': goto yy915;
+ case '9': goto yy940;
case 'A':
- case 'a': goto yy906;
+ case 'a': goto yy931;
case 'D':
- case 'd': goto yy910;
+ case 'd': goto yy935;
case 'F':
- case 'f': goto yy904;
+ case 'f': goto yy929;
case 'J':
- case 'j': goto yy903;
+ case 'j': goto yy928;
case 'M':
- case 'm': goto yy905;
+ case 'm': goto yy930;
case 'N':
- case 'n': goto yy909;
+ case 'n': goto yy934;
case 'O':
- case 'o': goto yy908;
+ case 'o': goto yy933;
case 'S':
- case 's': goto yy907;
- case 'W': goto yy911;
- default: goto yy878;
+ case 's': goto yy932;
+ case 'W': goto yy936;
+ default: goto yy903;
}
-yy728:
+yy753:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '0') goto yy886;
- if (yych <= '1') goto yy887;
- if (yych <= '9') goto yy888;
+ if (yych <= '0') goto yy911;
+ if (yych <= '1') goto yy912;
+ if (yych <= '9') goto yy913;
goto yy56;
-yy729:
+yy754:
++YYCURSOR;
- if ((YYLIMIT - YYCURSOR) < 11) YYFILL(11);
+ if ((YYLIMIT - YYCURSOR) < 12) YYFILL(12);
yych = *YYCURSOR;
-yy730:
- switch (yych) {
- case '\t':
- case ' ': goto yy729;
- case '-':
- case '.': goto yy877;
- case 'A':
- case 'a': goto yy739;
- case 'D':
- case 'd': goto yy743;
- case 'F':
- case 'f': goto yy737;
- case 'H':
- case 'h': goto yy63;
- case 'I': goto yy732;
- case 'J':
- case 'j': goto yy736;
- case 'M':
- case 'm': goto yy738;
- case 'N':
- case 'n': goto yy742;
- case 'O':
- case 'o': goto yy741;
- case 'S':
- case 's': goto yy740;
- case 'T':
- case 't': goto yy68;
- case 'V': goto yy734;
- case 'W':
- case 'w': goto yy67;
- case 'X': goto yy735;
- case 'Y':
- case 'y': goto yy66;
- default: goto yy56;
+yy755:
+ if (yych <= 'W') {
+ if (yych <= 'G') {
+ if (yych <= '.') {
+ if (yych <= 0x1F) {
+ if (yych == '\t') goto yy754;
+ goto yy56;
+ } else {
+ if (yych <= ' ') goto yy754;
+ if (yych <= ',') goto yy56;
+ goto yy902;
+ }
+ } else {
+ if (yych <= 'C') {
+ if (yych == 'A') goto yy764;
+ goto yy56;
+ } else {
+ if (yych <= 'D') goto yy768;
+ if (yych == 'F') goto yy762;
+ goto yy56;
+ }
+ }
+ } else {
+ if (yych <= 'N') {
+ if (yych <= 'J') {
+ if (yych <= 'H') goto yy65;
+ if (yych <= 'I') goto yy757;
+ goto yy761;
+ } else {
+ if (yych <= 'L') goto yy56;
+ if (yych <= 'M') goto yy763;
+ goto yy767;
+ }
+ } else {
+ if (yych <= 'S') {
+ if (yych <= 'O') goto yy766;
+ if (yych <= 'R') goto yy56;
+ goto yy765;
+ } else {
+ if (yych <= 'T') goto yy70;
+ if (yych <= 'U') goto yy63;
+ if (yych <= 'V') goto yy759;
+ goto yy69;
+ }
+ }
+ }
+ } else {
+ if (yych <= 'l') {
+ if (yych <= 'd') {
+ if (yych <= '`') {
+ if (yych <= 'X') goto yy760;
+ if (yych <= 'Y') goto yy68;
+ goto yy56;
+ } else {
+ if (yych <= 'a') goto yy764;
+ if (yych <= 'c') goto yy56;
+ goto yy768;
+ }
+ } else {
+ if (yych <= 'g') {
+ if (yych == 'f') goto yy762;
+ goto yy56;
+ } else {
+ if (yych <= 'h') goto yy65;
+ if (yych == 'j') goto yy761;
+ goto yy56;
+ }
+ }
+ } else {
+ if (yych <= 'u') {
+ if (yych <= 'o') {
+ if (yych <= 'm') goto yy763;
+ if (yych <= 'n') goto yy767;
+ goto yy766;
+ } else {
+ if (yych <= 'r') goto yy56;
+ if (yych <= 's') goto yy765;
+ if (yych <= 't') goto yy70;
+ goto yy63;
+ }
+ } else {
+ if (yych <= 'x') {
+ if (yych == 'w') goto yy69;
+ goto yy56;
+ } else {
+ if (yych <= 'y') goto yy68;
+ if (yych == 0xC2) goto yy62;
+ goto yy56;
+ }
+ }
+ }
}
-yy731:
+yy756:
yych = *++YYCURSOR;
- if (yych <= '/') goto yy878;
- if (yych <= '0') goto yy870;
- if (yych <= '2') goto yy871;
- if (yych <= '3') goto yy872;
- goto yy878;
-yy732:
+ if (yych <= '/') goto yy903;
+ if (yych <= '0') goto yy895;
+ if (yych <= '2') goto yy896;
+ if (yych <= '3') goto yy897;
+ goto yy903;
+yy757:
++YYCURSOR;
if ((yych = *YYCURSOR) <= 'U') {
- if (yych == 'I') goto yy869;
+ if (yych == 'I') goto yy894;
} else {
- if (yych == 'W') goto yy733;
- if (yych <= 'X') goto yy823;
+ if (yych == 'W') goto yy758;
+ if (yych <= 'X') goto yy848;
}
-yy733:
-#line 1352 "ext/date/lib/parse_date.re"
+yy758:
+#line 1395 "ext/date/lib/parse_date.re"
{
int length = 0;
DEBUG_OUTPUT("datenodayrev");
@@ -11258,201 +11839,207 @@ yy733:
TIMELIB_DEINIT;
return TIMELIB_DATE_NO_DAY;
}
-#line 11262 "ext/date/lib/parse_date.c"
-yy734:
+#line 11843 "ext/date/lib/parse_date.c"
+yy759:
yych = *++YYCURSOR;
- if (yych == 'I') goto yy867;
- goto yy733;
-yy735:
+ if (yych == 'I') goto yy892;
+ goto yy758;
+yy760:
yych = *++YYCURSOR;
- if (yych == 'I') goto yy866;
- goto yy733;
-yy736:
+ if (yych == 'I') goto yy891;
+ goto yy758;
+yy761:
yych = *++YYCURSOR;
if (yych <= 'U') {
- if (yych == 'A') goto yy859;
+ if (yych == 'A') goto yy884;
if (yych <= 'T') goto yy56;
- goto yy858;
+ goto yy883;
} else {
if (yych <= 'a') {
if (yych <= '`') goto yy56;
- goto yy859;
+ goto yy884;
} else {
- if (yych == 'u') goto yy858;
+ if (yych == 'u') goto yy883;
goto yy56;
}
}
-yy737:
+yy762:
yych = *++YYCURSOR;
if (yych <= 'R') {
if (yych <= 'N') {
- if (yych == 'E') goto yy852;
+ if (yych == 'E') goto yy877;
goto yy56;
} else {
- if (yych <= 'O') goto yy98;
+ if (yych <= 'O') goto yy100;
if (yych <= 'Q') goto yy56;
- goto yy97;
+ goto yy99;
}
} else {
if (yych <= 'n') {
- if (yych == 'e') goto yy852;
+ if (yych == 'e') goto yy877;
goto yy56;
} else {
- if (yych <= 'o') goto yy98;
- if (yych == 'r') goto yy97;
+ if (yych <= 'o') goto yy100;
+ if (yych == 'r') goto yy99;
goto yy56;
}
}
-yy738:
+yy763:
yych = *++YYCURSOR;
- if (yych <= 'O') {
- if (yych <= 'H') {
- if (yych == 'A') goto yy849;
- goto yy56;
+ if (yych <= 'S') {
+ if (yych <= 'I') {
+ if (yych == 'A') goto yy874;
+ if (yych <= 'H') goto yy56;
+ goto yy138;
} else {
- if (yych <= 'I') goto yy117;
- if (yych <= 'N') goto yy56;
- goto yy116;
+ if (yych == 'O') goto yy137;
+ if (yych <= 'R') goto yy56;
+ goto yy139;
}
} else {
- if (yych <= 'h') {
- if (yych == 'a') goto yy849;
- goto yy56;
+ if (yych <= 'i') {
+ if (yych == 'a') goto yy874;
+ if (yych <= 'h') goto yy56;
+ goto yy138;
} else {
- if (yych <= 'i') goto yy117;
- if (yych == 'o') goto yy116;
- goto yy56;
+ if (yych <= 'o') {
+ if (yych <= 'n') goto yy56;
+ goto yy137;
+ } else {
+ if (yych == 's') goto yy139;
+ goto yy56;
+ }
}
}
-yy739:
+yy764:
yych = *++YYCURSOR;
if (yych <= 'U') {
- if (yych == 'P') goto yy843;
+ if (yych == 'P') goto yy868;
if (yych <= 'T') goto yy56;
- goto yy842;
+ goto yy867;
} else {
if (yych <= 'p') {
if (yych <= 'o') goto yy56;
- goto yy843;
+ goto yy868;
} else {
- if (yych == 'u') goto yy842;
+ if (yych == 'u') goto yy867;
goto yy56;
}
}
-yy740:
+yy765:
yych = *++YYCURSOR;
if (yych <= 'U') {
if (yych <= 'D') {
- if (yych == 'A') goto yy126;
+ if (yych == 'A') goto yy119;
goto yy56;
} else {
- if (yych <= 'E') goto yy835;
+ if (yych <= 'E') goto yy860;
if (yych <= 'T') goto yy56;
- goto yy125;
+ goto yy118;
}
} else {
if (yych <= 'd') {
- if (yych == 'a') goto yy126;
+ if (yych == 'a') goto yy119;
goto yy56;
} else {
- if (yych <= 'e') goto yy835;
- if (yych == 'u') goto yy125;
+ if (yych <= 'e') goto yy860;
+ if (yych == 'u') goto yy118;
goto yy56;
}
}
-yy741:
+yy766:
yych = *++YYCURSOR;
- if (yych == 'C') goto yy830;
- if (yych == 'c') goto yy830;
+ if (yych == 'C') goto yy855;
+ if (yych == 'c') goto yy855;
goto yy56;
-yy742:
+yy767:
yych = *++YYCURSOR;
- if (yych == 'O') goto yy824;
- if (yych == 'o') goto yy824;
+ if (yych == 'O') goto yy849;
+ if (yych == 'o') goto yy849;
goto yy56;
-yy743:
+yy768:
yych = *++YYCURSOR;
if (yych <= 'E') {
- if (yych == 'A') goto yy113;
+ if (yych == 'A') goto yy115;
if (yych <= 'D') goto yy56;
- goto yy817;
+ goto yy842;
} else {
if (yych <= 'a') {
if (yych <= '`') goto yy56;
- goto yy113;
+ goto yy115;
} else {
- if (yych == 'e') goto yy817;
+ if (yych == 'e') goto yy842;
goto yy56;
}
}
-yy744:
+yy769:
yych = *++YYCURSOR;
if (yych <= '/') goto yy60;
- if (yych <= '0') goto yy814;
- if (yych <= '9') goto yy815;
+ if (yych <= '0') goto yy839;
+ if (yych <= '9') goto yy840;
goto yy60;
-yy745:
+yy770:
yych = *++YYCURSOR;
if (yych <= '/') goto yy60;
- if (yych <= '2') goto yy783;
- if (yych <= '9') goto yy762;
+ if (yych <= '2') goto yy808;
+ if (yych <= '9') goto yy787;
goto yy60;
-yy746:
+yy771:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '0') goto yy763;
- if (yych <= '1') goto yy764;
+ if (yych <= '0') goto yy788;
+ if (yych <= '1') goto yy789;
goto yy56;
-yy747:
+yy772:
yych = *++YYCURSOR;
if (yych <= '/') goto yy60;
- if (yych <= '9') goto yy762;
+ if (yych <= '9') goto yy787;
goto yy60;
-yy748:
+yy773:
yych = *++YYCURSOR;
if (yych <= '/') goto yy60;
- if (yych <= '5') goto yy758;
- if (yych <= '6') goto yy759;
+ if (yych <= '5') goto yy783;
+ if (yych <= '6') goto yy784;
if (yych <= '9') goto yy54;
goto yy60;
-yy749:
+yy774:
yych = *++YYCURSOR;
if (yych <= '5') {
if (yych <= '/') goto yy56;
- if (yych <= '0') goto yy750;
- if (yych <= '4') goto yy751;
- goto yy752;
+ if (yych <= '0') goto yy775;
+ if (yych <= '4') goto yy776;
+ goto yy777;
} else {
if (yych <= 'E') {
if (yych <= 'D') goto yy56;
- goto yy82;
+ goto yy84;
} else {
- if (yych == 'e') goto yy82;
+ if (yych == 'e') goto yy84;
goto yy56;
}
}
-yy750:
+yy775:
yych = *++YYCURSOR;
if (yych <= '0') goto yy56;
- if (yych <= '9') goto yy753;
+ if (yych <= '9') goto yy778;
goto yy56;
-yy751:
+yy776:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '9') goto yy753;
+ if (yych <= '9') goto yy778;
goto yy56;
-yy752:
+yy777:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
if (yych >= '4') goto yy56;
-yy753:
+yy778:
yyaccept = 16;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych == '-') goto yy755;
- if (yych <= '/') goto yy754;
- if (yych <= '7') goto yy756;
-yy754:
-#line 1463 "ext/date/lib/parse_date.re"
+ if (yych == '-') goto yy780;
+ if (yych <= '/') goto yy779;
+ if (yych <= '7') goto yy781;
+yy779:
+#line 1506 "ext/date/lib/parse_date.re"
{
timelib_sll w, d;
DEBUG_OUTPUT("isoweek");
@@ -11470,14 +12057,14 @@ yy754:
TIMELIB_DEINIT;
return TIMELIB_ISO_WEEK;
}
-#line 11474 "ext/date/lib/parse_date.c"
-yy755:
+#line 12061 "ext/date/lib/parse_date.c"
+yy780:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
if (yych >= '8') goto yy56;
-yy756:
+yy781:
++YYCURSOR;
-#line 1444 "ext/date/lib/parse_date.re"
+#line 1487 "ext/date/lib/parse_date.re"
{
timelib_sll w, d;
DEBUG_OUTPUT("isoweekday");
@@ -11495,25 +12082,25 @@ yy756:
TIMELIB_DEINIT;
return TIMELIB_ISO_WEEK;
}
-#line 11499 "ext/date/lib/parse_date.c"
-yy758:
+#line 12086 "ext/date/lib/parse_date.c"
+yy783:
yych = *++YYCURSOR;
if (yych <= '/') goto yy60;
- if (yych <= '9') goto yy760;
+ if (yych <= '9') goto yy785;
goto yy60;
-yy759:
+yy784:
yych = *++YYCURSOR;
if (yych <= '/') goto yy60;
- if (yych <= '6') goto yy760;
+ if (yych <= '6') goto yy785;
if (yych <= '9') goto yy54;
goto yy60;
-yy760:
+yy785:
yyaccept = 17;
yych = *(YYMARKER = ++YYCURSOR);
if (yybm[0+yych] & 2) {
goto yy54;
}
- if (yych <= 'W') {
+ if (yych <= 'X') {
if (yych <= 'F') {
if (yych <= ' ') {
if (yych == '\t') goto yy60;
@@ -11527,35 +12114,41 @@ yy760:
if (yych == 'H') goto yy60;
if (yych >= 'M') goto yy60;
} else {
- if (yych <= 'R') goto yy761;
- if (yych <= 'T') goto yy60;
- if (yych >= 'W') goto yy60;
+ if (yych <= 'U') {
+ if (yych >= 'S') goto yy60;
+ } else {
+ if (yych == 'W') goto yy60;
+ }
}
}
} else {
- if (yych <= 'h') {
- if (yych <= 'd') {
- if (yych == 'Y') goto yy60;
- if (yych >= 'd') goto yy60;
+ if (yych <= 'm') {
+ if (yych <= 'e') {
+ if (yych <= 'Y') goto yy60;
+ if (yych == 'd') goto yy60;
} else {
- if (yych == 'f') goto yy60;
- if (yych >= 'h') goto yy60;
+ if (yych <= 'g') {
+ if (yych <= 'f') goto yy60;
+ } else {
+ if (yych <= 'h') goto yy60;
+ if (yych >= 'm') goto yy60;
+ }
}
} else {
- if (yych <= 't') {
- if (yych == 'm') goto yy60;
- if (yych >= 's') goto yy60;
+ if (yych <= 'w') {
+ if (yych <= 'r') goto yy786;
+ if (yych != 'v') goto yy60;
} else {
- if (yych <= 'w') {
- if (yych >= 'w') goto yy60;
+ if (yych <= 'y') {
+ if (yych >= 'y') goto yy60;
} else {
- if (yych == 'y') goto yy60;
+ if (yych == 0xC2) goto yy60;
}
}
}
}
-yy761:
-#line 1430 "ext/date/lib/parse_date.re"
+yy786:
+#line 1473 "ext/date/lib/parse_date.re"
{
int length = 0;
DEBUG_OUTPUT("pgydotd");
@@ -11568,62 +12161,62 @@ yy761:
TIMELIB_DEINIT;
return TIMELIB_PG_YEARDAY;
}
-#line 11572 "ext/date/lib/parse_date.c"
-yy762:
+#line 12165 "ext/date/lib/parse_date.c"
+yy787:
yych = *++YYCURSOR;
if (yych <= '/') goto yy60;
- if (yych <= '9') goto yy760;
+ if (yych <= '9') goto yy785;
goto yy60;
-yy763:
+yy788:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '9') goto yy765;
+ if (yych <= '9') goto yy790;
goto yy56;
-yy764:
+yy789:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
if (yych >= '3') goto yy56;
-yy765:
+yy790:
yych = *++YYCURSOR;
if (yych != ':') goto yy56;
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '0') goto yy767;
- if (yych <= '2') goto yy768;
- if (yych <= '3') goto yy769;
+ if (yych <= '0') goto yy792;
+ if (yych <= '2') goto yy793;
+ if (yych <= '3') goto yy794;
goto yy56;
-yy767:
+yy792:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '9') goto yy770;
+ if (yych <= '9') goto yy795;
goto yy56;
-yy768:
+yy793:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '9') goto yy770;
+ if (yych <= '9') goto yy795;
goto yy56;
-yy769:
+yy794:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
if (yych >= '2') goto yy56;
-yy770:
+yy795:
yych = *++YYCURSOR;
if (yych != ' ') goto yy56;
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '1') goto yy772;
- if (yych <= '2') goto yy773;
+ if (yych <= '1') goto yy797;
+ if (yych <= '2') goto yy798;
goto yy56;
-yy772:
+yy797:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '9') goto yy774;
+ if (yych <= '9') goto yy799;
goto yy56;
-yy773:
+yy798:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
if (yych >= '5') goto yy56;
-yy774:
+yy799:
yych = *++YYCURSOR;
if (yych != ':') goto yy56;
yych = *++YYCURSOR;
@@ -11636,21 +12229,21 @@ yy774:
if (yych != ':') goto yy56;
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '5') goto yy779;
- if (yych <= '6') goto yy780;
+ if (yych <= '5') goto yy804;
+ if (yych <= '6') goto yy805;
goto yy56;
-yy779:
+yy804:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '9') goto yy781;
+ if (yych <= '9') goto yy806;
goto yy56;
-yy780:
+yy805:
yych = *++YYCURSOR;
if (yych != '0') goto yy56;
-yy781:
+yy806:
++YYCURSOR;
-yy782:
-#line 1404 "ext/date/lib/parse_date.re"
+yy807:
+#line 1447 "ext/date/lib/parse_date.re"
{
int tz_not_found;
DEBUG_OUTPUT("xmlrpc | xmlrpcnocolon | soap | wddx | exif");
@@ -11675,263 +12268,296 @@ yy782:
TIMELIB_DEINIT;
return TIMELIB_XMLRPC_SOAP;
}
-#line 11679 "ext/date/lib/parse_date.c"
-yy783:
+#line 12272 "ext/date/lib/parse_date.c"
+yy808:
yych = *++YYCURSOR;
if (yych <= '2') {
if (yych <= '/') goto yy60;
- if (yych >= '1') goto yy785;
+ if (yych >= '1') goto yy810;
} else {
- if (yych <= '3') goto yy786;
- if (yych <= '9') goto yy760;
+ if (yych <= '3') goto yy811;
+ if (yych <= '9') goto yy785;
goto yy60;
}
-yy784:
+yy809:
yyaccept = 17;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych <= 'V') {
- if (yych <= 'D') {
+ if (yych <= 'W') {
+ if (yych <= 'E') {
if (yych <= ' ') {
if (yych == '\t') goto yy60;
- if (yych <= 0x1F) goto yy761;
+ if (yych <= 0x1F) goto yy786;
goto yy60;
} else {
- if (yych <= '/') goto yy761;
- if (yych <= '9') goto yy787;
- if (yych <= 'C') goto yy761;
- goto yy60;
+ if (yych <= '9') {
+ if (yych <= '/') goto yy786;
+ goto yy812;
+ } else {
+ if (yych == 'D') goto yy60;
+ goto yy786;
+ }
}
} else {
- if (yych <= 'H') {
- if (yych == 'F') goto yy60;
- if (yych <= 'G') goto yy761;
- goto yy60;
+ if (yych <= 'L') {
+ if (yych == 'G') goto yy786;
+ if (yych <= 'H') goto yy60;
+ goto yy786;
} else {
- if (yych <= 'M') {
- if (yych <= 'L') goto yy761;
- goto yy60;
+ if (yych <= 'R') {
+ if (yych <= 'M') goto yy60;
+ goto yy786;
} else {
- if (yych <= 'R') goto yy761;
- if (yych <= 'T') goto yy60;
- goto yy761;
+ if (yych == 'V') goto yy786;
+ goto yy60;
}
}
}
} else {
- if (yych <= 'h') {
- if (yych <= 'c') {
- if (yych == 'X') goto yy761;
- if (yych <= 'Y') goto yy60;
- goto yy761;
+ if (yych <= 'l') {
+ if (yych <= 'd') {
+ if (yych == 'Y') goto yy60;
+ if (yych <= 'c') goto yy786;
+ goto yy60;
} else {
- if (yych <= 'e') {
- if (yych <= 'd') goto yy60;
- goto yy761;
- } else {
- if (yych == 'g') goto yy761;
+ if (yych <= 'f') {
+ if (yych <= 'e') goto yy786;
goto yy60;
+ } else {
+ if (yych == 'h') goto yy60;
+ goto yy786;
}
}
} else {
- if (yych <= 't') {
- if (yych == 'm') goto yy60;
- if (yych <= 'r') goto yy761;
- goto yy60;
+ if (yych <= 'w') {
+ if (yych <= 'r') {
+ if (yych <= 'm') goto yy60;
+ goto yy786;
+ } else {
+ if (yych == 'v') goto yy786;
+ goto yy60;
+ }
} else {
- if (yych <= 'w') {
- if (yych <= 'v') goto yy761;
+ if (yych <= 'y') {
+ if (yych <= 'x') goto yy786;
goto yy60;
} else {
- if (yych == 'y') goto yy60;
- goto yy761;
+ if (yych == 0xC2) goto yy60;
+ goto yy786;
}
}
}
}
-yy785:
+yy810:
yyaccept = 17;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych <= 'V') {
- if (yych <= 'D') {
+ if (yych <= 'W') {
+ if (yych <= 'E') {
if (yych <= ' ') {
if (yych == '\t') goto yy60;
- if (yych <= 0x1F) goto yy761;
+ if (yych <= 0x1F) goto yy786;
goto yy60;
} else {
- if (yych <= '/') goto yy761;
- if (yych <= '9') goto yy787;
- if (yych <= 'C') goto yy761;
- goto yy60;
+ if (yych <= '9') {
+ if (yych <= '/') goto yy786;
+ goto yy812;
+ } else {
+ if (yych == 'D') goto yy60;
+ goto yy786;
+ }
}
} else {
- if (yych <= 'H') {
- if (yych == 'F') goto yy60;
- if (yych <= 'G') goto yy761;
- goto yy60;
+ if (yych <= 'L') {
+ if (yych == 'G') goto yy786;
+ if (yych <= 'H') goto yy60;
+ goto yy786;
} else {
- if (yych <= 'M') {
- if (yych <= 'L') goto yy761;
- goto yy60;
+ if (yych <= 'R') {
+ if (yych <= 'M') goto yy60;
+ goto yy786;
} else {
- if (yych <= 'R') goto yy761;
- if (yych <= 'T') goto yy60;
- goto yy761;
+ if (yych == 'V') goto yy786;
+ goto yy60;
}
}
}
} else {
- if (yych <= 'h') {
- if (yych <= 'c') {
- if (yych == 'X') goto yy761;
- if (yych <= 'Y') goto yy60;
- goto yy761;
+ if (yych <= 'l') {
+ if (yych <= 'd') {
+ if (yych == 'Y') goto yy60;
+ if (yych <= 'c') goto yy786;
+ goto yy60;
} else {
- if (yych <= 'e') {
- if (yych <= 'd') goto yy60;
- goto yy761;
- } else {
- if (yych == 'g') goto yy761;
+ if (yych <= 'f') {
+ if (yych <= 'e') goto yy786;
goto yy60;
+ } else {
+ if (yych == 'h') goto yy60;
+ goto yy786;
}
}
} else {
- if (yych <= 't') {
- if (yych == 'm') goto yy60;
- if (yych <= 'r') goto yy761;
- goto yy60;
+ if (yych <= 'w') {
+ if (yych <= 'r') {
+ if (yych <= 'm') goto yy60;
+ goto yy786;
+ } else {
+ if (yych == 'v') goto yy786;
+ goto yy60;
+ }
} else {
- if (yych <= 'w') {
- if (yych <= 'v') goto yy761;
+ if (yych <= 'y') {
+ if (yych <= 'x') goto yy786;
goto yy60;
} else {
- if (yych == 'y') goto yy60;
- goto yy761;
+ if (yych == 0xC2) goto yy60;
+ goto yy786;
}
}
}
}
-yy786:
+yy811:
yyaccept = 17;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych <= 'V') {
+ if (yych <= 'W') {
if (yych <= 'D') {
if (yych <= ' ') {
if (yych == '\t') goto yy60;
- if (yych <= 0x1F) goto yy761;
+ if (yych <= 0x1F) goto yy786;
goto yy60;
} else {
if (yych <= '1') {
- if (yych <= '/') goto yy761;
+ if (yych <= '/') goto yy786;
} else {
if (yych <= '9') goto yy54;
- if (yych <= 'C') goto yy761;
+ if (yych <= 'C') goto yy786;
goto yy60;
}
}
} else {
- if (yych <= 'H') {
- if (yych == 'F') goto yy60;
- if (yych <= 'G') goto yy761;
- goto yy60;
- } else {
- if (yych <= 'M') {
- if (yych <= 'L') goto yy761;
+ if (yych <= 'L') {
+ if (yych <= 'F') {
+ if (yych <= 'E') goto yy786;
goto yy60;
} else {
- if (yych <= 'R') goto yy761;
- if (yych <= 'T') goto yy60;
- goto yy761;
+ if (yych == 'H') goto yy60;
+ goto yy786;
+ }
+ } else {
+ if (yych <= 'R') {
+ if (yych <= 'M') goto yy60;
+ goto yy786;
+ } else {
+ if (yych == 'V') goto yy786;
+ goto yy60;
}
}
}
} else {
- if (yych <= 'h') {
- if (yych <= 'c') {
- if (yych == 'X') goto yy761;
- if (yych <= 'Y') goto yy60;
- goto yy761;
+ if (yych <= 'l') {
+ if (yych <= 'd') {
+ if (yych == 'Y') goto yy60;
+ if (yych <= 'c') goto yy786;
+ goto yy60;
} else {
- if (yych <= 'e') {
- if (yych <= 'd') goto yy60;
- goto yy761;
- } else {
- if (yych == 'g') goto yy761;
+ if (yych <= 'f') {
+ if (yych <= 'e') goto yy786;
goto yy60;
+ } else {
+ if (yych == 'h') goto yy60;
+ goto yy786;
}
}
} else {
- if (yych <= 't') {
- if (yych == 'm') goto yy60;
- if (yych <= 'r') goto yy761;
- goto yy60;
+ if (yych <= 'w') {
+ if (yych <= 'r') {
+ if (yych <= 'm') goto yy60;
+ goto yy786;
+ } else {
+ if (yych == 'v') goto yy786;
+ goto yy60;
+ }
} else {
- if (yych <= 'w') {
- if (yych <= 'v') goto yy761;
+ if (yych <= 'y') {
+ if (yych <= 'x') goto yy786;
goto yy60;
} else {
- if (yych == 'y') goto yy60;
- goto yy761;
+ if (yych == 0xC2) goto yy60;
+ goto yy786;
}
}
}
}
-yy787:
+yy812:
yyaccept = 18;
yych = *(YYMARKER = ++YYCURSOR);
if (yybm[0+yych] & 2) {
goto yy54;
}
- if (yych <= 'W') {
- if (yych <= 'F') {
+ if (yych <= 'X') {
+ if (yych <= 'G') {
if (yych <= ' ') {
if (yych == '\t') goto yy59;
if (yych >= ' ') goto yy59;
} else {
- if (yych == 'D') goto yy64;
- if (yych >= 'F') goto yy65;
+ if (yych <= 'D') {
+ if (yych >= 'D') goto yy66;
+ } else {
+ if (yych == 'F') goto yy67;
+ }
}
} else {
- if (yych <= 'M') {
- if (yych == 'H') goto yy63;
- if (yych >= 'M') goto yy62;
+ if (yych <= 'S') {
+ if (yych <= 'L') {
+ if (yych <= 'H') goto yy65;
+ } else {
+ if (yych <= 'M') goto yy61;
+ if (yych >= 'S') goto yy64;
+ }
} else {
- if (yych <= 'S') {
- if (yych >= 'S') goto yy61;
+ if (yych <= 'U') {
+ if (yych <= 'T') goto yy814;
+ goto yy63;
} else {
- if (yych <= 'T') goto yy789;
- if (yych >= 'W') goto yy67;
+ if (yych == 'W') goto yy69;
}
}
}
} else {
- if (yych <= 'l') {
- if (yych <= 'd') {
- if (yych == 'Y') goto yy66;
- if (yych >= 'd') goto yy64;
+ if (yych <= 'r') {
+ if (yych <= 'f') {
+ if (yych <= 'c') {
+ if (yych <= 'Y') goto yy68;
+ } else {
+ if (yych <= 'd') goto yy66;
+ if (yych >= 'f') goto yy67;
+ }
} else {
- if (yych <= 'f') {
- if (yych >= 'f') goto yy65;
+ if (yych <= 'h') {
+ if (yych >= 'h') goto yy65;
} else {
- if (yych == 'h') goto yy63;
+ if (yych == 'm') goto yy61;
}
}
} else {
- if (yych <= 't') {
- if (yych <= 'm') goto yy62;
- if (yych <= 'r') goto yy788;
- if (yych <= 's') goto yy61;
- goto yy790;
+ if (yych <= 'w') {
+ if (yych <= 't') {
+ if (yych <= 's') goto yy64;
+ goto yy815;
+ } else {
+ if (yych <= 'u') goto yy63;
+ if (yych >= 'w') goto yy69;
+ }
} else {
- if (yych <= 'w') {
- if (yych >= 'w') goto yy67;
+ if (yych <= 'y') {
+ if (yych >= 'y') goto yy68;
} else {
- if (yych == 'y') goto yy66;
+ if (yych == 0xC2) goto yy62;
}
}
}
}
-yy788:
-#line 1392 "ext/date/lib/parse_date.re"
+yy813:
+#line 1435 "ext/date/lib/parse_date.re"
{
DEBUG_OUTPUT("datenocolon");
TIMELIB_INIT;
@@ -11942,143 +12568,143 @@ yy788:
TIMELIB_DEINIT;
return TIMELIB_DATE_NOCOLON;
}
-#line 11946 "ext/date/lib/parse_date.c"
-yy789:
+#line 12572 "ext/date/lib/parse_date.c"
+yy814:
yych = *++YYCURSOR;
if (yych <= 'H') {
if (yych <= '2') {
if (yych <= '/') goto yy56;
- if (yych <= '1') goto yy804;
- goto yy805;
+ if (yych <= '1') goto yy829;
+ goto yy830;
} else {
- if (yych <= '9') goto yy806;
+ if (yych <= '9') goto yy831;
if (yych <= 'G') goto yy56;
- goto yy69;
+ goto yy71;
}
} else {
if (yych <= 'g') {
- if (yych == 'U') goto yy70;
+ if (yych == 'U') goto yy72;
goto yy56;
} else {
- if (yych <= 'h') goto yy69;
- if (yych == 'u') goto yy70;
+ if (yych <= 'h') goto yy71;
+ if (yych == 'u') goto yy72;
goto yy56;
}
}
-yy790:
+yy815:
yych = *++YYCURSOR;
if (yych <= 'H') {
if (yych <= '2') {
if (yych <= '/') goto yy56;
- if (yych >= '2') goto yy792;
+ if (yych >= '2') goto yy817;
} else {
- if (yych <= '9') goto yy793;
+ if (yych <= '9') goto yy818;
if (yych <= 'G') goto yy56;
- goto yy69;
+ goto yy71;
}
} else {
if (yych <= 'g') {
- if (yych == 'U') goto yy70;
+ if (yych == 'U') goto yy72;
goto yy56;
} else {
- if (yych <= 'h') goto yy69;
- if (yych == 'u') goto yy70;
+ if (yych <= 'h') goto yy71;
+ if (yych == 'u') goto yy72;
goto yy56;
}
}
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '5') goto yy798;
- if (yych <= '9') goto yy793;
+ if (yych <= '5') goto yy823;
+ if (yych <= '9') goto yy818;
goto yy56;
-yy792:
+yy817:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '4') goto yy798;
- if (yych <= '5') goto yy794;
+ if (yych <= '4') goto yy823;
+ if (yych <= '5') goto yy819;
goto yy56;
-yy793:
+yy818:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
if (yych >= '6') goto yy56;
-yy794:
+yy819:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
if (yych >= ':') goto yy56;
-yy795:
+yy820:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '5') goto yy796;
- if (yych <= '6') goto yy797;
+ if (yych <= '5') goto yy821;
+ if (yych <= '6') goto yy822;
goto yy56;
-yy796:
+yy821:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '9') goto yy781;
+ if (yych <= '9') goto yy806;
goto yy56;
-yy797:
+yy822:
yych = *++YYCURSOR;
- if (yych == '0') goto yy781;
+ if (yych == '0') goto yy806;
goto yy56;
-yy798:
+yy823:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '5') goto yy799;
- if (yych <= '9') goto yy795;
+ if (yych <= '5') goto yy824;
+ if (yych <= '9') goto yy820;
goto yy56;
-yy799:
+yy824:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '5') goto yy800;
- if (yych <= '6') goto yy801;
- if (yych <= '9') goto yy795;
+ if (yych <= '5') goto yy825;
+ if (yych <= '6') goto yy826;
+ if (yych <= '9') goto yy820;
goto yy56;
-yy800:
+yy825:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '5') goto yy802;
- if (yych <= '6') goto yy803;
- if (yych <= '9') goto yy781;
+ if (yych <= '5') goto yy827;
+ if (yych <= '6') goto yy828;
+ if (yych <= '9') goto yy806;
goto yy56;
-yy801:
+yy826:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '0') goto yy802;
- if (yych <= '5') goto yy796;
- if (yych <= '6') goto yy797;
+ if (yych <= '0') goto yy827;
+ if (yych <= '5') goto yy821;
+ if (yych <= '6') goto yy822;
goto yy56;
-yy802:
+yy827:
yych = *++YYCURSOR;
- if (yych <= '/') goto yy782;
- if (yych <= '9') goto yy781;
- goto yy782;
-yy803:
+ if (yych <= '/') goto yy807;
+ if (yych <= '9') goto yy806;
+ goto yy807;
+yy828:
yych = *++YYCURSOR;
- if (yych == '0') goto yy781;
- goto yy782;
-yy804:
+ if (yych == '0') goto yy806;
+ goto yy807;
+yy829:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '5') goto yy813;
- if (yych <= '9') goto yy806;
- if (yych <= ':') goto yy807;
+ if (yych <= '5') goto yy838;
+ if (yych <= '9') goto yy831;
+ if (yych <= ':') goto yy832;
goto yy56;
-yy805:
+yy830:
yych = *++YYCURSOR;
if (yych <= '5') {
if (yych <= '/') goto yy56;
- if (yych <= '4') goto yy813;
- goto yy794;
+ if (yych <= '4') goto yy838;
+ goto yy819;
} else {
- if (yych == ':') goto yy807;
+ if (yych == ':') goto yy832;
goto yy56;
}
-yy806:
+yy831:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '5') goto yy794;
+ if (yych <= '5') goto yy819;
if (yych != ':') goto yy56;
-yy807:
+yy832:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
if (yych >= '6') goto yy56;
@@ -12089,654 +12715,654 @@ yy807:
if (yych != ':') goto yy56;
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '5') goto yy811;
- if (yych <= '6') goto yy812;
+ if (yych <= '5') goto yy836;
+ if (yych <= '6') goto yy837;
goto yy56;
-yy811:
+yy836:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '9') goto yy781;
+ if (yych <= '9') goto yy806;
goto yy56;
-yy812:
+yy837:
yych = *++YYCURSOR;
- if (yych == '0') goto yy781;
+ if (yych == '0') goto yy806;
goto yy56;
-yy813:
+yy838:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '5') goto yy799;
- if (yych <= '9') goto yy795;
- if (yych <= ':') goto yy807;
+ if (yych <= '5') goto yy824;
+ if (yych <= '9') goto yy820;
+ if (yych <= ':') goto yy832;
goto yy56;
-yy814:
+yy839:
yych = *++YYCURSOR;
if (yych <= '2') {
if (yych <= '/') goto yy60;
- if (yych <= '0') goto yy816;
- goto yy785;
+ if (yych <= '0') goto yy841;
+ goto yy810;
} else {
- if (yych <= '3') goto yy786;
- if (yych <= '9') goto yy760;
+ if (yych <= '3') goto yy811;
+ if (yych <= '9') goto yy785;
goto yy60;
}
-yy815:
+yy840:
yych = *++YYCURSOR;
if (yych <= '2') {
if (yych <= '/') goto yy60;
- if (yych <= '0') goto yy784;
- goto yy785;
+ if (yych <= '0') goto yy809;
+ goto yy810;
} else {
- if (yych <= '3') goto yy786;
- if (yych <= '9') goto yy760;
+ if (yych <= '3') goto yy811;
+ if (yych <= '9') goto yy785;
goto yy60;
}
-yy816:
+yy841:
yych = *++YYCURSOR;
if (yych <= '/') goto yy60;
- if (yych <= '9') goto yy787;
+ if (yych <= '9') goto yy812;
goto yy60;
-yy817:
+yy842:
yych = *++YYCURSOR;
- if (yych == 'C') goto yy818;
+ if (yych == 'C') goto yy843;
if (yych != 'c') goto yy56;
-yy818:
+yy843:
yyaccept = 19;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych == 'E') goto yy819;
- if (yych != 'e') goto yy733;
-yy819:
+ if (yych == 'E') goto yy844;
+ if (yych != 'e') goto yy758;
+yy844:
yych = *++YYCURSOR;
- if (yych == 'M') goto yy820;
+ if (yych == 'M') goto yy845;
if (yych != 'm') goto yy56;
-yy820:
+yy845:
yych = *++YYCURSOR;
- if (yych == 'B') goto yy821;
+ if (yych == 'B') goto yy846;
if (yych != 'b') goto yy56;
-yy821:
+yy846:
yych = *++YYCURSOR;
- if (yych == 'E') goto yy822;
+ if (yych == 'E') goto yy847;
if (yych != 'e') goto yy56;
-yy822:
+yy847:
yych = *++YYCURSOR;
- if (yych == 'R') goto yy823;
+ if (yych == 'R') goto yy848;
if (yych != 'r') goto yy56;
-yy823:
+yy848:
yych = *++YYCURSOR;
- goto yy733;
-yy824:
+ goto yy758;
+yy849:
yych = *++YYCURSOR;
- if (yych == 'V') goto yy825;
+ if (yych == 'V') goto yy850;
if (yych != 'v') goto yy56;
-yy825:
+yy850:
yyaccept = 19;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych == 'E') goto yy826;
- if (yych != 'e') goto yy733;
-yy826:
+ if (yych == 'E') goto yy851;
+ if (yych != 'e') goto yy758;
+yy851:
yych = *++YYCURSOR;
- if (yych == 'M') goto yy827;
+ if (yych == 'M') goto yy852;
if (yych != 'm') goto yy56;
-yy827:
+yy852:
yych = *++YYCURSOR;
- if (yych == 'B') goto yy828;
+ if (yych == 'B') goto yy853;
if (yych != 'b') goto yy56;
-yy828:
+yy853:
yych = *++YYCURSOR;
- if (yych == 'E') goto yy829;
+ if (yych == 'E') goto yy854;
if (yych != 'e') goto yy56;
-yy829:
+yy854:
yych = *++YYCURSOR;
- if (yych == 'R') goto yy823;
- if (yych == 'r') goto yy823;
+ if (yych == 'R') goto yy848;
+ if (yych == 'r') goto yy848;
goto yy56;
-yy830:
+yy855:
yych = *++YYCURSOR;
- if (yych == 'T') goto yy831;
+ if (yych == 'T') goto yy856;
if (yych != 't') goto yy56;
-yy831:
+yy856:
yyaccept = 19;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych == 'O') goto yy832;
- if (yych != 'o') goto yy733;
-yy832:
+ if (yych == 'O') goto yy857;
+ if (yych != 'o') goto yy758;
+yy857:
yych = *++YYCURSOR;
- if (yych == 'B') goto yy833;
+ if (yych == 'B') goto yy858;
if (yych != 'b') goto yy56;
-yy833:
+yy858:
yych = *++YYCURSOR;
- if (yych == 'E') goto yy834;
+ if (yych == 'E') goto yy859;
if (yych != 'e') goto yy56;
-yy834:
+yy859:
yych = *++YYCURSOR;
- if (yych == 'R') goto yy823;
- if (yych == 'r') goto yy823;
+ if (yych == 'R') goto yy848;
+ if (yych == 'r') goto yy848;
goto yy56;
-yy835:
+yy860:
yych = *++YYCURSOR;
if (yych <= 'P') {
- if (yych == 'C') goto yy128;
+ if (yych == 'C') goto yy121;
if (yych <= 'O') goto yy56;
} else {
if (yych <= 'c') {
if (yych <= 'b') goto yy56;
- goto yy128;
+ goto yy121;
} else {
if (yych != 'p') goto yy56;
}
}
-yy836:
+yy861:
yych = *++YYCURSOR;
- if (yych == 'T') goto yy837;
- if (yych != 't') goto yy733;
-yy837:
+ if (yych == 'T') goto yy862;
+ if (yych != 't') goto yy758;
+yy862:
yyaccept = 19;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych == 'E') goto yy838;
- if (yych != 'e') goto yy733;
-yy838:
+ if (yych == 'E') goto yy863;
+ if (yych != 'e') goto yy758;
+yy863:
yych = *++YYCURSOR;
- if (yych == 'M') goto yy839;
+ if (yych == 'M') goto yy864;
if (yych != 'm') goto yy56;
-yy839:
+yy864:
yych = *++YYCURSOR;
- if (yych == 'B') goto yy840;
+ if (yych == 'B') goto yy865;
if (yych != 'b') goto yy56;
-yy840:
+yy865:
yych = *++YYCURSOR;
- if (yych == 'E') goto yy841;
+ if (yych == 'E') goto yy866;
if (yych != 'e') goto yy56;
-yy841:
+yy866:
yych = *++YYCURSOR;
- if (yych == 'R') goto yy823;
- if (yych == 'r') goto yy823;
+ if (yych == 'R') goto yy848;
+ if (yych == 'r') goto yy848;
goto yy56;
-yy842:
+yy867:
yych = *++YYCURSOR;
- if (yych == 'G') goto yy846;
- if (yych == 'g') goto yy846;
+ if (yych == 'G') goto yy871;
+ if (yych == 'g') goto yy871;
goto yy56;
-yy843:
+yy868:
yych = *++YYCURSOR;
- if (yych == 'R') goto yy844;
+ if (yych == 'R') goto yy869;
if (yych != 'r') goto yy56;
-yy844:
+yy869:
yyaccept = 19;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych == 'I') goto yy845;
- if (yych != 'i') goto yy733;
-yy845:
+ if (yych == 'I') goto yy870;
+ if (yych != 'i') goto yy758;
+yy870:
yych = *++YYCURSOR;
- if (yych == 'L') goto yy823;
- if (yych == 'l') goto yy823;
+ if (yych == 'L') goto yy848;
+ if (yych == 'l') goto yy848;
goto yy56;
-yy846:
+yy871:
yyaccept = 19;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych == 'U') goto yy847;
- if (yych != 'u') goto yy733;
-yy847:
+ if (yych == 'U') goto yy872;
+ if (yych != 'u') goto yy758;
+yy872:
yych = *++YYCURSOR;
- if (yych == 'S') goto yy848;
+ if (yych == 'S') goto yy873;
if (yych != 's') goto yy56;
-yy848:
+yy873:
yych = *++YYCURSOR;
- if (yych == 'T') goto yy823;
- if (yych == 't') goto yy823;
+ if (yych == 'T') goto yy848;
+ if (yych == 't') goto yy848;
goto yy56;
-yy849:
+yy874:
yych = *++YYCURSOR;
if (yych <= 'Y') {
- if (yych == 'R') goto yy850;
+ if (yych == 'R') goto yy875;
if (yych <= 'X') goto yy56;
- goto yy823;
+ goto yy848;
} else {
if (yych <= 'r') {
if (yych <= 'q') goto yy56;
} else {
- if (yych == 'y') goto yy823;
+ if (yych == 'y') goto yy848;
goto yy56;
}
}
-yy850:
+yy875:
yyaccept = 19;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych == 'C') goto yy851;
- if (yych != 'c') goto yy733;
-yy851:
+ if (yych == 'C') goto yy876;
+ if (yych != 'c') goto yy758;
+yy876:
yych = *++YYCURSOR;
- if (yych == 'H') goto yy823;
- if (yych == 'h') goto yy823;
+ if (yych == 'H') goto yy848;
+ if (yych == 'h') goto yy848;
goto yy56;
-yy852:
+yy877:
yych = *++YYCURSOR;
- if (yych == 'B') goto yy853;
+ if (yych == 'B') goto yy878;
if (yych != 'b') goto yy56;
-yy853:
+yy878:
yyaccept = 19;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych == 'R') goto yy854;
- if (yych != 'r') goto yy733;
-yy854:
+ if (yych == 'R') goto yy879;
+ if (yych != 'r') goto yy758;
+yy879:
yych = *++YYCURSOR;
- if (yych == 'U') goto yy855;
+ if (yych == 'U') goto yy880;
if (yych != 'u') goto yy56;
-yy855:
+yy880:
yych = *++YYCURSOR;
- if (yych == 'A') goto yy856;
+ if (yych == 'A') goto yy881;
if (yych != 'a') goto yy56;
-yy856:
+yy881:
yych = *++YYCURSOR;
- if (yych == 'R') goto yy857;
+ if (yych == 'R') goto yy882;
if (yych != 'r') goto yy56;
-yy857:
+yy882:
yych = *++YYCURSOR;
- if (yych == 'Y') goto yy823;
- if (yych == 'y') goto yy823;
+ if (yych == 'Y') goto yy848;
+ if (yych == 'y') goto yy848;
goto yy56;
-yy858:
+yy883:
yych = *++YYCURSOR;
if (yych <= 'N') {
- if (yych == 'L') goto yy865;
+ if (yych == 'L') goto yy890;
if (yych <= 'M') goto yy56;
- goto yy864;
+ goto yy889;
} else {
if (yych <= 'l') {
if (yych <= 'k') goto yy56;
- goto yy865;
+ goto yy890;
} else {
- if (yych == 'n') goto yy864;
+ if (yych == 'n') goto yy889;
goto yy56;
}
}
-yy859:
+yy884:
yych = *++YYCURSOR;
- if (yych == 'N') goto yy860;
+ if (yych == 'N') goto yy885;
if (yych != 'n') goto yy56;
-yy860:
+yy885:
yyaccept = 19;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych == 'U') goto yy861;
- if (yych != 'u') goto yy733;
-yy861:
+ if (yych == 'U') goto yy886;
+ if (yych != 'u') goto yy758;
+yy886:
yych = *++YYCURSOR;
- if (yych == 'A') goto yy862;
+ if (yych == 'A') goto yy887;
if (yych != 'a') goto yy56;
-yy862:
+yy887:
yych = *++YYCURSOR;
- if (yych == 'R') goto yy863;
+ if (yych == 'R') goto yy888;
if (yych != 'r') goto yy56;
-yy863:
+yy888:
yych = *++YYCURSOR;
- if (yych == 'Y') goto yy823;
- if (yych == 'y') goto yy823;
+ if (yych == 'Y') goto yy848;
+ if (yych == 'y') goto yy848;
goto yy56;
-yy864:
+yy889:
yych = *++YYCURSOR;
- if (yych == 'E') goto yy823;
- if (yych == 'e') goto yy823;
- goto yy733;
-yy865:
+ if (yych == 'E') goto yy848;
+ if (yych == 'e') goto yy848;
+ goto yy758;
+yy890:
yych = *++YYCURSOR;
- if (yych == 'Y') goto yy823;
- if (yych == 'y') goto yy823;
- goto yy733;
-yy866:
+ if (yych == 'Y') goto yy848;
+ if (yych == 'y') goto yy848;
+ goto yy758;
+yy891:
yych = *++YYCURSOR;
- if (yych == 'I') goto yy823;
- goto yy733;
-yy867:
+ if (yych == 'I') goto yy848;
+ goto yy758;
+yy892:
yych = *++YYCURSOR;
- if (yych != 'I') goto yy733;
+ if (yych != 'I') goto yy758;
yych = *++YYCURSOR;
- if (yych == 'I') goto yy823;
- goto yy733;
-yy869:
+ if (yych == 'I') goto yy848;
+ goto yy758;
+yy894:
yych = *++YYCURSOR;
- if (yych == 'I') goto yy823;
- goto yy733;
-yy870:
+ if (yych == 'I') goto yy848;
+ goto yy758;
+yy895:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '0') goto yy885;
- if (yych <= '9') goto yy884;
+ if (yych <= '0') goto yy910;
+ if (yych <= '9') goto yy909;
goto yy56;
-yy871:
+yy896:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '9') goto yy883;
+ if (yych <= '9') goto yy908;
goto yy56;
-yy872:
+yy897:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '5') goto yy881;
- if (yych <= '6') goto yy880;
+ if (yych <= '5') goto yy906;
+ if (yych <= '6') goto yy905;
goto yy56;
-yy873:
+yy898:
yych = *++YYCURSOR;
- if (yych == 'E') goto yy852;
- if (yych == 'e') goto yy852;
+ if (yych == 'E') goto yy877;
+ if (yych == 'e') goto yy877;
goto yy56;
-yy874:
+yy899:
yych = *++YYCURSOR;
- if (yych == 'A') goto yy849;
- if (yych == 'a') goto yy849;
+ if (yych == 'A') goto yy874;
+ if (yych == 'a') goto yy874;
goto yy56;
-yy875:
+yy900:
yych = *++YYCURSOR;
- if (yych == 'E') goto yy879;
- if (yych == 'e') goto yy879;
+ if (yych == 'E') goto yy904;
+ if (yych == 'e') goto yy904;
goto yy56;
-yy876:
+yy901:
yych = *++YYCURSOR;
- if (yych == 'E') goto yy817;
- if (yych == 'e') goto yy817;
+ if (yych == 'E') goto yy842;
+ if (yych == 'e') goto yy842;
goto yy56;
-yy877:
+yy902:
++YYCURSOR;
if ((YYLIMIT - YYCURSOR) < 9) YYFILL(9);
yych = *YYCURSOR;
-yy878:
+yy903:
switch (yych) {
case '\t':
case ' ':
case '-':
- case '.': goto yy877;
+ case '.': goto yy902;
case 'A':
- case 'a': goto yy739;
+ case 'a': goto yy764;
case 'D':
- case 'd': goto yy876;
+ case 'd': goto yy901;
case 'F':
- case 'f': goto yy873;
- case 'I': goto yy732;
+ case 'f': goto yy898;
+ case 'I': goto yy757;
case 'J':
- case 'j': goto yy736;
+ case 'j': goto yy761;
case 'M':
- case 'm': goto yy874;
+ case 'm': goto yy899;
case 'N':
- case 'n': goto yy742;
+ case 'n': goto yy767;
case 'O':
- case 'o': goto yy741;
+ case 'o': goto yy766;
case 'S':
- case 's': goto yy875;
- case 'V': goto yy734;
- case 'X': goto yy735;
+ case 's': goto yy900;
+ case 'V': goto yy759;
+ case 'X': goto yy760;
default: goto yy56;
}
-yy879:
+yy904:
yych = *++YYCURSOR;
- if (yych == 'P') goto yy836;
- if (yych == 'p') goto yy836;
+ if (yych == 'P') goto yy861;
+ if (yych == 'p') goto yy861;
goto yy56;
-yy880:
+yy905:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '6') goto yy882;
+ if (yych <= '6') goto yy907;
goto yy56;
-yy881:
+yy906:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
if (yych >= ':') goto yy56;
-yy882:
+yy907:
yych = *++YYCURSOR;
- goto yy761;
-yy883:
+ goto yy786;
+yy908:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '9') goto yy882;
+ if (yych <= '9') goto yy907;
goto yy56;
-yy884:
+yy909:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '9') goto yy882;
+ if (yych <= '9') goto yy907;
goto yy56;
-yy885:
+yy910:
yych = *++YYCURSOR;
if (yych <= '0') goto yy56;
- if (yych <= '9') goto yy882;
+ if (yych <= '9') goto yy907;
goto yy56;
-yy886:
+yy911:
yych = *++YYCURSOR;
if (yych <= '.') goto yy56;
- if (yych <= '/') goto yy889;
- if (yych <= '9') goto yy897;
+ if (yych <= '/') goto yy914;
+ if (yych <= '9') goto yy922;
goto yy56;
-yy887:
+yy912:
yych = *++YYCURSOR;
if (yych <= '.') goto yy56;
- if (yych <= '/') goto yy889;
- if (yych <= '2') goto yy897;
+ if (yych <= '/') goto yy914;
+ if (yych <= '2') goto yy922;
goto yy56;
-yy888:
+yy913:
yych = *++YYCURSOR;
if (yych != '/') goto yy56;
-yy889:
+yy914:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '2') goto yy890;
- if (yych <= '3') goto yy891;
- if (yych <= '9') goto yy892;
+ if (yych <= '2') goto yy915;
+ if (yych <= '3') goto yy916;
+ if (yych <= '9') goto yy917;
goto yy56;
-yy890:
+yy915:
yyaccept = 20;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'n') {
- if (yych <= '/') goto yy394;
- if (yych <= '9') goto yy892;
- if (yych <= 'm') goto yy394;
- goto yy894;
+ if (yych <= '/') goto yy419;
+ if (yych <= '9') goto yy917;
+ if (yych <= 'm') goto yy419;
+ goto yy919;
} else {
if (yych <= 'r') {
- if (yych <= 'q') goto yy394;
- goto yy895;
+ if (yych <= 'q') goto yy419;
+ goto yy920;
} else {
- if (yych <= 's') goto yy893;
- if (yych <= 't') goto yy896;
- goto yy394;
+ if (yych <= 's') goto yy918;
+ if (yych <= 't') goto yy921;
+ goto yy419;
}
}
-yy891:
+yy916:
yyaccept = 20;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'n') {
- if (yych <= '/') goto yy394;
- if (yych <= '1') goto yy892;
- if (yych <= 'm') goto yy394;
- goto yy894;
+ if (yych <= '/') goto yy419;
+ if (yych <= '1') goto yy917;
+ if (yych <= 'm') goto yy419;
+ goto yy919;
} else {
if (yych <= 'r') {
- if (yych <= 'q') goto yy394;
- goto yy895;
+ if (yych <= 'q') goto yy419;
+ goto yy920;
} else {
- if (yych <= 's') goto yy893;
- if (yych <= 't') goto yy896;
- goto yy394;
+ if (yych <= 's') goto yy918;
+ if (yych <= 't') goto yy921;
+ goto yy419;
}
}
-yy892:
+yy917:
yyaccept = 20;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'q') {
- if (yych == 'n') goto yy894;
- goto yy394;
+ if (yych == 'n') goto yy919;
+ goto yy419;
} else {
- if (yych <= 'r') goto yy895;
- if (yych <= 's') goto yy893;
- if (yych <= 't') goto yy896;
- goto yy394;
+ if (yych <= 'r') goto yy920;
+ if (yych <= 's') goto yy918;
+ if (yych <= 't') goto yy921;
+ goto yy419;
}
-yy893:
+yy918:
yych = *++YYCURSOR;
- if (yych == 't') goto yy393;
+ if (yych == 't') goto yy418;
goto yy56;
-yy894:
+yy919:
yych = *++YYCURSOR;
- if (yych == 'd') goto yy393;
+ if (yych == 'd') goto yy418;
goto yy56;
-yy895:
+yy920:
yych = *++YYCURSOR;
- if (yych == 'd') goto yy393;
+ if (yych == 'd') goto yy418;
goto yy56;
-yy896:
+yy921:
yych = *++YYCURSOR;
- if (yych == 'h') goto yy393;
+ if (yych == 'h') goto yy418;
goto yy56;
-yy897:
+yy922:
yych = *++YYCURSOR;
if (yych != '/') goto yy56;
yych = *++YYCURSOR;
if (yych <= '2') {
if (yych <= '/') goto yy56;
- if (yych >= '1') goto yy900;
+ if (yych >= '1') goto yy925;
} else {
- if (yych <= '3') goto yy901;
- if (yych <= '9') goto yy892;
+ if (yych <= '3') goto yy926;
+ if (yych <= '9') goto yy917;
goto yy56;
}
yyaccept = 20;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'n') {
- if (yych <= '/') goto yy394;
- if (yych <= '9') goto yy902;
- if (yych <= 'm') goto yy394;
- goto yy894;
+ if (yych <= '/') goto yy419;
+ if (yych <= '9') goto yy927;
+ if (yych <= 'm') goto yy419;
+ goto yy919;
} else {
if (yych <= 'r') {
- if (yych <= 'q') goto yy394;
- goto yy895;
+ if (yych <= 'q') goto yy419;
+ goto yy920;
} else {
- if (yych <= 's') goto yy893;
- if (yych <= 't') goto yy896;
- goto yy394;
+ if (yych <= 's') goto yy918;
+ if (yych <= 't') goto yy921;
+ goto yy419;
}
}
-yy900:
+yy925:
yyaccept = 20;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'n') {
- if (yych <= '/') goto yy394;
- if (yych <= '9') goto yy902;
- if (yych <= 'm') goto yy394;
- goto yy894;
+ if (yych <= '/') goto yy419;
+ if (yych <= '9') goto yy927;
+ if (yych <= 'm') goto yy419;
+ goto yy919;
} else {
if (yych <= 'r') {
- if (yych <= 'q') goto yy394;
- goto yy895;
+ if (yych <= 'q') goto yy419;
+ goto yy920;
} else {
- if (yych <= 's') goto yy893;
- if (yych <= 't') goto yy896;
- goto yy394;
+ if (yych <= 's') goto yy918;
+ if (yych <= 't') goto yy921;
+ goto yy419;
}
}
-yy901:
+yy926:
yyaccept = 20;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'n') {
- if (yych <= '/') goto yy394;
- if (yych <= '1') goto yy902;
- if (yych <= 'm') goto yy394;
- goto yy894;
+ if (yych <= '/') goto yy419;
+ if (yych <= '1') goto yy927;
+ if (yych <= 'm') goto yy419;
+ goto yy919;
} else {
if (yych <= 'r') {
- if (yych <= 'q') goto yy394;
- goto yy895;
+ if (yych <= 'q') goto yy419;
+ goto yy920;
} else {
- if (yych <= 's') goto yy893;
- if (yych <= 't') goto yy896;
- goto yy394;
+ if (yych <= 's') goto yy918;
+ if (yych <= 't') goto yy921;
+ goto yy419;
}
}
-yy902:
+yy927:
yyaccept = 20;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'n') {
- if (yych == '/') goto yy393;
- if (yych <= 'm') goto yy394;
- goto yy894;
+ if (yych == '/') goto yy418;
+ if (yych <= 'm') goto yy419;
+ goto yy919;
} else {
if (yych <= 'r') {
- if (yych <= 'q') goto yy394;
- goto yy895;
+ if (yych <= 'q') goto yy419;
+ goto yy920;
} else {
- if (yych <= 's') goto yy893;
- if (yych <= 't') goto yy896;
- goto yy394;
+ if (yych <= 's') goto yy918;
+ if (yych <= 't') goto yy921;
+ goto yy419;
}
}
-yy903:
+yy928:
yych = *++YYCURSOR;
if (yych <= 'U') {
- if (yych == 'A') goto yy983;
+ if (yych == 'A') goto yy1008;
if (yych <= 'T') goto yy56;
- goto yy982;
+ goto yy1007;
} else {
if (yych <= 'a') {
if (yych <= '`') goto yy56;
- goto yy983;
+ goto yy1008;
} else {
- if (yych == 'u') goto yy982;
+ if (yych == 'u') goto yy1007;
goto yy56;
}
}
-yy904:
+yy929:
yych = *++YYCURSOR;
- if (yych == 'E') goto yy980;
- if (yych == 'e') goto yy980;
+ if (yych == 'E') goto yy1005;
+ if (yych == 'e') goto yy1005;
goto yy56;
-yy905:
+yy930:
yych = *++YYCURSOR;
- if (yych == 'A') goto yy977;
- if (yych == 'a') goto yy977;
+ if (yych == 'A') goto yy1002;
+ if (yych == 'a') goto yy1002;
goto yy56;
-yy906:
+yy931:
yych = *++YYCURSOR;
if (yych <= 'U') {
- if (yych == 'P') goto yy974;
+ if (yych == 'P') goto yy999;
if (yych <= 'T') goto yy56;
- goto yy973;
+ goto yy998;
} else {
if (yych <= 'p') {
if (yych <= 'o') goto yy56;
- goto yy974;
+ goto yy999;
} else {
- if (yych == 'u') goto yy973;
+ if (yych == 'u') goto yy998;
goto yy56;
}
}
-yy907:
+yy932:
yych = *++YYCURSOR;
- if (yych == 'E') goto yy970;
- if (yych == 'e') goto yy970;
+ if (yych == 'E') goto yy995;
+ if (yych == 'e') goto yy995;
goto yy56;
-yy908:
+yy933:
yych = *++YYCURSOR;
- if (yych == 'C') goto yy968;
- if (yych == 'c') goto yy968;
+ if (yych == 'C') goto yy993;
+ if (yych == 'c') goto yy993;
goto yy56;
-yy909:
+yy934:
yych = *++YYCURSOR;
- if (yych == 'O') goto yy966;
- if (yych == 'o') goto yy966;
+ if (yych == 'O') goto yy991;
+ if (yych == 'o') goto yy991;
goto yy56;
-yy910:
+yy935:
yych = *++YYCURSOR;
- if (yych == 'E') goto yy964;
- if (yych == 'e') goto yy964;
+ if (yych == 'E') goto yy989;
+ if (yych == 'e') goto yy989;
goto yy56;
-yy911:
+yy936:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '0') goto yy750;
- if (yych <= '4') goto yy751;
- if (yych <= '5') goto yy752;
+ if (yych <= '0') goto yy775;
+ if (yych <= '4') goto yy776;
+ if (yych <= '5') goto yy777;
goto yy56;
-yy912:
+yy937:
yyaccept = 21;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych == '-') goto yy916;
- if (yych <= '/') goto yy913;
- if (yych <= '9') goto yy935;
-yy913:
-#line 1269 "ext/date/lib/parse_date.re"
+ if (yych == '-') goto yy941;
+ if (yych <= '/') goto yy938;
+ if (yych <= '9') goto yy960;
+yy938:
+#line 1312 "ext/date/lib/parse_date.re"
{
int length = 0;
DEBUG_OUTPUT("gnudateshorter");
@@ -12749,639 +13375,639 @@ yy913:
TIMELIB_DEINIT;
return TIMELIB_ISO_DATE;
}
-#line 12753 "ext/date/lib/parse_date.c"
-yy914:
+#line 13379 "ext/date/lib/parse_date.c"
+yy939:
yyaccept = 21;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych == '-') goto yy916;
- if (yych <= '/') goto yy913;
- if (yych <= '2') goto yy935;
- goto yy913;
-yy915:
+ if (yych == '-') goto yy941;
+ if (yych <= '/') goto yy938;
+ if (yych <= '2') goto yy960;
+ goto yy938;
+yy940:
yyaccept = 21;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych != '-') goto yy913;
-yy916:
+ if (yych != '-') goto yy938;
+yy941:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '2') goto yy917;
- if (yych <= '3') goto yy918;
- if (yych <= '9') goto yy919;
+ if (yych <= '2') goto yy942;
+ if (yych <= '3') goto yy943;
+ if (yych <= '9') goto yy944;
goto yy56;
-yy917:
+yy942:
yyaccept = 12;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'm') {
if (yych <= '9') {
- if (yych <= '/') goto yy596;
- goto yy919;
+ if (yych <= '/') goto yy621;
+ goto yy944;
} else {
- if (yych == 'T') goto yy924;
- goto yy596;
+ if (yych == 'T') goto yy949;
+ goto yy621;
}
} else {
if (yych <= 'r') {
- if (yych <= 'n') goto yy921;
- if (yych <= 'q') goto yy596;
- goto yy922;
+ if (yych <= 'n') goto yy946;
+ if (yych <= 'q') goto yy621;
+ goto yy947;
} else {
- if (yych <= 's') goto yy920;
- if (yych <= 't') goto yy923;
- goto yy596;
+ if (yych <= 's') goto yy945;
+ if (yych <= 't') goto yy948;
+ goto yy621;
}
}
-yy918:
+yy943:
yyaccept = 12;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'm') {
if (yych <= '1') {
- if (yych <= '/') goto yy596;
+ if (yych <= '/') goto yy621;
} else {
- if (yych == 'T') goto yy924;
- goto yy596;
+ if (yych == 'T') goto yy949;
+ goto yy621;
}
} else {
if (yych <= 'r') {
- if (yych <= 'n') goto yy921;
- if (yych <= 'q') goto yy596;
- goto yy922;
+ if (yych <= 'n') goto yy946;
+ if (yych <= 'q') goto yy621;
+ goto yy947;
} else {
- if (yych <= 's') goto yy920;
- if (yych <= 't') goto yy923;
- goto yy596;
+ if (yych <= 's') goto yy945;
+ if (yych <= 't') goto yy948;
+ goto yy621;
}
}
-yy919:
+yy944:
yyaccept = 12;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'n') {
- if (yych == 'T') goto yy924;
- if (yych <= 'm') goto yy596;
- goto yy921;
+ if (yych == 'T') goto yy949;
+ if (yych <= 'm') goto yy621;
+ goto yy946;
} else {
if (yych <= 'r') {
- if (yych <= 'q') goto yy596;
- goto yy922;
+ if (yych <= 'q') goto yy621;
+ goto yy947;
} else {
- if (yych <= 's') goto yy920;
- if (yych <= 't') goto yy923;
- goto yy596;
+ if (yych <= 's') goto yy945;
+ if (yych <= 't') goto yy948;
+ goto yy621;
}
}
-yy920:
+yy945:
yych = *++YYCURSOR;
- if (yych == 't') goto yy934;
+ if (yych == 't') goto yy959;
goto yy56;
-yy921:
+yy946:
yych = *++YYCURSOR;
- if (yych == 'd') goto yy934;
+ if (yych == 'd') goto yy959;
goto yy56;
-yy922:
+yy947:
yych = *++YYCURSOR;
- if (yych == 'd') goto yy934;
+ if (yych == 'd') goto yy959;
goto yy56;
-yy923:
+yy948:
yych = *++YYCURSOR;
- if (yych == 'h') goto yy934;
+ if (yych == 'h') goto yy959;
goto yy56;
-yy924:
+yy949:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '1') goto yy925;
- if (yych <= '2') goto yy926;
- if (yych <= '9') goto yy927;
+ if (yych <= '1') goto yy950;
+ if (yych <= '2') goto yy951;
+ if (yych <= '9') goto yy952;
goto yy56;
-yy925:
+yy950:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '9') goto yy927;
- if (yych <= ':') goto yy928;
+ if (yych <= '9') goto yy952;
+ if (yych <= ':') goto yy953;
goto yy56;
-yy926:
+yy951:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '4') goto yy927;
- if (yych == ':') goto yy928;
+ if (yych <= '4') goto yy952;
+ if (yych == ':') goto yy953;
goto yy56;
-yy927:
+yy952:
yych = *++YYCURSOR;
if (yych != ':') goto yy56;
-yy928:
+yy953:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '5') goto yy929;
- if (yych <= '9') goto yy930;
+ if (yych <= '5') goto yy954;
+ if (yych <= '9') goto yy955;
goto yy56;
-yy929:
+yy954:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '9') goto yy930;
- if (yych <= ':') goto yy931;
+ if (yych <= '9') goto yy955;
+ if (yych <= ':') goto yy956;
goto yy56;
-yy930:
+yy955:
yych = *++YYCURSOR;
if (yych != ':') goto yy56;
-yy931:
+yy956:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '5') goto yy932;
- if (yych <= '6') goto yy933;
- if (yych <= '9') goto yy781;
+ if (yych <= '5') goto yy957;
+ if (yych <= '6') goto yy958;
+ if (yych <= '9') goto yy806;
goto yy56;
-yy932:
+yy957:
yych = *++YYCURSOR;
- if (yych <= '/') goto yy782;
- if (yych <= '9') goto yy781;
- goto yy782;
-yy933:
+ if (yych <= '/') goto yy807;
+ if (yych <= '9') goto yy806;
+ goto yy807;
+yy958:
yych = *++YYCURSOR;
- if (yych == '0') goto yy781;
- goto yy782;
-yy934:
+ if (yych == '0') goto yy806;
+ goto yy807;
+yy959:
yyaccept = 12;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych == 'T') goto yy924;
- goto yy596;
-yy935:
+ if (yych == 'T') goto yy949;
+ goto yy621;
+yy960:
yyaccept = 21;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych != '-') goto yy913;
+ if (yych != '-') goto yy938;
yych = *++YYCURSOR;
if (yych <= '2') {
if (yych <= '/') goto yy56;
- if (yych >= '1') goto yy938;
+ if (yych >= '1') goto yy963;
} else {
- if (yych <= '3') goto yy939;
- if (yych <= '9') goto yy919;
+ if (yych <= '3') goto yy964;
+ if (yych <= '9') goto yy944;
goto yy56;
}
yyaccept = 12;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'm') {
if (yych <= '9') {
- if (yych <= '/') goto yy596;
- goto yy940;
+ if (yych <= '/') goto yy621;
+ goto yy965;
} else {
- if (yych == 'T') goto yy924;
- goto yy596;
+ if (yych == 'T') goto yy949;
+ goto yy621;
}
} else {
if (yych <= 'r') {
- if (yych <= 'n') goto yy921;
- if (yych <= 'q') goto yy596;
- goto yy922;
+ if (yych <= 'n') goto yy946;
+ if (yych <= 'q') goto yy621;
+ goto yy947;
} else {
- if (yych <= 's') goto yy920;
- if (yych <= 't') goto yy923;
- goto yy596;
+ if (yych <= 's') goto yy945;
+ if (yych <= 't') goto yy948;
+ goto yy621;
}
}
-yy938:
+yy963:
yyaccept = 12;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'm') {
if (yych <= '9') {
- if (yych <= '/') goto yy596;
- goto yy940;
+ if (yych <= '/') goto yy621;
+ goto yy965;
} else {
- if (yych == 'T') goto yy924;
- goto yy596;
+ if (yych == 'T') goto yy949;
+ goto yy621;
}
} else {
if (yych <= 'r') {
- if (yych <= 'n') goto yy921;
- if (yych <= 'q') goto yy596;
- goto yy922;
+ if (yych <= 'n') goto yy946;
+ if (yych <= 'q') goto yy621;
+ goto yy947;
} else {
- if (yych <= 's') goto yy920;
- if (yych <= 't') goto yy923;
- goto yy596;
+ if (yych <= 's') goto yy945;
+ if (yych <= 't') goto yy948;
+ goto yy621;
}
}
-yy939:
+yy964:
yyaccept = 12;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'm') {
if (yych <= '1') {
- if (yych <= '/') goto yy596;
+ if (yych <= '/') goto yy621;
} else {
- if (yych == 'T') goto yy924;
- goto yy596;
+ if (yych == 'T') goto yy949;
+ goto yy621;
}
} else {
if (yych <= 'r') {
- if (yych <= 'n') goto yy921;
- if (yych <= 'q') goto yy596;
- goto yy922;
+ if (yych <= 'n') goto yy946;
+ if (yych <= 'q') goto yy621;
+ goto yy947;
} else {
- if (yych <= 's') goto yy920;
- if (yych <= 't') goto yy923;
- goto yy596;
+ if (yych <= 's') goto yy945;
+ if (yych <= 't') goto yy948;
+ goto yy621;
}
}
-yy940:
+yy965:
yyaccept = 20;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'n') {
- if (yych == 'T') goto yy941;
- if (yych <= 'm') goto yy394;
- goto yy921;
+ if (yych == 'T') goto yy966;
+ if (yych <= 'm') goto yy419;
+ goto yy946;
} else {
if (yych <= 'r') {
- if (yych <= 'q') goto yy394;
- goto yy922;
+ if (yych <= 'q') goto yy419;
+ goto yy947;
} else {
- if (yych <= 's') goto yy920;
- if (yych <= 't') goto yy923;
- goto yy394;
+ if (yych <= 's') goto yy945;
+ if (yych <= 't') goto yy948;
+ goto yy419;
}
}
-yy941:
+yy966:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '1') goto yy942;
- if (yych <= '2') goto yy943;
- if (yych <= '9') goto yy927;
+ if (yych <= '1') goto yy967;
+ if (yych <= '2') goto yy968;
+ if (yych <= '9') goto yy952;
goto yy56;
-yy942:
+yy967:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '9') goto yy944;
- if (yych <= ':') goto yy928;
+ if (yych <= '9') goto yy969;
+ if (yych <= ':') goto yy953;
goto yy56;
-yy943:
+yy968:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '4') goto yy944;
- if (yych == ':') goto yy928;
+ if (yych <= '4') goto yy969;
+ if (yych == ':') goto yy953;
goto yy56;
-yy944:
+yy969:
yych = *++YYCURSOR;
if (yych != ':') goto yy56;
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '5') goto yy946;
- if (yych <= '9') goto yy930;
+ if (yych <= '5') goto yy971;
+ if (yych <= '9') goto yy955;
goto yy56;
-yy946:
+yy971:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '9') goto yy947;
- if (yych <= ':') goto yy931;
+ if (yych <= '9') goto yy972;
+ if (yych <= ':') goto yy956;
goto yy56;
-yy947:
+yy972:
yych = *++YYCURSOR;
if (yych != ':') goto yy56;
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '5') goto yy949;
- if (yych <= '6') goto yy950;
- if (yych <= '9') goto yy781;
+ if (yych <= '5') goto yy974;
+ if (yych <= '6') goto yy975;
+ if (yych <= '9') goto yy806;
goto yy56;
-yy949:
+yy974:
yych = *++YYCURSOR;
- if (yych <= '/') goto yy782;
- if (yych <= '9') goto yy951;
- goto yy782;
-yy950:
+ if (yych <= '/') goto yy807;
+ if (yych <= '9') goto yy976;
+ goto yy807;
+yy975:
yych = *++YYCURSOR;
- if (yych != '0') goto yy782;
-yy951:
+ if (yych != '0') goto yy807;
+yy976:
yyaccept = 22;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych != '.') goto yy782;
+ if (yych != '.') goto yy807;
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
if (yych >= ':') goto yy56;
-yy953:
+yy978:
yyaccept = 22;
YYMARKER = ++YYCURSOR;
if ((YYLIMIT - YYCURSOR) < 9) YYFILL(9);
yych = *YYCURSOR;
if (yych <= '-') {
- if (yych == '+') goto yy956;
- if (yych <= ',') goto yy782;
- goto yy956;
+ if (yych == '+') goto yy981;
+ if (yych <= ',') goto yy807;
+ goto yy981;
} else {
if (yych <= '9') {
- if (yych <= '/') goto yy782;
- goto yy953;
+ if (yych <= '/') goto yy807;
+ goto yy978;
} else {
- if (yych != 'G') goto yy782;
+ if (yych != 'G') goto yy807;
}
}
yych = *++YYCURSOR;
- if (yych == 'M') goto yy962;
+ if (yych == 'M') goto yy987;
goto yy56;
-yy956:
+yy981:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '1') goto yy957;
- if (yych <= '2') goto yy958;
- if (yych <= '9') goto yy959;
+ if (yych <= '1') goto yy982;
+ if (yych <= '2') goto yy983;
+ if (yych <= '9') goto yy984;
goto yy56;
-yy957:
+yy982:
yych = *++YYCURSOR;
- if (yych <= '/') goto yy782;
- if (yych <= '9') goto yy959;
- if (yych <= ':') goto yy960;
- goto yy782;
-yy958:
+ if (yych <= '/') goto yy807;
+ if (yych <= '9') goto yy984;
+ if (yych <= ':') goto yy985;
+ goto yy807;
+yy983:
yych = *++YYCURSOR;
if (yych <= '5') {
- if (yych <= '/') goto yy782;
- if (yych >= '5') goto yy961;
+ if (yych <= '/') goto yy807;
+ if (yych >= '5') goto yy986;
} else {
- if (yych <= '9') goto yy781;
- if (yych <= ':') goto yy960;
- goto yy782;
+ if (yych <= '9') goto yy806;
+ if (yych <= ':') goto yy985;
+ goto yy807;
}
-yy959:
+yy984:
yych = *++YYCURSOR;
- if (yych <= '/') goto yy782;
- if (yych <= '5') goto yy961;
- if (yych <= '9') goto yy781;
- if (yych >= ';') goto yy782;
-yy960:
+ if (yych <= '/') goto yy807;
+ if (yych <= '5') goto yy986;
+ if (yych <= '9') goto yy806;
+ if (yych >= ';') goto yy807;
+yy985:
yych = *++YYCURSOR;
- if (yych <= '/') goto yy782;
- if (yych <= '5') goto yy961;
- if (yych <= '9') goto yy781;
- goto yy782;
-yy961:
+ if (yych <= '/') goto yy807;
+ if (yych <= '5') goto yy986;
+ if (yych <= '9') goto yy806;
+ goto yy807;
+yy986:
yych = *++YYCURSOR;
- if (yych <= '/') goto yy782;
- if (yych <= '9') goto yy781;
- goto yy782;
-yy962:
+ if (yych <= '/') goto yy807;
+ if (yych <= '9') goto yy806;
+ goto yy807;
+yy987:
yych = *++YYCURSOR;
if (yych != 'T') goto yy56;
yych = *++YYCURSOR;
- if (yych == '+') goto yy956;
- if (yych == '-') goto yy956;
+ if (yych == '+') goto yy981;
+ if (yych == '-') goto yy981;
goto yy56;
-yy964:
+yy989:
yych = *++YYCURSOR;
- if (yych == 'C') goto yy965;
+ if (yych == 'C') goto yy990;
if (yych != 'c') goto yy56;
-yy965:
+yy990:
yyaccept = 19;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'D') {
- if (yych == '-') goto yy706;
- goto yy733;
+ if (yych == '-') goto yy731;
+ goto yy758;
} else {
- if (yych <= 'E') goto yy819;
- if (yych == 'e') goto yy819;
- goto yy733;
+ if (yych <= 'E') goto yy844;
+ if (yych == 'e') goto yy844;
+ goto yy758;
}
-yy966:
+yy991:
yych = *++YYCURSOR;
- if (yych == 'V') goto yy967;
+ if (yych == 'V') goto yy992;
if (yych != 'v') goto yy56;
-yy967:
+yy992:
yyaccept = 19;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'D') {
- if (yych == '-') goto yy706;
- goto yy733;
+ if (yych == '-') goto yy731;
+ goto yy758;
} else {
- if (yych <= 'E') goto yy826;
- if (yych == 'e') goto yy826;
- goto yy733;
+ if (yych <= 'E') goto yy851;
+ if (yych == 'e') goto yy851;
+ goto yy758;
}
-yy968:
+yy993:
yych = *++YYCURSOR;
- if (yych == 'T') goto yy969;
+ if (yych == 'T') goto yy994;
if (yych != 't') goto yy56;
-yy969:
+yy994:
yyaccept = 19;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'N') {
- if (yych == '-') goto yy706;
- goto yy733;
+ if (yych == '-') goto yy731;
+ goto yy758;
} else {
- if (yych <= 'O') goto yy832;
- if (yych == 'o') goto yy832;
- goto yy733;
+ if (yych <= 'O') goto yy857;
+ if (yych == 'o') goto yy857;
+ goto yy758;
}
-yy970:
+yy995:
yych = *++YYCURSOR;
- if (yych == 'P') goto yy971;
+ if (yych == 'P') goto yy996;
if (yych != 'p') goto yy56;
-yy971:
+yy996:
yyaccept = 19;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'S') {
- if (yych == '-') goto yy706;
- goto yy733;
+ if (yych == '-') goto yy731;
+ goto yy758;
} else {
- if (yych <= 'T') goto yy972;
- if (yych != 't') goto yy733;
+ if (yych <= 'T') goto yy997;
+ if (yych != 't') goto yy758;
}
-yy972:
+yy997:
yyaccept = 19;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'D') {
- if (yych == '-') goto yy706;
- goto yy733;
+ if (yych == '-') goto yy731;
+ goto yy758;
} else {
- if (yych <= 'E') goto yy838;
- if (yych == 'e') goto yy838;
- goto yy733;
+ if (yych <= 'E') goto yy863;
+ if (yych == 'e') goto yy863;
+ goto yy758;
}
-yy973:
+yy998:
yych = *++YYCURSOR;
- if (yych == 'G') goto yy976;
- if (yych == 'g') goto yy976;
+ if (yych == 'G') goto yy1001;
+ if (yych == 'g') goto yy1001;
goto yy56;
-yy974:
+yy999:
yych = *++YYCURSOR;
- if (yych == 'R') goto yy975;
+ if (yych == 'R') goto yy1000;
if (yych != 'r') goto yy56;
-yy975:
+yy1000:
yyaccept = 19;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'H') {
- if (yych == '-') goto yy706;
- goto yy733;
+ if (yych == '-') goto yy731;
+ goto yy758;
} else {
- if (yych <= 'I') goto yy845;
- if (yych == 'i') goto yy845;
- goto yy733;
+ if (yych <= 'I') goto yy870;
+ if (yych == 'i') goto yy870;
+ goto yy758;
}
-yy976:
+yy1001:
yyaccept = 19;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'T') {
- if (yych == '-') goto yy706;
- goto yy733;
+ if (yych == '-') goto yy731;
+ goto yy758;
} else {
- if (yych <= 'U') goto yy847;
- if (yych == 'u') goto yy847;
- goto yy733;
+ if (yych <= 'U') goto yy872;
+ if (yych == 'u') goto yy872;
+ goto yy758;
}
-yy977:
+yy1002:
yych = *++YYCURSOR;
if (yych <= 'Y') {
- if (yych == 'R') goto yy978;
+ if (yych == 'R') goto yy1003;
if (yych <= 'X') goto yy56;
- goto yy979;
+ goto yy1004;
} else {
if (yych <= 'r') {
if (yych <= 'q') goto yy56;
} else {
- if (yych == 'y') goto yy979;
+ if (yych == 'y') goto yy1004;
goto yy56;
}
}
-yy978:
+yy1003:
yyaccept = 19;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'B') {
- if (yych == '-') goto yy706;
- goto yy733;
+ if (yych == '-') goto yy731;
+ goto yy758;
} else {
- if (yych <= 'C') goto yy851;
- if (yych == 'c') goto yy851;
- goto yy733;
+ if (yych <= 'C') goto yy876;
+ if (yych == 'c') goto yy876;
+ goto yy758;
}
-yy979:
+yy1004:
yyaccept = 19;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych == '-') goto yy706;
- goto yy733;
-yy980:
+ if (yych == '-') goto yy731;
+ goto yy758;
+yy1005:
yych = *++YYCURSOR;
- if (yych == 'B') goto yy981;
+ if (yych == 'B') goto yy1006;
if (yych != 'b') goto yy56;
-yy981:
+yy1006:
yyaccept = 19;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'Q') {
- if (yych == '-') goto yy706;
- goto yy733;
+ if (yych == '-') goto yy731;
+ goto yy758;
} else {
- if (yych <= 'R') goto yy854;
- if (yych == 'r') goto yy854;
- goto yy733;
+ if (yych <= 'R') goto yy879;
+ if (yych == 'r') goto yy879;
+ goto yy758;
}
-yy982:
+yy1007:
yych = *++YYCURSOR;
if (yych <= 'N') {
- if (yych == 'L') goto yy986;
+ if (yych == 'L') goto yy1011;
if (yych <= 'M') goto yy56;
- goto yy985;
+ goto yy1010;
} else {
if (yych <= 'l') {
if (yych <= 'k') goto yy56;
- goto yy986;
+ goto yy1011;
} else {
- if (yych == 'n') goto yy985;
+ if (yych == 'n') goto yy1010;
goto yy56;
}
}
-yy983:
+yy1008:
yych = *++YYCURSOR;
- if (yych == 'N') goto yy984;
+ if (yych == 'N') goto yy1009;
if (yych != 'n') goto yy56;
-yy984:
+yy1009:
yyaccept = 19;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'T') {
- if (yych == '-') goto yy706;
- goto yy733;
+ if (yych == '-') goto yy731;
+ goto yy758;
} else {
- if (yych <= 'U') goto yy861;
- if (yych == 'u') goto yy861;
- goto yy733;
+ if (yych <= 'U') goto yy886;
+ if (yych == 'u') goto yy886;
+ goto yy758;
}
-yy985:
+yy1010:
yyaccept = 19;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'D') {
- if (yych == '-') goto yy706;
- goto yy733;
+ if (yych == '-') goto yy731;
+ goto yy758;
} else {
- if (yych <= 'E') goto yy823;
- if (yych == 'e') goto yy823;
- goto yy733;
+ if (yych <= 'E') goto yy848;
+ if (yych == 'e') goto yy848;
+ goto yy758;
}
-yy986:
+yy1011:
yyaccept = 19;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'X') {
- if (yych == '-') goto yy706;
- goto yy733;
+ if (yych == '-') goto yy731;
+ goto yy758;
} else {
- if (yych <= 'Y') goto yy823;
- if (yych == 'y') goto yy823;
- goto yy733;
+ if (yych <= 'Y') goto yy848;
+ if (yych == 'y') goto yy848;
+ goto yy758;
}
-yy987:
+yy1012:
yych = *++YYCURSOR;
if (yych <= '.') {
if (yych <= '\t') {
- if (yych <= 0x08) goto yy517;
- goto yy670;
+ if (yych <= 0x08) goto yy542;
+ goto yy695;
} else {
- if (yych <= ',') goto yy517;
- if (yych <= '-') goto yy671;
- goto yy670;
+ if (yych <= ',') goto yy542;
+ if (yych <= '-') goto yy696;
+ goto yy695;
}
} else {
if (yych <= 'U') {
- if (yych <= '/') goto yy669;
- if (yych <= 'T') goto yy517;
- goto yy77;
+ if (yych <= '/') goto yy694;
+ if (yych <= 'T') goto yy542;
+ goto yy79;
} else {
- if (yych == 'u') goto yy77;
- goto yy517;
+ if (yych == 'u') goto yy79;
+ goto yy542;
}
}
-yy988:
+yy1013:
yych = *++YYCURSOR;
if (yych <= 'P') {
- if (yych == 'C') goto yy128;
+ if (yych == 'C') goto yy121;
if (yych <= 'O') goto yy56;
- goto yy525;
+ goto yy550;
} else {
if (yych <= 'c') {
if (yych <= 'b') goto yy56;
- goto yy128;
+ goto yy121;
} else {
- if (yych == 'p') goto yy525;
+ if (yych == 'p') goto yy550;
goto yy56;
}
}
-yy989:
+yy1014:
yych = *++YYCURSOR;
if (yych <= '9') {
if (yych <= ',') {
- if (yych == '\t') goto yy991;
- goto yy993;
+ if (yych == '\t') goto yy1016;
+ goto yy1018;
} else {
- if (yych <= '-') goto yy990;
- if (yych <= '.') goto yy670;
- if (yych <= '/') goto yy669;
- goto yy680;
+ if (yych <= '-') goto yy1015;
+ if (yych <= '.') goto yy695;
+ if (yych <= '/') goto yy694;
+ goto yy705;
}
} else {
if (yych <= 'q') {
- if (yych == 'n') goto yy409;
- goto yy993;
+ if (yych == 'n') goto yy434;
+ goto yy1018;
} else {
- if (yych <= 'r') goto yy410;
- if (yych <= 's') goto yy403;
- if (yych <= 't') goto yy407;
- goto yy993;
+ if (yych <= 'r') goto yy435;
+ if (yych <= 's') goto yy429;
+ if (yych <= 't') goto yy432;
+ goto yy1018;
}
}
-yy990:
+yy1015:
yych = *++YYCURSOR;
switch (yych) {
- case '0': goto yy994;
- case '1': goto yy995;
+ case '0': goto yy1019;
+ case '1': goto yy1020;
case '2':
case '3':
case '4':
@@ -13389,279 +14015,366 @@ yy990:
case '6':
case '7':
case '8':
- case '9': goto yy557;
+ case '9': goto yy582;
case 'A':
- case 'a': goto yy561;
+ case 'a': goto yy586;
case 'D':
- case 'd': goto yy565;
+ case 'd': goto yy590;
case 'F':
- case 'f': goto yy559;
+ case 'f': goto yy584;
case 'J':
- case 'j': goto yy558;
+ case 'j': goto yy583;
case 'M':
- case 'm': goto yy560;
+ case 'm': goto yy585;
case 'N':
- case 'n': goto yy564;
+ case 'n': goto yy589;
case 'O':
- case 'o': goto yy563;
+ case 'o': goto yy588;
case 'S':
- case 's': goto yy562;
- default: goto yy517;
+ case 's': goto yy587;
+ default: goto yy542;
}
-yy991:
+yy1016:
yych = *++YYCURSOR;
- if (yych <= '/') goto yy993;
- if (yych <= '0') goto yy675;
- if (yych <= '1') goto yy676;
- if (yych <= '9') goto yy677;
- goto yy993;
-yy992:
+ if (yych <= '/') goto yy1018;
+ if (yych <= '0') goto yy700;
+ if (yych <= '1') goto yy701;
+ if (yych <= '9') goto yy702;
+ goto yy1018;
+yy1017:
++YYCURSOR;
if ((YYLIMIT - YYCURSOR) < 13) YYFILL(13);
yych = *YYCURSOR;
-yy993:
- switch (yych) {
- case '\t':
- case ' ': goto yy992;
- case '-':
- case '.': goto yy516;
- case 'A':
- case 'a': goto yy513;
- case 'D':
- case 'd': goto yy405;
- case 'F':
- case 'f': goto yy406;
- case 'H':
- case 'h': goto yy63;
- case 'I': goto yy414;
- case 'J':
- case 'j': goto yy418;
- case 'M':
- case 'm': goto yy404;
- case 'N':
- case 'n': goto yy421;
- case 'O':
- case 'o': goto yy420;
- case 'S':
- case 's': goto yy402;
- case 'T':
- case 't': goto yy68;
- case 'V': goto yy416;
- case 'W':
- case 'w': goto yy67;
- case 'X': goto yy417;
- case 'Y':
- case 'y': goto yy66;
- default: goto yy56;
+yy1018:
+ if (yych <= 'W') {
+ if (yych <= 'G') {
+ if (yych <= '.') {
+ if (yych <= 0x1F) {
+ if (yych == '\t') goto yy1017;
+ goto yy56;
+ } else {
+ if (yych <= ' ') goto yy1017;
+ if (yych <= ',') goto yy56;
+ goto yy541;
+ }
+ } else {
+ if (yych <= 'C') {
+ if (yych == 'A') goto yy538;
+ goto yy56;
+ } else {
+ if (yych <= 'D') goto yy430;
+ if (yych == 'F') goto yy431;
+ goto yy56;
+ }
+ }
+ } else {
+ if (yych <= 'N') {
+ if (yych <= 'J') {
+ if (yych <= 'H') goto yy65;
+ if (yych <= 'I') goto yy439;
+ goto yy443;
+ } else {
+ if (yych <= 'L') goto yy56;
+ if (yych <= 'M') goto yy427;
+ goto yy446;
+ }
+ } else {
+ if (yych <= 'S') {
+ if (yych <= 'O') goto yy445;
+ if (yych <= 'R') goto yy56;
+ goto yy428;
+ } else {
+ if (yych <= 'T') goto yy70;
+ if (yych <= 'U') goto yy63;
+ if (yych <= 'V') goto yy441;
+ goto yy69;
+ }
+ }
+ }
+ } else {
+ if (yych <= 'l') {
+ if (yych <= 'd') {
+ if (yych <= '`') {
+ if (yych <= 'X') goto yy442;
+ if (yych <= 'Y') goto yy68;
+ goto yy56;
+ } else {
+ if (yych <= 'a') goto yy538;
+ if (yych <= 'c') goto yy56;
+ goto yy430;
+ }
+ } else {
+ if (yych <= 'g') {
+ if (yych == 'f') goto yy431;
+ goto yy56;
+ } else {
+ if (yych <= 'h') goto yy65;
+ if (yych == 'j') goto yy443;
+ goto yy56;
+ }
+ }
+ } else {
+ if (yych <= 'u') {
+ if (yych <= 'o') {
+ if (yych <= 'm') goto yy427;
+ if (yych <= 'n') goto yy446;
+ goto yy445;
+ } else {
+ if (yych <= 'r') goto yy56;
+ if (yych <= 's') goto yy428;
+ if (yych <= 't') goto yy70;
+ goto yy63;
+ }
+ } else {
+ if (yych <= 'x') {
+ if (yych == 'w') goto yy69;
+ goto yy56;
+ } else {
+ if (yych <= 'y') goto yy68;
+ if (yych == 0xC2) goto yy62;
+ goto yy56;
+ }
+ }
+ }
}
-yy994:
+yy1019:
yych = *++YYCURSOR;
if (yych <= '.') {
if (yych <= ',') goto yy56;
- if (yych <= '-') goto yy594;
- goto yy541;
+ if (yych <= '-') goto yy619;
+ goto yy566;
} else {
if (yych <= '/') goto yy56;
- if (yych <= '9') goto yy996;
+ if (yych <= '9') goto yy1021;
goto yy56;
}
-yy995:
+yy1020:
yych = *++YYCURSOR;
if (yych <= '.') {
if (yych <= ',') goto yy56;
- if (yych <= '-') goto yy594;
- goto yy541;
+ if (yych <= '-') goto yy619;
+ goto yy566;
} else {
if (yych <= '/') goto yy56;
if (yych >= '3') goto yy56;
}
-yy996:
+yy1021:
yych = *++YYCURSOR;
if (yych <= ',') goto yy56;
- if (yych <= '-') goto yy997;
- if (yych <= '.') goto yy541;
+ if (yych <= '-') goto yy1022;
+ if (yych <= '.') goto yy566;
goto yy56;
-yy997:
+yy1022:
yych = *++YYCURSOR;
if (yych <= '2') {
if (yych <= '/') goto yy56;
- if (yych >= '1') goto yy999;
+ if (yych >= '1') goto yy1024;
} else {
- if (yych <= '3') goto yy1000;
- if (yych <= '9') goto yy598;
+ if (yych <= '3') goto yy1025;
+ if (yych <= '9') goto yy623;
goto yy56;
}
yyaccept = 12;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'n') {
- if (yych <= '/') goto yy596;
- if (yych <= '9') goto yy1001;
- if (yych <= 'm') goto yy596;
- goto yy600;
+ if (yych <= '/') goto yy621;
+ if (yych <= '9') goto yy1026;
+ if (yych <= 'm') goto yy621;
+ goto yy625;
} else {
if (yych <= 'r') {
- if (yych <= 'q') goto yy596;
- goto yy601;
+ if (yych <= 'q') goto yy621;
+ goto yy626;
} else {
- if (yych <= 's') goto yy599;
- if (yych <= 't') goto yy602;
- goto yy596;
+ if (yych <= 's') goto yy624;
+ if (yych <= 't') goto yy627;
+ goto yy621;
}
}
-yy999:
+yy1024:
yyaccept = 12;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'n') {
- if (yych <= '/') goto yy596;
- if (yych <= '9') goto yy1001;
- if (yych <= 'm') goto yy596;
- goto yy600;
+ if (yych <= '/') goto yy621;
+ if (yych <= '9') goto yy1026;
+ if (yych <= 'm') goto yy621;
+ goto yy625;
} else {
if (yych <= 'r') {
- if (yych <= 'q') goto yy596;
- goto yy601;
+ if (yych <= 'q') goto yy621;
+ goto yy626;
} else {
- if (yych <= 's') goto yy599;
- if (yych <= 't') goto yy602;
- goto yy596;
+ if (yych <= 's') goto yy624;
+ if (yych <= 't') goto yy627;
+ goto yy621;
}
}
-yy1000:
+yy1025:
yyaccept = 12;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'n') {
if (yych <= '1') {
- if (yych <= '/') goto yy596;
+ if (yych <= '/') goto yy621;
} else {
- if (yych <= '9') goto yy543;
- if (yych <= 'm') goto yy596;
- goto yy600;
+ if (yych <= '9') goto yy568;
+ if (yych <= 'm') goto yy621;
+ goto yy625;
}
} else {
if (yych <= 'r') {
- if (yych <= 'q') goto yy596;
- goto yy601;
+ if (yych <= 'q') goto yy621;
+ goto yy626;
} else {
- if (yych <= 's') goto yy599;
- if (yych <= 't') goto yy602;
- goto yy596;
+ if (yych <= 's') goto yy624;
+ if (yych <= 't') goto yy627;
+ goto yy621;
}
}
-yy1001:
+yy1026:
yyaccept = 14;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'n') {
- if (yych <= '/') goto yy703;
- if (yych <= '9') goto yy544;
- if (yych <= 'm') goto yy703;
- goto yy600;
+ if (yych <= '/') goto yy728;
+ if (yych <= '9') goto yy569;
+ if (yych <= 'm') goto yy728;
+ goto yy625;
} else {
if (yych <= 'r') {
- if (yych <= 'q') goto yy703;
- goto yy601;
+ if (yych <= 'q') goto yy728;
+ goto yy626;
} else {
- if (yych <= 's') goto yy599;
- if (yych <= 't') goto yy602;
- goto yy703;
+ if (yych <= 's') goto yy624;
+ if (yych <= 't') goto yy627;
+ goto yy728;
}
}
-yy1002:
+yy1027:
yych = *++YYCURSOR;
if (yych <= '9') {
if (yych <= '-') {
- if (yych == '\t') goto yy991;
- if (yych <= ',') goto yy993;
- goto yy990;
+ if (yych == '\t') goto yy1016;
+ if (yych <= ',') goto yy1018;
+ goto yy1015;
} else {
- if (yych <= '.') goto yy1003;
- if (yych <= '/') goto yy669;
- if (yych <= '5') goto yy1005;
- goto yy680;
+ if (yych <= '.') goto yy1028;
+ if (yych <= '/') goto yy694;
+ if (yych <= '5') goto yy1030;
+ goto yy705;
}
} else {
if (yych <= 'q') {
- if (yych <= ':') goto yy1004;
- if (yych == 'n') goto yy409;
- goto yy993;
+ if (yych <= ':') goto yy1029;
+ if (yych == 'n') goto yy434;
+ goto yy1018;
} else {
- if (yych <= 'r') goto yy410;
- if (yych <= 's') goto yy403;
- if (yych <= 't') goto yy407;
- goto yy993;
+ if (yych <= 'r') goto yy435;
+ if (yych <= 's') goto yy429;
+ if (yych <= 't') goto yy432;
+ goto yy1018;
}
}
-yy1003:
+yy1028:
yych = *++YYCURSOR;
if (yych <= '1') {
- if (yych <= '/') goto yy517;
- if (yych <= '0') goto yy1027;
- goto yy1028;
+ if (yych <= '/') goto yy542;
+ if (yych <= '0') goto yy1052;
+ goto yy1053;
} else {
- if (yych <= '5') goto yy1029;
- if (yych <= '9') goto yy1030;
- goto yy517;
+ if (yych <= '5') goto yy1054;
+ if (yych <= '9') goto yy1055;
+ goto yy542;
}
-yy1004:
+yy1029:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '5') goto yy1022;
- if (yych <= '9') goto yy1023;
+ if (yych <= '5') goto yy1047;
+ if (yych <= '9') goto yy1048;
goto yy56;
-yy1005:
+yy1030:
yych = *++YYCURSOR;
- if (yych == '-') goto yy724;
+ if (yych == '-') goto yy749;
if (yych <= '/') goto yy60;
if (yych >= ':') goto yy60;
yyaccept = 23;
yych = *(YYMARKER = ++YYCURSOR);
- switch (yych) {
- case '\t':
- case ' ':
- case 'A':
- case 'D':
- case 'F':
- case 'H':
- case 'I':
- case 'J':
- case 'M':
- case 'N':
- case 'O':
- case 'S':
- case 'T':
- case 'V':
- case 'X':
- case 'Y':
- case 'a':
- case 'd':
- case 'f':
- case 'h':
- case 'j':
- case 'm':
- case 'n':
- case 'o':
- case 's':
- case 't':
- case 'w':
- case 'y': goto yy730;
- case '-': goto yy727;
- case '.': goto yy731;
- case '/': goto yy728;
- case '0': goto yy1008;
- case '1': goto yy1009;
- case '2': goto yy1010;
- case '3': goto yy1011;
- case '4':
- case '5': goto yy1012;
- case '6': goto yy1013;
- case '7':
- case '8':
- case '9': goto yy54;
- case ':': goto yy746;
- case 'W': goto yy749;
- default: goto yy1007;
+ if (yych <= 'L') {
+ if (yych <= '3') {
+ if (yych <= '-') {
+ if (yych <= 0x1F) {
+ if (yych == '\t') goto yy755;
+ } else {
+ if (yych <= ' ') goto yy755;
+ if (yych >= '-') goto yy752;
+ }
+ } else {
+ if (yych <= '0') {
+ if (yych <= '.') goto yy756;
+ if (yych <= '/') goto yy753;
+ goto yy1033;
+ } else {
+ if (yych <= '1') goto yy1034;
+ if (yych <= '2') goto yy1035;
+ goto yy1036;
+ }
+ }
+ } else {
+ if (yych <= 'A') {
+ if (yych <= '9') {
+ if (yych <= '5') goto yy1037;
+ if (yych <= '6') goto yy1038;
+ goto yy54;
+ } else {
+ if (yych <= ':') goto yy771;
+ if (yych >= 'A') goto yy755;
+ }
+ } else {
+ if (yych <= 'E') {
+ if (yych == 'D') goto yy755;
+ } else {
+ if (yych == 'G') goto yy1032;
+ if (yych <= 'J') goto yy755;
+ }
+ }
+ }
+ } else {
+ if (yych <= 'h') {
+ if (yych <= '`') {
+ if (yych <= 'V') {
+ if (yych <= 'O') goto yy755;
+ if (yych >= 'S') goto yy755;
+ } else {
+ if (yych <= 'W') goto yy774;
+ if (yych <= 'Y') goto yy755;
+ }
+ } else {
+ if (yych <= 'd') {
+ if (yych <= 'a') goto yy755;
+ if (yych >= 'd') goto yy755;
+ } else {
+ if (yych == 'f') goto yy755;
+ if (yych >= 'h') goto yy755;
+ }
+ }
+ } else {
+ if (yych <= 'u') {
+ if (yych <= 'l') {
+ if (yych == 'j') goto yy755;
+ } else {
+ if (yych <= 'o') goto yy755;
+ if (yych >= 's') goto yy755;
+ }
+ } else {
+ if (yych <= 'x') {
+ if (yych == 'w') goto yy755;
+ } else {
+ if (yych <= 'y') goto yy755;
+ if (yych == 0xC2) goto yy755;
+ }
+ }
+ }
}
-yy1007:
-#line 1161 "ext/date/lib/parse_date.re"
+yy1032:
+#line 1204 "ext/date/lib/parse_date.re"
{
DEBUG_OUTPUT("gnunocolon");
TIMELIB_INIT;
@@ -13683,49 +14396,49 @@ yy1007:
TIMELIB_DEINIT;
return TIMELIB_GNU_NOCOLON;
}
-#line 13687 "ext/date/lib/parse_date.c"
-yy1008:
+#line 14400 "ext/date/lib/parse_date.c"
+yy1033:
yych = *++YYCURSOR;
if (yych <= '/') goto yy60;
- if (yych <= '0') goto yy1020;
- if (yych <= '9') goto yy1021;
+ if (yych <= '0') goto yy1045;
+ if (yych <= '9') goto yy1046;
goto yy60;
-yy1009:
+yy1034:
yych = *++YYCURSOR;
if (yych <= '/') goto yy60;
- if (yych <= '2') goto yy1019;
- if (yych <= '9') goto yy1018;
+ if (yych <= '2') goto yy1044;
+ if (yych <= '9') goto yy1043;
goto yy60;
-yy1010:
+yy1035:
yych = *++YYCURSOR;
if (yych <= '/') goto yy60;
- if (yych <= '9') goto yy1018;
+ if (yych <= '9') goto yy1043;
goto yy60;
-yy1011:
+yy1036:
yych = *++YYCURSOR;
if (yych <= '/') goto yy60;
- if (yych <= '5') goto yy1016;
- if (yych <= '6') goto yy1017;
- if (yych <= '9') goto yy1014;
+ if (yych <= '5') goto yy1041;
+ if (yych <= '6') goto yy1042;
+ if (yych <= '9') goto yy1039;
goto yy60;
-yy1012:
+yy1037:
yych = *++YYCURSOR;
if (yych <= '/') goto yy60;
- if (yych <= '9') goto yy1014;
+ if (yych <= '9') goto yy1039;
goto yy60;
-yy1013:
+yy1038:
yych = *++YYCURSOR;
if (yych <= '/') goto yy60;
- if (yych <= '0') goto yy1014;
+ if (yych <= '0') goto yy1039;
if (yych <= '9') goto yy54;
goto yy60;
-yy1014:
+yy1039:
yyaccept = 24;
yych = *(YYMARKER = ++YYCURSOR);
if (yybm[0+yych] & 2) {
goto yy54;
}
- if (yych <= 'W') {
+ if (yych <= 'X') {
if (yych <= 'F') {
if (yych <= ' ') {
if (yych == '\t') goto yy60;
@@ -13739,35 +14452,41 @@ yy1014:
if (yych == 'H') goto yy60;
if (yych >= 'M') goto yy60;
} else {
- if (yych <= 'R') goto yy1015;
- if (yych <= 'T') goto yy60;
- if (yych >= 'W') goto yy60;
+ if (yych <= 'U') {
+ if (yych >= 'S') goto yy60;
+ } else {
+ if (yych == 'W') goto yy60;
+ }
}
}
} else {
- if (yych <= 'h') {
- if (yych <= 'd') {
- if (yych == 'Y') goto yy60;
- if (yych >= 'd') goto yy60;
+ if (yych <= 'm') {
+ if (yych <= 'e') {
+ if (yych <= 'Y') goto yy60;
+ if (yych == 'd') goto yy60;
} else {
- if (yych == 'f') goto yy60;
- if (yych >= 'h') goto yy60;
+ if (yych <= 'g') {
+ if (yych <= 'f') goto yy60;
+ } else {
+ if (yych <= 'h') goto yy60;
+ if (yych >= 'm') goto yy60;
+ }
}
} else {
- if (yych <= 't') {
- if (yych == 'm') goto yy60;
- if (yych >= 's') goto yy60;
+ if (yych <= 'w') {
+ if (yych <= 'r') goto yy1040;
+ if (yych != 'v') goto yy60;
} else {
- if (yych <= 'w') {
- if (yych >= 'w') goto yy60;
+ if (yych <= 'y') {
+ if (yych >= 'y') goto yy60;
} else {
- if (yych == 'y') goto yy60;
+ if (yych == 0xC2) goto yy60;
}
}
}
}
-yy1015:
-#line 1207 "ext/date/lib/parse_date.re"
+yy1040:
+#line 1250 "ext/date/lib/parse_date.re"
{
int tz_not_found;
DEBUG_OUTPUT("iso8601nocolon");
@@ -13786,863 +14505,939 @@ yy1015:
TIMELIB_DEINIT;
return TIMELIB_ISO_NOCOLON;
}
-#line 13790 "ext/date/lib/parse_date.c"
-yy1016:
+#line 14509 "ext/date/lib/parse_date.c"
+yy1041:
yyaccept = 24;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych <= 'V') {
- if (yych <= 'D') {
+ if (yych <= 'W') {
+ if (yych <= 'E') {
if (yych <= ' ') {
if (yych == '\t') goto yy60;
- if (yych <= 0x1F) goto yy1015;
+ if (yych <= 0x1F) goto yy1040;
goto yy60;
} else {
- if (yych <= '/') goto yy1015;
- if (yych <= '9') goto yy760;
- if (yych <= 'C') goto yy1015;
- goto yy60;
+ if (yych <= '9') {
+ if (yych <= '/') goto yy1040;
+ goto yy785;
+ } else {
+ if (yych == 'D') goto yy60;
+ goto yy1040;
+ }
}
} else {
- if (yych <= 'H') {
- if (yych == 'F') goto yy60;
- if (yych <= 'G') goto yy1015;
- goto yy60;
+ if (yych <= 'L') {
+ if (yych == 'G') goto yy1040;
+ if (yych <= 'H') goto yy60;
+ goto yy1040;
} else {
- if (yych <= 'M') {
- if (yych <= 'L') goto yy1015;
- goto yy60;
+ if (yych <= 'R') {
+ if (yych <= 'M') goto yy60;
+ goto yy1040;
} else {
- if (yych <= 'R') goto yy1015;
- if (yych <= 'T') goto yy60;
- goto yy1015;
+ if (yych == 'V') goto yy1040;
+ goto yy60;
}
}
}
} else {
- if (yych <= 'h') {
- if (yych <= 'c') {
- if (yych == 'X') goto yy1015;
- if (yych <= 'Y') goto yy60;
- goto yy1015;
+ if (yych <= 'l') {
+ if (yych <= 'd') {
+ if (yych == 'Y') goto yy60;
+ if (yych <= 'c') goto yy1040;
+ goto yy60;
} else {
- if (yych <= 'e') {
- if (yych <= 'd') goto yy60;
- goto yy1015;
- } else {
- if (yych == 'g') goto yy1015;
+ if (yych <= 'f') {
+ if (yych <= 'e') goto yy1040;
goto yy60;
+ } else {
+ if (yych == 'h') goto yy60;
+ goto yy1040;
}
}
} else {
- if (yych <= 't') {
- if (yych == 'm') goto yy60;
- if (yych <= 'r') goto yy1015;
- goto yy60;
+ if (yych <= 'w') {
+ if (yych <= 'r') {
+ if (yych <= 'm') goto yy60;
+ goto yy1040;
+ } else {
+ if (yych == 'v') goto yy1040;
+ goto yy60;
+ }
} else {
- if (yych <= 'w') {
- if (yych <= 'v') goto yy1015;
+ if (yych <= 'y') {
+ if (yych <= 'x') goto yy1040;
goto yy60;
} else {
- if (yych == 'y') goto yy60;
- goto yy1015;
+ if (yych == 0xC2) goto yy60;
+ goto yy1040;
}
}
}
}
-yy1017:
+yy1042:
yyaccept = 24;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych <= 'V') {
+ if (yych <= 'W') {
if (yych <= 'D') {
if (yych <= ' ') {
if (yych == '\t') goto yy60;
- if (yych <= 0x1F) goto yy1015;
+ if (yych <= 0x1F) goto yy1040;
goto yy60;
} else {
if (yych <= '6') {
- if (yych <= '/') goto yy1015;
- goto yy760;
+ if (yych <= '/') goto yy1040;
+ goto yy785;
} else {
if (yych <= '9') goto yy54;
- if (yych <= 'C') goto yy1015;
+ if (yych <= 'C') goto yy1040;
goto yy60;
}
}
} else {
- if (yych <= 'H') {
- if (yych == 'F') goto yy60;
- if (yych <= 'G') goto yy1015;
- goto yy60;
- } else {
- if (yych <= 'M') {
- if (yych <= 'L') goto yy1015;
+ if (yych <= 'L') {
+ if (yych <= 'F') {
+ if (yych <= 'E') goto yy1040;
goto yy60;
} else {
- if (yych <= 'R') goto yy1015;
- if (yych <= 'T') goto yy60;
- goto yy1015;
+ if (yych == 'H') goto yy60;
+ goto yy1040;
+ }
+ } else {
+ if (yych <= 'R') {
+ if (yych <= 'M') goto yy60;
+ goto yy1040;
+ } else {
+ if (yych == 'V') goto yy1040;
+ goto yy60;
}
}
}
} else {
- if (yych <= 'h') {
- if (yych <= 'c') {
- if (yych == 'X') goto yy1015;
- if (yych <= 'Y') goto yy60;
- goto yy1015;
+ if (yych <= 'l') {
+ if (yych <= 'd') {
+ if (yych == 'Y') goto yy60;
+ if (yych <= 'c') goto yy1040;
+ goto yy60;
} else {
- if (yych <= 'e') {
- if (yych <= 'd') goto yy60;
- goto yy1015;
- } else {
- if (yych == 'g') goto yy1015;
+ if (yych <= 'f') {
+ if (yych <= 'e') goto yy1040;
goto yy60;
+ } else {
+ if (yych == 'h') goto yy60;
+ goto yy1040;
}
}
} else {
- if (yych <= 't') {
- if (yych == 'm') goto yy60;
- if (yych <= 'r') goto yy1015;
- goto yy60;
+ if (yych <= 'w') {
+ if (yych <= 'r') {
+ if (yych <= 'm') goto yy60;
+ goto yy1040;
+ } else {
+ if (yych == 'v') goto yy1040;
+ goto yy60;
+ }
} else {
- if (yych <= 'w') {
- if (yych <= 'v') goto yy1015;
+ if (yych <= 'y') {
+ if (yych <= 'x') goto yy1040;
goto yy60;
} else {
- if (yych == 'y') goto yy60;
- goto yy1015;
+ if (yych == 0xC2) goto yy60;
+ goto yy1040;
}
}
}
}
-yy1018:
+yy1043:
yyaccept = 24;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych <= 'V') {
- if (yych <= 'D') {
+ if (yych <= 'W') {
+ if (yych <= 'E') {
if (yych <= ' ') {
if (yych == '\t') goto yy60;
- if (yych <= 0x1F) goto yy1015;
+ if (yych <= 0x1F) goto yy1040;
goto yy60;
} else {
- if (yych <= '/') goto yy1015;
- if (yych <= '9') goto yy760;
- if (yych <= 'C') goto yy1015;
- goto yy60;
+ if (yych <= '9') {
+ if (yych <= '/') goto yy1040;
+ goto yy785;
+ } else {
+ if (yych == 'D') goto yy60;
+ goto yy1040;
+ }
}
} else {
- if (yych <= 'H') {
- if (yych == 'F') goto yy60;
- if (yych <= 'G') goto yy1015;
- goto yy60;
+ if (yych <= 'L') {
+ if (yych == 'G') goto yy1040;
+ if (yych <= 'H') goto yy60;
+ goto yy1040;
} else {
- if (yych <= 'M') {
- if (yych <= 'L') goto yy1015;
- goto yy60;
+ if (yych <= 'R') {
+ if (yych <= 'M') goto yy60;
+ goto yy1040;
} else {
- if (yych <= 'R') goto yy1015;
- if (yych <= 'T') goto yy60;
- goto yy1015;
+ if (yych == 'V') goto yy1040;
+ goto yy60;
}
}
}
} else {
- if (yych <= 'h') {
- if (yych <= 'c') {
- if (yych == 'X') goto yy1015;
- if (yych <= 'Y') goto yy60;
- goto yy1015;
+ if (yych <= 'l') {
+ if (yych <= 'd') {
+ if (yych == 'Y') goto yy60;
+ if (yych <= 'c') goto yy1040;
+ goto yy60;
} else {
- if (yych <= 'e') {
- if (yych <= 'd') goto yy60;
- goto yy1015;
- } else {
- if (yych == 'g') goto yy1015;
+ if (yych <= 'f') {
+ if (yych <= 'e') goto yy1040;
goto yy60;
+ } else {
+ if (yych == 'h') goto yy60;
+ goto yy1040;
}
}
} else {
- if (yych <= 't') {
- if (yych == 'm') goto yy60;
- if (yych <= 'r') goto yy1015;
- goto yy60;
+ if (yych <= 'w') {
+ if (yych <= 'r') {
+ if (yych <= 'm') goto yy60;
+ goto yy1040;
+ } else {
+ if (yych == 'v') goto yy1040;
+ goto yy60;
+ }
} else {
- if (yych <= 'w') {
- if (yych <= 'v') goto yy1015;
+ if (yych <= 'y') {
+ if (yych <= 'x') goto yy1040;
goto yy60;
} else {
- if (yych == 'y') goto yy60;
- goto yy1015;
+ if (yych == 0xC2) goto yy60;
+ goto yy1040;
}
}
}
}
-yy1019:
+yy1044:
yyaccept = 24;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych <= 'T') {
- if (yych <= '9') {
- if (yych <= ' ') {
- if (yych == '\t') goto yy60;
- if (yych <= 0x1F) goto yy1015;
- goto yy60;
+ if (yych <= 'V') {
+ if (yych <= 'C') {
+ if (yych <= '/') {
+ if (yych <= '\t') {
+ if (yych <= 0x08) goto yy1040;
+ goto yy60;
+ } else {
+ if (yych == ' ') goto yy60;
+ goto yy1040;
+ }
} else {
- if (yych <= '0') {
- if (yych <= '/') goto yy1015;
- goto yy784;
+ if (yych <= '2') {
+ if (yych <= '0') goto yy809;
+ goto yy810;
} else {
- if (yych <= '2') goto yy785;
- if (yych <= '3') goto yy786;
- goto yy760;
+ if (yych <= '3') goto yy811;
+ if (yych <= '9') goto yy785;
+ goto yy1040;
}
}
} else {
- if (yych <= 'G') {
- if (yych <= 'D') {
- if (yych <= 'C') goto yy1015;
- goto yy60;
+ if (yych <= 'H') {
+ if (yych <= 'E') {
+ if (yych <= 'D') goto yy60;
+ goto yy1040;
} else {
- if (yych == 'F') goto yy60;
- goto yy1015;
+ if (yych == 'G') goto yy1040;
+ goto yy60;
}
} else {
- if (yych <= 'L') {
- if (yych <= 'H') goto yy60;
- goto yy1015;
- } else {
- if (yych <= 'M') goto yy60;
- if (yych <= 'R') goto yy1015;
+ if (yych <= 'M') {
+ if (yych <= 'L') goto yy1040;
goto yy60;
+ } else {
+ if (yych <= 'R') goto yy1040;
+ if (yych <= 'U') goto yy60;
+ goto yy1040;
}
}
}
} else {
- if (yych <= 'g') {
- if (yych <= 'Y') {
- if (yych == 'W') goto yy60;
- if (yych <= 'X') goto yy1015;
- goto yy60;
+ if (yych <= 'l') {
+ if (yych <= 'd') {
+ if (yych <= 'X') {
+ if (yych <= 'W') goto yy60;
+ goto yy1040;
+ } else {
+ if (yych <= 'Y') goto yy60;
+ if (yych <= 'c') goto yy1040;
+ goto yy60;
+ }
} else {
- if (yych <= 'd') {
- if (yych <= 'c') goto yy1015;
+ if (yych <= 'f') {
+ if (yych <= 'e') goto yy1040;
goto yy60;
} else {
- if (yych == 'f') goto yy60;
- goto yy1015;
+ if (yych == 'h') goto yy60;
+ goto yy1040;
}
}
} else {
- if (yych <= 't') {
- if (yych <= 'l') {
- if (yych <= 'h') goto yy60;
- goto yy1015;
- } else {
+ if (yych <= 'w') {
+ if (yych <= 'r') {
if (yych <= 'm') goto yy60;
- if (yych <= 'r') goto yy1015;
+ goto yy1040;
+ } else {
+ if (yych == 'v') goto yy1040;
goto yy60;
}
} else {
- if (yych <= 'w') {
- if (yych <= 'v') goto yy1015;
+ if (yych <= 'y') {
+ if (yych <= 'x') goto yy1040;
goto yy60;
} else {
- if (yych == 'y') goto yy60;
- goto yy1015;
+ if (yych == 0xC2) goto yy60;
+ goto yy1040;
}
}
}
}
-yy1020:
+yy1045:
yyaccept = 24;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych <= 'T') {
- if (yych <= '9') {
- if (yych <= ' ') {
- if (yych == '\t') goto yy60;
- if (yych <= 0x1F) goto yy1015;
- goto yy60;
+ if (yych <= 'V') {
+ if (yych <= 'C') {
+ if (yych <= '/') {
+ if (yych <= '\t') {
+ if (yych <= 0x08) goto yy1040;
+ goto yy60;
+ } else {
+ if (yych == ' ') goto yy60;
+ goto yy1040;
+ }
} else {
- if (yych <= '0') {
- if (yych <= '/') goto yy1015;
- goto yy816;
+ if (yych <= '2') {
+ if (yych <= '0') goto yy841;
+ goto yy810;
} else {
- if (yych <= '2') goto yy785;
- if (yych <= '3') goto yy786;
- goto yy760;
+ if (yych <= '3') goto yy811;
+ if (yych <= '9') goto yy785;
+ goto yy1040;
}
}
} else {
- if (yych <= 'G') {
- if (yych <= 'D') {
- if (yych <= 'C') goto yy1015;
- goto yy60;
+ if (yych <= 'H') {
+ if (yych <= 'E') {
+ if (yych <= 'D') goto yy60;
+ goto yy1040;
} else {
- if (yych == 'F') goto yy60;
- goto yy1015;
+ if (yych == 'G') goto yy1040;
+ goto yy60;
}
} else {
- if (yych <= 'L') {
- if (yych <= 'H') goto yy60;
- goto yy1015;
- } else {
- if (yych <= 'M') goto yy60;
- if (yych <= 'R') goto yy1015;
+ if (yych <= 'M') {
+ if (yych <= 'L') goto yy1040;
goto yy60;
+ } else {
+ if (yych <= 'R') goto yy1040;
+ if (yych <= 'U') goto yy60;
+ goto yy1040;
}
}
}
} else {
- if (yych <= 'g') {
- if (yych <= 'Y') {
- if (yych == 'W') goto yy60;
- if (yych <= 'X') goto yy1015;
- goto yy60;
+ if (yych <= 'l') {
+ if (yych <= 'd') {
+ if (yych <= 'X') {
+ if (yych <= 'W') goto yy60;
+ goto yy1040;
+ } else {
+ if (yych <= 'Y') goto yy60;
+ if (yych <= 'c') goto yy1040;
+ goto yy60;
+ }
} else {
- if (yych <= 'd') {
- if (yych <= 'c') goto yy1015;
+ if (yych <= 'f') {
+ if (yych <= 'e') goto yy1040;
goto yy60;
} else {
- if (yych == 'f') goto yy60;
- goto yy1015;
+ if (yych == 'h') goto yy60;
+ goto yy1040;
}
}
} else {
- if (yych <= 't') {
- if (yych <= 'l') {
- if (yych <= 'h') goto yy60;
- goto yy1015;
- } else {
+ if (yych <= 'w') {
+ if (yych <= 'r') {
if (yych <= 'm') goto yy60;
- if (yych <= 'r') goto yy1015;
+ goto yy1040;
+ } else {
+ if (yych == 'v') goto yy1040;
goto yy60;
}
} else {
- if (yych <= 'w') {
- if (yych <= 'v') goto yy1015;
+ if (yych <= 'y') {
+ if (yych <= 'x') goto yy1040;
goto yy60;
} else {
- if (yych == 'y') goto yy60;
- goto yy1015;
+ if (yych == 0xC2) goto yy60;
+ goto yy1040;
}
}
}
}
-yy1021:
+yy1046:
yyaccept = 24;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych <= 'T') {
- if (yych <= '9') {
- if (yych <= ' ') {
- if (yych == '\t') goto yy60;
- if (yych <= 0x1F) goto yy1015;
- goto yy60;
+ if (yych <= 'V') {
+ if (yych <= 'C') {
+ if (yych <= '/') {
+ if (yych <= '\t') {
+ if (yych <= 0x08) goto yy1040;
+ goto yy60;
+ } else {
+ if (yych == ' ') goto yy60;
+ goto yy1040;
+ }
} else {
- if (yych <= '0') {
- if (yych <= '/') goto yy1015;
- goto yy784;
+ if (yych <= '2') {
+ if (yych <= '0') goto yy809;
+ goto yy810;
} else {
- if (yych <= '2') goto yy785;
- if (yych <= '3') goto yy786;
- goto yy760;
+ if (yych <= '3') goto yy811;
+ if (yych <= '9') goto yy785;
+ goto yy1040;
}
}
} else {
- if (yych <= 'G') {
- if (yych <= 'D') {
- if (yych <= 'C') goto yy1015;
- goto yy60;
+ if (yych <= 'H') {
+ if (yych <= 'E') {
+ if (yych <= 'D') goto yy60;
+ goto yy1040;
} else {
- if (yych == 'F') goto yy60;
- goto yy1015;
+ if (yych == 'G') goto yy1040;
+ goto yy60;
}
} else {
- if (yych <= 'L') {
- if (yych <= 'H') goto yy60;
- goto yy1015;
- } else {
- if (yych <= 'M') goto yy60;
- if (yych <= 'R') goto yy1015;
+ if (yych <= 'M') {
+ if (yych <= 'L') goto yy1040;
goto yy60;
+ } else {
+ if (yych <= 'R') goto yy1040;
+ if (yych <= 'U') goto yy60;
+ goto yy1040;
}
}
}
} else {
- if (yych <= 'g') {
- if (yych <= 'Y') {
- if (yych == 'W') goto yy60;
- if (yych <= 'X') goto yy1015;
- goto yy60;
+ if (yych <= 'l') {
+ if (yych <= 'd') {
+ if (yych <= 'X') {
+ if (yych <= 'W') goto yy60;
+ goto yy1040;
+ } else {
+ if (yych <= 'Y') goto yy60;
+ if (yych <= 'c') goto yy1040;
+ goto yy60;
+ }
} else {
- if (yych <= 'd') {
- if (yych <= 'c') goto yy1015;
+ if (yych <= 'f') {
+ if (yych <= 'e') goto yy1040;
goto yy60;
} else {
- if (yych == 'f') goto yy60;
- goto yy1015;
+ if (yych == 'h') goto yy60;
+ goto yy1040;
}
}
} else {
- if (yych <= 't') {
- if (yych <= 'l') {
- if (yych <= 'h') goto yy60;
- goto yy1015;
- } else {
+ if (yych <= 'w') {
+ if (yych <= 'r') {
if (yych <= 'm') goto yy60;
- if (yych <= 'r') goto yy1015;
+ goto yy1040;
+ } else {
+ if (yych == 'v') goto yy1040;
goto yy60;
}
} else {
- if (yych <= 'w') {
- if (yych <= 'v') goto yy1015;
+ if (yych <= 'y') {
+ if (yych <= 'x') goto yy1040;
goto yy60;
} else {
- if (yych == 'y') goto yy60;
- goto yy1015;
+ if (yych == 0xC2) goto yy60;
+ goto yy1040;
}
}
}
}
-yy1022:
+yy1047:
yyaccept = 10;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '/') {
- if (yych == '.') goto yy1024;
- goto yy430;
+ if (yych == '.') goto yy1049;
+ goto yy455;
} else {
- if (yych <= '9') goto yy1023;
- if (yych <= ':') goto yy1024;
- goto yy430;
+ if (yych <= '9') goto yy1048;
+ if (yych <= ':') goto yy1049;
+ goto yy455;
}
-yy1023:
+yy1048:
yyaccept = 10;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych == '.') goto yy1024;
- if (yych != ':') goto yy430;
-yy1024:
+ if (yych == '.') goto yy1049;
+ if (yych != ':') goto yy455;
+yy1049:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '5') goto yy1025;
- if (yych <= '6') goto yy1026;
- if (yych <= '9') goto yy435;
+ if (yych <= '5') goto yy1050;
+ if (yych <= '6') goto yy1051;
+ if (yych <= '9') goto yy460;
goto yy56;
-yy1025:
+yy1050:
yyaccept = 10;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych == '.') goto yy436;
- if (yych <= '/') goto yy430;
- if (yych <= '9') goto yy435;
- goto yy430;
-yy1026:
+ if (yych == '.') goto yy461;
+ if (yych <= '/') goto yy455;
+ if (yych <= '9') goto yy460;
+ goto yy455;
+yy1051:
yyaccept = 10;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych == '.') goto yy436;
- if (yych == '0') goto yy435;
- goto yy430;
-yy1027:
+ if (yych == '.') goto yy461;
+ if (yych == '0') goto yy460;
+ goto yy455;
+yy1052:
yyaccept = 10;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '.') {
- if (yych <= ',') goto yy430;
- if (yych <= '-') goto yy541;
- goto yy1031;
+ if (yych <= ',') goto yy455;
+ if (yych <= '-') goto yy566;
+ goto yy1056;
} else {
- if (yych <= '/') goto yy430;
- if (yych <= '9') goto yy1030;
- if (yych <= ':') goto yy1024;
- goto yy430;
+ if (yych <= '/') goto yy455;
+ if (yych <= '9') goto yy1055;
+ if (yych <= ':') goto yy1049;
+ goto yy455;
}
-yy1028:
+yy1053:
yyaccept = 10;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '/') {
- if (yych <= ',') goto yy430;
- if (yych <= '-') goto yy541;
- if (yych <= '.') goto yy1031;
- goto yy430;
+ if (yych <= ',') goto yy455;
+ if (yych <= '-') goto yy566;
+ if (yych <= '.') goto yy1056;
+ goto yy455;
} else {
- if (yych <= '2') goto yy1030;
- if (yych <= '9') goto yy1023;
- if (yych <= ':') goto yy1024;
- goto yy430;
+ if (yych <= '2') goto yy1055;
+ if (yych <= '9') goto yy1048;
+ if (yych <= ':') goto yy1049;
+ goto yy455;
}
-yy1029:
+yy1054:
yyaccept = 10;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '.') {
- if (yych <= ',') goto yy430;
- if (yych <= '-') goto yy541;
- goto yy1031;
+ if (yych <= ',') goto yy455;
+ if (yych <= '-') goto yy566;
+ goto yy1056;
} else {
- if (yych <= '/') goto yy430;
- if (yych <= '9') goto yy1023;
- if (yych <= ':') goto yy1024;
- goto yy430;
+ if (yych <= '/') goto yy455;
+ if (yych <= '9') goto yy1048;
+ if (yych <= ':') goto yy1049;
+ goto yy455;
}
-yy1030:
+yy1055:
yyaccept = 10;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '.') {
- if (yych <= ',') goto yy430;
- if (yych <= '-') goto yy541;
+ if (yych <= ',') goto yy455;
+ if (yych <= '-') goto yy566;
} else {
- if (yych == ':') goto yy1024;
- goto yy430;
+ if (yych == ':') goto yy1049;
+ goto yy455;
}
-yy1031:
+yy1056:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '5') goto yy1032;
- if (yych <= '6') goto yy1033;
- if (yych <= '9') goto yy549;
+ if (yych <= '5') goto yy1057;
+ if (yych <= '6') goto yy1058;
+ if (yych <= '9') goto yy574;
goto yy56;
-yy1032:
+yy1057:
yyaccept = 10;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych == '.') goto yy436;
- if (yych <= '/') goto yy430;
- if (yych <= '9') goto yy1034;
- goto yy430;
-yy1033:
+ if (yych == '.') goto yy461;
+ if (yych <= '/') goto yy455;
+ if (yych <= '9') goto yy1059;
+ goto yy455;
+yy1058:
yyaccept = 10;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '/') {
- if (yych == '.') goto yy436;
- goto yy430;
+ if (yych == '.') goto yy461;
+ goto yy455;
} else {
- if (yych <= '0') goto yy1034;
- if (yych <= '9') goto yy550;
- goto yy430;
+ if (yych <= '0') goto yy1059;
+ if (yych <= '9') goto yy575;
+ goto yy455;
}
-yy1034:
+yy1059:
yyaccept = 10;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych == '.') goto yy436;
- if (yych <= '/') goto yy430;
- if (yych <= '9') goto yy544;
- goto yy430;
-yy1035:
+ if (yych == '.') goto yy461;
+ if (yych <= '/') goto yy455;
+ if (yych <= '9') goto yy569;
+ goto yy455;
+yy1060:
yych = *++YYCURSOR;
if (yych <= '9') {
if (yych <= '-') {
- if (yych == '\t') goto yy399;
- if (yych <= ',') goto yy401;
- goto yy990;
+ if (yych == '\t') goto yy424;
+ if (yych <= ',') goto yy426;
+ goto yy1015;
} else {
- if (yych <= '.') goto yy413;
- if (yych <= '/') goto yy411;
- if (yych <= '5') goto yy1005;
- goto yy680;
+ if (yych <= '.') goto yy438;
+ if (yych <= '/') goto yy436;
+ if (yych <= '5') goto yy1030;
+ goto yy705;
}
} else {
if (yych <= 'q') {
- if (yych <= ':') goto yy422;
- if (yych == 'n') goto yy409;
- goto yy401;
+ if (yych <= ':') goto yy447;
+ if (yych == 'n') goto yy434;
+ goto yy426;
} else {
- if (yych <= 'r') goto yy410;
- if (yych <= 's') goto yy403;
- if (yych <= 't') goto yy407;
- goto yy401;
+ if (yych <= 'r') goto yy435;
+ if (yych <= 's') goto yy429;
+ if (yych <= 't') goto yy432;
+ goto yy426;
}
}
-yy1036:
+yy1061:
yych = *++YYCURSOR;
if (yych <= '9') {
if (yych <= '-') {
- if (yych == '\t') goto yy991;
- if (yych <= ',') goto yy993;
- goto yy990;
+ if (yych == '\t') goto yy1016;
+ if (yych <= ',') goto yy1018;
+ goto yy1015;
} else {
- if (yych <= '.') goto yy1003;
- if (yych <= '/') goto yy411;
- if (yych <= '5') goto yy1005;
- goto yy680;
+ if (yych <= '.') goto yy1028;
+ if (yych <= '/') goto yy436;
+ if (yych <= '5') goto yy1030;
+ goto yy705;
}
} else {
if (yych <= 'q') {
- if (yych <= ':') goto yy1004;
- if (yych == 'n') goto yy409;
- goto yy993;
+ if (yych <= ':') goto yy1029;
+ if (yych == 'n') goto yy434;
+ goto yy1018;
} else {
- if (yych <= 'r') goto yy410;
- if (yych <= 's') goto yy403;
- if (yych <= 't') goto yy407;
- goto yy993;
+ if (yych <= 'r') goto yy435;
+ if (yych <= 's') goto yy429;
+ if (yych <= 't') goto yy432;
+ goto yy1018;
}
}
-yy1037:
+yy1062:
yych = *++YYCURSOR;
if (yych <= 'E') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'D') goto yy141;
+ if (yych <= 'D') goto yy166;
}
} else {
if (yych <= 'd') {
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'Z') goto yy166;
if (yych <= '`') goto yy3;
- goto yy141;
+ goto yy166;
} else {
- if (yych <= 'e') goto yy1038;
- if (yych <= 'z') goto yy141;
+ if (yych <= 'e') goto yy1063;
+ if (yych <= 'z') goto yy166;
goto yy3;
}
}
-yy1038:
+yy1063:
yych = *++YYCURSOR;
if (yych <= 'V') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'U') goto yy142;
+ if (yych <= 'U') goto yy167;
}
} else {
if (yych <= 'u') {
- if (yych <= 'Z') goto yy142;
+ if (yych <= 'Z') goto yy167;
if (yych <= '`') goto yy3;
- goto yy142;
+ goto yy167;
} else {
- if (yych <= 'v') goto yy1039;
- if (yych <= 'z') goto yy142;
+ if (yych <= 'v') goto yy1064;
+ if (yych <= 'z') goto yy167;
goto yy3;
}
}
-yy1039:
+yy1064:
yych = *++YYCURSOR;
if (yych <= 'I') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'H') goto yy143;
+ if (yych <= 'H') goto yy168;
}
} else {
if (yych <= 'h') {
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'Z') goto yy168;
if (yych <= '`') goto yy3;
- goto yy143;
+ goto yy168;
} else {
- if (yych <= 'i') goto yy1040;
- if (yych <= 'z') goto yy143;
+ if (yych <= 'i') goto yy1065;
+ if (yych <= 'z') goto yy168;
goto yy3;
}
}
-yy1040:
+yy1065:
yych = *++YYCURSOR;
if (yych <= 'O') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'N') goto yy144;
+ if (yych <= 'N') goto yy169;
}
} else {
if (yych <= 'n') {
- if (yych <= 'Z') goto yy144;
+ if (yych <= 'Z') goto yy169;
if (yych <= '`') goto yy3;
- goto yy144;
+ goto yy169;
} else {
- if (yych <= 'o') goto yy1041;
- if (yych <= 'z') goto yy144;
+ if (yych <= 'o') goto yy1066;
+ if (yych <= 'z') goto yy169;
goto yy3;
}
}
-yy1041:
+yy1066:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'T') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
- if (yych <= 'U') goto yy1042;
+ if (yych <= 'U') goto yy1067;
if (yych != 'u') goto yy3;
}
-yy1042:
+yy1067:
yych = *++YYCURSOR;
- if (yych == 'S') goto yy1043;
+ if (yych == 'S') goto yy1068;
if (yych != 's') goto yy56;
-yy1043:
+yy1068:
yych = *++YYCURSOR;
- if (yych == '\t') goto yy1044;
+ if (yych == '\t') goto yy1069;
if (yych != ' ') goto yy56;
-yy1044:
+yy1069:
++YYCURSOR;
- if ((YYLIMIT - YYCURSOR) < 11) YYFILL(11);
+ if ((YYLIMIT - YYCURSOR) < 12) YYFILL(12);
yych = *YYCURSOR;
-yy1045:
- if (yych <= 'W') {
- if (yych <= 'F') {
+yy1070:
+ if (yych <= 'X') {
+ if (yych <= 'G') {
if (yych <= ' ') {
- if (yych == '\t') goto yy1044;
+ if (yych == '\t') goto yy1069;
if (yych <= 0x1F) goto yy56;
- goto yy1044;
+ goto yy1069;
} else {
- if (yych == 'D') goto yy1049;
- if (yych <= 'E') goto yy56;
- goto yy1050;
+ if (yych <= 'D') {
+ if (yych <= 'C') goto yy56;
+ goto yy1076;
+ } else {
+ if (yych == 'F') goto yy1077;
+ goto yy56;
+ }
}
} else {
- if (yych <= 'M') {
- if (yych == 'H') goto yy1048;
- if (yych <= 'L') goto yy56;
- goto yy1047;
- } else {
- if (yych <= 'S') {
+ if (yych <= 'S') {
+ if (yych <= 'L') {
+ if (yych <= 'H') goto yy1075;
+ goto yy56;
+ } else {
+ if (yych <= 'M') goto yy1071;
if (yych <= 'R') goto yy56;
+ goto yy1074;
+ }
+ } else {
+ if (yych <= 'U') {
+ if (yych <= 'T') goto yy1080;
+ goto yy1073;
} else {
- if (yych <= 'T') goto yy1053;
- if (yych <= 'V') goto yy56;
- goto yy1052;
+ if (yych == 'W') goto yy1079;
+ goto yy56;
}
}
}
} else {
- if (yych <= 'l') {
- if (yych <= 'd') {
- if (yych == 'Y') goto yy1051;
- if (yych <= 'c') goto yy56;
- goto yy1049;
- } else {
- if (yych <= 'f') {
+ if (yych <= 'r') {
+ if (yych <= 'f') {
+ if (yych <= 'c') {
+ if (yych <= 'Y') goto yy1078;
+ goto yy56;
+ } else {
+ if (yych <= 'd') goto yy1076;
if (yych <= 'e') goto yy56;
- goto yy1050;
+ goto yy1077;
+ }
+ } else {
+ if (yych <= 'h') {
+ if (yych <= 'g') goto yy56;
+ goto yy1075;
} else {
- if (yych == 'h') goto yy1048;
- goto yy56;
+ if (yych != 'm') goto yy56;
}
}
} else {
- if (yych <= 't') {
- if (yych <= 'm') goto yy1047;
- if (yych <= 'r') goto yy56;
- if (yych >= 't') goto yy1053;
- } else {
- if (yych <= 'w') {
+ if (yych <= 'w') {
+ if (yych <= 't') {
+ if (yych <= 's') goto yy1074;
+ goto yy1080;
+ } else {
+ if (yych <= 'u') goto yy1073;
if (yych <= 'v') goto yy56;
- goto yy1052;
+ goto yy1079;
+ }
+ } else {
+ if (yych <= 'y') {
+ if (yych <= 'x') goto yy56;
+ goto yy1078;
} else {
- if (yych == 'y') goto yy1051;
+ if (yych == 0xC2) goto yy1072;
goto yy56;
}
}
}
}
-yy1046:
+yy1071:
yych = *++YYCURSOR;
- if (yych <= 'U') {
- if (yych <= 'D') {
- if (yych == 'A') goto yy1118;
+ if (yych <= 'S') {
+ if (yych <= 'N') {
+ if (yych == 'I') goto yy1155;
goto yy56;
} else {
- if (yych <= 'E') goto yy1119;
- if (yych <= 'T') goto yy56;
- goto yy1117;
+ if (yych <= 'O') goto yy1154;
+ if (yych <= 'R') goto yy56;
+ goto yy1156;
}
} else {
- if (yych <= 'd') {
- if (yych == 'a') goto yy1118;
+ if (yych <= 'n') {
+ if (yych == 'i') goto yy1155;
goto yy56;
} else {
- if (yych <= 'e') goto yy1119;
- if (yych == 'u') goto yy1117;
+ if (yych <= 'o') goto yy1154;
+ if (yych == 's') goto yy1156;
goto yy56;
}
}
-yy1047:
+yy1072:
yych = *++YYCURSOR;
- if (yych <= 'O') {
- if (yych == 'I') goto yy1109;
- if (yych <= 'N') goto yy56;
- goto yy1108;
+ if (yych == 0xB5) goto yy1151;
+ goto yy56;
+yy1073:
+ yych = *++YYCURSOR;
+ if (yych == 'S') goto yy1149;
+ if (yych == 's') goto yy1149;
+ goto yy56;
+yy1074:
+ yych = *++YYCURSOR;
+ if (yych <= 'U') {
+ if (yych <= 'D') {
+ if (yych == 'A') goto yy1136;
+ goto yy56;
+ } else {
+ if (yych <= 'E') goto yy1137;
+ if (yych <= 'T') goto yy56;
+ goto yy1135;
+ }
} else {
- if (yych <= 'i') {
- if (yych <= 'h') goto yy56;
- goto yy1109;
+ if (yych <= 'd') {
+ if (yych == 'a') goto yy1136;
+ goto yy56;
} else {
- if (yych == 'o') goto yy1108;
+ if (yych <= 'e') goto yy1137;
+ if (yych == 'u') goto yy1135;
goto yy56;
}
}
-yy1048:
+yy1075:
yych = *++YYCURSOR;
- if (yych == 'O') goto yy1106;
- if (yych == 'o') goto yy1106;
+ if (yych == 'O') goto yy1133;
+ if (yych == 'o') goto yy1133;
goto yy56;
-yy1049:
+yy1076:
yych = *++YYCURSOR;
- if (yych == 'A') goto yy1105;
- if (yych == 'a') goto yy1105;
+ if (yych == 'A') goto yy1132;
+ if (yych == 'a') goto yy1132;
goto yy56;
-yy1050:
+yy1077:
yych = *++YYCURSOR;
if (yych <= 'R') {
- if (yych == 'O') goto yy1090;
+ if (yych == 'O') goto yy1117;
if (yych <= 'Q') goto yy56;
- goto yy1089;
+ goto yy1116;
} else {
if (yych <= 'o') {
if (yych <= 'n') goto yy56;
- goto yy1090;
+ goto yy1117;
} else {
- if (yych == 'r') goto yy1089;
+ if (yych == 'r') goto yy1116;
goto yy56;
}
}
-yy1051:
+yy1078:
yych = *++YYCURSOR;
- if (yych == 'E') goto yy1086;
- if (yych == 'e') goto yy1086;
+ if (yych == 'E') goto yy1113;
+ if (yych == 'e') goto yy1113;
goto yy56;
-yy1052:
+yy1079:
yych = *++YYCURSOR;
- if (yych == 'E') goto yy1072;
- if (yych == 'e') goto yy1072;
+ if (yych == 'E') goto yy1099;
+ if (yych == 'e') goto yy1099;
goto yy56;
-yy1053:
+yy1080:
yych = *++YYCURSOR;
if (yych <= 'U') {
- if (yych == 'H') goto yy1054;
+ if (yych == 'H') goto yy1081;
if (yych <= 'T') goto yy56;
- goto yy1055;
+ goto yy1082;
} else {
if (yych <= 'h') {
if (yych <= 'g') goto yy56;
} else {
- if (yych == 'u') goto yy1055;
+ if (yych == 'u') goto yy1082;
goto yy56;
}
}
-yy1054:
+yy1081:
yych = *++YYCURSOR;
- if (yych == 'U') goto yy1067;
- if (yych == 'u') goto yy1067;
+ if (yych == 'U') goto yy1094;
+ if (yych == 'u') goto yy1094;
goto yy56;
-yy1055:
+yy1082:
yych = *++YYCURSOR;
- if (yych == 'E') goto yy1056;
+ if (yych == 'E') goto yy1083;
if (yych != 'e') goto yy56;
-yy1056:
+yy1083:
yyaccept = 25;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= ' ') {
- if (yych == '\t') goto yy1058;
- if (yych >= ' ') goto yy1058;
+ if (yych == '\t') goto yy1085;
+ if (yych >= ' ') goto yy1085;
} else {
if (yych <= 'S') {
- if (yych >= 'S') goto yy1060;
+ if (yych >= 'S') goto yy1087;
} else {
- if (yych == 's') goto yy1060;
+ if (yych == 's') goto yy1087;
}
}
-yy1057:
-#line 1603 "ext/date/lib/parse_date.re"
+yy1084:
+#line 1646 "ext/date/lib/parse_date.re"
{
timelib_sll i;
int behavior = 0;
@@ -14658,49 +15453,49 @@ yy1057:
TIMELIB_DEINIT;
return TIMELIB_RELATIVE;
}
-#line 14662 "ext/date/lib/parse_date.c"
-yy1058:
+#line 15457 "ext/date/lib/parse_date.c"
+yy1085:
++YYCURSOR;
if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2);
yych = *YYCURSOR;
if (yych <= ' ') {
- if (yych == '\t') goto yy1058;
+ if (yych == '\t') goto yy1085;
if (yych <= 0x1F) goto yy56;
- goto yy1058;
+ goto yy1085;
} else {
if (yych <= 'O') {
if (yych <= 'N') goto yy56;
- goto yy1064;
+ goto yy1091;
} else {
- if (yych == 'o') goto yy1064;
+ if (yych == 'o') goto yy1091;
goto yy56;
}
}
-yy1060:
+yy1087:
yych = *++YYCURSOR;
- if (yych == 'D') goto yy1061;
+ if (yych == 'D') goto yy1088;
if (yych != 'd') goto yy56;
-yy1061:
+yy1088:
yych = *++YYCURSOR;
- if (yych == 'A') goto yy1062;
+ if (yych == 'A') goto yy1089;
if (yych != 'a') goto yy56;
-yy1062:
+yy1089:
yych = *++YYCURSOR;
- if (yych == 'Y') goto yy1063;
+ if (yych == 'Y') goto yy1090;
if (yych != 'y') goto yy56;
-yy1063:
+yy1090:
yyaccept = 25;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych == '\t') goto yy1058;
- if (yych == ' ') goto yy1058;
- goto yy1057;
-yy1064:
+ if (yych == '\t') goto yy1085;
+ if (yych == ' ') goto yy1085;
+ goto yy1084;
+yy1091:
yych = *++YYCURSOR;
- if (yych == 'F') goto yy1065;
+ if (yych == 'F') goto yy1092;
if (yych != 'f') goto yy56;
-yy1065:
+yy1092:
++YYCURSOR;
-#line 1076 "ext/date/lib/parse_date.re"
+#line 1119 "ext/date/lib/parse_date.re"
{
timelib_sll i;
int behavior = 0;
@@ -14721,100 +15516,100 @@ yy1065:
TIMELIB_DEINIT;
return TIMELIB_WEEK_DAY_OF_MONTH;
}
-#line 14725 "ext/date/lib/parse_date.c"
-yy1067:
+#line 15520 "ext/date/lib/parse_date.c"
+yy1094:
yyaccept = 25;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= ' ') {
- if (yych == '\t') goto yy1058;
- if (yych <= 0x1F) goto yy1057;
- goto yy1058;
+ if (yych == '\t') goto yy1085;
+ if (yych <= 0x1F) goto yy1084;
+ goto yy1085;
} else {
if (yych <= 'R') {
- if (yych <= 'Q') goto yy1057;
+ if (yych <= 'Q') goto yy1084;
} else {
- if (yych != 'r') goto yy1057;
+ if (yych != 'r') goto yy1084;
}
}
yych = *++YYCURSOR;
- if (yych == 'S') goto yy1069;
+ if (yych == 'S') goto yy1096;
if (yych != 's') goto yy56;
-yy1069:
+yy1096:
yych = *++YYCURSOR;
- if (yych == 'D') goto yy1070;
+ if (yych == 'D') goto yy1097;
if (yych != 'd') goto yy56;
-yy1070:
+yy1097:
yych = *++YYCURSOR;
- if (yych == 'A') goto yy1071;
+ if (yych == 'A') goto yy1098;
if (yych != 'a') goto yy56;
-yy1071:
+yy1098:
yych = *++YYCURSOR;
- if (yych == 'Y') goto yy1063;
- if (yych == 'y') goto yy1063;
+ if (yych == 'Y') goto yy1090;
+ if (yych == 'y') goto yy1090;
goto yy56;
-yy1072:
+yy1099:
yych = *++YYCURSOR;
if (yych <= 'E') {
if (yych <= 'C') goto yy56;
- if (yych <= 'D') goto yy1074;
+ if (yych <= 'D') goto yy1101;
} else {
if (yych <= 'c') goto yy56;
- if (yych <= 'd') goto yy1074;
+ if (yych <= 'd') goto yy1101;
if (yych >= 'f') goto yy56;
}
yych = *++YYCURSOR;
- if (yych == 'K') goto yy1080;
- if (yych == 'k') goto yy1080;
+ if (yych == 'K') goto yy1107;
+ if (yych == 'k') goto yy1107;
goto yy56;
-yy1074:
+yy1101:
yyaccept = 25;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= ' ') {
- if (yych == '\t') goto yy1058;
- if (yych <= 0x1F) goto yy1057;
- goto yy1058;
+ if (yych == '\t') goto yy1085;
+ if (yych <= 0x1F) goto yy1084;
+ goto yy1085;
} else {
if (yych <= 'N') {
- if (yych <= 'M') goto yy1057;
+ if (yych <= 'M') goto yy1084;
} else {
- if (yych != 'n') goto yy1057;
+ if (yych != 'n') goto yy1084;
}
}
yych = *++YYCURSOR;
- if (yych == 'E') goto yy1076;
+ if (yych == 'E') goto yy1103;
if (yych != 'e') goto yy56;
-yy1076:
+yy1103:
yych = *++YYCURSOR;
- if (yych == 'S') goto yy1077;
+ if (yych == 'S') goto yy1104;
if (yych != 's') goto yy56;
-yy1077:
+yy1104:
yych = *++YYCURSOR;
- if (yych == 'D') goto yy1078;
+ if (yych == 'D') goto yy1105;
if (yych != 'd') goto yy56;
-yy1078:
+yy1105:
yych = *++YYCURSOR;
- if (yych == 'A') goto yy1079;
+ if (yych == 'A') goto yy1106;
if (yych != 'a') goto yy56;
-yy1079:
+yy1106:
yych = *++YYCURSOR;
- if (yych == 'Y') goto yy1063;
- if (yych == 'y') goto yy1063;
+ if (yych == 'Y') goto yy1090;
+ if (yych == 'y') goto yy1090;
goto yy56;
-yy1080:
+yy1107:
yyaccept = 26;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'S') {
- if (yych == 'D') goto yy1083;
- if (yych >= 'S') goto yy1082;
+ if (yych == 'D') goto yy1110;
+ if (yych >= 'S') goto yy1109;
} else {
if (yych <= 'd') {
- if (yych >= 'd') goto yy1083;
+ if (yych >= 'd') goto yy1110;
} else {
- if (yych == 's') goto yy1082;
+ if (yych == 's') goto yy1109;
}
}
-yy1081:
-#line 1579 "ext/date/lib/parse_date.re"
+yy1108:
+#line 1622 "ext/date/lib/parse_date.re"
{
timelib_sll i;
int behavior = 0;
@@ -14837,2531 +15632,2666 @@ yy1081:
TIMELIB_DEINIT;
return TIMELIB_RELATIVE;
}
-#line 14841 "ext/date/lib/parse_date.c"
-yy1082:
+#line 15636 "ext/date/lib/parse_date.c"
+yy1109:
yych = *++YYCURSOR;
- goto yy1057;
-yy1083:
+ goto yy1084;
+yy1110:
yych = *++YYCURSOR;
- if (yych == 'A') goto yy1084;
+ if (yych == 'A') goto yy1111;
if (yych != 'a') goto yy56;
-yy1084:
+yy1111:
yych = *++YYCURSOR;
- if (yych == 'Y') goto yy1085;
+ if (yych == 'Y') goto yy1112;
if (yych != 'y') goto yy56;
-yy1085:
+yy1112:
yych = *++YYCURSOR;
- if (yych == 'S') goto yy1082;
- if (yych == 's') goto yy1082;
- goto yy1057;
-yy1086:
+ if (yych == 'S') goto yy1109;
+ if (yych == 's') goto yy1109;
+ goto yy1084;
+yy1113:
yych = *++YYCURSOR;
- if (yych == 'A') goto yy1087;
+ if (yych == 'A') goto yy1114;
if (yych != 'a') goto yy56;
-yy1087:
+yy1114:
yych = *++YYCURSOR;
- if (yych == 'R') goto yy1088;
+ if (yych == 'R') goto yy1115;
if (yych != 'r') goto yy56;
-yy1088:
+yy1115:
yych = *++YYCURSOR;
- if (yych == 'S') goto yy1082;
- if (yych == 's') goto yy1082;
- goto yy1057;
-yy1089:
+ if (yych == 'S') goto yy1109;
+ if (yych == 's') goto yy1109;
+ goto yy1084;
+yy1116:
yych = *++YYCURSOR;
- if (yych == 'I') goto yy1102;
- if (yych == 'i') goto yy1102;
+ if (yych == 'I') goto yy1129;
+ if (yych == 'i') goto yy1129;
goto yy56;
-yy1090:
+yy1117:
yych = *++YYCURSOR;
- if (yych == 'R') goto yy1091;
+ if (yych == 'R') goto yy1118;
if (yych != 'r') goto yy56;
-yy1091:
+yy1118:
yych = *++YYCURSOR;
- if (yych == 'T') goto yy1092;
+ if (yych == 'T') goto yy1119;
if (yych != 't') goto yy56;
-yy1092:
+yy1119:
yych = *++YYCURSOR;
if (yych <= 'N') {
- if (yych == 'H') goto yy1094;
+ if (yych == 'H') goto yy1121;
if (yych <= 'M') goto yy56;
} else {
if (yych <= 'h') {
if (yych <= 'g') goto yy56;
- goto yy1094;
+ goto yy1121;
} else {
if (yych != 'n') goto yy56;
}
}
yych = *++YYCURSOR;
- if (yych == 'I') goto yy1099;
- if (yych == 'i') goto yy1099;
+ if (yych == 'I') goto yy1126;
+ if (yych == 'i') goto yy1126;
goto yy56;
-yy1094:
+yy1121:
yych = *++YYCURSOR;
- if (yych == 'N') goto yy1095;
+ if (yych == 'N') goto yy1122;
if (yych != 'n') goto yy56;
-yy1095:
+yy1122:
yych = *++YYCURSOR;
- if (yych == 'I') goto yy1096;
+ if (yych == 'I') goto yy1123;
if (yych != 'i') goto yy56;
-yy1096:
+yy1123:
yych = *++YYCURSOR;
- if (yych == 'G') goto yy1097;
+ if (yych == 'G') goto yy1124;
if (yych != 'g') goto yy56;
-yy1097:
+yy1124:
yych = *++YYCURSOR;
- if (yych == 'H') goto yy1098;
+ if (yych == 'H') goto yy1125;
if (yych != 'h') goto yy56;
-yy1098:
+yy1125:
yych = *++YYCURSOR;
- if (yych == 'T') goto yy1088;
- if (yych == 't') goto yy1088;
+ if (yych == 'T') goto yy1115;
+ if (yych == 't') goto yy1115;
goto yy56;
-yy1099:
+yy1126:
yych = *++YYCURSOR;
- if (yych == 'G') goto yy1100;
+ if (yych == 'G') goto yy1127;
if (yych != 'g') goto yy56;
-yy1100:
+yy1127:
yych = *++YYCURSOR;
- if (yych == 'H') goto yy1101;
+ if (yych == 'H') goto yy1128;
if (yych != 'h') goto yy56;
-yy1101:
+yy1128:
yych = *++YYCURSOR;
- if (yych == 'T') goto yy1088;
- if (yych == 't') goto yy1088;
+ if (yych == 'T') goto yy1115;
+ if (yych == 't') goto yy1115;
goto yy56;
-yy1102:
+yy1129:
yyaccept = 25;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= ' ') {
- if (yych == '\t') goto yy1058;
- if (yych <= 0x1F) goto yy1057;
- goto yy1058;
+ if (yych == '\t') goto yy1085;
+ if (yych <= 0x1F) goto yy1084;
+ goto yy1085;
} else {
if (yych <= 'D') {
- if (yych <= 'C') goto yy1057;
+ if (yych <= 'C') goto yy1084;
} else {
- if (yych != 'd') goto yy1057;
+ if (yych != 'd') goto yy1084;
}
}
yych = *++YYCURSOR;
- if (yych == 'A') goto yy1104;
+ if (yych == 'A') goto yy1131;
if (yych != 'a') goto yy56;
-yy1104:
+yy1131:
yych = *++YYCURSOR;
- if (yych == 'Y') goto yy1063;
- if (yych == 'y') goto yy1063;
+ if (yych == 'Y') goto yy1090;
+ if (yych == 'y') goto yy1090;
goto yy56;
-yy1105:
+yy1132:
yych = *++YYCURSOR;
- if (yych == 'Y') goto yy1088;
- if (yych == 'y') goto yy1088;
+ if (yych == 'Y') goto yy1115;
+ if (yych == 'y') goto yy1115;
goto yy56;
-yy1106:
+yy1133:
yych = *++YYCURSOR;
- if (yych == 'U') goto yy1107;
+ if (yych == 'U') goto yy1134;
if (yych != 'u') goto yy56;
-yy1107:
+yy1134:
yych = *++YYCURSOR;
- if (yych == 'R') goto yy1088;
- if (yych == 'r') goto yy1088;
+ if (yych == 'R') goto yy1115;
+ if (yych == 'r') goto yy1115;
goto yy56;
-yy1108:
+yy1135:
yych = *++YYCURSOR;
- if (yych == 'N') goto yy1113;
- if (yych == 'n') goto yy1113;
+ if (yych == 'N') goto yy1146;
+ if (yych == 'n') goto yy1146;
goto yy56;
-yy1109:
+yy1136:
yych = *++YYCURSOR;
- if (yych == 'N') goto yy1110;
- if (yych != 'n') goto yy56;
-yy1110:
+ if (yych == 'T') goto yy1141;
+ if (yych == 't') goto yy1141;
+ goto yy56;
+yy1137:
+ yych = *++YYCURSOR;
+ if (yych == 'C') goto yy1138;
+ if (yych != 'c') goto yy56;
+yy1138:
yyaccept = 25;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych <= 'U') {
- if (yych == 'S') goto yy1082;
- if (yych <= 'T') goto yy1057;
+ if (yych <= 'S') {
+ if (yych == 'O') goto yy1139;
+ if (yych <= 'R') goto yy1084;
+ goto yy1109;
} else {
- if (yych <= 's') {
- if (yych <= 'r') goto yy1057;
- goto yy1082;
+ if (yych <= 'o') {
+ if (yych <= 'n') goto yy1084;
} else {
- if (yych != 'u') goto yy1057;
+ if (yych == 's') goto yy1109;
+ goto yy1084;
}
}
+yy1139:
yych = *++YYCURSOR;
- if (yych == 'T') goto yy1112;
- if (yych != 't') goto yy56;
-yy1112:
+ if (yych == 'N') goto yy1140;
+ if (yych != 'n') goto yy56;
+yy1140:
yych = *++YYCURSOR;
- if (yych == 'E') goto yy1088;
- if (yych == 'e') goto yy1088;
+ if (yych == 'D') goto yy1115;
+ if (yych == 'd') goto yy1115;
goto yy56;
-yy1113:
+yy1141:
yyaccept = 25;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych <= 'D') {
- if (yych <= 0x1F) {
- if (yych == '\t') goto yy1058;
- goto yy1057;
+ if (yych <= ' ') {
+ if (yych == '\t') goto yy1085;
+ if (yych <= 0x1F) goto yy1084;
+ goto yy1085;
+ } else {
+ if (yych <= 'U') {
+ if (yych <= 'T') goto yy1084;
} else {
- if (yych <= ' ') goto yy1058;
- if (yych <= 'C') goto yy1057;
+ if (yych != 'u') goto yy1084;
}
+ }
+ yych = *++YYCURSOR;
+ if (yych == 'R') goto yy1143;
+ if (yych != 'r') goto yy56;
+yy1143:
+ yych = *++YYCURSOR;
+ if (yych == 'D') goto yy1144;
+ if (yych != 'd') goto yy56;
+yy1144:
+ yych = *++YYCURSOR;
+ if (yych == 'A') goto yy1145;
+ if (yych != 'a') goto yy56;
+yy1145:
+ yych = *++YYCURSOR;
+ if (yych == 'Y') goto yy1090;
+ if (yych == 'y') goto yy1090;
+ goto yy56;
+yy1146:
+ yyaccept = 25;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych <= ' ') {
+ if (yych == '\t') goto yy1085;
+ if (yych <= 0x1F) goto yy1084;
+ goto yy1085;
} else {
- if (yych <= 'c') {
- if (yych == 'T') goto yy1115;
- goto yy1057;
+ if (yych <= 'D') {
+ if (yych <= 'C') goto yy1084;
} else {
- if (yych <= 'd') goto yy1114;
- if (yych == 't') goto yy1115;
- goto yy1057;
+ if (yych != 'd') goto yy1084;
}
}
-yy1114:
yych = *++YYCURSOR;
- if (yych == 'A') goto yy1116;
- if (yych == 'a') goto yy1116;
- goto yy56;
-yy1115:
+ if (yych == 'A') goto yy1148;
+ if (yych != 'a') goto yy56;
+yy1148:
yych = *++YYCURSOR;
- if (yych == 'H') goto yy1088;
- if (yych == 'h') goto yy1088;
+ if (yych == 'Y') goto yy1090;
+ if (yych == 'y') goto yy1090;
goto yy56;
-yy1116:
+yy1149:
+ yych = *++YYCURSOR;
+ if (yych == 'E') goto yy1150;
+ if (yych != 'e') goto yy56;
+yy1150:
yych = *++YYCURSOR;
- if (yych == 'Y') goto yy1063;
- if (yych == 'y') goto yy1063;
+ if (yych == 'C') goto yy1115;
+ if (yych == 'c') goto yy1115;
goto yy56;
-yy1117:
+yy1151:
+ yych = *++YYCURSOR;
+ if (yych == 'S') goto yy1152;
+ if (yych != 's') goto yy56;
+yy1152:
+ yyaccept = 25;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych == 'E') goto yy1153;
+ if (yych != 'e') goto yy1084;
+yy1153:
yych = *++YYCURSOR;
- if (yych == 'N') goto yy1128;
- if (yych == 'n') goto yy1128;
+ if (yych == 'C') goto yy1115;
+ if (yych == 'c') goto yy1115;
goto yy56;
-yy1118:
+yy1154:
yych = *++YYCURSOR;
- if (yych == 'T') goto yy1123;
- if (yych == 't') goto yy1123;
+ if (yych == 'N') goto yy1177;
+ if (yych == 'n') goto yy1177;
goto yy56;
-yy1119:
+yy1155:
yych = *++YYCURSOR;
- if (yych == 'C') goto yy1120;
- if (yych != 'c') goto yy56;
-yy1120:
- yyaccept = 25;
- yych = *(YYMARKER = ++YYCURSOR);
- if (yych <= 'S') {
- if (yych == 'O') goto yy1121;
- if (yych <= 'R') goto yy1057;
- goto yy1082;
+ if (yych <= 'N') {
+ if (yych <= 'K') {
+ if (yych == 'C') goto yy1159;
+ goto yy56;
+ } else {
+ if (yych <= 'L') goto yy1158;
+ if (yych <= 'M') goto yy56;
+ goto yy1160;
+ }
} else {
- if (yych <= 'o') {
- if (yych <= 'n') goto yy1057;
+ if (yych <= 'k') {
+ if (yych == 'c') goto yy1159;
+ goto yy56;
} else {
- if (yych == 's') goto yy1082;
- goto yy1057;
+ if (yych <= 'l') goto yy1158;
+ if (yych == 'n') goto yy1160;
+ goto yy56;
}
}
-yy1121:
+yy1156:
+ yyaccept = 25;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych == 'E') goto yy1157;
+ if (yych != 'e') goto yy1084;
+yy1157:
yych = *++YYCURSOR;
- if (yych == 'N') goto yy1122;
- if (yych != 'n') goto yy56;
-yy1122:
+ if (yych == 'C') goto yy1115;
+ if (yych == 'c') goto yy1115;
+ goto yy56;
+yy1158:
yych = *++YYCURSOR;
- if (yych == 'D') goto yy1088;
- if (yych == 'd') goto yy1088;
+ if (yych == 'L') goto yy1170;
+ if (yych == 'l') goto yy1170;
goto yy56;
-yy1123:
+yy1159:
+ yych = *++YYCURSOR;
+ if (yych == 'R') goto yy1163;
+ if (yych == 'r') goto yy1163;
+ goto yy56;
+yy1160:
yyaccept = 25;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych <= ' ') {
- if (yych == '\t') goto yy1058;
- if (yych <= 0x1F) goto yy1057;
- goto yy1058;
+ if (yych <= 'U') {
+ if (yych == 'S') goto yy1109;
+ if (yych <= 'T') goto yy1084;
} else {
- if (yych <= 'U') {
- if (yych <= 'T') goto yy1057;
+ if (yych <= 's') {
+ if (yych <= 'r') goto yy1084;
+ goto yy1109;
} else {
- if (yych != 'u') goto yy1057;
+ if (yych != 'u') goto yy1084;
}
}
yych = *++YYCURSOR;
- if (yych == 'R') goto yy1125;
- if (yych != 'r') goto yy56;
-yy1125:
+ if (yych == 'T') goto yy1162;
+ if (yych != 't') goto yy56;
+yy1162:
yych = *++YYCURSOR;
- if (yych == 'D') goto yy1126;
- if (yych != 'd') goto yy56;
-yy1126:
+ if (yych == 'E') goto yy1115;
+ if (yych == 'e') goto yy1115;
+ goto yy56;
+yy1163:
yych = *++YYCURSOR;
- if (yych == 'A') goto yy1127;
- if (yych != 'a') goto yy56;
-yy1127:
+ if (yych == 'O') goto yy1164;
+ if (yych != 'o') goto yy56;
+yy1164:
yych = *++YYCURSOR;
- if (yych == 'Y') goto yy1063;
- if (yych == 'y') goto yy1063;
+ if (yych == 'S') goto yy1165;
+ if (yych != 's') goto yy56;
+yy1165:
+ yych = *++YYCURSOR;
+ if (yych == 'E') goto yy1166;
+ if (yych != 'e') goto yy56;
+yy1166:
+ yych = *++YYCURSOR;
+ if (yych == 'C') goto yy1167;
+ if (yych != 'c') goto yy56;
+yy1167:
+ yych = *++YYCURSOR;
+ if (yych == 'O') goto yy1168;
+ if (yych != 'o') goto yy56;
+yy1168:
+ yych = *++YYCURSOR;
+ if (yych == 'N') goto yy1169;
+ if (yych != 'n') goto yy56;
+yy1169:
+ yych = *++YYCURSOR;
+ if (yych == 'D') goto yy1115;
+ if (yych == 'd') goto yy1115;
goto yy56;
-yy1128:
+yy1170:
+ yych = *++YYCURSOR;
+ if (yych == 'I') goto yy1171;
+ if (yych != 'i') goto yy56;
+yy1171:
+ yych = *++YYCURSOR;
+ if (yych == 'S') goto yy1172;
+ if (yych != 's') goto yy56;
+yy1172:
+ yych = *++YYCURSOR;
+ if (yych == 'E') goto yy1173;
+ if (yych != 'e') goto yy56;
+yy1173:
+ yych = *++YYCURSOR;
+ if (yych == 'C') goto yy1174;
+ if (yych != 'c') goto yy56;
+yy1174:
+ yych = *++YYCURSOR;
+ if (yych == 'O') goto yy1175;
+ if (yych != 'o') goto yy56;
+yy1175:
+ yych = *++YYCURSOR;
+ if (yych == 'N') goto yy1176;
+ if (yych != 'n') goto yy56;
+yy1176:
+ yych = *++YYCURSOR;
+ if (yych == 'D') goto yy1115;
+ if (yych == 'd') goto yy1115;
+ goto yy56;
+yy1177:
yyaccept = 25;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych <= ' ') {
- if (yych == '\t') goto yy1058;
- if (yych <= 0x1F) goto yy1057;
- goto yy1058;
+ if (yych <= 'D') {
+ if (yych <= 0x1F) {
+ if (yych == '\t') goto yy1085;
+ goto yy1084;
+ } else {
+ if (yych <= ' ') goto yy1085;
+ if (yych <= 'C') goto yy1084;
+ }
} else {
- if (yych <= 'D') {
- if (yych <= 'C') goto yy1057;
+ if (yych <= 'c') {
+ if (yych == 'T') goto yy1179;
+ goto yy1084;
} else {
- if (yych != 'd') goto yy1057;
+ if (yych <= 'd') goto yy1178;
+ if (yych == 't') goto yy1179;
+ goto yy1084;
}
}
+yy1178:
yych = *++YYCURSOR;
- if (yych == 'A') goto yy1130;
- if (yych != 'a') goto yy56;
-yy1130:
+ if (yych == 'A') goto yy1180;
+ if (yych == 'a') goto yy1180;
+ goto yy56;
+yy1179:
yych = *++YYCURSOR;
- if (yych == 'Y') goto yy1063;
- if (yych == 'y') goto yy1063;
+ if (yych == 'H') goto yy1115;
+ if (yych == 'h') goto yy1115;
goto yy56;
-yy1131:
+yy1180:
+ yych = *++YYCURSOR;
+ if (yych == 'Y') goto yy1090;
+ if (yych == 'y') goto yy1090;
+ goto yy56;
+yy1181:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'D') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy141;
+ goto yy166;
}
} else {
if (yych <= '_') {
- if (yych <= 'E') goto yy1038;
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'E') goto yy1063;
+ if (yych <= 'Z') goto yy166;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'd') {
if (yych <= '`') goto yy3;
- goto yy146;
+ goto yy171;
} else {
- if (yych <= 'e') goto yy1132;
- if (yych <= 'z') goto yy146;
+ if (yych <= 'e') goto yy1182;
+ if (yych <= 'z') goto yy171;
goto yy3;
}
}
}
-yy1132:
+yy1182:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'U') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy142;
+ goto yy167;
}
} else {
if (yych <= '_') {
- if (yych <= 'V') goto yy1039;
- if (yych <= 'Z') goto yy142;
+ if (yych <= 'V') goto yy1064;
+ if (yych <= 'Z') goto yy167;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'u') {
if (yych <= '`') goto yy3;
- goto yy150;
+ goto yy175;
} else {
- if (yych <= 'v') goto yy1133;
- if (yych <= 'z') goto yy150;
+ if (yych <= 'v') goto yy1183;
+ if (yych <= 'z') goto yy175;
goto yy3;
}
}
}
-yy1133:
+yy1183:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'H') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy143;
+ goto yy168;
}
} else {
if (yych <= '_') {
- if (yych <= 'I') goto yy1040;
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'I') goto yy1065;
+ if (yych <= 'Z') goto yy168;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'h') {
if (yych <= '`') goto yy3;
- goto yy151;
+ goto yy176;
} else {
- if (yych <= 'i') goto yy1134;
- if (yych <= 'z') goto yy151;
+ if (yych <= 'i') goto yy1184;
+ if (yych <= 'z') goto yy176;
goto yy3;
}
}
}
-yy1134:
+yy1184:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'N') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy144;
+ goto yy169;
}
} else {
if (yych <= '_') {
- if (yych <= 'O') goto yy1041;
- if (yych <= 'Z') goto yy144;
+ if (yych <= 'O') goto yy1066;
+ if (yych <= 'Z') goto yy169;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'n') {
if (yych <= '`') goto yy3;
- goto yy152;
+ goto yy177;
} else {
- if (yych <= 'o') goto yy1135;
- if (yych <= 'z') goto yy152;
+ if (yych <= 'o') goto yy1185;
+ if (yych <= 'z') goto yy177;
goto yy3;
}
}
}
-yy1135:
+yy1185:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'T') {
if (yych <= ',') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
if (yych == '.') goto yy3;
- if (yych <= '/') goto yy147;
+ if (yych <= '/') goto yy172;
goto yy3;
}
} else {
if (yych <= '`') {
- if (yych <= 'U') goto yy1042;
- if (yych == '_') goto yy147;
+ if (yych <= 'U') goto yy1067;
+ if (yych == '_') goto yy172;
goto yy3;
} else {
- if (yych == 'u') goto yy1136;
- if (yych <= 'z') goto yy153;
+ if (yych == 'u') goto yy1186;
+ if (yych <= 'z') goto yy178;
goto yy3;
}
}
-yy1136:
+yy1186:
yych = *++YYCURSOR;
- if (yych == 'S') goto yy1043;
- if (yych != 's') goto yy154;
+ if (yych == 'S') goto yy1068;
+ if (yych != 's') goto yy179;
yych = *++YYCURSOR;
if (yybm[0+yych] & 16) {
- goto yy153;
+ goto yy178;
}
if (yych <= ',') {
if (yych <= '\t') {
if (yych <= 0x08) goto yy56;
- goto yy1044;
+ goto yy1069;
} else {
- if (yych == ' ') goto yy1044;
+ if (yych == ' ') goto yy1069;
goto yy56;
}
} else {
if (yych <= '/') {
if (yych == '.') goto yy56;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '_') goto yy147;
+ if (yych == '_') goto yy172;
goto yy56;
}
}
-yy1138:
+yy1188:
yych = *++YYCURSOR;
if (yych <= 'G') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'F') goto yy141;
- goto yy1152;
+ if (yych <= 'F') goto yy166;
+ goto yy1202;
}
} else {
if (yych <= 'f') {
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'Z') goto yy166;
if (yych <= '`') goto yy3;
- goto yy141;
+ goto yy166;
} else {
- if (yych <= 'g') goto yy1152;
- if (yych <= 'z') goto yy141;
+ if (yych <= 'g') goto yy1202;
+ if (yych <= 'z') goto yy166;
goto yy3;
}
}
-yy1139:
+yy1189:
yych = *++YYCURSOR;
if (yych <= 'E') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'D') goto yy141;
+ if (yych <= 'D') goto yy166;
}
} else {
if (yych <= 'd') {
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'Z') goto yy166;
if (yych <= '`') goto yy3;
- goto yy141;
+ goto yy166;
} else {
- if (yych <= 'e') goto yy1140;
- if (yych <= 'z') goto yy141;
+ if (yych <= 'e') goto yy1190;
+ if (yych <= 'z') goto yy166;
goto yy3;
}
}
-yy1140:
+yy1190:
yych = *++YYCURSOR;
if (yych <= 'V') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'U') goto yy142;
+ if (yych <= 'U') goto yy167;
}
} else {
if (yych <= 'u') {
- if (yych <= 'Z') goto yy142;
+ if (yych <= 'Z') goto yy167;
if (yych <= '`') goto yy3;
- goto yy142;
+ goto yy167;
} else {
- if (yych <= 'v') goto yy1141;
- if (yych <= 'z') goto yy142;
+ if (yych <= 'v') goto yy1191;
+ if (yych <= 'z') goto yy167;
goto yy3;
}
}
-yy1141:
+yy1191:
yych = *++YYCURSOR;
if (yych <= 'E') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'D') goto yy143;
+ if (yych <= 'D') goto yy168;
}
} else {
if (yych <= 'd') {
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'Z') goto yy168;
if (yych <= '`') goto yy3;
- goto yy143;
+ goto yy168;
} else {
- if (yych <= 'e') goto yy1142;
- if (yych <= 'z') goto yy143;
+ if (yych <= 'e') goto yy1192;
+ if (yych <= 'z') goto yy168;
goto yy3;
}
}
-yy1142:
+yy1192:
yych = *++YYCURSOR;
if (yych <= 'N') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'M') goto yy144;
+ if (yych <= 'M') goto yy169;
}
} else {
if (yych <= 'm') {
- if (yych <= 'Z') goto yy144;
+ if (yych <= 'Z') goto yy169;
if (yych <= '`') goto yy3;
- goto yy144;
+ goto yy169;
} else {
- if (yych <= 'n') goto yy1143;
- if (yych <= 'z') goto yy144;
+ if (yych <= 'n') goto yy1193;
+ if (yych <= 'z') goto yy169;
goto yy3;
}
}
-yy1143:
+yy1193:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'S') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
- if (yych <= 'T') goto yy1144;
+ if (yych <= 'T') goto yy1194;
if (yych != 't') goto yy3;
}
-yy1144:
+yy1194:
yych = *++YYCURSOR;
- if (yych == 'H') goto yy1145;
+ if (yych == 'H') goto yy1195;
if (yych != 'h') goto yy56;
-yy1145:
+yy1195:
yych = *++YYCURSOR;
- if (yych == '\t') goto yy1146;
+ if (yych == '\t') goto yy1196;
if (yych != ' ') goto yy56;
-yy1146:
+yy1196:
++YYCURSOR;
- if ((YYLIMIT - YYCURSOR) < 11) YYFILL(11);
+ if ((YYLIMIT - YYCURSOR) < 12) YYFILL(12);
yych = *YYCURSOR;
-yy1147:
- if (yych <= 'W') {
- if (yych <= 'F') {
+yy1197:
+ if (yych <= 'X') {
+ if (yych <= 'G') {
if (yych <= ' ') {
- if (yych == '\t') goto yy1146;
+ if (yych == '\t') goto yy1196;
if (yych <= 0x1F) goto yy56;
- goto yy1146;
+ goto yy1196;
} else {
- if (yych == 'D') goto yy1049;
- if (yych <= 'E') goto yy56;
- goto yy1050;
+ if (yych <= 'D') {
+ if (yych <= 'C') goto yy56;
+ goto yy1076;
+ } else {
+ if (yych == 'F') goto yy1077;
+ goto yy56;
+ }
}
} else {
- if (yych <= 'M') {
- if (yych == 'H') goto yy1048;
- if (yych <= 'L') goto yy56;
- goto yy1047;
- } else {
- if (yych <= 'S') {
+ if (yych <= 'S') {
+ if (yych <= 'L') {
+ if (yych <= 'H') goto yy1075;
+ goto yy56;
+ } else {
+ if (yych <= 'M') goto yy1071;
if (yych <= 'R') goto yy56;
- goto yy1046;
+ goto yy1074;
+ }
+ } else {
+ if (yych <= 'U') {
+ if (yych <= 'T') goto yy1080;
+ goto yy1073;
} else {
- if (yych <= 'T') goto yy1053;
- if (yych <= 'V') goto yy56;
+ if (yych != 'W') goto yy56;
}
}
}
} else {
- if (yych <= 'l') {
- if (yych <= 'd') {
- if (yych == 'Y') goto yy1051;
- if (yych <= 'c') goto yy56;
- goto yy1049;
- } else {
- if (yych <= 'f') {
+ if (yych <= 'r') {
+ if (yych <= 'f') {
+ if (yych <= 'c') {
+ if (yych <= 'Y') goto yy1078;
+ goto yy56;
+ } else {
+ if (yych <= 'd') goto yy1076;
if (yych <= 'e') goto yy56;
- goto yy1050;
+ goto yy1077;
+ }
+ } else {
+ if (yych <= 'h') {
+ if (yych <= 'g') goto yy56;
+ goto yy1075;
} else {
- if (yych == 'h') goto yy1048;
+ if (yych == 'm') goto yy1071;
goto yy56;
}
}
} else {
- if (yych <= 't') {
- if (yych <= 'm') goto yy1047;
- if (yych <= 'r') goto yy56;
- if (yych <= 's') goto yy1046;
- goto yy1053;
- } else {
- if (yych <= 'w') {
+ if (yych <= 'w') {
+ if (yych <= 't') {
+ if (yych <= 's') goto yy1074;
+ goto yy1080;
+ } else {
+ if (yych <= 'u') goto yy1073;
if (yych <= 'v') goto yy56;
+ }
+ } else {
+ if (yych <= 'y') {
+ if (yych <= 'x') goto yy56;
+ goto yy1078;
} else {
- if (yych == 'y') goto yy1051;
+ if (yych == 0xC2) goto yy1072;
goto yy56;
}
}
}
}
yych = *++YYCURSOR;
- if (yych == 'E') goto yy1149;
+ if (yych == 'E') goto yy1199;
if (yych != 'e') goto yy56;
-yy1149:
+yy1199:
yych = *++YYCURSOR;
if (yych <= 'E') {
if (yych <= 'C') goto yy56;
- if (yych <= 'D') goto yy1074;
+ if (yych <= 'D') goto yy1101;
} else {
if (yych <= 'c') goto yy56;
- if (yych <= 'd') goto yy1074;
+ if (yych <= 'd') goto yy1101;
if (yych >= 'f') goto yy56;
}
yych = *++YYCURSOR;
- if (yych == 'K') goto yy1151;
+ if (yych == 'K') goto yy1201;
if (yych != 'k') goto yy56;
-yy1151:
+yy1201:
yych = *++YYCURSOR;
if (yych <= 'S') {
- if (yych == 'D') goto yy1083;
+ if (yych == 'D') goto yy1110;
if (yych <= 'R') goto yy56;
- goto yy1082;
+ goto yy1109;
} else {
if (yych <= 'd') {
if (yych <= 'c') goto yy56;
- goto yy1083;
+ goto yy1110;
} else {
- if (yych == 's') goto yy1082;
+ if (yych == 's') goto yy1109;
goto yy56;
}
}
-yy1152:
+yy1202:
yych = *++YYCURSOR;
if (yych <= 'H') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'G') goto yy142;
+ if (yych <= 'G') goto yy167;
}
} else {
if (yych <= 'g') {
- if (yych <= 'Z') goto yy142;
+ if (yych <= 'Z') goto yy167;
if (yych <= '`') goto yy3;
- goto yy142;
+ goto yy167;
} else {
- if (yych <= 'h') goto yy1153;
- if (yych <= 'z') goto yy142;
+ if (yych <= 'h') goto yy1203;
+ if (yych <= 'z') goto yy167;
goto yy3;
}
}
-yy1153:
+yy1203:
yych = *++YYCURSOR;
if (yych <= 'T') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'S') goto yy143;
+ if (yych <= 'S') goto yy168;
}
} else {
if (yych <= 's') {
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'Z') goto yy168;
if (yych <= '`') goto yy3;
- goto yy143;
+ goto yy168;
} else {
- if (yych <= 't') goto yy1154;
- if (yych <= 'z') goto yy143;
+ if (yych <= 't') goto yy1204;
+ if (yych <= 'z') goto yy168;
goto yy3;
}
}
-yy1154:
+yy1204:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '@') {
if (yych <= 0x1F) {
- if (yych == '\t') goto yy1146;
+ if (yych == '\t') goto yy1196;
goto yy3;
} else {
- if (yych <= ' ') goto yy1146;
- if (yych == ')') goto yy139;
+ if (yych <= ' ') goto yy1196;
+ if (yych == ')') goto yy164;
goto yy3;
}
} else {
if (yych <= '`') {
- if (yych == 'H') goto yy1155;
- if (yych <= 'Z') goto yy144;
+ if (yych == 'H') goto yy1205;
+ if (yych <= 'Z') goto yy169;
goto yy3;
} else {
- if (yych == 'h') goto yy1155;
- if (yych <= 'z') goto yy144;
+ if (yych == 'h') goto yy1205;
+ if (yych <= 'z') goto yy169;
goto yy3;
}
}
-yy1155:
+yy1205:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 0x1F) {
- if (yych == '\t') goto yy1146;
+ if (yych == '\t') goto yy1196;
goto yy3;
} else {
- if (yych <= ' ') goto yy1146;
- if (yych == ')') goto yy139;
+ if (yych <= ' ') goto yy1196;
+ if (yych == ')') goto yy164;
goto yy3;
}
-yy1156:
+yy1206:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'F') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy141;
+ goto yy166;
}
} else {
if (yych <= '_') {
- if (yych <= 'G') goto yy1152;
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'G') goto yy1202;
+ if (yych <= 'Z') goto yy166;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'f') {
if (yych <= '`') goto yy3;
- goto yy146;
+ goto yy171;
} else {
- if (yych <= 'g') goto yy1164;
- if (yych <= 'z') goto yy146;
+ if (yych <= 'g') goto yy1214;
+ if (yych <= 'z') goto yy171;
goto yy3;
}
}
}
-yy1157:
+yy1207:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'D') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy141;
+ goto yy166;
}
} else {
if (yych <= '_') {
- if (yych <= 'E') goto yy1140;
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'E') goto yy1190;
+ if (yych <= 'Z') goto yy166;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'd') {
if (yych <= '`') goto yy3;
- goto yy146;
+ goto yy171;
} else {
- if (yych <= 'e') goto yy1158;
- if (yych <= 'z') goto yy146;
+ if (yych <= 'e') goto yy1208;
+ if (yych <= 'z') goto yy171;
goto yy3;
}
}
}
-yy1158:
+yy1208:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'U') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy142;
+ goto yy167;
}
} else {
if (yych <= '_') {
- if (yych <= 'V') goto yy1141;
- if (yych <= 'Z') goto yy142;
+ if (yych <= 'V') goto yy1191;
+ if (yych <= 'Z') goto yy167;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'u') {
if (yych <= '`') goto yy3;
- goto yy150;
+ goto yy175;
} else {
- if (yych <= 'v') goto yy1159;
- if (yych <= 'z') goto yy150;
+ if (yych <= 'v') goto yy1209;
+ if (yych <= 'z') goto yy175;
goto yy3;
}
}
}
-yy1159:
+yy1209:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'D') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy143;
+ goto yy168;
}
} else {
if (yych <= '_') {
- if (yych <= 'E') goto yy1142;
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'E') goto yy1192;
+ if (yych <= 'Z') goto yy168;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'd') {
if (yych <= '`') goto yy3;
- goto yy151;
+ goto yy176;
} else {
- if (yych <= 'e') goto yy1160;
- if (yych <= 'z') goto yy151;
+ if (yych <= 'e') goto yy1210;
+ if (yych <= 'z') goto yy176;
goto yy3;
}
}
}
-yy1160:
+yy1210:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'M') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy144;
+ goto yy169;
}
} else {
if (yych <= '_') {
- if (yych <= 'N') goto yy1143;
- if (yych <= 'Z') goto yy144;
+ if (yych <= 'N') goto yy1193;
+ if (yych <= 'Z') goto yy169;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'm') {
if (yych <= '`') goto yy3;
- goto yy152;
+ goto yy177;
} else {
- if (yych <= 'n') goto yy1161;
- if (yych <= 'z') goto yy152;
+ if (yych <= 'n') goto yy1211;
+ if (yych <= 'z') goto yy177;
goto yy3;
}
}
}
-yy1161:
+yy1211:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'S') {
if (yych <= ',') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
if (yych == '.') goto yy3;
- if (yych <= '/') goto yy147;
+ if (yych <= '/') goto yy172;
goto yy3;
}
} else {
if (yych <= '`') {
- if (yych <= 'T') goto yy1144;
- if (yych == '_') goto yy147;
+ if (yych <= 'T') goto yy1194;
+ if (yych == '_') goto yy172;
goto yy3;
} else {
- if (yych == 't') goto yy1162;
- if (yych <= 'z') goto yy153;
+ if (yych == 't') goto yy1212;
+ if (yych <= 'z') goto yy178;
goto yy3;
}
}
-yy1162:
+yy1212:
yych = *++YYCURSOR;
- if (yych == 'H') goto yy1145;
- if (yych != 'h') goto yy154;
-yy1163:
+ if (yych == 'H') goto yy1195;
+ if (yych != 'h') goto yy179;
+yy1213:
yych = *++YYCURSOR;
if (yybm[0+yych] & 16) {
- goto yy153;
+ goto yy178;
}
if (yych <= ',') {
if (yych <= '\t') {
if (yych <= 0x08) goto yy56;
- goto yy1146;
+ goto yy1196;
} else {
- if (yych == ' ') goto yy1146;
+ if (yych == ' ') goto yy1196;
goto yy56;
}
} else {
if (yych <= '/') {
if (yych == '.') goto yy56;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '_') goto yy147;
+ if (yych == '_') goto yy172;
goto yy56;
}
}
-yy1164:
+yy1214:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'G') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy142;
+ goto yy167;
}
} else {
if (yych <= '_') {
- if (yych <= 'H') goto yy1153;
- if (yych <= 'Z') goto yy142;
+ if (yych <= 'H') goto yy1203;
+ if (yych <= 'Z') goto yy167;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'g') {
if (yych <= '`') goto yy3;
- goto yy150;
+ goto yy175;
} else {
- if (yych <= 'h') goto yy1165;
- if (yych <= 'z') goto yy150;
+ if (yych <= 'h') goto yy1215;
+ if (yych <= 'z') goto yy175;
goto yy3;
}
}
}
-yy1165:
+yy1215:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'S') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy143;
+ goto yy168;
}
} else {
if (yych <= '_') {
- if (yych <= 'T') goto yy1154;
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'T') goto yy1204;
+ if (yych <= 'Z') goto yy168;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 's') {
if (yych <= '`') goto yy3;
- goto yy151;
+ goto yy176;
} else {
- if (yych <= 't') goto yy1166;
- if (yych <= 'z') goto yy151;
+ if (yych <= 't') goto yy1216;
+ if (yych <= 'z') goto yy176;
goto yy3;
}
}
}
-yy1166:
+yy1216:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '/') {
if (yych <= '(') {
if (yych <= '\t') {
if (yych <= 0x08) goto yy3;
- goto yy1146;
+ goto yy1196;
} else {
- if (yych == ' ') goto yy1146;
+ if (yych == ' ') goto yy1196;
goto yy3;
}
} else {
if (yych <= ',') {
- if (yych <= ')') goto yy139;
+ if (yych <= ')') goto yy164;
goto yy3;
} else {
if (yych == '.') goto yy3;
- goto yy147;
+ goto yy172;
}
}
} else {
if (yych <= '^') {
if (yych <= 'G') {
if (yych <= '@') goto yy3;
- goto yy144;
+ goto yy169;
} else {
- if (yych <= 'H') goto yy1155;
- if (yych <= 'Z') goto yy144;
+ if (yych <= 'H') goto yy1205;
+ if (yych <= 'Z') goto yy169;
goto yy3;
}
} else {
if (yych <= 'g') {
- if (yych <= '_') goto yy147;
+ if (yych <= '_') goto yy172;
if (yych <= '`') goto yy3;
- goto yy152;
+ goto yy177;
} else {
- if (yych <= 'h') goto yy1167;
- if (yych <= 'z') goto yy152;
+ if (yych <= 'h') goto yy1217;
+ if (yych <= 'z') goto yy177;
goto yy3;
}
}
}
-yy1167:
+yy1217:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yybm[0+yych] & 16) {
- goto yy153;
+ goto yy178;
}
if (yych <= ')') {
if (yych <= 0x1F) {
- if (yych == '\t') goto yy1146;
+ if (yych == '\t') goto yy1196;
goto yy3;
} else {
- if (yych <= ' ') goto yy1146;
+ if (yych <= ' ') goto yy1196;
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
}
} else {
if (yych <= '.') {
- if (yych == '-') goto yy147;
+ if (yych == '-') goto yy172;
goto yy3;
} else {
- if (yych <= '/') goto yy147;
- if (yych == '_') goto yy147;
+ if (yych <= '/') goto yy172;
+ if (yych == '_') goto yy172;
goto yy3;
}
}
-yy1168:
+yy1218:
yych = *++YYCURSOR;
if (yych <= 'V') {
if (yych <= 'B') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= '@') goto yy3;
- goto yy141;
+ goto yy166;
} else {
if (yych <= 'O') {
- if (yych <= 'C') goto yy1184;
- goto yy141;
+ if (yych <= 'C') goto yy1234;
+ goto yy166;
} else {
- if (yych <= 'P') goto yy1186;
- if (yych <= 'U') goto yy141;
- goto yy1185;
+ if (yych <= 'P') goto yy1236;
+ if (yych <= 'U') goto yy166;
+ goto yy1235;
}
}
} else {
if (yych <= 'o') {
if (yych <= '`') {
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'Z') goto yy166;
goto yy3;
} else {
- if (yych == 'c') goto yy1184;
- goto yy141;
+ if (yych == 'c') goto yy1234;
+ goto yy166;
}
} else {
if (yych <= 'u') {
- if (yych <= 'p') goto yy1186;
- goto yy141;
+ if (yych <= 'p') goto yy1236;
+ goto yy166;
} else {
- if (yych <= 'v') goto yy1185;
- if (yych <= 'z') goto yy141;
+ if (yych <= 'v') goto yy1235;
+ if (yych <= 'z') goto yy166;
goto yy3;
}
}
}
-yy1169:
+yy1219:
yych = *++YYCURSOR;
if (yych <= 'T') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'S') goto yy141;
- goto yy1179;
+ if (yych <= 'S') goto yy166;
+ goto yy1229;
}
} else {
if (yych <= 's') {
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'Z') goto yy166;
if (yych <= '`') goto yy3;
- goto yy141;
+ goto yy166;
} else {
- if (yych <= 't') goto yy1179;
- if (yych <= 'z') goto yy141;
+ if (yych <= 't') goto yy1229;
+ if (yych <= 'z') goto yy166;
goto yy3;
}
}
-yy1170:
+yy1220:
yych = *++YYCURSOR;
if (yych <= 'X') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'W') goto yy141;
- goto yy1176;
+ if (yych <= 'W') goto yy166;
+ goto yy1226;
}
} else {
if (yych <= 'w') {
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'Z') goto yy166;
if (yych <= '`') goto yy3;
- goto yy141;
+ goto yy166;
} else {
- if (yych <= 'x') goto yy1176;
- if (yych <= 'z') goto yy141;
+ if (yych <= 'x') goto yy1226;
+ if (yych <= 'z') goto yy166;
goto yy3;
}
}
-yy1171:
+yy1221:
yych = *++YYCURSOR;
if (yych <= 'N') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'M') goto yy141;
+ if (yych <= 'M') goto yy166;
}
} else {
if (yych <= 'm') {
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'Z') goto yy166;
if (yych <= '`') goto yy3;
- goto yy141;
+ goto yy166;
} else {
- if (yych <= 'n') goto yy1172;
- if (yych <= 'z') goto yy141;
+ if (yych <= 'n') goto yy1222;
+ if (yych <= 'z') goto yy166;
goto yy3;
}
}
-yy1172:
+yy1222:
yych = *++YYCURSOR;
if (yych <= 'D') {
if (yych <= ')') {
- if (yych <= '(') goto yy166;
- goto yy139;
+ if (yych <= '(') goto yy191;
+ goto yy164;
} else {
- if (yych <= '@') goto yy166;
- if (yych <= 'C') goto yy142;
+ if (yych <= '@') goto yy191;
+ if (yych <= 'C') goto yy167;
}
} else {
if (yych <= 'c') {
- if (yych <= 'Z') goto yy142;
- if (yych <= '`') goto yy166;
- goto yy142;
+ if (yych <= 'Z') goto yy167;
+ if (yych <= '`') goto yy191;
+ goto yy167;
} else {
- if (yych <= 'd') goto yy1173;
- if (yych <= 'z') goto yy142;
- goto yy166;
+ if (yych <= 'd') goto yy1223;
+ if (yych <= 'z') goto yy167;
+ goto yy191;
}
}
-yy1173:
+yy1223:
yych = *++YYCURSOR;
if (yych <= 'A') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= '@') goto yy3;
} else {
if (yych <= '`') {
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'Z') goto yy168;
goto yy3;
} else {
- if (yych <= 'a') goto yy1174;
- if (yych <= 'z') goto yy143;
+ if (yych <= 'a') goto yy1224;
+ if (yych <= 'z') goto yy168;
goto yy3;
}
}
-yy1174:
+yy1224:
yych = *++YYCURSOR;
if (yych <= 'Y') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'X') goto yy144;
+ if (yych <= 'X') goto yy169;
}
} else {
if (yych <= 'x') {
- if (yych <= 'Z') goto yy144;
+ if (yych <= 'Z') goto yy169;
if (yych <= '`') goto yy3;
- goto yy144;
+ goto yy169;
} else {
- if (yych <= 'y') goto yy1175;
- if (yych <= 'z') goto yy144;
+ if (yych <= 'y') goto yy1225;
+ if (yych <= 'z') goto yy169;
goto yy3;
}
}
-yy1175:
+yy1225:
yych = *++YYCURSOR;
- if (yych == ')') goto yy139;
- goto yy166;
-yy1176:
+ if (yych == ')') goto yy164;
+ goto yy191;
+yy1226:
yych = *++YYCURSOR;
if (yych <= 'T') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'S') goto yy142;
+ if (yych <= 'S') goto yy167;
}
} else {
if (yych <= 's') {
- if (yych <= 'Z') goto yy142;
+ if (yych <= 'Z') goto yy167;
if (yych <= '`') goto yy3;
- goto yy142;
+ goto yy167;
} else {
- if (yych <= 't') goto yy1177;
- if (yych <= 'z') goto yy142;
+ if (yych <= 't') goto yy1227;
+ if (yych <= 'z') goto yy167;
goto yy3;
}
}
-yy1177:
+yy1227:
yych = *++YYCURSOR;
if (yych <= 'H') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'G') goto yy143;
+ if (yych <= 'G') goto yy168;
}
} else {
if (yych <= 'g') {
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'Z') goto yy168;
if (yych <= '`') goto yy3;
- goto yy143;
+ goto yy168;
} else {
- if (yych <= 'h') goto yy1178;
- if (yych <= 'z') goto yy143;
+ if (yych <= 'h') goto yy1228;
+ if (yych <= 'z') goto yy168;
goto yy3;
}
}
-yy1178:
+yy1228:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '(') {
if (yych <= '\t') {
if (yych <= 0x08) goto yy3;
- goto yy1146;
+ goto yy1196;
} else {
- if (yych == ' ') goto yy1146;
+ if (yych == ' ') goto yy1196;
goto yy3;
}
} else {
if (yych <= 'Z') {
- if (yych <= ')') goto yy139;
+ if (yych <= ')') goto yy164;
if (yych <= '@') goto yy3;
- goto yy144;
+ goto yy169;
} else {
if (yych <= '`') goto yy3;
- if (yych <= 'z') goto yy144;
+ if (yych <= 'z') goto yy169;
goto yy3;
}
}
-yy1179:
+yy1229:
yych = *++YYCURSOR;
if (yych <= 'U') {
if (yych <= ')') {
- if (yych <= '(') goto yy166;
- goto yy139;
+ if (yych <= '(') goto yy191;
+ goto yy164;
} else {
- if (yych <= '@') goto yy166;
- if (yych <= 'T') goto yy142;
+ if (yych <= '@') goto yy191;
+ if (yych <= 'T') goto yy167;
}
} else {
if (yych <= 't') {
- if (yych <= 'Z') goto yy142;
- if (yych <= '`') goto yy166;
- goto yy142;
+ if (yych <= 'Z') goto yy167;
+ if (yych <= '`') goto yy191;
+ goto yy167;
} else {
- if (yych <= 'u') goto yy1180;
- if (yych <= 'z') goto yy142;
- goto yy166;
+ if (yych <= 'u') goto yy1230;
+ if (yych <= 'z') goto yy167;
+ goto yy191;
}
}
-yy1180:
+yy1230:
yych = *++YYCURSOR;
if (yych <= 'R') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'Q') goto yy143;
+ if (yych <= 'Q') goto yy168;
}
} else {
if (yych <= 'q') {
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'Z') goto yy168;
if (yych <= '`') goto yy3;
- goto yy143;
+ goto yy168;
} else {
- if (yych <= 'r') goto yy1181;
- if (yych <= 'z') goto yy143;
+ if (yych <= 'r') goto yy1231;
+ if (yych <= 'z') goto yy168;
goto yy3;
}
}
-yy1181:
+yy1231:
yych = *++YYCURSOR;
if (yych <= 'D') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'C') goto yy144;
+ if (yych <= 'C') goto yy169;
}
} else {
if (yych <= 'c') {
- if (yych <= 'Z') goto yy144;
+ if (yych <= 'Z') goto yy169;
if (yych <= '`') goto yy3;
- goto yy144;
+ goto yy169;
} else {
- if (yych <= 'd') goto yy1182;
- if (yych <= 'z') goto yy144;
+ if (yych <= 'd') goto yy1232;
+ if (yych <= 'z') goto yy169;
goto yy3;
}
}
-yy1182:
+yy1232:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '@') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
- if (yych <= 'A') goto yy1183;
+ if (yych <= 'A') goto yy1233;
if (yych != 'a') goto yy3;
}
-yy1183:
+yy1233:
yych = *++YYCURSOR;
- if (yych == 'Y') goto yy172;
- if (yych == 'y') goto yy172;
+ if (yych == 'Y') goto yy197;
+ if (yych == 'y') goto yy197;
goto yy56;
-yy1184:
+yy1234:
yych = *++YYCURSOR;
if (yych <= 'O') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'N') goto yy142;
- goto yy1195;
+ if (yych <= 'N') goto yy167;
+ goto yy1245;
}
} else {
if (yych <= 'n') {
- if (yych <= 'Z') goto yy142;
+ if (yych <= 'Z') goto yy167;
if (yych <= '`') goto yy3;
- goto yy142;
+ goto yy167;
} else {
- if (yych <= 'o') goto yy1195;
- if (yych <= 'z') goto yy142;
+ if (yych <= 'o') goto yy1245;
+ if (yych <= 'z') goto yy167;
goto yy3;
}
}
-yy1185:
+yy1235:
yych = *++YYCURSOR;
if (yych <= 'E') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'D') goto yy142;
- goto yy1192;
+ if (yych <= 'D') goto yy167;
+ goto yy1242;
}
} else {
if (yych <= 'd') {
- if (yych <= 'Z') goto yy142;
+ if (yych <= 'Z') goto yy167;
if (yych <= '`') goto yy3;
- goto yy142;
+ goto yy167;
} else {
- if (yych <= 'e') goto yy1192;
- if (yych <= 'z') goto yy142;
+ if (yych <= 'e') goto yy1242;
+ if (yych <= 'z') goto yy167;
goto yy3;
}
}
-yy1186:
+yy1236:
yyaccept = 5;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '/') {
if (yych <= '(') {
if (yych <= '\t') {
- if (yych <= 0x08) goto yy193;
- goto yy195;
+ if (yych <= 0x08) goto yy218;
+ goto yy220;
} else {
- if (yych == ' ') goto yy195;
- goto yy193;
+ if (yych == ' ') goto yy220;
+ goto yy218;
}
} else {
if (yych <= ',') {
- if (yych <= ')') goto yy139;
- goto yy193;
+ if (yych <= ')') goto yy164;
+ goto yy218;
} else {
- if (yych <= '-') goto yy196;
- if (yych <= '.') goto yy195;
- goto yy193;
+ if (yych <= '-') goto yy221;
+ if (yych <= '.') goto yy220;
+ goto yy218;
}
}
} else {
if (yych <= 'Z') {
if (yych <= '@') {
- if (yych <= '9') goto yy195;
- goto yy193;
+ if (yych <= '9') goto yy220;
+ goto yy218;
} else {
- if (yych != 'T') goto yy142;
+ if (yych != 'T') goto yy167;
}
} else {
if (yych <= 's') {
- if (yych <= '`') goto yy193;
- goto yy142;
+ if (yych <= '`') goto yy218;
+ goto yy167;
} else {
- if (yych <= 't') goto yy1187;
- if (yych <= 'z') goto yy142;
- goto yy193;
+ if (yych <= 't') goto yy1237;
+ if (yych <= 'z') goto yy167;
+ goto yy218;
}
}
}
-yy1187:
+yy1237:
yyaccept = 5;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '/') {
if (yych <= '(') {
if (yych <= '\t') {
- if (yych <= 0x08) goto yy193;
- goto yy195;
+ if (yych <= 0x08) goto yy218;
+ goto yy220;
} else {
- if (yych == ' ') goto yy195;
- goto yy193;
+ if (yych == ' ') goto yy220;
+ goto yy218;
}
} else {
if (yych <= ',') {
- if (yych <= ')') goto yy139;
- goto yy193;
+ if (yych <= ')') goto yy164;
+ goto yy218;
} else {
- if (yych <= '-') goto yy196;
- if (yych <= '.') goto yy195;
- goto yy193;
+ if (yych <= '-') goto yy221;
+ if (yych <= '.') goto yy220;
+ goto yy218;
}
}
} else {
if (yych <= 'Z') {
if (yych <= '@') {
- if (yych <= '9') goto yy195;
- goto yy193;
+ if (yych <= '9') goto yy220;
+ goto yy218;
} else {
- if (yych != 'E') goto yy143;
+ if (yych != 'E') goto yy168;
}
} else {
if (yych <= 'd') {
- if (yych <= '`') goto yy193;
- goto yy143;
+ if (yych <= '`') goto yy218;
+ goto yy168;
} else {
- if (yych <= 'e') goto yy1188;
- if (yych <= 'z') goto yy143;
- goto yy193;
+ if (yych <= 'e') goto yy1238;
+ if (yych <= 'z') goto yy168;
+ goto yy218;
}
}
}
-yy1188:
+yy1238:
yych = *++YYCURSOR;
if (yych <= 'M') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'L') goto yy144;
+ if (yych <= 'L') goto yy169;
}
} else {
if (yych <= 'l') {
- if (yych <= 'Z') goto yy144;
+ if (yych <= 'Z') goto yy169;
if (yych <= '`') goto yy3;
- goto yy144;
+ goto yy169;
} else {
- if (yych <= 'm') goto yy1189;
- if (yych <= 'z') goto yy144;
+ if (yych <= 'm') goto yy1239;
+ if (yych <= 'z') goto yy169;
goto yy3;
}
}
-yy1189:
+yy1239:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'A') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
- if (yych <= 'B') goto yy1190;
+ if (yych <= 'B') goto yy1240;
if (yych != 'b') goto yy3;
}
-yy1190:
+yy1240:
yych = *++YYCURSOR;
- if (yych == 'E') goto yy1191;
+ if (yych == 'E') goto yy1241;
if (yych != 'e') goto yy56;
-yy1191:
+yy1241:
yych = *++YYCURSOR;
- if (yych == 'R') goto yy204;
- if (yych == 'r') goto yy204;
+ if (yych == 'R') goto yy229;
+ if (yych == 'r') goto yy229;
goto yy56;
-yy1192:
+yy1242:
yych = *++YYCURSOR;
if (yych <= 'N') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'M') goto yy143;
+ if (yych <= 'M') goto yy168;
}
} else {
if (yych <= 'm') {
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'Z') goto yy168;
if (yych <= '`') goto yy3;
- goto yy143;
+ goto yy168;
} else {
- if (yych <= 'n') goto yy1193;
- if (yych <= 'z') goto yy143;
+ if (yych <= 'n') goto yy1243;
+ if (yych <= 'z') goto yy168;
goto yy3;
}
}
-yy1193:
+yy1243:
yych = *++YYCURSOR;
if (yych <= 'T') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'S') goto yy144;
+ if (yych <= 'S') goto yy169;
}
} else {
if (yych <= 's') {
- if (yych <= 'Z') goto yy144;
+ if (yych <= 'Z') goto yy169;
if (yych <= '`') goto yy3;
- goto yy144;
+ goto yy169;
} else {
- if (yych <= 't') goto yy1194;
- if (yych <= 'z') goto yy144;
+ if (yych <= 't') goto yy1244;
+ if (yych <= 'z') goto yy169;
goto yy3;
}
}
-yy1194:
+yy1244:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'G') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
- if (yych <= 'H') goto yy1145;
- if (yych == 'h') goto yy1145;
+ if (yych <= 'H') goto yy1195;
+ if (yych == 'h') goto yy1195;
goto yy3;
}
-yy1195:
+yy1245:
yych = *++YYCURSOR;
if (yych <= 'N') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'M') goto yy143;
+ if (yych <= 'M') goto yy168;
}
} else {
if (yych <= 'm') {
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'Z') goto yy168;
if (yych <= '`') goto yy3;
- goto yy143;
+ goto yy168;
} else {
- if (yych <= 'n') goto yy1196;
- if (yych <= 'z') goto yy143;
+ if (yych <= 'n') goto yy1246;
+ if (yych <= 'z') goto yy168;
goto yy3;
}
}
-yy1196:
+yy1246:
yych = *++YYCURSOR;
if (yych <= 'D') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'C') goto yy144;
- goto yy1155;
+ if (yych <= 'C') goto yy169;
+ goto yy1205;
}
} else {
if (yych <= 'c') {
- if (yych <= 'Z') goto yy144;
+ if (yych <= 'Z') goto yy169;
if (yych <= '`') goto yy3;
- goto yy144;
+ goto yy169;
} else {
- if (yych <= 'd') goto yy1155;
- if (yych <= 'z') goto yy144;
+ if (yych <= 'd') goto yy1205;
+ if (yych <= 'z') goto yy169;
goto yy3;
}
}
-yy1197:
+yy1247:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'U') {
if (yych <= '/') {
if (yych <= ',') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
if (yych == '.') goto yy3;
- goto yy147;
+ goto yy172;
}
} else {
if (yych <= 'C') {
if (yych <= '@') goto yy3;
- if (yych <= 'B') goto yy141;
- goto yy1184;
+ if (yych <= 'B') goto yy166;
+ goto yy1234;
} else {
- if (yych == 'P') goto yy1186;
- goto yy141;
+ if (yych == 'P') goto yy1236;
+ goto yy166;
}
}
} else {
if (yych <= 'b') {
if (yych <= '^') {
- if (yych <= 'V') goto yy1185;
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'V') goto yy1235;
+ if (yych <= 'Z') goto yy166;
goto yy3;
} else {
- if (yych <= '_') goto yy147;
+ if (yych <= '_') goto yy172;
if (yych <= '`') goto yy3;
- goto yy146;
+ goto yy171;
}
} else {
if (yych <= 'p') {
- if (yych <= 'c') goto yy1213;
- if (yych <= 'o') goto yy146;
- goto yy1215;
+ if (yych <= 'c') goto yy1263;
+ if (yych <= 'o') goto yy171;
+ goto yy1265;
} else {
- if (yych == 'v') goto yy1214;
- if (yych <= 'z') goto yy146;
+ if (yych == 'v') goto yy1264;
+ if (yych <= 'z') goto yy171;
goto yy3;
}
}
}
-yy1198:
+yy1248:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'S') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy141;
+ goto yy166;
}
} else {
if (yych <= '_') {
- if (yych <= 'T') goto yy1179;
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'T') goto yy1229;
+ if (yych <= 'Z') goto yy166;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 's') {
if (yych <= '`') goto yy3;
- goto yy146;
+ goto yy171;
} else {
- if (yych <= 't') goto yy1208;
- if (yych <= 'z') goto yy146;
+ if (yych <= 't') goto yy1258;
+ if (yych <= 'z') goto yy171;
goto yy3;
}
}
}
-yy1199:
+yy1249:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'W') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy141;
+ goto yy166;
}
} else {
if (yych <= '_') {
- if (yych <= 'X') goto yy1176;
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'X') goto yy1226;
+ if (yych <= 'Z') goto yy166;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'w') {
if (yych <= '`') goto yy3;
- goto yy146;
+ goto yy171;
} else {
- if (yych <= 'x') goto yy1205;
- if (yych <= 'z') goto yy146;
+ if (yych <= 'x') goto yy1255;
+ if (yych <= 'z') goto yy171;
goto yy3;
}
}
}
-yy1200:
+yy1250:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'M') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy141;
+ goto yy166;
}
} else {
if (yych <= '_') {
- if (yych <= 'N') goto yy1172;
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'N') goto yy1222;
+ if (yych <= 'Z') goto yy166;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'm') {
if (yych <= '`') goto yy3;
- goto yy146;
+ goto yy171;
} else {
- if (yych <= 'n') goto yy1201;
- if (yych <= 'z') goto yy146;
+ if (yych <= 'n') goto yy1251;
+ if (yych <= 'z') goto yy171;
goto yy3;
}
}
}
-yy1201:
+yy1251:
yyaccept = 4;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'C') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
- if (yych <= ',') goto yy166;
- goto yy147;
+ if (yych == ')') goto yy164;
+ if (yych <= ',') goto yy191;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
- if (yych <= '@') goto yy166;
- goto yy142;
+ if (yych == '/') goto yy172;
+ if (yych <= '@') goto yy191;
+ goto yy167;
}
} else {
if (yych <= '_') {
- if (yych <= 'D') goto yy1173;
- if (yych <= 'Z') goto yy142;
- if (yych <= '^') goto yy166;
- goto yy147;
+ if (yych <= 'D') goto yy1223;
+ if (yych <= 'Z') goto yy167;
+ if (yych <= '^') goto yy191;
+ goto yy172;
} else {
if (yych <= 'c') {
- if (yych <= '`') goto yy166;
- goto yy150;
+ if (yych <= '`') goto yy191;
+ goto yy175;
} else {
- if (yych <= 'd') goto yy1202;
- if (yych <= 'z') goto yy150;
- goto yy166;
+ if (yych <= 'd') goto yy1252;
+ if (yych <= 'z') goto yy175;
+ goto yy191;
}
}
}
-yy1202:
+yy1252:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '@') {
if (yych <= ',') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
if (yych == '.') goto yy3;
- if (yych <= '/') goto yy147;
+ if (yych <= '/') goto yy172;
goto yy3;
}
} else {
if (yych <= '_') {
- if (yych <= 'A') goto yy1174;
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'A') goto yy1224;
+ if (yych <= 'Z') goto yy168;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= '`') goto yy3;
- if (yych <= 'a') goto yy1203;
- if (yych <= 'z') goto yy151;
+ if (yych <= 'a') goto yy1253;
+ if (yych <= 'z') goto yy176;
goto yy3;
}
}
-yy1203:
+yy1253:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'X') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy144;
+ goto yy169;
}
} else {
if (yych <= '_') {
- if (yych <= 'Y') goto yy1175;
- if (yych <= 'Z') goto yy144;
+ if (yych <= 'Y') goto yy1225;
+ if (yych <= 'Z') goto yy169;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'x') {
if (yych <= '`') goto yy3;
- goto yy152;
+ goto yy177;
} else {
- if (yych <= 'y') goto yy1204;
- if (yych <= 'z') goto yy152;
+ if (yych <= 'y') goto yy1254;
+ if (yych <= 'z') goto yy177;
goto yy3;
}
}
}
-yy1204:
+yy1254:
yyaccept = 4;
yych = *(YYMARKER = ++YYCURSOR);
if (yybm[0+yych] & 16) {
- goto yy153;
+ goto yy178;
}
if (yych <= '-') {
- if (yych == ')') goto yy139;
- if (yych <= ',') goto yy166;
- goto yy147;
+ if (yych == ')') goto yy164;
+ if (yych <= ',') goto yy191;
+ goto yy172;
} else {
if (yych <= '/') {
- if (yych <= '.') goto yy166;
- goto yy147;
+ if (yych <= '.') goto yy191;
+ goto yy172;
} else {
- if (yych == '_') goto yy147;
- goto yy166;
+ if (yych == '_') goto yy172;
+ goto yy191;
}
}
-yy1205:
+yy1255:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'S') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy142;
+ goto yy167;
}
} else {
if (yych <= '_') {
- if (yych <= 'T') goto yy1177;
- if (yych <= 'Z') goto yy142;
+ if (yych <= 'T') goto yy1227;
+ if (yych <= 'Z') goto yy167;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 's') {
if (yych <= '`') goto yy3;
- goto yy150;
+ goto yy175;
} else {
- if (yych <= 't') goto yy1206;
- if (yych <= 'z') goto yy150;
+ if (yych <= 't') goto yy1256;
+ if (yych <= 'z') goto yy175;
goto yy3;
}
}
}
-yy1206:
+yy1256:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'G') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy143;
+ goto yy168;
}
} else {
if (yych <= '_') {
- if (yych <= 'H') goto yy1178;
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'H') goto yy1228;
+ if (yych <= 'Z') goto yy168;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'g') {
if (yych <= '`') goto yy3;
- goto yy151;
+ goto yy176;
} else {
- if (yych <= 'h') goto yy1207;
- if (yych <= 'z') goto yy151;
+ if (yych <= 'h') goto yy1257;
+ if (yych <= 'z') goto yy176;
goto yy3;
}
}
}
-yy1207:
+yy1257:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '-') {
if (yych <= ' ') {
- if (yych == '\t') goto yy1146;
+ if (yych == '\t') goto yy1196;
if (yych <= 0x1F) goto yy3;
- goto yy1146;
+ goto yy1196;
} else {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
}
} else {
if (yych <= 'Z') {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy144;
+ goto yy169;
} else {
if (yych <= '_') {
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= '`') goto yy3;
- if (yych <= 'z') goto yy152;
+ if (yych <= 'z') goto yy177;
goto yy3;
}
}
}
-yy1208:
+yy1258:
yyaccept = 4;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'T') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
- if (yych <= ',') goto yy166;
- goto yy147;
+ if (yych == ')') goto yy164;
+ if (yych <= ',') goto yy191;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
- if (yych <= '@') goto yy166;
- goto yy142;
+ if (yych == '/') goto yy172;
+ if (yych <= '@') goto yy191;
+ goto yy167;
}
} else {
if (yych <= '_') {
- if (yych <= 'U') goto yy1180;
- if (yych <= 'Z') goto yy142;
- if (yych <= '^') goto yy166;
- goto yy147;
+ if (yych <= 'U') goto yy1230;
+ if (yych <= 'Z') goto yy167;
+ if (yych <= '^') goto yy191;
+ goto yy172;
} else {
if (yych <= 't') {
- if (yych <= '`') goto yy166;
- goto yy150;
+ if (yych <= '`') goto yy191;
+ goto yy175;
} else {
- if (yych <= 'u') goto yy1209;
- if (yych <= 'z') goto yy150;
- goto yy166;
+ if (yych <= 'u') goto yy1259;
+ if (yych <= 'z') goto yy175;
+ goto yy191;
}
}
}
-yy1209:
+yy1259:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'Q') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy143;
+ goto yy168;
}
} else {
if (yych <= '_') {
- if (yych <= 'R') goto yy1181;
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'R') goto yy1231;
+ if (yych <= 'Z') goto yy168;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'q') {
if (yych <= '`') goto yy3;
- goto yy151;
+ goto yy176;
} else {
- if (yych <= 'r') goto yy1210;
- if (yych <= 'z') goto yy151;
+ if (yych <= 'r') goto yy1260;
+ if (yych <= 'z') goto yy176;
goto yy3;
}
}
}
-yy1210:
+yy1260:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'C') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy144;
+ goto yy169;
}
} else {
if (yych <= '_') {
- if (yych <= 'D') goto yy1182;
- if (yych <= 'Z') goto yy144;
+ if (yych <= 'D') goto yy1232;
+ if (yych <= 'Z') goto yy169;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'c') {
if (yych <= '`') goto yy3;
- goto yy152;
+ goto yy177;
} else {
- if (yych <= 'd') goto yy1211;
- if (yych <= 'z') goto yy152;
+ if (yych <= 'd') goto yy1261;
+ if (yych <= 'z') goto yy177;
goto yy3;
}
}
}
-yy1211:
+yy1261:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '@') {
if (yych <= ',') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
if (yych == '.') goto yy3;
- if (yych <= '/') goto yy147;
+ if (yych <= '/') goto yy172;
goto yy3;
}
} else {
if (yych <= '_') {
- if (yych <= 'A') goto yy1183;
+ if (yych <= 'A') goto yy1233;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= '`') goto yy3;
- if (yych <= 'a') goto yy1212;
- if (yych <= 'z') goto yy153;
+ if (yych <= 'a') goto yy1262;
+ if (yych <= 'z') goto yy178;
goto yy3;
}
}
-yy1212:
+yy1262:
yych = *++YYCURSOR;
- if (yych == 'Y') goto yy172;
- if (yych == 'y') goto yy185;
- goto yy154;
-yy1213:
+ if (yych == 'Y') goto yy197;
+ if (yych == 'y') goto yy210;
+ goto yy179;
+yy1263:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'N') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy142;
+ goto yy167;
}
} else {
if (yych <= '_') {
- if (yych <= 'O') goto yy1195;
- if (yych <= 'Z') goto yy142;
+ if (yych <= 'O') goto yy1245;
+ if (yych <= 'Z') goto yy167;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'n') {
if (yych <= '`') goto yy3;
- goto yy150;
+ goto yy175;
} else {
- if (yych <= 'o') goto yy1224;
- if (yych <= 'z') goto yy150;
+ if (yych <= 'o') goto yy1274;
+ if (yych <= 'z') goto yy175;
goto yy3;
}
}
}
-yy1214:
+yy1264:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'D') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy142;
+ goto yy167;
}
} else {
if (yych <= '_') {
- if (yych <= 'E') goto yy1192;
- if (yych <= 'Z') goto yy142;
+ if (yych <= 'E') goto yy1242;
+ if (yych <= 'Z') goto yy167;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'd') {
if (yych <= '`') goto yy3;
- goto yy150;
+ goto yy175;
} else {
- if (yych <= 'e') goto yy1221;
- if (yych <= 'z') goto yy150;
+ if (yych <= 'e') goto yy1271;
+ if (yych <= 'z') goto yy175;
goto yy3;
}
}
}
-yy1215:
+yy1265:
yyaccept = 5;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '9') {
if (yych <= '(') {
if (yych <= '\t') {
- if (yych <= 0x08) goto yy193;
- goto yy195;
+ if (yych <= 0x08) goto yy218;
+ goto yy220;
} else {
- if (yych == ' ') goto yy195;
- goto yy193;
+ if (yych == ' ') goto yy220;
+ goto yy218;
}
} else {
if (yych <= '-') {
- if (yych <= ')') goto yy139;
- if (yych <= ',') goto yy193;
- goto yy311;
+ if (yych <= ')') goto yy164;
+ if (yych <= ',') goto yy218;
+ goto yy336;
} else {
- if (yych == '/') goto yy147;
- goto yy195;
+ if (yych == '/') goto yy172;
+ goto yy220;
}
}
} else {
if (yych <= '^') {
if (yych <= 'S') {
- if (yych <= '@') goto yy193;
- goto yy142;
+ if (yych <= '@') goto yy218;
+ goto yy167;
} else {
- if (yych <= 'T') goto yy1187;
- if (yych <= 'Z') goto yy142;
- goto yy193;
+ if (yych <= 'T') goto yy1237;
+ if (yych <= 'Z') goto yy167;
+ goto yy218;
}
} else {
if (yych <= 's') {
- if (yych <= '_') goto yy147;
- if (yych <= '`') goto yy193;
- goto yy150;
+ if (yych <= '_') goto yy172;
+ if (yych <= '`') goto yy218;
+ goto yy175;
} else {
- if (yych <= 't') goto yy1216;
- if (yych <= 'z') goto yy150;
- goto yy193;
+ if (yych <= 't') goto yy1266;
+ if (yych <= 'z') goto yy175;
+ goto yy218;
}
}
}
-yy1216:
+yy1266:
yyaccept = 5;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '9') {
if (yych <= '(') {
if (yych <= '\t') {
- if (yych <= 0x08) goto yy193;
- goto yy195;
+ if (yych <= 0x08) goto yy218;
+ goto yy220;
} else {
- if (yych == ' ') goto yy195;
- goto yy193;
+ if (yych == ' ') goto yy220;
+ goto yy218;
}
} else {
if (yych <= '-') {
- if (yych <= ')') goto yy139;
- if (yych <= ',') goto yy193;
- goto yy311;
+ if (yych <= ')') goto yy164;
+ if (yych <= ',') goto yy218;
+ goto yy336;
} else {
- if (yych == '/') goto yy147;
- goto yy195;
+ if (yych == '/') goto yy172;
+ goto yy220;
}
}
} else {
if (yych <= '^') {
if (yych <= 'D') {
- if (yych <= '@') goto yy193;
- goto yy143;
+ if (yych <= '@') goto yy218;
+ goto yy168;
} else {
- if (yych <= 'E') goto yy1188;
- if (yych <= 'Z') goto yy143;
- goto yy193;
+ if (yych <= 'E') goto yy1238;
+ if (yych <= 'Z') goto yy168;
+ goto yy218;
}
} else {
if (yych <= 'd') {
- if (yych <= '_') goto yy147;
- if (yych <= '`') goto yy193;
- goto yy151;
+ if (yych <= '_') goto yy172;
+ if (yych <= '`') goto yy218;
+ goto yy176;
} else {
- if (yych <= 'e') goto yy1217;
- if (yych <= 'z') goto yy151;
- goto yy193;
+ if (yych <= 'e') goto yy1267;
+ if (yych <= 'z') goto yy176;
+ goto yy218;
}
}
}
-yy1217:
+yy1267:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'L') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy144;
+ goto yy169;
}
} else {
if (yych <= '_') {
- if (yych <= 'M') goto yy1189;
- if (yych <= 'Z') goto yy144;
+ if (yych <= 'M') goto yy1239;
+ if (yych <= 'Z') goto yy169;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'l') {
if (yych <= '`') goto yy3;
- goto yy152;
+ goto yy177;
} else {
- if (yych <= 'm') goto yy1218;
- if (yych <= 'z') goto yy152;
+ if (yych <= 'm') goto yy1268;
+ if (yych <= 'z') goto yy177;
goto yy3;
}
}
}
-yy1218:
+yy1268:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'A') {
if (yych <= ',') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
if (yych == '.') goto yy3;
- if (yych <= '/') goto yy147;
+ if (yych <= '/') goto yy172;
goto yy3;
}
} else {
if (yych <= '`') {
- if (yych <= 'B') goto yy1190;
- if (yych == '_') goto yy147;
+ if (yych <= 'B') goto yy1240;
+ if (yych == '_') goto yy172;
goto yy3;
} else {
- if (yych == 'b') goto yy1219;
- if (yych <= 'z') goto yy153;
+ if (yych == 'b') goto yy1269;
+ if (yych <= 'z') goto yy178;
goto yy3;
}
}
-yy1219:
+yy1269:
yych = *++YYCURSOR;
- if (yych == 'E') goto yy1191;
- if (yych != 'e') goto yy154;
+ if (yych == 'E') goto yy1241;
+ if (yych != 'e') goto yy179;
yych = *++YYCURSOR;
- if (yych == 'R') goto yy204;
- if (yych == 'r') goto yy316;
- goto yy154;
-yy1221:
+ if (yych == 'R') goto yy229;
+ if (yych == 'r') goto yy341;
+ goto yy179;
+yy1271:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'M') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy143;
+ goto yy168;
}
} else {
if (yych <= '_') {
- if (yych <= 'N') goto yy1193;
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'N') goto yy1243;
+ if (yych <= 'Z') goto yy168;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'm') {
if (yych <= '`') goto yy3;
- goto yy151;
+ goto yy176;
} else {
- if (yych <= 'n') goto yy1222;
- if (yych <= 'z') goto yy151;
+ if (yych <= 'n') goto yy1272;
+ if (yych <= 'z') goto yy176;
goto yy3;
}
}
}
-yy1222:
+yy1272:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'S') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy144;
+ goto yy169;
}
} else {
if (yych <= '_') {
- if (yych <= 'T') goto yy1194;
- if (yych <= 'Z') goto yy144;
+ if (yych <= 'T') goto yy1244;
+ if (yych <= 'Z') goto yy169;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 's') {
if (yych <= '`') goto yy3;
- goto yy152;
+ goto yy177;
} else {
- if (yych <= 't') goto yy1223;
- if (yych <= 'z') goto yy152;
+ if (yych <= 't') goto yy1273;
+ if (yych <= 'z') goto yy177;
goto yy3;
}
}
}
-yy1223:
+yy1273:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'G') {
if (yych <= ',') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
if (yych == '.') goto yy3;
- if (yych <= '/') goto yy147;
+ if (yych <= '/') goto yy172;
goto yy3;
}
} else {
if (yych <= '`') {
- if (yych <= 'H') goto yy1145;
- if (yych == '_') goto yy147;
+ if (yych <= 'H') goto yy1195;
+ if (yych == '_') goto yy172;
goto yy3;
} else {
- if (yych == 'h') goto yy1163;
- if (yych <= 'z') goto yy153;
+ if (yych == 'h') goto yy1213;
+ if (yych <= 'z') goto yy178;
goto yy3;
}
}
-yy1224:
+yy1274:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'M') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy143;
+ goto yy168;
}
} else {
if (yych <= '_') {
- if (yych <= 'N') goto yy1196;
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'N') goto yy1246;
+ if (yych <= 'Z') goto yy168;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'm') {
if (yych <= '`') goto yy3;
- goto yy151;
+ goto yy176;
} else {
- if (yych <= 'n') goto yy1225;
- if (yych <= 'z') goto yy151;
+ if (yych <= 'n') goto yy1275;
+ if (yych <= 'z') goto yy176;
goto yy3;
}
}
}
-yy1225:
+yy1275:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'C') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy144;
+ goto yy169;
}
} else {
if (yych <= '_') {
- if (yych <= 'D') goto yy1155;
- if (yych <= 'Z') goto yy144;
+ if (yych <= 'D') goto yy1205;
+ if (yych <= 'Z') goto yy169;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'c') {
if (yych <= '`') goto yy3;
- goto yy152;
+ goto yy177;
} else {
- if (yych <= 'd') goto yy1167;
- if (yych <= 'z') goto yy152;
+ if (yych <= 'd') goto yy1217;
+ if (yych <= 'z') goto yy177;
goto yy3;
}
}
}
-yy1226:
+yy1276:
yych = *++YYCURSOR;
if (yych <= 'C') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'B') goto yy141;
+ if (yych <= 'B') goto yy166;
}
} else {
if (yych <= 'b') {
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'Z') goto yy166;
if (yych <= '`') goto yy3;
- goto yy141;
+ goto yy166;
} else {
- if (yych <= 'c') goto yy1227;
- if (yych <= 'z') goto yy141;
+ if (yych <= 'c') goto yy1277;
+ if (yych <= 'z') goto yy166;
goto yy3;
}
}
-yy1227:
+yy1277:
yych = *++YYCURSOR;
if (yych <= 'K') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'J') goto yy142;
+ if (yych <= 'J') goto yy167;
}
} else {
if (yych <= 'j') {
- if (yych <= 'Z') goto yy142;
+ if (yych <= 'Z') goto yy167;
if (yych <= '`') goto yy3;
- goto yy142;
+ goto yy167;
} else {
- if (yych <= 'k') goto yy1228;
- if (yych <= 'z') goto yy142;
+ if (yych <= 'k') goto yy1278;
+ if (yych <= 'z') goto yy167;
goto yy3;
}
}
-yy1228:
+yy1278:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= ')') {
- if (yych == ' ') goto yy1229;
+ if (yych == ' ') goto yy1279;
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= 'Z') {
if (yych <= '@') goto yy3;
- goto yy143;
+ goto yy168;
} else {
if (yych <= '`') goto yy3;
- if (yych <= 'z') goto yy143;
+ if (yych <= 'z') goto yy168;
goto yy3;
}
}
-yy1229:
+yy1279:
yych = *++YYCURSOR;
- if (yych == 'O') goto yy1230;
+ if (yych == 'O') goto yy1280;
if (yych != 'o') goto yy56;
-yy1230:
+yy1280:
yych = *++YYCURSOR;
- if (yych == 'F') goto yy1231;
+ if (yych == 'F') goto yy1281;
if (yych != 'f') goto yy56;
-yy1231:
+yy1281:
yych = *++YYCURSOR;
if (yych != ' ') goto yy56;
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '1') goto yy1233;
- if (yych <= '2') goto yy1235;
- if (yych <= '9') goto yy1236;
+ if (yych <= '1') goto yy1283;
+ if (yych <= '2') goto yy1285;
+ if (yych <= '9') goto yy1286;
goto yy56;
-yy1233:
+yy1283:
yyaccept = 27;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych <= '/') goto yy1237;
- if (yych <= '9') goto yy1236;
- goto yy1237;
-yy1234:
-#line 1053 "ext/date/lib/parse_date.re"
+ if (yych <= '/') goto yy1287;
+ if (yych <= '9') goto yy1286;
+ goto yy1287;
+yy1284:
+#line 1096 "ext/date/lib/parse_date.re"
{
DEBUG_OUTPUT("backof | frontof");
TIMELIB_INIT;
@@ -17383,246 +18313,246 @@ yy1234:
TIMELIB_DEINIT;
return TIMELIB_LF_DAY_OF_MONTH;
}
-#line 17387 "ext/date/lib/parse_date.c"
-yy1235:
+#line 18317 "ext/date/lib/parse_date.c"
+yy1285:
yyaccept = 27;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych <= '/') goto yy1237;
- if (yych >= '5') goto yy1237;
-yy1236:
+ if (yych <= '/') goto yy1287;
+ if (yych >= '5') goto yy1287;
+yy1286:
yyaccept = 27;
YYMARKER = ++YYCURSOR;
if ((YYLIMIT - YYCURSOR) < 5) YYFILL(5);
yych = *YYCURSOR;
-yy1237:
+yy1287:
if (yych <= 'A') {
if (yych <= 0x1F) {
- if (yych == '\t') goto yy1236;
- goto yy1234;
+ if (yych == '\t') goto yy1286;
+ goto yy1284;
} else {
- if (yych <= ' ') goto yy1236;
- if (yych <= '@') goto yy1234;
+ if (yych <= ' ') goto yy1286;
+ if (yych <= '@') goto yy1284;
}
} else {
if (yych <= '`') {
- if (yych != 'P') goto yy1234;
+ if (yych != 'P') goto yy1284;
} else {
- if (yych <= 'a') goto yy1238;
- if (yych != 'p') goto yy1234;
+ if (yych <= 'a') goto yy1288;
+ if (yych != 'p') goto yy1284;
}
}
-yy1238:
+yy1288:
yych = *++YYCURSOR;
if (yych <= 'L') {
if (yych != '.') goto yy56;
} else {
- if (yych <= 'M') goto yy1240;
- if (yych == 'm') goto yy1240;
+ if (yych <= 'M') goto yy1290;
+ if (yych == 'm') goto yy1290;
goto yy56;
}
yych = *++YYCURSOR;
- if (yych == 'M') goto yy1240;
+ if (yych == 'M') goto yy1290;
if (yych != 'm') goto yy56;
-yy1240:
+yy1290:
yych = *++YYCURSOR;
if (yych <= 0x1F) {
- if (yych <= 0x00) goto yy1242;
- if (yych == '\t') goto yy1242;
+ if (yych <= 0x00) goto yy1292;
+ if (yych == '\t') goto yy1292;
goto yy56;
} else {
- if (yych <= ' ') goto yy1242;
+ if (yych <= ' ') goto yy1292;
if (yych != '.') goto yy56;
}
yych = *++YYCURSOR;
if (yych <= '\t') {
- if (yych <= 0x00) goto yy1242;
+ if (yych <= 0x00) goto yy1292;
if (yych <= 0x08) goto yy56;
} else {
if (yych != ' ') goto yy56;
}
-yy1242:
+yy1292:
yych = *++YYCURSOR;
- goto yy1234;
-yy1243:
+ goto yy1284;
+yy1293:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'B') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy141;
+ goto yy166;
}
} else {
if (yych <= '_') {
- if (yych <= 'C') goto yy1227;
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'C') goto yy1277;
+ if (yych <= 'Z') goto yy166;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'b') {
if (yych <= '`') goto yy3;
- goto yy146;
+ goto yy171;
} else {
- if (yych <= 'c') goto yy1244;
- if (yych <= 'z') goto yy146;
+ if (yych <= 'c') goto yy1294;
+ if (yych <= 'z') goto yy171;
goto yy3;
}
}
}
-yy1244:
+yy1294:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'J') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy142;
+ goto yy167;
}
} else {
if (yych <= '_') {
- if (yych <= 'K') goto yy1228;
- if (yych <= 'Z') goto yy142;
+ if (yych <= 'K') goto yy1278;
+ if (yych <= 'Z') goto yy167;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'j') {
if (yych <= '`') goto yy3;
- goto yy150;
+ goto yy175;
} else {
- if (yych <= 'k') goto yy1245;
- if (yych <= 'z') goto yy150;
+ if (yych <= 'k') goto yy1295;
+ if (yych <= 'z') goto yy175;
goto yy3;
}
}
}
-yy1245:
+yy1295:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '.') {
if (yych <= '(') {
- if (yych == ' ') goto yy1229;
+ if (yych == ' ') goto yy1279;
goto yy3;
} else {
- if (yych <= ')') goto yy139;
- if (yych == '-') goto yy147;
+ if (yych <= ')') goto yy164;
+ if (yych == '-') goto yy172;
goto yy3;
}
} else {
if (yych <= '^') {
- if (yych <= '/') goto yy147;
+ if (yych <= '/') goto yy172;
if (yych <= '@') goto yy3;
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'Z') goto yy168;
goto yy3;
} else {
- if (yych <= '_') goto yy147;
+ if (yych <= '_') goto yy172;
if (yych <= '`') goto yy3;
- if (yych <= 'z') goto yy151;
+ if (yych <= 'z') goto yy176;
goto yy3;
}
}
-yy1246:
+yy1296:
yych = *++YYCURSOR;
if (yych <= 'S') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'R') goto yy141;
+ if (yych <= 'R') goto yy166;
}
} else {
if (yych <= 'r') {
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'Z') goto yy166;
if (yych <= '`') goto yy3;
- goto yy141;
+ goto yy166;
} else {
- if (yych <= 's') goto yy1247;
- if (yych <= 'z') goto yy141;
+ if (yych <= 's') goto yy1297;
+ if (yych <= 'z') goto yy166;
goto yy3;
}
}
-yy1247:
+yy1297:
yych = *++YYCURSOR;
if (yych <= 'T') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'S') goto yy142;
+ if (yych <= 'S') goto yy167;
}
} else {
if (yych <= 's') {
- if (yych <= 'Z') goto yy142;
+ if (yych <= 'Z') goto yy167;
if (yych <= '`') goto yy3;
- goto yy142;
+ goto yy167;
} else {
- if (yych <= 't') goto yy1248;
- if (yych <= 'z') goto yy142;
+ if (yych <= 't') goto yy1298;
+ if (yych <= 'z') goto yy167;
goto yy3;
}
}
-yy1248:
+yy1298:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '(') {
if (yych <= '\t') {
if (yych <= 0x08) goto yy3;
- goto yy1044;
+ goto yy1069;
} else {
if (yych != ' ') goto yy3;
}
} else {
if (yych <= 'Z') {
- if (yych <= ')') goto yy139;
+ if (yych <= ')') goto yy164;
if (yych <= '@') goto yy3;
- goto yy143;
+ goto yy168;
} else {
if (yych <= '`') goto yy3;
- if (yych <= 'z') goto yy143;
+ if (yych <= 'z') goto yy168;
goto yy3;
}
}
-yy1249:
+yy1299:
yych = *++YYCURSOR;
- if (yych == 'D') goto yy1250;
- if (yych != 'd') goto yy1045;
-yy1250:
+ if (yych == 'D') goto yy1300;
+ if (yych != 'd') goto yy1070;
+yy1300:
yych = *++YYCURSOR;
- if (yych == 'A') goto yy1251;
+ if (yych == 'A') goto yy1301;
if (yych != 'a') goto yy56;
-yy1251:
+yy1301:
yych = *++YYCURSOR;
- if (yych == 'Y') goto yy1252;
+ if (yych == 'Y') goto yy1302;
if (yych != 'y') goto yy56;
-yy1252:
+yy1302:
yyaccept = 25;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'R') {
- if (yych != ' ') goto yy1057;
+ if (yych != ' ') goto yy1084;
} else {
- if (yych <= 'S') goto yy1082;
- if (yych == 's') goto yy1082;
- goto yy1057;
+ if (yych <= 'S') goto yy1109;
+ if (yych == 's') goto yy1109;
+ goto yy1084;
}
yych = *++YYCURSOR;
- if (yych == 'O') goto yy1254;
+ if (yych == 'O') goto yy1304;
if (yych != 'o') goto yy56;
-yy1254:
+yy1304:
yych = *++YYCURSOR;
- if (yych == 'F') goto yy1255;
+ if (yych == 'F') goto yy1305;
if (yych != 'f') goto yy56;
-yy1255:
+yy1305:
++YYCURSOR;
-#line 1036 "ext/date/lib/parse_date.re"
+#line 1079 "ext/date/lib/parse_date.re"
{
DEBUG_OUTPUT("firstdayof | lastdayof");
TIMELIB_INIT;
@@ -17638,1367 +18568,1369 @@ yy1255:
TIMELIB_DEINIT;
return TIMELIB_LF_DAY_OF_MONTH;
}
-#line 17642 "ext/date/lib/parse_date.c"
-yy1257:
+#line 18572 "ext/date/lib/parse_date.c"
+yy1307:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'R') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy141;
+ goto yy166;
}
} else {
if (yych <= '_') {
- if (yych <= 'S') goto yy1247;
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'S') goto yy1297;
+ if (yych <= 'Z') goto yy166;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'r') {
if (yych <= '`') goto yy3;
- goto yy146;
+ goto yy171;
} else {
- if (yych <= 's') goto yy1258;
- if (yych <= 'z') goto yy146;
+ if (yych <= 's') goto yy1308;
+ if (yych <= 'z') goto yy171;
goto yy3;
}
}
}
-yy1258:
+yy1308:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'S') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy142;
+ goto yy167;
}
} else {
if (yych <= '_') {
- if (yych <= 'T') goto yy1248;
- if (yych <= 'Z') goto yy142;
+ if (yych <= 'T') goto yy1298;
+ if (yych <= 'Z') goto yy167;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 's') {
if (yych <= '`') goto yy3;
- goto yy150;
+ goto yy175;
} else {
- if (yych <= 't') goto yy1259;
- if (yych <= 'z') goto yy150;
+ if (yych <= 't') goto yy1309;
+ if (yych <= 'z') goto yy175;
goto yy3;
}
}
}
-yy1259:
+yy1309:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '-') {
if (yych <= ' ') {
- if (yych == '\t') goto yy1044;
+ if (yych == '\t') goto yy1069;
if (yych <= 0x1F) goto yy3;
- goto yy1249;
+ goto yy1299;
} else {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
}
} else {
if (yych <= 'Z') {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy143;
+ goto yy168;
} else {
if (yych <= '_') {
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= '`') goto yy3;
- if (yych <= 'z') goto yy151;
+ if (yych <= 'z') goto yy176;
goto yy3;
}
}
}
-yy1260:
+yy1310:
yych = *++YYCURSOR;
if (yych <= 'B') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'A') goto yy141;
- goto yy1296;
+ if (yych <= 'A') goto yy166;
+ goto yy1346;
}
} else {
if (yych <= 'a') {
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'Z') goto yy166;
if (yych <= '`') goto yy3;
- goto yy141;
+ goto yy166;
} else {
- if (yych <= 'b') goto yy1296;
- if (yych <= 'z') goto yy141;
+ if (yych <= 'b') goto yy1346;
+ if (yych <= 'z') goto yy166;
goto yy3;
}
}
-yy1261:
+yy1311:
yych = *++YYCURSOR;
if (yych <= 'R') {
if (yych <= '@') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
- if (yych == 'F') goto yy1286;
- if (yych <= 'Q') goto yy141;
- goto yy1285;
+ if (yych == 'F') goto yy1336;
+ if (yych <= 'Q') goto yy166;
+ goto yy1335;
}
} else {
if (yych <= 'f') {
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'Z') goto yy166;
if (yych <= '`') goto yy3;
- if (yych <= 'e') goto yy141;
- goto yy1286;
+ if (yych <= 'e') goto yy166;
+ goto yy1336;
} else {
- if (yych == 'r') goto yy1285;
- if (yych <= 'z') goto yy141;
+ if (yych == 'r') goto yy1335;
+ if (yych <= 'z') goto yy166;
goto yy3;
}
}
-yy1262:
+yy1312:
yych = *++YYCURSOR;
if (yych <= 'U') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'T') goto yy141;
- goto yy1282;
+ if (yych <= 'T') goto yy166;
+ goto yy1332;
}
} else {
if (yych <= 't') {
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'Z') goto yy166;
if (yych <= '`') goto yy3;
- goto yy141;
+ goto yy166;
} else {
- if (yych <= 'u') goto yy1282;
- if (yych <= 'z') goto yy141;
+ if (yych <= 'u') goto yy1332;
+ if (yych <= 'z') goto yy166;
goto yy3;
}
}
-yy1263:
+yy1313:
yych = *++YYCURSOR;
if (yych <= 'O') {
if (yych <= '@') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
- if (yych == 'I') goto yy1265;
- if (yych <= 'N') goto yy141;
+ if (yych == 'I') goto yy1315;
+ if (yych <= 'N') goto yy166;
}
} else {
if (yych <= 'i') {
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'Z') goto yy166;
if (yych <= '`') goto yy3;
- if (yych <= 'h') goto yy141;
- goto yy1265;
+ if (yych <= 'h') goto yy166;
+ goto yy1315;
} else {
- if (yych == 'o') goto yy1264;
- if (yych <= 'z') goto yy141;
+ if (yych == 'o') goto yy1314;
+ if (yych <= 'z') goto yy166;
goto yy3;
}
}
-yy1264:
+yy1314:
yych = *++YYCURSOR;
if (yych <= 'N') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'M') goto yy142;
- goto yy1268;
+ if (yych <= 'M') goto yy167;
+ goto yy1318;
}
} else {
if (yych <= 'm') {
- if (yych <= 'Z') goto yy142;
+ if (yych <= 'Z') goto yy167;
if (yych <= '`') goto yy3;
- goto yy142;
+ goto yy167;
} else {
- if (yych <= 'n') goto yy1268;
- if (yych <= 'z') goto yy142;
+ if (yych <= 'n') goto yy1318;
+ if (yych <= 'z') goto yy167;
goto yy3;
}
}
-yy1265:
+yy1315:
yych = *++YYCURSOR;
if (yych <= 'D') {
if (yych <= ')') {
- if (yych <= '(') goto yy166;
- goto yy139;
+ if (yych <= '(') goto yy191;
+ goto yy164;
} else {
- if (yych <= '@') goto yy166;
- if (yych <= 'C') goto yy142;
+ if (yych <= '@') goto yy191;
+ if (yych <= 'C') goto yy167;
}
} else {
if (yych <= 'c') {
- if (yych <= 'Z') goto yy142;
- if (yych <= '`') goto yy166;
- goto yy142;
+ if (yych <= 'Z') goto yy167;
+ if (yych <= '`') goto yy191;
+ goto yy167;
} else {
- if (yych <= 'd') goto yy1266;
- if (yych <= 'z') goto yy142;
- goto yy166;
+ if (yych <= 'd') goto yy1316;
+ if (yych <= 'z') goto yy167;
+ goto yy191;
}
}
-yy1266:
+yy1316:
yych = *++YYCURSOR;
if (yych <= 'A') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= '@') goto yy3;
} else {
if (yych <= '`') {
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'Z') goto yy168;
goto yy3;
} else {
- if (yych <= 'a') goto yy1267;
- if (yych <= 'z') goto yy143;
+ if (yych <= 'a') goto yy1317;
+ if (yych <= 'z') goto yy168;
goto yy3;
}
}
-yy1267:
+yy1317:
yych = *++YYCURSOR;
if (yych <= 'Y') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'X') goto yy144;
- goto yy1175;
+ if (yych <= 'X') goto yy169;
+ goto yy1225;
}
} else {
if (yych <= 'x') {
- if (yych <= 'Z') goto yy144;
+ if (yych <= 'Z') goto yy169;
if (yych <= '`') goto yy3;
- goto yy144;
+ goto yy169;
} else {
- if (yych <= 'y') goto yy1175;
- if (yych <= 'z') goto yy144;
+ if (yych <= 'y') goto yy1225;
+ if (yych <= 'z') goto yy169;
goto yy3;
}
}
-yy1268:
+yy1318:
yych = *++YYCURSOR;
if (yych <= 'T') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'S') goto yy143;
+ if (yych <= 'S') goto yy168;
}
} else {
if (yych <= 's') {
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'Z') goto yy168;
if (yych <= '`') goto yy3;
- goto yy143;
+ goto yy168;
} else {
- if (yych <= 't') goto yy1269;
- if (yych <= 'z') goto yy143;
+ if (yych <= 't') goto yy1319;
+ if (yych <= 'z') goto yy168;
goto yy3;
}
}
-yy1269:
+yy1319:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= ')') {
- if (yych == ' ') goto yy1270;
+ if (yych == ' ') goto yy1320;
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= 'Z') {
if (yych <= '@') goto yy3;
- goto yy144;
+ goto yy169;
} else {
if (yych <= '`') goto yy3;
- if (yych <= 'z') goto yy144;
+ if (yych <= 'z') goto yy169;
goto yy3;
}
}
-yy1270:
+yy1320:
yych = *++YYCURSOR;
- if (yych == 'O') goto yy1271;
+ if (yych == 'O') goto yy1321;
if (yych != 'o') goto yy56;
-yy1271:
+yy1321:
yych = *++YYCURSOR;
- if (yych == 'F') goto yy1272;
+ if (yych == 'F') goto yy1322;
if (yych != 'f') goto yy56;
-yy1272:
+yy1322:
yych = *++YYCURSOR;
if (yych != ' ') goto yy56;
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '1') goto yy1274;
- if (yych <= '2') goto yy1275;
- if (yych <= '9') goto yy1276;
+ if (yych <= '1') goto yy1324;
+ if (yych <= '2') goto yy1325;
+ if (yych <= '9') goto yy1326;
goto yy56;
-yy1274:
+yy1324:
yyaccept = 27;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych <= '/') goto yy1277;
- if (yych <= '9') goto yy1276;
- goto yy1277;
-yy1275:
+ if (yych <= '/') goto yy1327;
+ if (yych <= '9') goto yy1326;
+ goto yy1327;
+yy1325:
yyaccept = 27;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych <= '/') goto yy1277;
- if (yych >= '5') goto yy1277;
-yy1276:
+ if (yych <= '/') goto yy1327;
+ if (yych >= '5') goto yy1327;
+yy1326:
yyaccept = 27;
YYMARKER = ++YYCURSOR;
if ((YYLIMIT - YYCURSOR) < 5) YYFILL(5);
yych = *YYCURSOR;
-yy1277:
+yy1327:
if (yych <= 'A') {
if (yych <= 0x1F) {
- if (yych == '\t') goto yy1276;
- goto yy1234;
+ if (yych == '\t') goto yy1326;
+ goto yy1284;
} else {
- if (yych <= ' ') goto yy1276;
- if (yych <= '@') goto yy1234;
+ if (yych <= ' ') goto yy1326;
+ if (yych <= '@') goto yy1284;
}
} else {
if (yych <= '`') {
- if (yych != 'P') goto yy1234;
+ if (yych != 'P') goto yy1284;
} else {
- if (yych <= 'a') goto yy1278;
- if (yych != 'p') goto yy1234;
+ if (yych <= 'a') goto yy1328;
+ if (yych != 'p') goto yy1284;
}
}
-yy1278:
+yy1328:
yych = *++YYCURSOR;
if (yych <= 'L') {
if (yych != '.') goto yy56;
} else {
- if (yych <= 'M') goto yy1280;
- if (yych == 'm') goto yy1280;
+ if (yych <= 'M') goto yy1330;
+ if (yych == 'm') goto yy1330;
goto yy56;
}
yych = *++YYCURSOR;
- if (yych == 'M') goto yy1280;
+ if (yych == 'M') goto yy1330;
if (yych != 'm') goto yy56;
-yy1280:
+yy1330:
yych = *++YYCURSOR;
if (yych <= 0x1F) {
- if (yych <= 0x00) goto yy1242;
- if (yych == '\t') goto yy1242;
+ if (yych <= 0x00) goto yy1292;
+ if (yych == '\t') goto yy1292;
goto yy56;
} else {
- if (yych <= ' ') goto yy1242;
+ if (yych <= ' ') goto yy1292;
if (yych != '.') goto yy56;
}
yych = *++YYCURSOR;
if (yych <= '\t') {
- if (yych <= 0x00) goto yy1242;
+ if (yych <= 0x00) goto yy1292;
if (yych <= 0x08) goto yy56;
- goto yy1242;
+ goto yy1292;
} else {
- if (yych == ' ') goto yy1242;
+ if (yych == ' ') goto yy1292;
goto yy56;
}
-yy1282:
+yy1332:
yych = *++YYCURSOR;
if (yych <= 'R') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'Q') goto yy142;
+ if (yych <= 'Q') goto yy167;
}
} else {
if (yych <= 'q') {
- if (yych <= 'Z') goto yy142;
+ if (yych <= 'Z') goto yy167;
if (yych <= '`') goto yy3;
- goto yy142;
+ goto yy167;
} else {
- if (yych <= 'r') goto yy1283;
- if (yych <= 'z') goto yy142;
+ if (yych <= 'r') goto yy1333;
+ if (yych <= 'z') goto yy167;
goto yy3;
}
}
-yy1283:
+yy1333:
yych = *++YYCURSOR;
if (yych <= 'T') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'S') goto yy143;
+ if (yych <= 'S') goto yy168;
}
} else {
if (yych <= 's') {
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'Z') goto yy168;
if (yych <= '`') goto yy3;
- goto yy143;
+ goto yy168;
} else {
- if (yych <= 't') goto yy1284;
- if (yych <= 'z') goto yy143;
+ if (yych <= 't') goto yy1334;
+ if (yych <= 'z') goto yy168;
goto yy3;
}
}
-yy1284:
+yy1334:
yych = *++YYCURSOR;
if (yych <= 'H') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'G') goto yy144;
- goto yy1155;
+ if (yych <= 'G') goto yy169;
+ goto yy1205;
}
} else {
if (yych <= 'g') {
- if (yych <= 'Z') goto yy144;
+ if (yych <= 'Z') goto yy169;
if (yych <= '`') goto yy3;
- goto yy144;
+ goto yy169;
} else {
- if (yych <= 'h') goto yy1155;
- if (yych <= 'z') goto yy144;
+ if (yych <= 'h') goto yy1205;
+ if (yych <= 'z') goto yy169;
goto yy3;
}
}
-yy1285:
+yy1335:
yych = *++YYCURSOR;
if (yych <= 'S') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'R') goto yy142;
- goto yy1288;
+ if (yych <= 'R') goto yy167;
+ goto yy1338;
}
} else {
if (yych <= 'r') {
- if (yych <= 'Z') goto yy142;
+ if (yych <= 'Z') goto yy167;
if (yych <= '`') goto yy3;
- goto yy142;
+ goto yy167;
} else {
- if (yych <= 's') goto yy1288;
- if (yych <= 'z') goto yy142;
+ if (yych <= 's') goto yy1338;
+ if (yych <= 'z') goto yy167;
goto yy3;
}
}
-yy1286:
+yy1336:
yych = *++YYCURSOR;
if (yych <= 'T') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'S') goto yy142;
+ if (yych <= 'S') goto yy167;
}
} else {
if (yych <= 's') {
- if (yych <= 'Z') goto yy142;
+ if (yych <= 'Z') goto yy167;
if (yych <= '`') goto yy3;
- goto yy142;
+ goto yy167;
} else {
- if (yych <= 't') goto yy1287;
- if (yych <= 'z') goto yy142;
+ if (yych <= 't') goto yy1337;
+ if (yych <= 'z') goto yy167;
goto yy3;
}
}
-yy1287:
+yy1337:
yych = *++YYCURSOR;
if (yych <= 'H') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'G') goto yy143;
- goto yy1178;
+ if (yych <= 'G') goto yy168;
+ goto yy1228;
}
} else {
if (yych <= 'g') {
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'Z') goto yy168;
if (yych <= '`') goto yy3;
- goto yy143;
+ goto yy168;
} else {
- if (yych <= 'h') goto yy1178;
- if (yych <= 'z') goto yy143;
+ if (yych <= 'h') goto yy1228;
+ if (yych <= 'z') goto yy168;
goto yy3;
}
}
-yy1288:
+yy1338:
yych = *++YYCURSOR;
if (yych <= 'T') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'S') goto yy143;
+ if (yych <= 'S') goto yy168;
}
} else {
if (yych <= 's') {
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'Z') goto yy168;
if (yych <= '`') goto yy3;
- goto yy143;
+ goto yy168;
} else {
- if (yych <= 't') goto yy1289;
- if (yych <= 'z') goto yy143;
+ if (yych <= 't') goto yy1339;
+ if (yych <= 'z') goto yy168;
goto yy3;
}
}
-yy1289:
+yy1339:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '(') {
if (yych <= '\t') {
if (yych <= 0x08) goto yy3;
- goto yy1146;
+ goto yy1196;
} else {
if (yych != ' ') goto yy3;
}
} else {
if (yych <= 'Z') {
- if (yych <= ')') goto yy139;
+ if (yych <= ')') goto yy164;
if (yych <= '@') goto yy3;
- goto yy144;
+ goto yy169;
} else {
if (yych <= '`') goto yy3;
- if (yych <= 'z') goto yy144;
+ if (yych <= 'z') goto yy169;
goto yy3;
}
}
-yy1290:
+yy1340:
yych = *++YYCURSOR;
- if (yych == 'D') goto yy1291;
- if (yych != 'd') goto yy1147;
-yy1291:
+ if (yych == 'D') goto yy1341;
+ if (yych != 'd') goto yy1197;
+yy1341:
yych = *++YYCURSOR;
- if (yych == 'A') goto yy1292;
+ if (yych == 'A') goto yy1342;
if (yych != 'a') goto yy56;
-yy1292:
+yy1342:
yych = *++YYCURSOR;
- if (yych == 'Y') goto yy1293;
+ if (yych == 'Y') goto yy1343;
if (yych != 'y') goto yy56;
-yy1293:
+yy1343:
yyaccept = 25;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'R') {
- if (yych != ' ') goto yy1057;
+ if (yych != ' ') goto yy1084;
} else {
- if (yych <= 'S') goto yy1082;
- if (yych == 's') goto yy1082;
- goto yy1057;
+ if (yych <= 'S') goto yy1109;
+ if (yych == 's') goto yy1109;
+ goto yy1084;
}
yych = *++YYCURSOR;
- if (yych == 'O') goto yy1295;
+ if (yych == 'O') goto yy1345;
if (yych != 'o') goto yy56;
-yy1295:
+yy1345:
yych = *++YYCURSOR;
- if (yych == 'F') goto yy1255;
- if (yych == 'f') goto yy1255;
+ if (yych == 'F') goto yy1305;
+ if (yych == 'f') goto yy1305;
goto yy56;
-yy1296:
+yy1346:
yyaccept = 5;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '/') {
if (yych <= '(') {
if (yych <= '\t') {
- if (yych <= 0x08) goto yy193;
- goto yy195;
+ if (yych <= 0x08) goto yy218;
+ goto yy220;
} else {
- if (yych == ' ') goto yy195;
- goto yy193;
+ if (yych == ' ') goto yy220;
+ goto yy218;
}
} else {
if (yych <= ',') {
- if (yych <= ')') goto yy139;
- goto yy193;
+ if (yych <= ')') goto yy164;
+ goto yy218;
} else {
- if (yych <= '-') goto yy196;
- if (yych <= '.') goto yy195;
- goto yy193;
+ if (yych <= '-') goto yy221;
+ if (yych <= '.') goto yy220;
+ goto yy218;
}
}
} else {
if (yych <= 'Z') {
if (yych <= '@') {
- if (yych <= '9') goto yy195;
- goto yy193;
+ if (yych <= '9') goto yy220;
+ goto yy218;
} else {
- if (yych != 'R') goto yy142;
+ if (yych != 'R') goto yy167;
}
} else {
if (yych <= 'q') {
- if (yych <= '`') goto yy193;
- goto yy142;
+ if (yych <= '`') goto yy218;
+ goto yy167;
} else {
- if (yych <= 'r') goto yy1297;
- if (yych <= 'z') goto yy142;
- goto yy193;
+ if (yych <= 'r') goto yy1347;
+ if (yych <= 'z') goto yy167;
+ goto yy218;
}
}
}
-yy1297:
+yy1347:
yych = *++YYCURSOR;
if (yych <= 'U') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'T') goto yy143;
+ if (yych <= 'T') goto yy168;
}
} else {
if (yych <= 't') {
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'Z') goto yy168;
if (yych <= '`') goto yy3;
- goto yy143;
+ goto yy168;
} else {
- if (yych <= 'u') goto yy1298;
- if (yych <= 'z') goto yy143;
+ if (yych <= 'u') goto yy1348;
+ if (yych <= 'z') goto yy168;
goto yy3;
}
}
-yy1298:
+yy1348:
yych = *++YYCURSOR;
if (yych <= 'A') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= '@') goto yy3;
} else {
if (yych <= '`') {
- if (yych <= 'Z') goto yy144;
+ if (yych <= 'Z') goto yy169;
goto yy3;
} else {
- if (yych <= 'a') goto yy1299;
- if (yych <= 'z') goto yy144;
+ if (yych <= 'a') goto yy1349;
+ if (yych <= 'z') goto yy169;
goto yy3;
}
}
-yy1299:
+yy1349:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'Q') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
- if (yych <= 'R') goto yy1300;
+ if (yych <= 'R') goto yy1350;
if (yych != 'r') goto yy3;
}
-yy1300:
+yy1350:
yych = *++YYCURSOR;
- if (yych == 'Y') goto yy204;
- if (yych == 'y') goto yy204;
+ if (yych == 'Y') goto yy229;
+ if (yych == 'y') goto yy229;
goto yy56;
-yy1301:
+yy1351:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'A') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy141;
+ goto yy166;
}
} else {
if (yych <= '_') {
- if (yych <= 'B') goto yy1296;
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'B') goto yy1346;
+ if (yych <= 'Z') goto yy166;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'a') {
if (yych <= '`') goto yy3;
- goto yy146;
+ goto yy171;
} else {
- if (yych <= 'b') goto yy1319;
- if (yych <= 'z') goto yy146;
+ if (yych <= 'b') goto yy1369;
+ if (yych <= 'z') goto yy171;
goto yy3;
}
}
}
-yy1302:
+yy1352:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'Q') {
if (yych <= '.') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
- if (yych == '-') goto yy147;
+ if (yych == '-') goto yy172;
goto yy3;
}
} else {
if (yych <= '@') {
- if (yych <= '/') goto yy147;
+ if (yych <= '/') goto yy172;
goto yy3;
} else {
- if (yych == 'F') goto yy1286;
- goto yy141;
+ if (yych == 'F') goto yy1336;
+ goto yy166;
}
}
} else {
if (yych <= '`') {
if (yych <= 'Z') {
- if (yych <= 'R') goto yy1285;
- goto yy141;
+ if (yych <= 'R') goto yy1335;
+ goto yy166;
} else {
- if (yych == '_') goto yy147;
+ if (yych == '_') goto yy172;
goto yy3;
}
} else {
if (yych <= 'q') {
- if (yych == 'f') goto yy1315;
- goto yy146;
+ if (yych == 'f') goto yy1365;
+ goto yy171;
} else {
- if (yych <= 'r') goto yy1314;
- if (yych <= 'z') goto yy146;
+ if (yych <= 'r') goto yy1364;
+ if (yych <= 'z') goto yy171;
goto yy3;
}
}
}
-yy1303:
+yy1353:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'T') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy141;
+ goto yy166;
}
} else {
if (yych <= '_') {
- if (yych <= 'U') goto yy1282;
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'U') goto yy1332;
+ if (yych <= 'Z') goto yy166;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 't') {
if (yych <= '`') goto yy3;
- goto yy146;
+ goto yy171;
} else {
- if (yych <= 'u') goto yy1311;
- if (yych <= 'z') goto yy146;
+ if (yych <= 'u') goto yy1361;
+ if (yych <= 'z') goto yy171;
goto yy3;
}
}
}
-yy1304:
+yy1354:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'N') {
if (yych <= '.') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
- if (yych == '-') goto yy147;
+ if (yych == '-') goto yy172;
goto yy3;
}
} else {
if (yych <= '@') {
- if (yych <= '/') goto yy147;
+ if (yych <= '/') goto yy172;
goto yy3;
} else {
- if (yych == 'I') goto yy1265;
- goto yy141;
+ if (yych == 'I') goto yy1315;
+ goto yy166;
}
}
} else {
if (yych <= '`') {
if (yych <= 'Z') {
- if (yych <= 'O') goto yy1264;
- goto yy141;
+ if (yych <= 'O') goto yy1314;
+ goto yy166;
} else {
- if (yych == '_') goto yy147;
+ if (yych == '_') goto yy172;
goto yy3;
}
} else {
if (yych <= 'n') {
- if (yych == 'i') goto yy1306;
- goto yy146;
+ if (yych == 'i') goto yy1356;
+ goto yy171;
} else {
- if (yych <= 'o') goto yy1305;
- if (yych <= 'z') goto yy146;
+ if (yych <= 'o') goto yy1355;
+ if (yych <= 'z') goto yy171;
goto yy3;
}
}
}
-yy1305:
+yy1355:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'M') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy142;
+ goto yy167;
}
} else {
if (yych <= '_') {
- if (yych <= 'N') goto yy1268;
- if (yych <= 'Z') goto yy142;
+ if (yych <= 'N') goto yy1318;
+ if (yych <= 'Z') goto yy167;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'm') {
if (yych <= '`') goto yy3;
- goto yy150;
+ goto yy175;
} else {
- if (yych <= 'n') goto yy1309;
- if (yych <= 'z') goto yy150;
+ if (yych <= 'n') goto yy1359;
+ if (yych <= 'z') goto yy175;
goto yy3;
}
}
}
-yy1306:
+yy1356:
yyaccept = 4;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'C') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
- if (yych <= ',') goto yy166;
- goto yy147;
+ if (yych == ')') goto yy164;
+ if (yych <= ',') goto yy191;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
- if (yych <= '@') goto yy166;
- goto yy142;
+ if (yych == '/') goto yy172;
+ if (yych <= '@') goto yy191;
+ goto yy167;
}
} else {
if (yych <= '_') {
- if (yych <= 'D') goto yy1266;
- if (yych <= 'Z') goto yy142;
- if (yych <= '^') goto yy166;
- goto yy147;
+ if (yych <= 'D') goto yy1316;
+ if (yych <= 'Z') goto yy167;
+ if (yych <= '^') goto yy191;
+ goto yy172;
} else {
if (yych <= 'c') {
- if (yych <= '`') goto yy166;
- goto yy150;
+ if (yych <= '`') goto yy191;
+ goto yy175;
} else {
- if (yych <= 'd') goto yy1307;
- if (yych <= 'z') goto yy150;
- goto yy166;
+ if (yych <= 'd') goto yy1357;
+ if (yych <= 'z') goto yy175;
+ goto yy191;
}
}
}
-yy1307:
+yy1357:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '@') {
if (yych <= ',') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
if (yych == '.') goto yy3;
- if (yych <= '/') goto yy147;
+ if (yych <= '/') goto yy172;
goto yy3;
}
} else {
if (yych <= '_') {
- if (yych <= 'A') goto yy1267;
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'A') goto yy1317;
+ if (yych <= 'Z') goto yy168;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= '`') goto yy3;
- if (yych <= 'a') goto yy1308;
- if (yych <= 'z') goto yy151;
+ if (yych <= 'a') goto yy1358;
+ if (yych <= 'z') goto yy176;
goto yy3;
}
}
-yy1308:
+yy1358:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'X') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy144;
+ goto yy169;
}
} else {
if (yych <= '_') {
- if (yych <= 'Y') goto yy1175;
- if (yych <= 'Z') goto yy144;
+ if (yych <= 'Y') goto yy1225;
+ if (yych <= 'Z') goto yy169;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'x') {
if (yych <= '`') goto yy3;
- goto yy152;
+ goto yy177;
} else {
- if (yych <= 'y') goto yy1204;
- if (yych <= 'z') goto yy152;
+ if (yych <= 'y') goto yy1254;
+ if (yych <= 'z') goto yy177;
goto yy3;
}
}
}
-yy1309:
+yy1359:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'S') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy143;
+ goto yy168;
}
} else {
if (yych <= '_') {
- if (yych <= 'T') goto yy1269;
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'T') goto yy1319;
+ if (yych <= 'Z') goto yy168;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 's') {
if (yych <= '`') goto yy3;
- goto yy151;
+ goto yy176;
} else {
- if (yych <= 't') goto yy1310;
- if (yych <= 'z') goto yy151;
+ if (yych <= 't') goto yy1360;
+ if (yych <= 'z') goto yy176;
goto yy3;
}
}
}
-yy1310:
+yy1360:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '.') {
if (yych <= '(') {
- if (yych == ' ') goto yy1270;
+ if (yych == ' ') goto yy1320;
goto yy3;
} else {
- if (yych <= ')') goto yy139;
- if (yych == '-') goto yy147;
+ if (yych <= ')') goto yy164;
+ if (yych == '-') goto yy172;
goto yy3;
}
} else {
if (yych <= '^') {
- if (yych <= '/') goto yy147;
+ if (yych <= '/') goto yy172;
if (yych <= '@') goto yy3;
- if (yych <= 'Z') goto yy144;
+ if (yych <= 'Z') goto yy169;
goto yy3;
} else {
- if (yych <= '_') goto yy147;
+ if (yych <= '_') goto yy172;
if (yych <= '`') goto yy3;
- if (yych <= 'z') goto yy152;
+ if (yych <= 'z') goto yy177;
goto yy3;
}
}
-yy1311:
+yy1361:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'Q') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy142;
+ goto yy167;
}
} else {
if (yych <= '_') {
- if (yych <= 'R') goto yy1283;
- if (yych <= 'Z') goto yy142;
+ if (yych <= 'R') goto yy1333;
+ if (yych <= 'Z') goto yy167;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'q') {
if (yych <= '`') goto yy3;
- goto yy150;
+ goto yy175;
} else {
- if (yych <= 'r') goto yy1312;
- if (yych <= 'z') goto yy150;
+ if (yych <= 'r') goto yy1362;
+ if (yych <= 'z') goto yy175;
goto yy3;
}
}
}
-yy1312:
+yy1362:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'S') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy143;
+ goto yy168;
}
} else {
if (yych <= '_') {
- if (yych <= 'T') goto yy1284;
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'T') goto yy1334;
+ if (yych <= 'Z') goto yy168;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 's') {
if (yych <= '`') goto yy3;
- goto yy151;
+ goto yy176;
} else {
- if (yych <= 't') goto yy1313;
- if (yych <= 'z') goto yy151;
+ if (yych <= 't') goto yy1363;
+ if (yych <= 'z') goto yy176;
goto yy3;
}
}
}
-yy1313:
+yy1363:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'G') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy144;
+ goto yy169;
}
} else {
if (yych <= '_') {
- if (yych <= 'H') goto yy1155;
- if (yych <= 'Z') goto yy144;
+ if (yych <= 'H') goto yy1205;
+ if (yych <= 'Z') goto yy169;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'g') {
if (yych <= '`') goto yy3;
- goto yy152;
+ goto yy177;
} else {
- if (yych <= 'h') goto yy1167;
- if (yych <= 'z') goto yy152;
+ if (yych <= 'h') goto yy1217;
+ if (yych <= 'z') goto yy177;
goto yy3;
}
}
}
-yy1314:
+yy1364:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'R') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy142;
+ goto yy167;
}
} else {
if (yych <= '_') {
- if (yych <= 'S') goto yy1288;
- if (yych <= 'Z') goto yy142;
+ if (yych <= 'S') goto yy1338;
+ if (yych <= 'Z') goto yy167;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'r') {
if (yych <= '`') goto yy3;
- goto yy150;
+ goto yy175;
} else {
- if (yych <= 's') goto yy1317;
- if (yych <= 'z') goto yy150;
+ if (yych <= 's') goto yy1367;
+ if (yych <= 'z') goto yy175;
goto yy3;
}
}
}
-yy1315:
+yy1365:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'S') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy142;
+ goto yy167;
}
} else {
if (yych <= '_') {
- if (yych <= 'T') goto yy1287;
- if (yych <= 'Z') goto yy142;
+ if (yych <= 'T') goto yy1337;
+ if (yych <= 'Z') goto yy167;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 's') {
if (yych <= '`') goto yy3;
- goto yy150;
+ goto yy175;
} else {
- if (yych <= 't') goto yy1316;
- if (yych <= 'z') goto yy150;
+ if (yych <= 't') goto yy1366;
+ if (yych <= 'z') goto yy175;
goto yy3;
}
}
}
-yy1316:
+yy1366:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'G') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy143;
+ goto yy168;
}
} else {
if (yych <= '_') {
- if (yych <= 'H') goto yy1178;
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'H') goto yy1228;
+ if (yych <= 'Z') goto yy168;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'g') {
if (yych <= '`') goto yy3;
- goto yy151;
+ goto yy176;
} else {
- if (yych <= 'h') goto yy1207;
- if (yych <= 'z') goto yy151;
+ if (yych <= 'h') goto yy1257;
+ if (yych <= 'z') goto yy176;
goto yy3;
}
}
}
-yy1317:
+yy1367:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'S') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy143;
+ goto yy168;
}
} else {
if (yych <= '_') {
- if (yych <= 'T') goto yy1289;
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'T') goto yy1339;
+ if (yych <= 'Z') goto yy168;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 's') {
if (yych <= '`') goto yy3;
- goto yy151;
+ goto yy176;
} else {
- if (yych <= 't') goto yy1318;
- if (yych <= 'z') goto yy151;
+ if (yych <= 't') goto yy1368;
+ if (yych <= 'z') goto yy176;
goto yy3;
}
}
}
-yy1318:
+yy1368:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '-') {
if (yych <= ' ') {
- if (yych == '\t') goto yy1146;
+ if (yych == '\t') goto yy1196;
if (yych <= 0x1F) goto yy3;
- goto yy1290;
+ goto yy1340;
} else {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
}
} else {
if (yych <= 'Z') {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy144;
+ goto yy169;
} else {
if (yych <= '_') {
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= '`') goto yy3;
- if (yych <= 'z') goto yy152;
+ if (yych <= 'z') goto yy177;
goto yy3;
}
}
}
-yy1319:
+yy1369:
yyaccept = 5;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '9') {
if (yych <= '(') {
if (yych <= '\t') {
- if (yych <= 0x08) goto yy193;
- goto yy195;
+ if (yych <= 0x08) goto yy218;
+ goto yy220;
} else {
- if (yych == ' ') goto yy195;
- goto yy193;
+ if (yych == ' ') goto yy220;
+ goto yy218;
}
} else {
if (yych <= '-') {
- if (yych <= ')') goto yy139;
- if (yych <= ',') goto yy193;
- goto yy311;
+ if (yych <= ')') goto yy164;
+ if (yych <= ',') goto yy218;
+ goto yy336;
} else {
- if (yych == '/') goto yy147;
- goto yy195;
+ if (yych == '/') goto yy172;
+ goto yy220;
}
}
} else {
if (yych <= '^') {
if (yych <= 'Q') {
- if (yych <= '@') goto yy193;
- goto yy142;
+ if (yych <= '@') goto yy218;
+ goto yy167;
} else {
- if (yych <= 'R') goto yy1297;
- if (yych <= 'Z') goto yy142;
- goto yy193;
+ if (yych <= 'R') goto yy1347;
+ if (yych <= 'Z') goto yy167;
+ goto yy218;
}
} else {
if (yych <= 'q') {
- if (yych <= '_') goto yy147;
- if (yych <= '`') goto yy193;
- goto yy150;
+ if (yych <= '_') goto yy172;
+ if (yych <= '`') goto yy218;
+ goto yy175;
} else {
- if (yych <= 'r') goto yy1320;
- if (yych <= 'z') goto yy150;
- goto yy193;
+ if (yych <= 'r') goto yy1370;
+ if (yych <= 'z') goto yy175;
+ goto yy218;
}
}
}
-yy1320:
+yy1370:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'T') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy143;
+ goto yy168;
}
} else {
if (yych <= '_') {
- if (yych <= 'U') goto yy1298;
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'U') goto yy1348;
+ if (yych <= 'Z') goto yy168;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 't') {
if (yych <= '`') goto yy3;
- goto yy151;
+ goto yy176;
} else {
- if (yych <= 'u') goto yy1321;
- if (yych <= 'z') goto yy151;
+ if (yych <= 'u') goto yy1371;
+ if (yych <= 'z') goto yy176;
goto yy3;
}
}
}
-yy1321:
+yy1371:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '@') {
if (yych <= ',') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
if (yych == '.') goto yy3;
- if (yych <= '/') goto yy147;
+ if (yych <= '/') goto yy172;
goto yy3;
}
} else {
if (yych <= '_') {
- if (yych <= 'A') goto yy1299;
- if (yych <= 'Z') goto yy144;
+ if (yych <= 'A') goto yy1349;
+ if (yych <= 'Z') goto yy169;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= '`') goto yy3;
- if (yych <= 'a') goto yy1322;
- if (yych <= 'z') goto yy152;
+ if (yych <= 'a') goto yy1372;
+ if (yych <= 'z') goto yy177;
goto yy3;
}
}
-yy1322:
+yy1372:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'Q') {
if (yych <= ',') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
if (yych == '.') goto yy3;
- if (yych <= '/') goto yy147;
+ if (yych <= '/') goto yy172;
goto yy3;
}
} else {
if (yych <= '`') {
- if (yych <= 'R') goto yy1300;
- if (yych == '_') goto yy147;
+ if (yych <= 'R') goto yy1350;
+ if (yych == '_') goto yy172;
goto yy3;
} else {
- if (yych == 'r') goto yy1323;
- if (yych <= 'z') goto yy153;
+ if (yych == 'r') goto yy1373;
+ if (yych <= 'z') goto yy178;
goto yy3;
}
}
-yy1323:
+yy1373:
yych = *++YYCURSOR;
- if (yych == 'Y') goto yy204;
- if (yych == 'y') goto yy316;
- goto yy154;
-yy1324:
+ if (yych == 'Y') goto yy229;
+ if (yych == 'y') goto yy341;
+ goto yy179;
+yy1374:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
if (yych >= ':') goto yy56;
-yy1325:
- ++YYCURSOR;
- if (YYLIMIT <= YYCURSOR) YYFILL(1);
+yy1375:
+ yyaccept = 28;
+ YYMARKER = ++YYCURSOR;
+ if ((YYLIMIT - YYCURSOR) < 7) YYFILL(7);
yych = *YYCURSOR;
- if (yych <= '/') goto yy1327;
- if (yych <= '9') goto yy1325;
-yy1327:
-#line 1010 "ext/date/lib/parse_date.re"
+ if (yych == '.') goto yy1378;
+ if (yych <= '/') goto yy1377;
+ if (yych <= '9') goto yy1375;
+yy1377:
+#line 1025 "ext/date/lib/parse_date.re"
{
timelib_ull i;
@@ -19023,413 +19955,461 @@ yy1327:
TIMELIB_DEINIT;
return TIMELIB_RELATIVE;
}
-#line 19027 "ext/date/lib/parse_date.c"
-yy1328:
+#line 19959 "ext/date/lib/parse_date.c"
+yy1378:
+ yych = *++YYCURSOR;
+ if (yych <= '/') goto yy56;
+ if (yych >= ':') goto yy56;
+ yych = *++YYCURSOR;
+ if (yych <= '/') goto yy56;
+ if (yych >= ':') goto yy56;
+ yych = *++YYCURSOR;
+ if (yych <= '/') goto yy56;
+ if (yych >= ':') goto yy56;
+ yych = *++YYCURSOR;
+ if (yych <= '/') goto yy56;
+ if (yych >= ':') goto yy56;
+ yych = *++YYCURSOR;
+ if (yych <= '/') goto yy56;
+ if (yych >= ':') goto yy56;
+ yych = *++YYCURSOR;
+ if (yych <= '/') goto yy56;
+ if (yych >= ':') goto yy56;
+ ++YYCURSOR;
+#line 1051 "ext/date/lib/parse_date.re"
+ {
+ timelib_ull i, ms;
+
+ TIMELIB_INIT;
+ TIMELIB_HAVE_RELATIVE();
+ TIMELIB_UNHAVE_DATE();
+ TIMELIB_UNHAVE_TIME();
+ TIMELIB_HAVE_TZ();
+
+ i = timelib_get_unsigned_nr((char **) &ptr, 24);
+ ms = timelib_get_unsigned_nr((char **) &ptr, 24);
+ s->time->y = 1970;
+ s->time->m = 1;
+ s->time->d = 1;
+ s->time->h = s->time->i = s->time->s = 0;
+ s->time->f = 0.0;
+ s->time->relative.s += i;
+ s->time->relative.f = ((double) ms) / 1000000.0;
+ s->time->is_localtime = 1;
+ s->time->zone_type = TIMELIB_ZONETYPE_OFFSET;
+ s->time->z = 0;
+ s->time->dst = 0;
+
+ TIMELIB_DEINIT;
+ return TIMELIB_RELATIVE;
+ }
+#line 20007 "ext/date/lib/parse_date.c"
+yy1386:
yych = *++YYCURSOR;
if (yych <= 'N') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'M') goto yy141;
- goto yy1369;
+ if (yych <= 'M') goto yy166;
+ goto yy1427;
}
} else {
if (yych <= 'm') {
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'Z') goto yy166;
if (yych <= '`') goto yy3;
- goto yy141;
+ goto yy166;
} else {
- if (yych <= 'n') goto yy1369;
- if (yych <= 'z') goto yy141;
+ if (yych <= 'n') goto yy1427;
+ if (yych <= 'z') goto yy166;
goto yy3;
}
}
-yy1329:
+yy1387:
yych = *++YYCURSOR;
if (yych <= 'U') {
if (yych <= '@') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
- if (yych == 'I') goto yy1361;
- if (yych <= 'T') goto yy141;
- goto yy1362;
+ if (yych == 'I') goto yy1419;
+ if (yych <= 'T') goto yy166;
+ goto yy1420;
}
} else {
if (yych <= 'i') {
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'Z') goto yy166;
if (yych <= '`') goto yy3;
- if (yych <= 'h') goto yy141;
- goto yy1361;
+ if (yych <= 'h') goto yy166;
+ goto yy1419;
} else {
- if (yych == 'u') goto yy1362;
- if (yych <= 'z') goto yy141;
+ if (yych == 'u') goto yy1420;
+ if (yych <= 'z') goto yy166;
goto yy3;
}
}
-yy1330:
+yy1388:
yych = *++YYCURSOR;
if (yych <= 'M') {
if (yych <= '@') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
- if (yych == 'D') goto yy1350;
- if (yych <= 'L') goto yy141;
- goto yy1351;
+ if (yych == 'D') goto yy1408;
+ if (yych <= 'L') goto yy166;
+ goto yy1409;
}
} else {
if (yych <= 'd') {
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'Z') goto yy166;
if (yych <= '`') goto yy3;
- if (yych <= 'c') goto yy141;
- goto yy1350;
+ if (yych <= 'c') goto yy166;
+ goto yy1408;
} else {
- if (yych == 'm') goto yy1351;
- if (yych <= 'z') goto yy141;
+ if (yych == 'm') goto yy1409;
+ if (yych <= 'z') goto yy166;
goto yy3;
}
}
-yy1331:
+yy1389:
yych = *++YYCURSOR;
if (yych <= 'E') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'D') goto yy141;
- goto yy1346;
+ if (yych <= 'D') goto yy166;
+ goto yy1404;
}
} else {
if (yych <= 'd') {
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'Z') goto yy166;
if (yych <= '`') goto yy3;
- goto yy141;
+ goto yy166;
} else {
- if (yych <= 'e') goto yy1346;
- if (yych <= 'z') goto yy141;
+ if (yych <= 'e') goto yy1404;
+ if (yych <= 'z') goto yy166;
goto yy3;
}
}
-yy1332:
+yy1390:
yych = *++YYCURSOR;
if (yych <= 'E') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'D') goto yy141;
- goto yy1342;
+ if (yych <= 'D') goto yy166;
+ goto yy1400;
}
} else {
if (yych <= 'd') {
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'Z') goto yy166;
if (yych <= '`') goto yy3;
- goto yy141;
+ goto yy166;
} else {
- if (yych <= 'e') goto yy1342;
- if (yych <= 'z') goto yy141;
+ if (yych <= 'e') goto yy1400;
+ if (yych <= 'z') goto yy166;
goto yy3;
}
}
-yy1333:
+yy1391:
yych = *++YYCURSOR;
if (yych <= '/') {
- if (yych == '.') goto yy1004;
+ if (yych == '.') goto yy1029;
goto yy56;
} else {
- if (yych <= '9') goto yy1336;
- if (yych <= ':') goto yy1004;
+ if (yych <= '9') goto yy1394;
+ if (yych <= ':') goto yy1029;
goto yy56;
}
-yy1334:
+yy1392:
yych = *++YYCURSOR;
if (yych <= '/') {
- if (yych == '.') goto yy1004;
+ if (yych == '.') goto yy1029;
goto yy56;
} else {
- if (yych <= '4') goto yy1336;
- if (yych == ':') goto yy1004;
+ if (yych <= '4') goto yy1394;
+ if (yych == ':') goto yy1029;
goto yy56;
}
-yy1335:
+yy1393:
yych = *++YYCURSOR;
- if (yych == '.') goto yy1004;
- if (yych == ':') goto yy1004;
+ if (yych == '.') goto yy1029;
+ if (yych == ':') goto yy1029;
goto yy56;
-yy1336:
+yy1394:
yych = *++YYCURSOR;
if (yych <= '/') {
- if (yych == '.') goto yy1004;
+ if (yych == '.') goto yy1029;
goto yy56;
} else {
- if (yych <= '5') goto yy1337;
- if (yych == ':') goto yy1004;
+ if (yych <= '5') goto yy1395;
+ if (yych == ':') goto yy1029;
goto yy56;
}
-yy1337:
+yy1395:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
if (yych >= ':') goto yy56;
yyaccept = 23;
yych = *(YYMARKER = ++YYCURSOR);
- if (yych <= '/') goto yy1007;
- if (yych <= '5') goto yy1339;
- if (yych <= '6') goto yy1340;
- goto yy1007;
-yy1339:
+ if (yych <= '/') goto yy1032;
+ if (yych <= '5') goto yy1397;
+ if (yych <= '6') goto yy1398;
+ goto yy1032;
+yy1397:
yych = *++YYCURSOR;
if (yych <= '/') goto yy56;
- if (yych <= '9') goto yy1341;
+ if (yych <= '9') goto yy1399;
goto yy56;
-yy1340:
+yy1398:
yych = *++YYCURSOR;
if (yych != '0') goto yy56;
-yy1341:
+yy1399:
yych = *++YYCURSOR;
- goto yy1015;
-yy1342:
+ goto yy1040;
+yy1400:
yych = *++YYCURSOR;
if (yych <= 'L') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'K') goto yy142;
+ if (yych <= 'K') goto yy167;
}
} else {
if (yych <= 'k') {
- if (yych <= 'Z') goto yy142;
+ if (yych <= 'Z') goto yy167;
if (yych <= '`') goto yy3;
- goto yy142;
+ goto yy167;
} else {
- if (yych <= 'l') goto yy1343;
- if (yych <= 'z') goto yy142;
+ if (yych <= 'l') goto yy1401;
+ if (yych <= 'z') goto yy167;
goto yy3;
}
}
-yy1343:
+yy1401:
yych = *++YYCURSOR;
if (yych <= 'F') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'E') goto yy143;
+ if (yych <= 'E') goto yy168;
}
} else {
if (yych <= 'e') {
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'Z') goto yy168;
if (yych <= '`') goto yy3;
- goto yy143;
+ goto yy168;
} else {
- if (yych <= 'f') goto yy1344;
- if (yych <= 'z') goto yy143;
+ if (yych <= 'f') goto yy1402;
+ if (yych <= 'z') goto yy168;
goto yy3;
}
}
-yy1344:
+yy1402:
yych = *++YYCURSOR;
if (yych <= 'T') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'S') goto yy144;
+ if (yych <= 'S') goto yy169;
}
} else {
if (yych <= 's') {
- if (yych <= 'Z') goto yy144;
+ if (yych <= 'Z') goto yy169;
if (yych <= '`') goto yy3;
- goto yy144;
+ goto yy169;
} else {
- if (yych <= 't') goto yy1345;
- if (yych <= 'z') goto yy144;
+ if (yych <= 't') goto yy1403;
+ if (yych <= 'z') goto yy169;
goto yy3;
}
}
-yy1345:
+yy1403:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'G') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
- if (yych <= 'H') goto yy1145;
- if (yych == 'h') goto yy1145;
+ if (yych <= 'H') goto yy1195;
+ if (yych == 'h') goto yy1195;
goto yy3;
}
-yy1346:
+yy1404:
yych = *++YYCURSOR;
if (yych <= 'S') {
if (yych <= ')') {
- if (yych <= '(') goto yy166;
- goto yy139;
+ if (yych <= '(') goto yy191;
+ goto yy164;
} else {
- if (yych <= '@') goto yy166;
- if (yych <= 'R') goto yy142;
+ if (yych <= '@') goto yy191;
+ if (yych <= 'R') goto yy167;
}
} else {
if (yych <= 'r') {
- if (yych <= 'Z') goto yy142;
- if (yych <= '`') goto yy166;
- goto yy142;
+ if (yych <= 'Z') goto yy167;
+ if (yych <= '`') goto yy191;
+ goto yy167;
} else {
- if (yych <= 's') goto yy1347;
- if (yych <= 'z') goto yy142;
- goto yy166;
+ if (yych <= 's') goto yy1405;
+ if (yych <= 'z') goto yy167;
+ goto yy191;
}
}
-yy1347:
+yy1405:
yych = *++YYCURSOR;
if (yych <= 'D') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'C') goto yy143;
+ if (yych <= 'C') goto yy168;
}
} else {
if (yych <= 'c') {
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'Z') goto yy168;
if (yych <= '`') goto yy3;
- goto yy143;
+ goto yy168;
} else {
- if (yych <= 'd') goto yy1348;
- if (yych <= 'z') goto yy143;
+ if (yych <= 'd') goto yy1406;
+ if (yych <= 'z') goto yy168;
goto yy3;
}
}
-yy1348:
+yy1406:
yych = *++YYCURSOR;
if (yych <= 'A') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= '@') goto yy3;
} else {
if (yych <= '`') {
- if (yych <= 'Z') goto yy144;
+ if (yych <= 'Z') goto yy169;
goto yy3;
} else {
- if (yych <= 'a') goto yy1349;
- if (yych <= 'z') goto yy144;
+ if (yych <= 'a') goto yy1407;
+ if (yych <= 'z') goto yy169;
goto yy3;
}
}
-yy1349:
+yy1407:
yych = *++YYCURSOR;
if (yych <= 'X') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
- if (yych <= 'Y') goto yy172;
- if (yych == 'y') goto yy172;
+ if (yych <= 'Y') goto yy197;
+ if (yych == 'y') goto yy197;
goto yy3;
}
-yy1350:
+yy1408:
yych = *++YYCURSOR;
if (yych <= 'A') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= '@') goto yy3;
- goto yy1358;
+ goto yy1416;
} else {
if (yych <= '`') {
- if (yych <= 'Z') goto yy142;
+ if (yych <= 'Z') goto yy167;
goto yy3;
} else {
- if (yych <= 'a') goto yy1358;
- if (yych <= 'z') goto yy142;
+ if (yych <= 'a') goto yy1416;
+ if (yych <= 'z') goto yy167;
goto yy3;
}
}
-yy1351:
+yy1409:
yych = *++YYCURSOR;
if (yych <= 'O') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'N') goto yy142;
+ if (yych <= 'N') goto yy167;
}
} else {
if (yych <= 'n') {
- if (yych <= 'Z') goto yy142;
+ if (yych <= 'Z') goto yy167;
if (yych <= '`') goto yy3;
- goto yy142;
+ goto yy167;
} else {
- if (yych <= 'o') goto yy1352;
- if (yych <= 'z') goto yy142;
+ if (yych <= 'o') goto yy1410;
+ if (yych <= 'z') goto yy167;
goto yy3;
}
}
-yy1352:
+yy1410:
yych = *++YYCURSOR;
if (yych <= 'R') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'Q') goto yy143;
+ if (yych <= 'Q') goto yy168;
}
} else {
if (yych <= 'q') {
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'Z') goto yy168;
if (yych <= '`') goto yy3;
- goto yy143;
+ goto yy168;
} else {
- if (yych <= 'r') goto yy1353;
- if (yych <= 'z') goto yy143;
+ if (yych <= 'r') goto yy1411;
+ if (yych <= 'z') goto yy168;
goto yy3;
}
}
-yy1353:
+yy1411:
yych = *++YYCURSOR;
if (yych <= 'R') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'Q') goto yy144;
+ if (yych <= 'Q') goto yy169;
}
} else {
if (yych <= 'q') {
- if (yych <= 'Z') goto yy144;
+ if (yych <= 'Z') goto yy169;
if (yych <= '`') goto yy3;
- goto yy144;
+ goto yy169;
} else {
- if (yych <= 'r') goto yy1354;
- if (yych <= 'z') goto yy144;
+ if (yych <= 'r') goto yy1412;
+ if (yych <= 'z') goto yy169;
goto yy3;
}
}
-yy1354:
+yy1412:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'N') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
- if (yych <= 'O') goto yy1355;
+ if (yych <= 'O') goto yy1413;
if (yych != 'o') goto yy3;
}
-yy1355:
+yy1413:
yych = *++YYCURSOR;
- if (yych == 'W') goto yy1356;
+ if (yych == 'W') goto yy1414;
if (yych != 'w') goto yy56;
-yy1356:
+yy1414:
++YYCURSOR;
-yy1357:
-#line 998 "ext/date/lib/parse_date.re"
+yy1415:
+#line 1013 "ext/date/lib/parse_date.re"
{
DEBUG_OUTPUT("tomorrow");
TIMELIB_INIT;
@@ -19440,39 +20420,39 @@ yy1357:
TIMELIB_DEINIT;
return TIMELIB_RELATIVE;
}
-#line 19444 "ext/date/lib/parse_date.c"
-yy1358:
+#line 20424 "ext/date/lib/parse_date.c"
+yy1416:
yych = *++YYCURSOR;
if (yych <= 'Y') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'X') goto yy143;
+ if (yych <= 'X') goto yy168;
}
} else {
if (yych <= 'x') {
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'Z') goto yy168;
if (yych <= '`') goto yy3;
- goto yy143;
+ goto yy168;
} else {
- if (yych <= 'y') goto yy1359;
- if (yych <= 'z') goto yy143;
+ if (yych <= 'y') goto yy1417;
+ if (yych <= 'z') goto yy168;
goto yy3;
}
}
-yy1359:
+yy1417:
++YYCURSOR;
if ((yych = *YYCURSOR) <= '@') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
} else {
- if (yych <= 'Z') goto yy144;
- if (yych <= '`') goto yy1360;
- if (yych <= 'z') goto yy144;
+ if (yych <= 'Z') goto yy169;
+ if (yych <= '`') goto yy1418;
+ if (yych <= 'z') goto yy169;
}
-yy1360:
-#line 988 "ext/date/lib/parse_date.re"
+yy1418:
+#line 1003 "ext/date/lib/parse_date.re"
{
DEBUG_OUTPUT("midnight | today");
TIMELIB_INIT;
@@ -19481,1939 +20461,1939 @@ yy1360:
TIMELIB_DEINIT;
return TIMELIB_RELATIVE;
}
-#line 19485 "ext/date/lib/parse_date.c"
-yy1361:
+#line 20465 "ext/date/lib/parse_date.c"
+yy1419:
yych = *++YYCURSOR;
if (yych <= 'S') {
if (yych <= '@') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
- if (yych <= 'Q') goto yy142;
- if (yych <= 'R') goto yy1367;
- goto yy1368;
+ if (yych <= 'Q') goto yy167;
+ if (yych <= 'R') goto yy1425;
+ goto yy1426;
}
} else {
if (yych <= 'q') {
- if (yych <= 'Z') goto yy142;
+ if (yych <= 'Z') goto yy167;
if (yych <= '`') goto yy3;
- goto yy142;
+ goto yy167;
} else {
- if (yych <= 'r') goto yy1367;
- if (yych <= 's') goto yy1368;
- if (yych <= 'z') goto yy142;
+ if (yych <= 'r') goto yy1425;
+ if (yych <= 's') goto yy1426;
+ if (yych <= 'z') goto yy167;
goto yy3;
}
}
-yy1362:
+yy1420:
yych = *++YYCURSOR;
if (yych <= 'R') {
if (yych <= ')') {
- if (yych <= '(') goto yy166;
- goto yy139;
+ if (yych <= '(') goto yy191;
+ goto yy164;
} else {
- if (yych <= '@') goto yy166;
- if (yych <= 'Q') goto yy142;
+ if (yych <= '@') goto yy191;
+ if (yych <= 'Q') goto yy167;
}
} else {
if (yych <= 'q') {
- if (yych <= 'Z') goto yy142;
- if (yych <= '`') goto yy166;
- goto yy142;
+ if (yych <= 'Z') goto yy167;
+ if (yych <= '`') goto yy191;
+ goto yy167;
} else {
- if (yych <= 'r') goto yy1363;
- if (yych <= 'z') goto yy142;
- goto yy166;
+ if (yych <= 'r') goto yy1421;
+ if (yych <= 'z') goto yy167;
+ goto yy191;
}
}
-yy1363:
+yy1421:
yych = *++YYCURSOR;
if (yych <= 'S') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'R') goto yy143;
+ if (yych <= 'R') goto yy168;
}
} else {
if (yych <= 'r') {
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'Z') goto yy168;
if (yych <= '`') goto yy3;
- goto yy143;
+ goto yy168;
} else {
- if (yych <= 's') goto yy1364;
- if (yych <= 'z') goto yy143;
+ if (yych <= 's') goto yy1422;
+ if (yych <= 'z') goto yy168;
goto yy3;
}
}
-yy1364:
+yy1422:
yych = *++YYCURSOR;
if (yych <= 'D') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'C') goto yy144;
+ if (yych <= 'C') goto yy169;
}
} else {
if (yych <= 'c') {
- if (yych <= 'Z') goto yy144;
+ if (yych <= 'Z') goto yy169;
if (yych <= '`') goto yy3;
- goto yy144;
+ goto yy169;
} else {
- if (yych <= 'd') goto yy1365;
- if (yych <= 'z') goto yy144;
+ if (yych <= 'd') goto yy1423;
+ if (yych <= 'z') goto yy169;
goto yy3;
}
}
-yy1365:
+yy1423:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '@') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
- if (yych <= 'A') goto yy1366;
+ if (yych <= 'A') goto yy1424;
if (yych != 'a') goto yy3;
}
-yy1366:
+yy1424:
yych = *++YYCURSOR;
- if (yych == 'Y') goto yy172;
- if (yych == 'y') goto yy172;
+ if (yych == 'Y') goto yy197;
+ if (yych == 'y') goto yy197;
goto yy56;
-yy1367:
+yy1425:
yych = *++YYCURSOR;
if (yych <= 'D') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'C') goto yy143;
- goto yy1178;
+ if (yych <= 'C') goto yy168;
+ goto yy1228;
}
} else {
if (yych <= 'c') {
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'Z') goto yy168;
if (yych <= '`') goto yy3;
- goto yy143;
+ goto yy168;
} else {
- if (yych <= 'd') goto yy1178;
- if (yych <= 'z') goto yy143;
+ if (yych <= 'd') goto yy1228;
+ if (yych <= 'z') goto yy168;
goto yy3;
}
}
-yy1368:
+yy1426:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '(') {
if (yych <= '\t') {
if (yych <= 0x08) goto yy3;
- goto yy1044;
+ goto yy1069;
} else {
- if (yych == ' ') goto yy1044;
+ if (yych == ' ') goto yy1069;
goto yy3;
}
} else {
if (yych <= 'Z') {
- if (yych <= ')') goto yy139;
+ if (yych <= ')') goto yy164;
if (yych <= '@') goto yy3;
- goto yy143;
+ goto yy168;
} else {
if (yych <= '`') goto yy3;
- if (yych <= 'z') goto yy143;
+ if (yych <= 'z') goto yy168;
goto yy3;
}
}
-yy1369:
+yy1427:
yych = *++YYCURSOR;
if (yych <= 'T') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'S') goto yy142;
+ if (yych <= 'S') goto yy167;
}
} else {
if (yych <= 's') {
- if (yych <= 'Z') goto yy142;
+ if (yych <= 'Z') goto yy167;
if (yych <= '`') goto yy3;
- goto yy142;
+ goto yy167;
} else {
- if (yych <= 't') goto yy1370;
- if (yych <= 'z') goto yy142;
+ if (yych <= 't') goto yy1428;
+ if (yych <= 'z') goto yy167;
goto yy3;
}
}
-yy1370:
+yy1428:
yych = *++YYCURSOR;
if (yych <= 'H') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'G') goto yy143;
- goto yy1178;
+ if (yych <= 'G') goto yy168;
+ goto yy1228;
}
} else {
if (yych <= 'g') {
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'Z') goto yy168;
if (yych <= '`') goto yy3;
- goto yy143;
+ goto yy168;
} else {
- if (yych <= 'h') goto yy1178;
- if (yych <= 'z') goto yy143;
+ if (yych <= 'h') goto yy1228;
+ if (yych <= 'z') goto yy168;
goto yy3;
}
}
-yy1371:
+yy1429:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'M') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy141;
+ goto yy166;
}
} else {
if (yych <= '_') {
- if (yych <= 'N') goto yy1369;
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'N') goto yy1427;
+ if (yych <= 'Z') goto yy166;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'm') {
if (yych <= '`') goto yy3;
- goto yy146;
+ goto yy171;
} else {
- if (yych <= 'n') goto yy1401;
- if (yych <= 'z') goto yy146;
+ if (yych <= 'n') goto yy1459;
+ if (yych <= 'z') goto yy171;
goto yy3;
}
}
}
-yy1372:
+yy1430:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'T') {
if (yych <= '.') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
- if (yych == '-') goto yy147;
+ if (yych == '-') goto yy172;
goto yy3;
}
} else {
if (yych <= '@') {
- if (yych <= '/') goto yy147;
+ if (yych <= '/') goto yy172;
goto yy3;
} else {
- if (yych == 'I') goto yy1361;
- goto yy141;
+ if (yych == 'I') goto yy1419;
+ goto yy166;
}
}
} else {
if (yych <= '`') {
if (yych <= 'Z') {
- if (yych <= 'U') goto yy1362;
- goto yy141;
+ if (yych <= 'U') goto yy1420;
+ goto yy166;
} else {
- if (yych == '_') goto yy147;
+ if (yych == '_') goto yy172;
goto yy3;
}
} else {
if (yych <= 't') {
- if (yych == 'i') goto yy1393;
- goto yy146;
+ if (yych == 'i') goto yy1451;
+ goto yy171;
} else {
- if (yych <= 'u') goto yy1394;
- if (yych <= 'z') goto yy146;
+ if (yych <= 'u') goto yy1452;
+ if (yych <= 'z') goto yy171;
goto yy3;
}
}
}
-yy1373:
+yy1431:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'L') {
if (yych <= '.') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
- if (yych == '-') goto yy147;
+ if (yych == '-') goto yy172;
goto yy3;
}
} else {
if (yych <= '@') {
- if (yych <= '/') goto yy147;
+ if (yych <= '/') goto yy172;
goto yy3;
} else {
- if (yych == 'D') goto yy1350;
- goto yy141;
+ if (yych == 'D') goto yy1408;
+ goto yy166;
}
}
} else {
if (yych <= '`') {
if (yych <= 'Z') {
- if (yych <= 'M') goto yy1351;
- goto yy141;
+ if (yych <= 'M') goto yy1409;
+ goto yy166;
} else {
- if (yych == '_') goto yy147;
+ if (yych == '_') goto yy172;
goto yy3;
}
} else {
if (yych <= 'l') {
- if (yych == 'd') goto yy1384;
- goto yy146;
+ if (yych == 'd') goto yy1442;
+ goto yy171;
} else {
- if (yych <= 'm') goto yy1385;
- if (yych <= 'z') goto yy146;
+ if (yych <= 'm') goto yy1443;
+ if (yych <= 'z') goto yy171;
goto yy3;
}
}
}
-yy1374:
+yy1432:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'D') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy141;
+ goto yy166;
}
} else {
if (yych <= '_') {
- if (yych <= 'E') goto yy1346;
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'E') goto yy1404;
+ if (yych <= 'Z') goto yy166;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'd') {
if (yych <= '`') goto yy3;
- goto yy146;
+ goto yy171;
} else {
- if (yych <= 'e') goto yy1380;
- if (yych <= 'z') goto yy146;
+ if (yych <= 'e') goto yy1438;
+ if (yych <= 'z') goto yy171;
goto yy3;
}
}
}
-yy1375:
+yy1433:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'D') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy141;
+ goto yy166;
}
} else {
if (yych <= '_') {
- if (yych <= 'E') goto yy1342;
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'E') goto yy1400;
+ if (yych <= 'Z') goto yy166;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'd') {
if (yych <= '`') goto yy3;
- goto yy146;
+ goto yy171;
} else {
- if (yych <= 'e') goto yy1376;
- if (yych <= 'z') goto yy146;
+ if (yych <= 'e') goto yy1434;
+ if (yych <= 'z') goto yy171;
goto yy3;
}
}
}
-yy1376:
+yy1434:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'K') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy142;
+ goto yy167;
}
} else {
if (yych <= '_') {
- if (yych <= 'L') goto yy1343;
- if (yych <= 'Z') goto yy142;
+ if (yych <= 'L') goto yy1401;
+ if (yych <= 'Z') goto yy167;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'k') {
if (yych <= '`') goto yy3;
- goto yy150;
+ goto yy175;
} else {
- if (yych <= 'l') goto yy1377;
- if (yych <= 'z') goto yy150;
+ if (yych <= 'l') goto yy1435;
+ if (yych <= 'z') goto yy175;
goto yy3;
}
}
}
-yy1377:
+yy1435:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'E') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy143;
+ goto yy168;
}
} else {
if (yych <= '_') {
- if (yych <= 'F') goto yy1344;
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'F') goto yy1402;
+ if (yych <= 'Z') goto yy168;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'e') {
if (yych <= '`') goto yy3;
- goto yy151;
+ goto yy176;
} else {
- if (yych <= 'f') goto yy1378;
- if (yych <= 'z') goto yy151;
+ if (yych <= 'f') goto yy1436;
+ if (yych <= 'z') goto yy176;
goto yy3;
}
}
}
-yy1378:
+yy1436:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'S') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy144;
+ goto yy169;
}
} else {
if (yych <= '_') {
- if (yych <= 'T') goto yy1345;
- if (yych <= 'Z') goto yy144;
+ if (yych <= 'T') goto yy1403;
+ if (yych <= 'Z') goto yy169;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 's') {
if (yych <= '`') goto yy3;
- goto yy152;
+ goto yy177;
} else {
- if (yych <= 't') goto yy1379;
- if (yych <= 'z') goto yy152;
+ if (yych <= 't') goto yy1437;
+ if (yych <= 'z') goto yy177;
goto yy3;
}
}
}
-yy1379:
+yy1437:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'G') {
if (yych <= ',') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
if (yych == '.') goto yy3;
- if (yych <= '/') goto yy147;
+ if (yych <= '/') goto yy172;
goto yy3;
}
} else {
if (yych <= '`') {
- if (yych <= 'H') goto yy1145;
- if (yych == '_') goto yy147;
+ if (yych <= 'H') goto yy1195;
+ if (yych == '_') goto yy172;
goto yy3;
} else {
- if (yych == 'h') goto yy1163;
- if (yych <= 'z') goto yy153;
+ if (yych == 'h') goto yy1213;
+ if (yych <= 'z') goto yy178;
goto yy3;
}
}
-yy1380:
+yy1438:
yyaccept = 4;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'R') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
- if (yych <= ',') goto yy166;
- goto yy147;
+ if (yych == ')') goto yy164;
+ if (yych <= ',') goto yy191;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
- if (yych <= '@') goto yy166;
- goto yy142;
+ if (yych == '/') goto yy172;
+ if (yych <= '@') goto yy191;
+ goto yy167;
}
} else {
if (yych <= '_') {
- if (yych <= 'S') goto yy1347;
- if (yych <= 'Z') goto yy142;
- if (yych <= '^') goto yy166;
- goto yy147;
+ if (yych <= 'S') goto yy1405;
+ if (yych <= 'Z') goto yy167;
+ if (yych <= '^') goto yy191;
+ goto yy172;
} else {
if (yych <= 'r') {
- if (yych <= '`') goto yy166;
- goto yy150;
+ if (yych <= '`') goto yy191;
+ goto yy175;
} else {
- if (yych <= 's') goto yy1381;
- if (yych <= 'z') goto yy150;
- goto yy166;
+ if (yych <= 's') goto yy1439;
+ if (yych <= 'z') goto yy175;
+ goto yy191;
}
}
}
-yy1381:
+yy1439:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'C') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy143;
+ goto yy168;
}
} else {
if (yych <= '_') {
- if (yych <= 'D') goto yy1348;
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'D') goto yy1406;
+ if (yych <= 'Z') goto yy168;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'c') {
if (yych <= '`') goto yy3;
- goto yy151;
+ goto yy176;
} else {
- if (yych <= 'd') goto yy1382;
- if (yych <= 'z') goto yy151;
+ if (yych <= 'd') goto yy1440;
+ if (yych <= 'z') goto yy176;
goto yy3;
}
}
}
-yy1382:
+yy1440:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '@') {
if (yych <= ',') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
if (yych == '.') goto yy3;
- if (yych <= '/') goto yy147;
+ if (yych <= '/') goto yy172;
goto yy3;
}
} else {
if (yych <= '_') {
- if (yych <= 'A') goto yy1349;
- if (yych <= 'Z') goto yy144;
+ if (yych <= 'A') goto yy1407;
+ if (yych <= 'Z') goto yy169;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= '`') goto yy3;
- if (yych <= 'a') goto yy1383;
- if (yych <= 'z') goto yy152;
+ if (yych <= 'a') goto yy1441;
+ if (yych <= 'z') goto yy177;
goto yy3;
}
}
-yy1383:
+yy1441:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'X') {
if (yych <= ',') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
if (yych == '.') goto yy3;
- if (yych <= '/') goto yy147;
+ if (yych <= '/') goto yy172;
goto yy3;
}
} else {
if (yych <= '`') {
- if (yych <= 'Y') goto yy172;
- if (yych == '_') goto yy147;
+ if (yych <= 'Y') goto yy197;
+ if (yych == '_') goto yy172;
goto yy3;
} else {
- if (yych == 'y') goto yy185;
- if (yych <= 'z') goto yy153;
+ if (yych == 'y') goto yy210;
+ if (yych <= 'z') goto yy178;
goto yy3;
}
}
-yy1384:
+yy1442:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '@') {
if (yych <= ',') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
if (yych == '.') goto yy3;
- if (yych <= '/') goto yy147;
+ if (yych <= '/') goto yy172;
goto yy3;
}
} else {
if (yych <= '_') {
- if (yych <= 'A') goto yy1358;
- if (yych <= 'Z') goto yy142;
+ if (yych <= 'A') goto yy1416;
+ if (yych <= 'Z') goto yy167;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= '`') goto yy3;
- if (yych <= 'a') goto yy1391;
- if (yych <= 'z') goto yy150;
+ if (yych <= 'a') goto yy1449;
+ if (yych <= 'z') goto yy175;
goto yy3;
}
}
-yy1385:
+yy1443:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'N') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy142;
+ goto yy167;
}
} else {
if (yych <= '_') {
- if (yych <= 'O') goto yy1352;
- if (yych <= 'Z') goto yy142;
+ if (yych <= 'O') goto yy1410;
+ if (yych <= 'Z') goto yy167;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'n') {
if (yych <= '`') goto yy3;
- goto yy150;
+ goto yy175;
} else {
- if (yych <= 'o') goto yy1386;
- if (yych <= 'z') goto yy150;
+ if (yych <= 'o') goto yy1444;
+ if (yych <= 'z') goto yy175;
goto yy3;
}
}
}
-yy1386:
+yy1444:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'Q') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy143;
+ goto yy168;
}
} else {
if (yych <= '_') {
- if (yych <= 'R') goto yy1353;
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'R') goto yy1411;
+ if (yych <= 'Z') goto yy168;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'q') {
if (yych <= '`') goto yy3;
- goto yy151;
+ goto yy176;
} else {
- if (yych <= 'r') goto yy1387;
- if (yych <= 'z') goto yy151;
+ if (yych <= 'r') goto yy1445;
+ if (yych <= 'z') goto yy176;
goto yy3;
}
}
}
-yy1387:
+yy1445:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'Q') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy144;
+ goto yy169;
}
} else {
if (yych <= '_') {
- if (yych <= 'R') goto yy1354;
- if (yych <= 'Z') goto yy144;
+ if (yych <= 'R') goto yy1412;
+ if (yych <= 'Z') goto yy169;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'q') {
if (yych <= '`') goto yy3;
- goto yy152;
+ goto yy177;
} else {
- if (yych <= 'r') goto yy1388;
- if (yych <= 'z') goto yy152;
+ if (yych <= 'r') goto yy1446;
+ if (yych <= 'z') goto yy177;
goto yy3;
}
}
}
-yy1388:
+yy1446:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'N') {
if (yych <= ',') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
if (yych == '.') goto yy3;
- if (yych <= '/') goto yy147;
+ if (yych <= '/') goto yy172;
goto yy3;
}
} else {
if (yych <= '`') {
- if (yych <= 'O') goto yy1355;
- if (yych == '_') goto yy147;
+ if (yych <= 'O') goto yy1413;
+ if (yych == '_') goto yy172;
goto yy3;
} else {
- if (yych == 'o') goto yy1389;
- if (yych <= 'z') goto yy153;
+ if (yych == 'o') goto yy1447;
+ if (yych <= 'z') goto yy178;
goto yy3;
}
}
-yy1389:
+yy1447:
yych = *++YYCURSOR;
- if (yych == 'W') goto yy1356;
- if (yych != 'w') goto yy154;
- yyaccept = 28;
+ if (yych == 'W') goto yy1414;
+ if (yych != 'w') goto yy179;
+ yyaccept = 29;
yych = *(YYMARKER = ++YYCURSOR);
if (yybm[0+yych] & 16) {
- goto yy153;
+ goto yy178;
}
if (yych <= '.') {
- if (yych == '-') goto yy147;
- goto yy1357;
+ if (yych == '-') goto yy172;
+ goto yy1415;
} else {
- if (yych <= '/') goto yy147;
- if (yych == '_') goto yy147;
- goto yy1357;
+ if (yych <= '/') goto yy172;
+ if (yych == '_') goto yy172;
+ goto yy1415;
}
-yy1391:
+yy1449:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'X') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy143;
+ goto yy168;
}
} else {
if (yych <= '_') {
- if (yych <= 'Y') goto yy1359;
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'Y') goto yy1417;
+ if (yych <= 'Z') goto yy168;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'x') {
if (yych <= '`') goto yy3;
- goto yy151;
+ goto yy176;
} else {
- if (yych <= 'y') goto yy1392;
- if (yych <= 'z') goto yy151;
+ if (yych <= 'y') goto yy1450;
+ if (yych <= 'z') goto yy176;
goto yy3;
}
}
}
-yy1392:
- yyaccept = 29;
+yy1450:
+ yyaccept = 30;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '/') {
if (yych <= ',') {
- if (yych == ')') goto yy139;
- goto yy1360;
+ if (yych == ')') goto yy164;
+ goto yy1418;
} else {
- if (yych == '.') goto yy1360;
- goto yy147;
+ if (yych == '.') goto yy1418;
+ goto yy172;
}
} else {
if (yych <= '^') {
- if (yych <= '@') goto yy1360;
- if (yych <= 'Z') goto yy144;
- goto yy1360;
+ if (yych <= '@') goto yy1418;
+ if (yych <= 'Z') goto yy169;
+ goto yy1418;
} else {
- if (yych <= '_') goto yy147;
- if (yych <= '`') goto yy1360;
- if (yych <= 'z') goto yy152;
- goto yy1360;
+ if (yych <= '_') goto yy172;
+ if (yych <= '`') goto yy1418;
+ if (yych <= 'z') goto yy177;
+ goto yy1418;
}
}
-yy1393:
+yy1451:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'R') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= '/') {
if (yych <= '.') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'Q') goto yy142;
- goto yy1367;
+ if (yych <= 'Q') goto yy167;
+ goto yy1425;
}
}
} else {
if (yych <= '`') {
if (yych <= 'Z') {
- if (yych <= 'S') goto yy1368;
- goto yy142;
+ if (yych <= 'S') goto yy1426;
+ goto yy167;
} else {
- if (yych == '_') goto yy147;
+ if (yych == '_') goto yy172;
goto yy3;
}
} else {
if (yych <= 'r') {
- if (yych <= 'q') goto yy150;
- goto yy1399;
+ if (yych <= 'q') goto yy175;
+ goto yy1457;
} else {
- if (yych <= 's') goto yy1400;
- if (yych <= 'z') goto yy150;
+ if (yych <= 's') goto yy1458;
+ if (yych <= 'z') goto yy175;
goto yy3;
}
}
}
-yy1394:
+yy1452:
yyaccept = 4;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'Q') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
- if (yych <= ',') goto yy166;
- goto yy147;
+ if (yych == ')') goto yy164;
+ if (yych <= ',') goto yy191;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
- if (yych <= '@') goto yy166;
- goto yy142;
+ if (yych == '/') goto yy172;
+ if (yych <= '@') goto yy191;
+ goto yy167;
}
} else {
if (yych <= '_') {
- if (yych <= 'R') goto yy1363;
- if (yych <= 'Z') goto yy142;
- if (yych <= '^') goto yy166;
- goto yy147;
+ if (yych <= 'R') goto yy1421;
+ if (yych <= 'Z') goto yy167;
+ if (yych <= '^') goto yy191;
+ goto yy172;
} else {
if (yych <= 'q') {
- if (yych <= '`') goto yy166;
- goto yy150;
+ if (yych <= '`') goto yy191;
+ goto yy175;
} else {
- if (yych <= 'r') goto yy1395;
- if (yych <= 'z') goto yy150;
- goto yy166;
+ if (yych <= 'r') goto yy1453;
+ if (yych <= 'z') goto yy175;
+ goto yy191;
}
}
}
-yy1395:
+yy1453:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'R') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy143;
+ goto yy168;
}
} else {
if (yych <= '_') {
- if (yych <= 'S') goto yy1364;
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'S') goto yy1422;
+ if (yych <= 'Z') goto yy168;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'r') {
if (yych <= '`') goto yy3;
- goto yy151;
+ goto yy176;
} else {
- if (yych <= 's') goto yy1396;
- if (yych <= 'z') goto yy151;
+ if (yych <= 's') goto yy1454;
+ if (yych <= 'z') goto yy176;
goto yy3;
}
}
}
-yy1396:
+yy1454:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'C') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy144;
+ goto yy169;
}
} else {
if (yych <= '_') {
- if (yych <= 'D') goto yy1365;
- if (yych <= 'Z') goto yy144;
+ if (yych <= 'D') goto yy1423;
+ if (yych <= 'Z') goto yy169;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'c') {
if (yych <= '`') goto yy3;
- goto yy152;
+ goto yy177;
} else {
- if (yych <= 'd') goto yy1397;
- if (yych <= 'z') goto yy152;
+ if (yych <= 'd') goto yy1455;
+ if (yych <= 'z') goto yy177;
goto yy3;
}
}
}
-yy1397:
+yy1455:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '@') {
if (yych <= ',') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
if (yych == '.') goto yy3;
- if (yych <= '/') goto yy147;
+ if (yych <= '/') goto yy172;
goto yy3;
}
} else {
if (yych <= '_') {
- if (yych <= 'A') goto yy1366;
+ if (yych <= 'A') goto yy1424;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= '`') goto yy3;
- if (yych <= 'a') goto yy1398;
- if (yych <= 'z') goto yy153;
+ if (yych <= 'a') goto yy1456;
+ if (yych <= 'z') goto yy178;
goto yy3;
}
}
-yy1398:
+yy1456:
yych = *++YYCURSOR;
- if (yych == 'Y') goto yy172;
- if (yych == 'y') goto yy185;
- goto yy154;
-yy1399:
+ if (yych == 'Y') goto yy197;
+ if (yych == 'y') goto yy210;
+ goto yy179;
+yy1457:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'C') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy143;
+ goto yy168;
}
} else {
if (yych <= '_') {
- if (yych <= 'D') goto yy1178;
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'D') goto yy1228;
+ if (yych <= 'Z') goto yy168;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'c') {
if (yych <= '`') goto yy3;
- goto yy151;
+ goto yy176;
} else {
- if (yych <= 'd') goto yy1207;
- if (yych <= 'z') goto yy151;
+ if (yych <= 'd') goto yy1257;
+ if (yych <= 'z') goto yy176;
goto yy3;
}
}
}
-yy1400:
+yy1458:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '-') {
if (yych <= ' ') {
- if (yych == '\t') goto yy1044;
+ if (yych == '\t') goto yy1069;
if (yych <= 0x1F) goto yy3;
- goto yy1044;
+ goto yy1069;
} else {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
}
} else {
if (yych <= 'Z') {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy143;
+ goto yy168;
} else {
if (yych <= '_') {
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= '`') goto yy3;
- if (yych <= 'z') goto yy151;
+ if (yych <= 'z') goto yy176;
goto yy3;
}
}
}
-yy1401:
+yy1459:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'S') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy142;
+ goto yy167;
}
} else {
if (yych <= '_') {
- if (yych <= 'T') goto yy1370;
- if (yych <= 'Z') goto yy142;
+ if (yych <= 'T') goto yy1428;
+ if (yych <= 'Z') goto yy167;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 's') {
if (yych <= '`') goto yy3;
- goto yy150;
+ goto yy175;
} else {
- if (yych <= 't') goto yy1402;
- if (yych <= 'z') goto yy150;
+ if (yych <= 't') goto yy1460;
+ if (yych <= 'z') goto yy175;
goto yy3;
}
}
}
-yy1402:
+yy1460:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'G') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy143;
+ goto yy168;
}
} else {
if (yych <= '_') {
- if (yych <= 'H') goto yy1178;
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'H') goto yy1228;
+ if (yych <= 'Z') goto yy168;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'g') {
if (yych <= '`') goto yy3;
- goto yy151;
+ goto yy176;
} else {
- if (yych <= 'h') goto yy1207;
- if (yych <= 'z') goto yy151;
+ if (yych <= 'h') goto yy1257;
+ if (yych <= 'z') goto yy176;
goto yy3;
}
}
}
-yy1403:
+yy1461:
yych = *++YYCURSOR;
if (yych <= 'Y') {
if (yych <= '@') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
- if (yych == 'R') goto yy1415;
- if (yych <= 'X') goto yy141;
- goto yy1416;
+ if (yych == 'R') goto yy1473;
+ if (yych <= 'X') goto yy166;
+ goto yy1474;
}
} else {
if (yych <= 'r') {
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'Z') goto yy166;
if (yych <= '`') goto yy3;
- if (yych <= 'q') goto yy141;
- goto yy1415;
+ if (yych <= 'q') goto yy166;
+ goto yy1473;
} else {
- if (yych == 'y') goto yy1416;
- if (yych <= 'z') goto yy141;
+ if (yych == 'y') goto yy1474;
+ if (yych <= 'z') goto yy166;
goto yy3;
}
}
-yy1404:
+yy1462:
yych = *++YYCURSOR;
if (yych <= 'D') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'C') goto yy141;
- goto yy1409;
+ if (yych <= 'C') goto yy166;
+ goto yy1467;
}
} else {
if (yych <= 'c') {
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'Z') goto yy166;
if (yych <= '`') goto yy3;
- goto yy141;
+ goto yy166;
} else {
- if (yych <= 'd') goto yy1409;
- if (yych <= 'z') goto yy141;
+ if (yych <= 'd') goto yy1467;
+ if (yych <= 'z') goto yy166;
goto yy3;
}
}
-yy1405:
+yy1463:
yych = *++YYCURSOR;
if (yych <= 'N') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'M') goto yy141;
+ if (yych <= 'M') goto yy166;
}
} else {
if (yych <= 'm') {
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'Z') goto yy166;
if (yych <= '`') goto yy3;
- goto yy141;
+ goto yy166;
} else {
- if (yych <= 'n') goto yy1406;
- if (yych <= 'z') goto yy141;
+ if (yych <= 'n') goto yy1464;
+ if (yych <= 'z') goto yy166;
goto yy3;
}
}
-yy1406:
+yy1464:
yych = *++YYCURSOR;
if (yych <= 'D') {
if (yych <= ')') {
- if (yych <= '(') goto yy166;
- goto yy139;
+ if (yych <= '(') goto yy191;
+ goto yy164;
} else {
- if (yych <= '@') goto yy166;
- if (yych <= 'C') goto yy142;
+ if (yych <= '@') goto yy191;
+ if (yych <= 'C') goto yy167;
}
} else {
if (yych <= 'c') {
- if (yych <= 'Z') goto yy142;
- if (yych <= '`') goto yy166;
- goto yy142;
+ if (yych <= 'Z') goto yy167;
+ if (yych <= '`') goto yy191;
+ goto yy167;
} else {
- if (yych <= 'd') goto yy1407;
- if (yych <= 'z') goto yy142;
- goto yy166;
+ if (yych <= 'd') goto yy1465;
+ if (yych <= 'z') goto yy167;
+ goto yy191;
}
}
-yy1407:
+yy1465:
yych = *++YYCURSOR;
if (yych <= 'A') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= '@') goto yy3;
} else {
if (yych <= '`') {
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'Z') goto yy168;
goto yy3;
} else {
- if (yych <= 'a') goto yy1408;
- if (yych <= 'z') goto yy143;
+ if (yych <= 'a') goto yy1466;
+ if (yych <= 'z') goto yy168;
goto yy3;
}
}
-yy1408:
+yy1466:
yych = *++YYCURSOR;
if (yych <= 'Y') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'X') goto yy144;
- goto yy1175;
+ if (yych <= 'X') goto yy169;
+ goto yy1225;
}
} else {
if (yych <= 'x') {
- if (yych <= 'Z') goto yy144;
+ if (yych <= 'Z') goto yy169;
if (yych <= '`') goto yy3;
- goto yy144;
+ goto yy169;
} else {
- if (yych <= 'y') goto yy1175;
- if (yych <= 'z') goto yy144;
+ if (yych <= 'y') goto yy1225;
+ if (yych <= 'z') goto yy169;
goto yy3;
}
}
-yy1409:
+yy1467:
yych = *++YYCURSOR;
if (yych <= 'N') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'M') goto yy142;
+ if (yych <= 'M') goto yy167;
}
} else {
if (yych <= 'm') {
- if (yych <= 'Z') goto yy142;
+ if (yych <= 'Z') goto yy167;
if (yych <= '`') goto yy3;
- goto yy142;
+ goto yy167;
} else {
- if (yych <= 'n') goto yy1410;
- if (yych <= 'z') goto yy142;
+ if (yych <= 'n') goto yy1468;
+ if (yych <= 'z') goto yy167;
goto yy3;
}
}
-yy1410:
+yy1468:
yych = *++YYCURSOR;
if (yych <= 'I') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'H') goto yy143;
+ if (yych <= 'H') goto yy168;
}
} else {
if (yych <= 'h') {
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'Z') goto yy168;
if (yych <= '`') goto yy3;
- goto yy143;
+ goto yy168;
} else {
- if (yych <= 'i') goto yy1411;
- if (yych <= 'z') goto yy143;
+ if (yych <= 'i') goto yy1469;
+ if (yych <= 'z') goto yy168;
goto yy3;
}
}
-yy1411:
+yy1469:
yych = *++YYCURSOR;
if (yych <= 'G') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'F') goto yy144;
+ if (yych <= 'F') goto yy169;
}
} else {
if (yych <= 'f') {
- if (yych <= 'Z') goto yy144;
+ if (yych <= 'Z') goto yy169;
if (yych <= '`') goto yy3;
- goto yy144;
+ goto yy169;
} else {
- if (yych <= 'g') goto yy1412;
- if (yych <= 'z') goto yy144;
+ if (yych <= 'g') goto yy1470;
+ if (yych <= 'z') goto yy169;
goto yy3;
}
}
-yy1412:
+yy1470:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'G') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
- if (yych <= 'H') goto yy1413;
+ if (yych <= 'H') goto yy1471;
if (yych != 'h') goto yy3;
}
-yy1413:
+yy1471:
yych = *++YYCURSOR;
- if (yych == 'T') goto yy1414;
+ if (yych == 'T') goto yy1472;
if (yych != 't') goto yy56;
-yy1414:
+yy1472:
yych = *++YYCURSOR;
- goto yy1360;
-yy1415:
+ goto yy1418;
+yy1473:
yyaccept = 5;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '/') {
if (yych <= '(') {
if (yych <= '\t') {
- if (yych <= 0x08) goto yy193;
- goto yy195;
+ if (yych <= 0x08) goto yy218;
+ goto yy220;
} else {
- if (yych == ' ') goto yy195;
- goto yy193;
+ if (yych == ' ') goto yy220;
+ goto yy218;
}
} else {
if (yych <= ',') {
- if (yych <= ')') goto yy139;
- goto yy193;
+ if (yych <= ')') goto yy164;
+ goto yy218;
} else {
- if (yych <= '-') goto yy196;
- if (yych <= '.') goto yy195;
- goto yy193;
+ if (yych <= '-') goto yy221;
+ if (yych <= '.') goto yy220;
+ goto yy218;
}
}
} else {
if (yych <= 'Z') {
if (yych <= '@') {
- if (yych <= '9') goto yy195;
- goto yy193;
+ if (yych <= '9') goto yy220;
+ goto yy218;
} else {
- if (yych == 'C') goto yy1417;
- goto yy142;
+ if (yych == 'C') goto yy1475;
+ goto yy167;
}
} else {
if (yych <= 'b') {
- if (yych <= '`') goto yy193;
- goto yy142;
+ if (yych <= '`') goto yy218;
+ goto yy167;
} else {
- if (yych <= 'c') goto yy1417;
- if (yych <= 'z') goto yy142;
- goto yy193;
+ if (yych <= 'c') goto yy1475;
+ if (yych <= 'z') goto yy167;
+ goto yy218;
}
}
}
-yy1416:
+yy1474:
yyaccept = 5;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '-') {
if (yych <= ' ') {
- if (yych == '\t') goto yy195;
- if (yych <= 0x1F) goto yy193;
- goto yy195;
+ if (yych == '\t') goto yy220;
+ if (yych <= 0x1F) goto yy218;
+ goto yy220;
} else {
- if (yych == ')') goto yy139;
- if (yych <= ',') goto yy193;
- goto yy196;
+ if (yych == ')') goto yy164;
+ if (yych <= ',') goto yy218;
+ goto yy221;
}
} else {
if (yych <= '@') {
- if (yych == '/') goto yy193;
- if (yych <= '9') goto yy195;
- goto yy193;
+ if (yych == '/') goto yy218;
+ if (yych <= '9') goto yy220;
+ goto yy218;
} else {
- if (yych <= 'Z') goto yy142;
- if (yych <= '`') goto yy193;
- if (yych <= 'z') goto yy142;
- goto yy193;
+ if (yych <= 'Z') goto yy167;
+ if (yych <= '`') goto yy218;
+ if (yych <= 'z') goto yy167;
+ goto yy218;
}
}
-yy1417:
+yy1475:
yych = *++YYCURSOR;
if (yych <= 'H') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'G') goto yy143;
- goto yy335;
+ if (yych <= 'G') goto yy168;
+ goto yy360;
}
} else {
if (yych <= 'g') {
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'Z') goto yy168;
if (yych <= '`') goto yy3;
- goto yy143;
+ goto yy168;
} else {
- if (yych <= 'h') goto yy335;
- if (yych <= 'z') goto yy143;
+ if (yych <= 'h') goto yy360;
+ if (yych <= 'z') goto yy168;
goto yy3;
}
}
-yy1418:
+yy1476:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'X') {
if (yych <= '.') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
- if (yych == '-') goto yy147;
+ if (yych == '-') goto yy172;
goto yy3;
}
} else {
if (yych <= '@') {
- if (yych <= '/') goto yy147;
+ if (yych <= '/') goto yy172;
goto yy3;
} else {
- if (yych == 'R') goto yy1415;
- goto yy141;
+ if (yych == 'R') goto yy1473;
+ goto yy166;
}
}
} else {
if (yych <= '`') {
if (yych <= 'Z') {
- if (yych <= 'Y') goto yy1416;
- goto yy141;
+ if (yych <= 'Y') goto yy1474;
+ goto yy166;
} else {
- if (yych == '_') goto yy147;
+ if (yych == '_') goto yy172;
goto yy3;
}
} else {
if (yych <= 'x') {
- if (yych == 'r') goto yy1430;
- goto yy146;
+ if (yych == 'r') goto yy1488;
+ goto yy171;
} else {
- if (yych <= 'y') goto yy1431;
- if (yych <= 'z') goto yy146;
+ if (yych <= 'y') goto yy1489;
+ if (yych <= 'z') goto yy171;
goto yy3;
}
}
}
-yy1419:
+yy1477:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'C') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy141;
+ goto yy166;
}
} else {
if (yych <= '_') {
- if (yych <= 'D') goto yy1409;
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'D') goto yy1467;
+ if (yych <= 'Z') goto yy166;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'c') {
if (yych <= '`') goto yy3;
- goto yy146;
+ goto yy171;
} else {
- if (yych <= 'd') goto yy1424;
- if (yych <= 'z') goto yy146;
+ if (yych <= 'd') goto yy1482;
+ if (yych <= 'z') goto yy171;
goto yy3;
}
}
}
-yy1420:
+yy1478:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'M') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy141;
+ goto yy166;
}
} else {
if (yych <= '_') {
- if (yych <= 'N') goto yy1406;
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'N') goto yy1464;
+ if (yych <= 'Z') goto yy166;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'm') {
if (yych <= '`') goto yy3;
- goto yy146;
+ goto yy171;
} else {
- if (yych <= 'n') goto yy1421;
- if (yych <= 'z') goto yy146;
+ if (yych <= 'n') goto yy1479;
+ if (yych <= 'z') goto yy171;
goto yy3;
}
}
}
-yy1421:
+yy1479:
yyaccept = 4;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'C') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
- if (yych <= ',') goto yy166;
- goto yy147;
+ if (yych == ')') goto yy164;
+ if (yych <= ',') goto yy191;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
- if (yych <= '@') goto yy166;
- goto yy142;
+ if (yych == '/') goto yy172;
+ if (yych <= '@') goto yy191;
+ goto yy167;
}
} else {
if (yych <= '_') {
- if (yych <= 'D') goto yy1407;
- if (yych <= 'Z') goto yy142;
- if (yych <= '^') goto yy166;
- goto yy147;
+ if (yych <= 'D') goto yy1465;
+ if (yych <= 'Z') goto yy167;
+ if (yych <= '^') goto yy191;
+ goto yy172;
} else {
if (yych <= 'c') {
- if (yych <= '`') goto yy166;
- goto yy150;
+ if (yych <= '`') goto yy191;
+ goto yy175;
} else {
- if (yych <= 'd') goto yy1422;
- if (yych <= 'z') goto yy150;
- goto yy166;
+ if (yych <= 'd') goto yy1480;
+ if (yych <= 'z') goto yy175;
+ goto yy191;
}
}
}
-yy1422:
+yy1480:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '@') {
if (yych <= ',') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
if (yych == '.') goto yy3;
- if (yych <= '/') goto yy147;
+ if (yych <= '/') goto yy172;
goto yy3;
}
} else {
if (yych <= '_') {
- if (yych <= 'A') goto yy1408;
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'A') goto yy1466;
+ if (yych <= 'Z') goto yy168;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= '`') goto yy3;
- if (yych <= 'a') goto yy1423;
- if (yych <= 'z') goto yy151;
+ if (yych <= 'a') goto yy1481;
+ if (yych <= 'z') goto yy176;
goto yy3;
}
}
-yy1423:
+yy1481:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'X') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy144;
+ goto yy169;
}
} else {
if (yych <= '_') {
- if (yych <= 'Y') goto yy1175;
- if (yych <= 'Z') goto yy144;
+ if (yych <= 'Y') goto yy1225;
+ if (yych <= 'Z') goto yy169;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'x') {
if (yych <= '`') goto yy3;
- goto yy152;
+ goto yy177;
} else {
- if (yych <= 'y') goto yy1204;
- if (yych <= 'z') goto yy152;
+ if (yych <= 'y') goto yy1254;
+ if (yych <= 'z') goto yy177;
goto yy3;
}
}
}
-yy1424:
+yy1482:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'M') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy142;
+ goto yy167;
}
} else {
if (yych <= '_') {
- if (yych <= 'N') goto yy1410;
- if (yych <= 'Z') goto yy142;
+ if (yych <= 'N') goto yy1468;
+ if (yych <= 'Z') goto yy167;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'm') {
if (yych <= '`') goto yy3;
- goto yy150;
+ goto yy175;
} else {
- if (yych <= 'n') goto yy1425;
- if (yych <= 'z') goto yy150;
+ if (yych <= 'n') goto yy1483;
+ if (yych <= 'z') goto yy175;
goto yy3;
}
}
}
-yy1425:
+yy1483:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'H') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy143;
+ goto yy168;
}
} else {
if (yych <= '_') {
- if (yych <= 'I') goto yy1411;
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'I') goto yy1469;
+ if (yych <= 'Z') goto yy168;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'h') {
if (yych <= '`') goto yy3;
- goto yy151;
+ goto yy176;
} else {
- if (yych <= 'i') goto yy1426;
- if (yych <= 'z') goto yy151;
+ if (yych <= 'i') goto yy1484;
+ if (yych <= 'z') goto yy176;
goto yy3;
}
}
}
-yy1426:
+yy1484:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'F') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy144;
+ goto yy169;
}
} else {
if (yych <= '_') {
- if (yych <= 'G') goto yy1412;
- if (yych <= 'Z') goto yy144;
+ if (yych <= 'G') goto yy1470;
+ if (yych <= 'Z') goto yy169;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'f') {
if (yych <= '`') goto yy3;
- goto yy152;
+ goto yy177;
} else {
- if (yych <= 'g') goto yy1427;
- if (yych <= 'z') goto yy152;
+ if (yych <= 'g') goto yy1485;
+ if (yych <= 'z') goto yy177;
goto yy3;
}
}
}
-yy1427:
+yy1485:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'G') {
if (yych <= ',') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
if (yych == '.') goto yy3;
- if (yych <= '/') goto yy147;
+ if (yych <= '/') goto yy172;
goto yy3;
}
} else {
if (yych <= '`') {
- if (yych <= 'H') goto yy1413;
- if (yych == '_') goto yy147;
+ if (yych <= 'H') goto yy1471;
+ if (yych == '_') goto yy172;
goto yy3;
} else {
- if (yych == 'h') goto yy1428;
- if (yych <= 'z') goto yy153;
+ if (yych == 'h') goto yy1486;
+ if (yych <= 'z') goto yy178;
goto yy3;
}
}
-yy1428:
+yy1486:
yych = *++YYCURSOR;
- if (yych == 'T') goto yy1414;
- if (yych != 't') goto yy154;
- yyaccept = 29;
+ if (yych == 'T') goto yy1472;
+ if (yych != 't') goto yy179;
+ yyaccept = 30;
yych = *(YYMARKER = ++YYCURSOR);
if (yybm[0+yych] & 16) {
- goto yy153;
+ goto yy178;
}
if (yych <= '.') {
- if (yych == '-') goto yy147;
- goto yy1360;
+ if (yych == '-') goto yy172;
+ goto yy1418;
} else {
- if (yych <= '/') goto yy147;
- if (yych == '_') goto yy147;
- goto yy1360;
+ if (yych <= '/') goto yy172;
+ if (yych == '_') goto yy172;
+ goto yy1418;
}
-yy1430:
+yy1488:
yyaccept = 5;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '9') {
if (yych <= '(') {
if (yych <= '\t') {
- if (yych <= 0x08) goto yy193;
- goto yy195;
+ if (yych <= 0x08) goto yy218;
+ goto yy220;
} else {
- if (yych == ' ') goto yy195;
- goto yy193;
+ if (yych == ' ') goto yy220;
+ goto yy218;
}
} else {
if (yych <= '-') {
- if (yych <= ')') goto yy139;
- if (yych <= ',') goto yy193;
- goto yy311;
+ if (yych <= ')') goto yy164;
+ if (yych <= ',') goto yy218;
+ goto yy336;
} else {
- if (yych == '/') goto yy147;
- goto yy195;
+ if (yych == '/') goto yy172;
+ goto yy220;
}
}
} else {
if (yych <= '^') {
if (yych <= 'B') {
- if (yych <= '@') goto yy193;
- goto yy142;
+ if (yych <= '@') goto yy218;
+ goto yy167;
} else {
- if (yych <= 'C') goto yy1417;
- if (yych <= 'Z') goto yy142;
- goto yy193;
+ if (yych <= 'C') goto yy1475;
+ if (yych <= 'Z') goto yy167;
+ goto yy218;
}
} else {
if (yych <= 'b') {
- if (yych <= '_') goto yy147;
- if (yych <= '`') goto yy193;
- goto yy150;
+ if (yych <= '_') goto yy172;
+ if (yych <= '`') goto yy218;
+ goto yy175;
} else {
- if (yych <= 'c') goto yy1432;
- if (yych <= 'z') goto yy150;
- goto yy193;
+ if (yych <= 'c') goto yy1490;
+ if (yych <= 'z') goto yy175;
+ goto yy218;
}
}
}
-yy1431:
+yy1489:
yyaccept = 5;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '.') {
if (yych <= ' ') {
- if (yych == '\t') goto yy195;
- if (yych <= 0x1F) goto yy193;
- goto yy195;
+ if (yych == '\t') goto yy220;
+ if (yych <= 0x1F) goto yy218;
+ goto yy220;
} else {
if (yych <= ')') {
- if (yych <= '(') goto yy193;
- goto yy139;
+ if (yych <= '(') goto yy218;
+ goto yy164;
} else {
- if (yych <= ',') goto yy193;
- if (yych <= '-') goto yy311;
- goto yy195;
+ if (yych <= ',') goto yy218;
+ if (yych <= '-') goto yy336;
+ goto yy220;
}
}
} else {
if (yych <= 'Z') {
- if (yych <= '/') goto yy147;
- if (yych <= '9') goto yy195;
- if (yych <= '@') goto yy193;
- goto yy142;
+ if (yych <= '/') goto yy172;
+ if (yych <= '9') goto yy220;
+ if (yych <= '@') goto yy218;
+ goto yy167;
} else {
if (yych <= '_') {
- if (yych <= '^') goto yy193;
- goto yy147;
+ if (yych <= '^') goto yy218;
+ goto yy172;
} else {
- if (yych <= '`') goto yy193;
- if (yych <= 'z') goto yy150;
- goto yy193;
+ if (yych <= '`') goto yy218;
+ if (yych <= 'z') goto yy175;
+ goto yy218;
}
}
}
-yy1432:
+yy1490:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'G') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy143;
+ goto yy168;
}
} else {
if (yych <= '_') {
- if (yych <= 'H') goto yy335;
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'H') goto yy360;
+ if (yych <= 'Z') goto yy168;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'g') {
if (yych <= '`') goto yy3;
- goto yy151;
+ goto yy176;
} else {
- if (yych <= 'h') goto yy346;
- if (yych <= 'z') goto yy151;
+ if (yych <= 'h') goto yy371;
+ if (yych <= 'z') goto yy176;
goto yy3;
}
}
}
-yy1433:
+yy1491:
yych = *++YYCURSOR;
if (yych <= 'W') {
if (yych <= 'N') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= '@') goto yy3;
- goto yy141;
+ goto yy166;
} else {
- if (yych <= 'O') goto yy1441;
- if (yych <= 'U') goto yy141;
- if (yych <= 'V') goto yy1442;
- goto yy1439;
+ if (yych <= 'O') goto yy1499;
+ if (yych <= 'U') goto yy166;
+ if (yych <= 'V') goto yy1500;
+ goto yy1497;
}
} else {
if (yych <= 'o') {
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'Z') goto yy166;
if (yych <= '`') goto yy3;
- if (yych <= 'n') goto yy141;
- goto yy1441;
+ if (yych <= 'n') goto yy166;
+ goto yy1499;
} else {
if (yych <= 'v') {
- if (yych <= 'u') goto yy141;
- goto yy1442;
+ if (yych <= 'u') goto yy166;
+ goto yy1500;
} else {
- if (yych <= 'w') goto yy1439;
- if (yych <= 'z') goto yy141;
+ if (yych <= 'w') goto yy1497;
+ if (yych <= 'z') goto yy166;
goto yy3;
}
}
}
-yy1434:
+yy1492:
yych = *++YYCURSOR;
if (yych <= 'X') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'W') goto yy141;
- goto yy1438;
+ if (yych <= 'W') goto yy166;
+ goto yy1496;
}
} else {
if (yych <= 'w') {
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'Z') goto yy166;
if (yych <= '`') goto yy3;
- goto yy141;
+ goto yy166;
} else {
- if (yych <= 'x') goto yy1438;
- if (yych <= 'z') goto yy141;
+ if (yych <= 'x') goto yy1496;
+ if (yych <= 'z') goto yy166;
goto yy3;
}
}
-yy1435:
+yy1493:
yych = *++YYCURSOR;
if (yych <= 'N') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'M') goto yy141;
+ if (yych <= 'M') goto yy166;
}
} else {
if (yych <= 'm') {
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'Z') goto yy166;
if (yych <= '`') goto yy3;
- goto yy141;
+ goto yy166;
} else {
- if (yych <= 'n') goto yy1436;
- if (yych <= 'z') goto yy141;
+ if (yych <= 'n') goto yy1494;
+ if (yych <= 'z') goto yy166;
goto yy3;
}
}
-yy1436:
+yy1494:
yych = *++YYCURSOR;
if (yych <= 'T') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'S') goto yy142;
+ if (yych <= 'S') goto yy167;
}
} else {
if (yych <= 's') {
- if (yych <= 'Z') goto yy142;
+ if (yych <= 'Z') goto yy167;
if (yych <= '`') goto yy3;
- goto yy142;
+ goto yy167;
} else {
- if (yych <= 't') goto yy1437;
- if (yych <= 'z') goto yy142;
+ if (yych <= 't') goto yy1495;
+ if (yych <= 'z') goto yy167;
goto yy3;
}
}
-yy1437:
+yy1495:
yych = *++YYCURSOR;
if (yych <= 'H') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'G') goto yy143;
- goto yy1178;
+ if (yych <= 'G') goto yy168;
+ goto yy1228;
}
} else {
if (yych <= 'g') {
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'Z') goto yy168;
if (yych <= '`') goto yy3;
- goto yy143;
+ goto yy168;
} else {
- if (yych <= 'h') goto yy1178;
- if (yych <= 'z') goto yy143;
+ if (yych <= 'h') goto yy1228;
+ if (yych <= 'z') goto yy168;
goto yy3;
}
}
-yy1438:
+yy1496:
yych = *++YYCURSOR;
if (yych <= 'T') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'S') goto yy142;
- goto yy1368;
+ if (yych <= 'S') goto yy167;
+ goto yy1426;
}
} else {
if (yych <= 's') {
- if (yych <= 'Z') goto yy142;
+ if (yych <= 'Z') goto yy167;
if (yych <= '`') goto yy3;
- goto yy142;
+ goto yy167;
} else {
- if (yych <= 't') goto yy1368;
- if (yych <= 'z') goto yy142;
+ if (yych <= 't') goto yy1426;
+ if (yych <= 'z') goto yy167;
goto yy3;
}
}
-yy1439:
+yy1497:
++YYCURSOR;
if ((yych = *YYCURSOR) <= '@') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
} else {
- if (yych <= 'Z') goto yy142;
- if (yych <= '`') goto yy1440;
- if (yych <= 'z') goto yy142;
+ if (yych <= 'Z') goto yy167;
+ if (yych <= '`') goto yy1498;
+ if (yych <= 'z') goto yy167;
}
-yy1440:
-#line 967 "ext/date/lib/parse_date.re"
+yy1498:
+#line 982 "ext/date/lib/parse_date.re"
{
DEBUG_OUTPUT("now");
TIMELIB_INIT;
@@ -21421,138 +22401,138 @@ yy1440:
TIMELIB_DEINIT;
return TIMELIB_RELATIVE;
}
-#line 21425 "ext/date/lib/parse_date.c"
-yy1441:
+#line 22405 "ext/date/lib/parse_date.c"
+yy1499:
yych = *++YYCURSOR;
if (yych <= 'N') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'M') goto yy142;
- goto yy1447;
+ if (yych <= 'M') goto yy167;
+ goto yy1505;
}
} else {
if (yych <= 'm') {
- if (yych <= 'Z') goto yy142;
+ if (yych <= 'Z') goto yy167;
if (yych <= '`') goto yy3;
- goto yy142;
+ goto yy167;
} else {
- if (yych <= 'n') goto yy1447;
- if (yych <= 'z') goto yy142;
+ if (yych <= 'n') goto yy1505;
+ if (yych <= 'z') goto yy167;
goto yy3;
}
}
-yy1442:
+yy1500:
yyaccept = 5;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '/') {
if (yych <= '(') {
if (yych <= '\t') {
- if (yych <= 0x08) goto yy193;
- goto yy195;
+ if (yych <= 0x08) goto yy218;
+ goto yy220;
} else {
- if (yych == ' ') goto yy195;
- goto yy193;
+ if (yych == ' ') goto yy220;
+ goto yy218;
}
} else {
if (yych <= ',') {
- if (yych <= ')') goto yy139;
- goto yy193;
+ if (yych <= ')') goto yy164;
+ goto yy218;
} else {
- if (yych <= '-') goto yy196;
- if (yych <= '.') goto yy195;
- goto yy193;
+ if (yych <= '-') goto yy221;
+ if (yych <= '.') goto yy220;
+ goto yy218;
}
}
} else {
if (yych <= 'Z') {
if (yych <= '@') {
- if (yych <= '9') goto yy195;
- goto yy193;
+ if (yych <= '9') goto yy220;
+ goto yy218;
} else {
- if (yych != 'E') goto yy142;
+ if (yych != 'E') goto yy167;
}
} else {
if (yych <= 'd') {
- if (yych <= '`') goto yy193;
- goto yy142;
+ if (yych <= '`') goto yy218;
+ goto yy167;
} else {
- if (yych <= 'e') goto yy1443;
- if (yych <= 'z') goto yy142;
- goto yy193;
+ if (yych <= 'e') goto yy1501;
+ if (yych <= 'z') goto yy167;
+ goto yy218;
}
}
}
-yy1443:
+yy1501:
yych = *++YYCURSOR;
if (yych <= 'M') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'L') goto yy143;
+ if (yych <= 'L') goto yy168;
}
} else {
if (yych <= 'l') {
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'Z') goto yy168;
if (yych <= '`') goto yy3;
- goto yy143;
+ goto yy168;
} else {
- if (yych <= 'm') goto yy1444;
- if (yych <= 'z') goto yy143;
+ if (yych <= 'm') goto yy1502;
+ if (yych <= 'z') goto yy168;
goto yy3;
}
}
-yy1444:
+yy1502:
yych = *++YYCURSOR;
if (yych <= 'B') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'A') goto yy144;
+ if (yych <= 'A') goto yy169;
}
} else {
if (yych <= 'a') {
- if (yych <= 'Z') goto yy144;
+ if (yych <= 'Z') goto yy169;
if (yych <= '`') goto yy3;
- goto yy144;
+ goto yy169;
} else {
- if (yych <= 'b') goto yy1445;
- if (yych <= 'z') goto yy144;
+ if (yych <= 'b') goto yy1503;
+ if (yych <= 'z') goto yy169;
goto yy3;
}
}
-yy1445:
+yy1503:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'D') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
- if (yych <= 'E') goto yy1446;
+ if (yych <= 'E') goto yy1504;
if (yych != 'e') goto yy3;
}
-yy1446:
+yy1504:
yych = *++YYCURSOR;
- if (yych == 'R') goto yy204;
- if (yych == 'r') goto yy204;
+ if (yych == 'R') goto yy229;
+ if (yych == 'r') goto yy229;
goto yy56;
-yy1447:
+yy1505:
++YYCURSOR;
if ((yych = *YYCURSOR) <= '@') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
} else {
- if (yych <= 'Z') goto yy143;
- if (yych <= '`') goto yy1448;
- if (yych <= 'z') goto yy143;
+ if (yych <= 'Z') goto yy168;
+ if (yych <= '`') goto yy1506;
+ if (yych <= 'z') goto yy168;
}
-yy1448:
-#line 976 "ext/date/lib/parse_date.re"
+yy1506:
+#line 991 "ext/date/lib/parse_date.re"
{
DEBUG_OUTPUT("noon");
TIMELIB_INIT;
@@ -21563,517 +22543,517 @@ yy1448:
TIMELIB_DEINIT;
return TIMELIB_RELATIVE;
}
-#line 21567 "ext/date/lib/parse_date.c"
-yy1449:
+#line 22547 "ext/date/lib/parse_date.c"
+yy1507:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'V') {
if (yych <= '.') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
- if (yych == '-') goto yy147;
+ if (yych == '-') goto yy172;
goto yy3;
}
} else {
if (yych <= 'N') {
- if (yych <= '/') goto yy147;
+ if (yych <= '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy141;
+ goto yy166;
} else {
- if (yych <= 'O') goto yy1441;
- if (yych <= 'U') goto yy141;
- goto yy1442;
+ if (yych <= 'O') goto yy1499;
+ if (yych <= 'U') goto yy166;
+ goto yy1500;
}
}
} else {
if (yych <= 'n') {
if (yych <= '^') {
- if (yych <= 'W') goto yy1439;
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'W') goto yy1497;
+ if (yych <= 'Z') goto yy166;
goto yy3;
} else {
- if (yych <= '_') goto yy147;
+ if (yych <= '_') goto yy172;
if (yych <= '`') goto yy3;
- goto yy146;
+ goto yy171;
}
} else {
if (yych <= 'v') {
- if (yych <= 'o') goto yy1456;
- if (yych <= 'u') goto yy146;
- goto yy1457;
+ if (yych <= 'o') goto yy1514;
+ if (yych <= 'u') goto yy171;
+ goto yy1515;
} else {
- if (yych <= 'w') goto yy1455;
- if (yych <= 'z') goto yy146;
+ if (yych <= 'w') goto yy1513;
+ if (yych <= 'z') goto yy171;
goto yy3;
}
}
}
-yy1450:
+yy1508:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'W') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy141;
+ goto yy166;
}
} else {
if (yych <= '_') {
- if (yych <= 'X') goto yy1438;
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'X') goto yy1496;
+ if (yych <= 'Z') goto yy166;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'w') {
if (yych <= '`') goto yy3;
- goto yy146;
+ goto yy171;
} else {
- if (yych <= 'x') goto yy1454;
- if (yych <= 'z') goto yy146;
+ if (yych <= 'x') goto yy1512;
+ if (yych <= 'z') goto yy171;
goto yy3;
}
}
}
-yy1451:
+yy1509:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'M') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy141;
+ goto yy166;
}
} else {
if (yych <= '_') {
- if (yych <= 'N') goto yy1436;
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'N') goto yy1494;
+ if (yych <= 'Z') goto yy166;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'm') {
if (yych <= '`') goto yy3;
- goto yy146;
+ goto yy171;
} else {
- if (yych <= 'n') goto yy1452;
- if (yych <= 'z') goto yy146;
+ if (yych <= 'n') goto yy1510;
+ if (yych <= 'z') goto yy171;
goto yy3;
}
}
}
-yy1452:
+yy1510:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'S') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy142;
+ goto yy167;
}
} else {
if (yych <= '_') {
- if (yych <= 'T') goto yy1437;
- if (yych <= 'Z') goto yy142;
+ if (yych <= 'T') goto yy1495;
+ if (yych <= 'Z') goto yy167;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 's') {
if (yych <= '`') goto yy3;
- goto yy150;
+ goto yy175;
} else {
- if (yych <= 't') goto yy1453;
- if (yych <= 'z') goto yy150;
+ if (yych <= 't') goto yy1511;
+ if (yych <= 'z') goto yy175;
goto yy3;
}
}
}
-yy1453:
+yy1511:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'G') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy143;
+ goto yy168;
}
} else {
if (yych <= '_') {
- if (yych <= 'H') goto yy1178;
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'H') goto yy1228;
+ if (yych <= 'Z') goto yy168;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'g') {
if (yych <= '`') goto yy3;
- goto yy151;
+ goto yy176;
} else {
- if (yych <= 'h') goto yy1207;
- if (yych <= 'z') goto yy151;
+ if (yych <= 'h') goto yy1257;
+ if (yych <= 'z') goto yy176;
goto yy3;
}
}
}
-yy1454:
+yy1512:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'S') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy142;
+ goto yy167;
}
} else {
if (yych <= '_') {
- if (yych <= 'T') goto yy1368;
- if (yych <= 'Z') goto yy142;
+ if (yych <= 'T') goto yy1426;
+ if (yych <= 'Z') goto yy167;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 's') {
if (yych <= '`') goto yy3;
- goto yy150;
+ goto yy175;
} else {
- if (yych <= 't') goto yy1400;
- if (yych <= 'z') goto yy150;
+ if (yych <= 't') goto yy1458;
+ if (yych <= 'z') goto yy175;
goto yy3;
}
}
}
-yy1455:
- yyaccept = 30;
+yy1513:
+ yyaccept = 31;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '/') {
if (yych <= ',') {
- if (yych == ')') goto yy139;
- goto yy1440;
+ if (yych == ')') goto yy164;
+ goto yy1498;
} else {
- if (yych == '.') goto yy1440;
- goto yy147;
+ if (yych == '.') goto yy1498;
+ goto yy172;
}
} else {
if (yych <= '^') {
- if (yych <= '@') goto yy1440;
- if (yych <= 'Z') goto yy142;
- goto yy1440;
+ if (yych <= '@') goto yy1498;
+ if (yych <= 'Z') goto yy167;
+ goto yy1498;
} else {
- if (yych <= '_') goto yy147;
- if (yych <= '`') goto yy1440;
- if (yych <= 'z') goto yy150;
- goto yy1440;
+ if (yych <= '_') goto yy172;
+ if (yych <= '`') goto yy1498;
+ if (yych <= 'z') goto yy175;
+ goto yy1498;
}
}
-yy1456:
+yy1514:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'M') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy142;
+ goto yy167;
}
} else {
if (yych <= '_') {
- if (yych <= 'N') goto yy1447;
- if (yych <= 'Z') goto yy142;
+ if (yych <= 'N') goto yy1505;
+ if (yych <= 'Z') goto yy167;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'm') {
if (yych <= '`') goto yy3;
- goto yy150;
+ goto yy175;
} else {
- if (yych <= 'n') goto yy1462;
- if (yych <= 'z') goto yy150;
+ if (yych <= 'n') goto yy1520;
+ if (yych <= 'z') goto yy175;
goto yy3;
}
}
}
-yy1457:
+yy1515:
yyaccept = 5;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '9') {
if (yych <= '(') {
if (yych <= '\t') {
- if (yych <= 0x08) goto yy193;
- goto yy195;
+ if (yych <= 0x08) goto yy218;
+ goto yy220;
} else {
- if (yych == ' ') goto yy195;
- goto yy193;
+ if (yych == ' ') goto yy220;
+ goto yy218;
}
} else {
if (yych <= '-') {
- if (yych <= ')') goto yy139;
- if (yych <= ',') goto yy193;
- goto yy311;
+ if (yych <= ')') goto yy164;
+ if (yych <= ',') goto yy218;
+ goto yy336;
} else {
- if (yych == '/') goto yy147;
- goto yy195;
+ if (yych == '/') goto yy172;
+ goto yy220;
}
}
} else {
if (yych <= '^') {
if (yych <= 'D') {
- if (yych <= '@') goto yy193;
- goto yy142;
+ if (yych <= '@') goto yy218;
+ goto yy167;
} else {
- if (yych <= 'E') goto yy1443;
- if (yych <= 'Z') goto yy142;
- goto yy193;
+ if (yych <= 'E') goto yy1501;
+ if (yych <= 'Z') goto yy167;
+ goto yy218;
}
} else {
if (yych <= 'd') {
- if (yych <= '_') goto yy147;
- if (yych <= '`') goto yy193;
- goto yy150;
+ if (yych <= '_') goto yy172;
+ if (yych <= '`') goto yy218;
+ goto yy175;
} else {
- if (yych <= 'e') goto yy1458;
- if (yych <= 'z') goto yy150;
- goto yy193;
+ if (yych <= 'e') goto yy1516;
+ if (yych <= 'z') goto yy175;
+ goto yy218;
}
}
}
-yy1458:
+yy1516:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'L') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy143;
+ goto yy168;
}
} else {
if (yych <= '_') {
- if (yych <= 'M') goto yy1444;
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'M') goto yy1502;
+ if (yych <= 'Z') goto yy168;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'l') {
if (yych <= '`') goto yy3;
- goto yy151;
+ goto yy176;
} else {
- if (yych <= 'm') goto yy1459;
- if (yych <= 'z') goto yy151;
+ if (yych <= 'm') goto yy1517;
+ if (yych <= 'z') goto yy176;
goto yy3;
}
}
}
-yy1459:
+yy1517:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'A') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy144;
+ goto yy169;
}
} else {
if (yych <= '_') {
- if (yych <= 'B') goto yy1445;
- if (yych <= 'Z') goto yy144;
+ if (yych <= 'B') goto yy1503;
+ if (yych <= 'Z') goto yy169;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'a') {
if (yych <= '`') goto yy3;
- goto yy152;
+ goto yy177;
} else {
- if (yych <= 'b') goto yy1460;
- if (yych <= 'z') goto yy152;
+ if (yych <= 'b') goto yy1518;
+ if (yych <= 'z') goto yy177;
goto yy3;
}
}
}
-yy1460:
+yy1518:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'D') {
if (yych <= ',') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
if (yych == '.') goto yy3;
- if (yych <= '/') goto yy147;
+ if (yych <= '/') goto yy172;
goto yy3;
}
} else {
if (yych <= '`') {
- if (yych <= 'E') goto yy1446;
- if (yych == '_') goto yy147;
+ if (yych <= 'E') goto yy1504;
+ if (yych == '_') goto yy172;
goto yy3;
} else {
- if (yych == 'e') goto yy1461;
- if (yych <= 'z') goto yy153;
+ if (yych == 'e') goto yy1519;
+ if (yych <= 'z') goto yy178;
goto yy3;
}
}
-yy1461:
+yy1519:
yych = *++YYCURSOR;
- if (yych == 'R') goto yy204;
- if (yych == 'r') goto yy316;
- goto yy154;
-yy1462:
- yyaccept = 31;
+ if (yych == 'R') goto yy229;
+ if (yych == 'r') goto yy341;
+ goto yy179;
+yy1520:
+ yyaccept = 32;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= '/') {
if (yych <= ',') {
- if (yych == ')') goto yy139;
- goto yy1448;
+ if (yych == ')') goto yy164;
+ goto yy1506;
} else {
- if (yych == '.') goto yy1448;
- goto yy147;
+ if (yych == '.') goto yy1506;
+ goto yy172;
}
} else {
if (yych <= '^') {
- if (yych <= '@') goto yy1448;
- if (yych <= 'Z') goto yy143;
- goto yy1448;
+ if (yych <= '@') goto yy1506;
+ if (yych <= 'Z') goto yy168;
+ goto yy1506;
} else {
- if (yych <= '_') goto yy147;
- if (yych <= '`') goto yy1448;
- if (yych <= 'z') goto yy151;
- goto yy1448;
+ if (yych <= '_') goto yy172;
+ if (yych <= '`') goto yy1506;
+ if (yych <= 'z') goto yy176;
+ goto yy1506;
}
}
-yy1463:
+yy1521:
yych = *++YYCURSOR;
if (yych <= 'S') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'R') goto yy141;
+ if (yych <= 'R') goto yy166;
}
} else {
if (yych <= 'r') {
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'Z') goto yy166;
if (yych <= '`') goto yy3;
- goto yy141;
+ goto yy166;
} else {
- if (yych <= 's') goto yy1464;
- if (yych <= 'z') goto yy141;
+ if (yych <= 's') goto yy1522;
+ if (yych <= 'z') goto yy166;
goto yy3;
}
}
-yy1464:
+yy1522:
yych = *++YYCURSOR;
if (yych <= 'T') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'S') goto yy142;
+ if (yych <= 'S') goto yy167;
}
} else {
if (yych <= 's') {
- if (yych <= 'Z') goto yy142;
+ if (yych <= 'Z') goto yy167;
if (yych <= '`') goto yy3;
- goto yy142;
+ goto yy167;
} else {
- if (yych <= 't') goto yy1465;
- if (yych <= 'z') goto yy142;
+ if (yych <= 't') goto yy1523;
+ if (yych <= 'z') goto yy167;
goto yy3;
}
}
-yy1465:
+yy1523:
yych = *++YYCURSOR;
if (yych <= 'E') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'D') goto yy143;
+ if (yych <= 'D') goto yy168;
}
} else {
if (yych <= 'd') {
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'Z') goto yy168;
if (yych <= '`') goto yy3;
- goto yy143;
+ goto yy168;
} else {
- if (yych <= 'e') goto yy1466;
- if (yych <= 'z') goto yy143;
+ if (yych <= 'e') goto yy1524;
+ if (yych <= 'z') goto yy168;
goto yy3;
}
}
-yy1466:
+yy1524:
yych = *++YYCURSOR;
if (yych <= 'R') {
if (yych <= ')') {
if (yych <= '(') goto yy3;
- goto yy139;
+ goto yy164;
} else {
if (yych <= '@') goto yy3;
- if (yych <= 'Q') goto yy144;
+ if (yych <= 'Q') goto yy169;
}
} else {
if (yych <= 'q') {
- if (yych <= 'Z') goto yy144;
+ if (yych <= 'Z') goto yy169;
if (yych <= '`') goto yy3;
- goto yy144;
+ goto yy169;
} else {
- if (yych <= 'r') goto yy1467;
- if (yych <= 'z') goto yy144;
+ if (yych <= 'r') goto yy1525;
+ if (yych <= 'z') goto yy169;
goto yy3;
}
}
-yy1467:
+yy1525:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'C') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
- if (yych <= 'D') goto yy1468;
+ if (yych <= 'D') goto yy1526;
if (yych != 'd') goto yy3;
}
-yy1468:
+yy1526:
yych = *++YYCURSOR;
- if (yych == 'A') goto yy1469;
+ if (yych == 'A') goto yy1527;
if (yych != 'a') goto yy56;
-yy1469:
+yy1527:
yych = *++YYCURSOR;
- if (yych == 'Y') goto yy1470;
+ if (yych == 'Y') goto yy1528;
if (yych != 'y') goto yy56;
-yy1470:
+yy1528:
++YYCURSOR;
-yy1471:
-#line 955 "ext/date/lib/parse_date.re"
+yy1529:
+#line 970 "ext/date/lib/parse_date.re"
{
DEBUG_OUTPUT("yesterday");
TIMELIB_INIT;
@@ -22084,172 +23064,172 @@ yy1471:
TIMELIB_DEINIT;
return TIMELIB_RELATIVE;
}
-#line 22088 "ext/date/lib/parse_date.c"
-yy1472:
+#line 23068 "ext/date/lib/parse_date.c"
+yy1530:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'R') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy141;
+ goto yy166;
}
} else {
if (yych <= '_') {
- if (yych <= 'S') goto yy1464;
- if (yych <= 'Z') goto yy141;
+ if (yych <= 'S') goto yy1522;
+ if (yych <= 'Z') goto yy166;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'r') {
if (yych <= '`') goto yy3;
- goto yy146;
+ goto yy171;
} else {
- if (yych <= 's') goto yy1473;
- if (yych <= 'z') goto yy146;
+ if (yych <= 's') goto yy1531;
+ if (yych <= 'z') goto yy171;
goto yy3;
}
}
}
-yy1473:
+yy1531:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'S') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy142;
+ goto yy167;
}
} else {
if (yych <= '_') {
- if (yych <= 'T') goto yy1465;
- if (yych <= 'Z') goto yy142;
+ if (yych <= 'T') goto yy1523;
+ if (yych <= 'Z') goto yy167;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 's') {
if (yych <= '`') goto yy3;
- goto yy150;
+ goto yy175;
} else {
- if (yych <= 't') goto yy1474;
- if (yych <= 'z') goto yy150;
+ if (yych <= 't') goto yy1532;
+ if (yych <= 'z') goto yy175;
goto yy3;
}
}
}
-yy1474:
+yy1532:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'D') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy143;
+ goto yy168;
}
} else {
if (yych <= '_') {
- if (yych <= 'E') goto yy1466;
- if (yych <= 'Z') goto yy143;
+ if (yych <= 'E') goto yy1524;
+ if (yych <= 'Z') goto yy168;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'd') {
if (yych <= '`') goto yy3;
- goto yy151;
+ goto yy176;
} else {
- if (yych <= 'e') goto yy1475;
- if (yych <= 'z') goto yy151;
+ if (yych <= 'e') goto yy1533;
+ if (yych <= 'z') goto yy176;
goto yy3;
}
}
}
-yy1475:
+yy1533:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'Q') {
if (yych <= '-') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
if (yych <= ',') goto yy3;
- goto yy147;
+ goto yy172;
} else {
- if (yych == '/') goto yy147;
+ if (yych == '/') goto yy172;
if (yych <= '@') goto yy3;
- goto yy144;
+ goto yy169;
}
} else {
if (yych <= '_') {
- if (yych <= 'R') goto yy1467;
- if (yych <= 'Z') goto yy144;
+ if (yych <= 'R') goto yy1525;
+ if (yych <= 'Z') goto yy169;
if (yych <= '^') goto yy3;
- goto yy147;
+ goto yy172;
} else {
if (yych <= 'q') {
if (yych <= '`') goto yy3;
- goto yy152;
+ goto yy177;
} else {
- if (yych <= 'r') goto yy1476;
- if (yych <= 'z') goto yy152;
+ if (yych <= 'r') goto yy1534;
+ if (yych <= 'z') goto yy177;
goto yy3;
}
}
}
-yy1476:
+yy1534:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if (yych <= 'C') {
if (yych <= ',') {
- if (yych == ')') goto yy139;
+ if (yych == ')') goto yy164;
goto yy3;
} else {
if (yych == '.') goto yy3;
- if (yych <= '/') goto yy147;
+ if (yych <= '/') goto yy172;
goto yy3;
}
} else {
if (yych <= '`') {
- if (yych <= 'D') goto yy1468;
- if (yych == '_') goto yy147;
+ if (yych <= 'D') goto yy1526;
+ if (yych == '_') goto yy172;
goto yy3;
} else {
- if (yych == 'd') goto yy1477;
- if (yych <= 'z') goto yy153;
+ if (yych == 'd') goto yy1535;
+ if (yych <= 'z') goto yy178;
goto yy3;
}
}
-yy1477:
+yy1535:
yych = *++YYCURSOR;
- if (yych == 'A') goto yy1469;
- if (yych != 'a') goto yy154;
+ if (yych == 'A') goto yy1527;
+ if (yych != 'a') goto yy179;
yych = *++YYCURSOR;
- if (yych == 'Y') goto yy1470;
- if (yych != 'y') goto yy154;
- yyaccept = 32;
+ if (yych == 'Y') goto yy1528;
+ if (yych != 'y') goto yy179;
+ yyaccept = 33;
yych = *(YYMARKER = ++YYCURSOR);
if (yybm[0+yych] & 16) {
- goto yy153;
+ goto yy178;
}
if (yych <= '.') {
- if (yych == '-') goto yy147;
- goto yy1471;
+ if (yych == '-') goto yy172;
+ goto yy1529;
} else {
- if (yych <= '/') goto yy147;
- if (yych == '_') goto yy147;
- goto yy1471;
+ if (yych <= '/') goto yy172;
+ if (yych == '_') goto yy172;
+ goto yy1529;
}
}
-#line 1729 "ext/date/lib/parse_date.re"
+#line 1772 "ext/date/lib/parse_date.re"
}
@@ -22556,7 +23536,6 @@ timelib_time *timelib_parse_from_format(char *format, char *string, size_t len,
s->time->m = 1;
s->time->d = 1;
s->time->h = s->time->i = s->time->s = 0;
- s->time->f = 0.0;
s->time->relative.s += tmp;
s->time->is_localtime = 1;
s->time->zone_type = TIMELIB_ZONETYPE_OFFSET;
@@ -22714,13 +23693,20 @@ void timelib_fill_holes(timelib_time *parsed, timelib_time *now, int options)
parsed->s = 0;
parsed->f = 0;
}
+ if (
+ parsed->y != TIMELIB_UNSET || parsed->m != TIMELIB_UNSET || parsed->d != TIMELIB_UNSET ||
+ parsed->h != TIMELIB_UNSET || parsed->i != TIMELIB_UNSET || parsed->s != TIMELIB_UNSET
+ ) {
+ if (parsed->f == TIMELIB_UNSET) parsed->f = 0;
+ } else {
+ if (parsed->f == TIMELIB_UNSET) parsed->f = now->f != TIMELIB_UNSET ? now->f : 0;
+ }
if (parsed->y == TIMELIB_UNSET) parsed->y = now->y != TIMELIB_UNSET ? now->y : 0;
- if (parsed->d == TIMELIB_UNSET) parsed->d = now->d != TIMELIB_UNSET ? now->d : 0;
if (parsed->m == TIMELIB_UNSET) parsed->m = now->m != TIMELIB_UNSET ? now->m : 0;
+ if (parsed->d == TIMELIB_UNSET) parsed->d = now->d != TIMELIB_UNSET ? now->d : 0;
if (parsed->h == TIMELIB_UNSET) parsed->h = now->h != TIMELIB_UNSET ? now->h : 0;
if (parsed->i == TIMELIB_UNSET) parsed->i = now->i != TIMELIB_UNSET ? now->i : 0;
if (parsed->s == TIMELIB_UNSET) parsed->s = now->s != TIMELIB_UNSET ? now->s : 0;
- if (parsed->f == TIMELIB_UNSET) parsed->f = now->f != TIMELIB_UNSET ? now->f : 0;
if (parsed->z == TIMELIB_UNSET) parsed->z = now->z != TIMELIB_UNSET ? now->z : 0;
if (parsed->dst == TIMELIB_UNSET) parsed->dst = now->dst != TIMELIB_UNSET ? now->dst : 0;
diff --git a/ext/date/lib/parse_date.re b/ext/date/lib/parse_date.re
index 8c506990d1..74d9ea3a0f 100644
--- a/ext/date/lib/parse_date.re
+++ b/ext/date/lib/parse_date.re
@@ -52,14 +52,15 @@
#define TIMELIB_UNSET -99999
-#define TIMELIB_SECOND 1
-#define TIMELIB_MINUTE 2
-#define TIMELIB_HOUR 3
-#define TIMELIB_DAY 4
-#define TIMELIB_MONTH 5
-#define TIMELIB_YEAR 6
-#define TIMELIB_WEEKDAY 7
-#define TIMELIB_SPECIAL 8
+#define TIMELIB_SECOND 1
+#define TIMELIB_MINUTE 2
+#define TIMELIB_HOUR 3
+#define TIMELIB_DAY 4
+#define TIMELIB_MONTH 5
+#define TIMELIB_YEAR 6
+#define TIMELIB_WEEKDAY 7
+#define TIMELIB_SPECIAL 8
+#define TIMELIB_MICROSEC 9
#define EOI 257
#define TIME 258
@@ -190,6 +191,18 @@ const static timelib_tz_lookup_table timelib_timezone_utc[] = {
};
static timelib_relunit const timelib_relunit_lookup[] = {
+ { "ms", TIMELIB_MICROSEC, 1000 },
+ { "msec", TIMELIB_MICROSEC, 1000 },
+ { "msecs", TIMELIB_MICROSEC, 1000 },
+ { "millisecond", TIMELIB_MICROSEC, 1000 },
+ { "milliseconds", TIMELIB_MICROSEC, 1000 },
+ { "µs", TIMELIB_MICROSEC, 1 },
+ { "usec", TIMELIB_MICROSEC, 1 },
+ { "usecs", TIMELIB_MICROSEC, 1 },
+ { "µsec", TIMELIB_MICROSEC, 1 },
+ { "µsecs", TIMELIB_MICROSEC, 1 },
+ { "microsecond", TIMELIB_MICROSEC, 1 },
+ { "microseconds", TIMELIB_MICROSEC, 1 },
{ "sec", TIMELIB_SECOND, 1 },
{ "secs", TIMELIB_SECOND, 1 },
{ "second", TIMELIB_SECOND, 1 },
@@ -655,12 +668,13 @@ static void timelib_set_relative(char **ptr, timelib_sll amount, int behavior, S
}
switch (relunit->unit) {
- case TIMELIB_SECOND: s->time->relative.s += amount * relunit->multiplier; break;
- case TIMELIB_MINUTE: s->time->relative.i += amount * relunit->multiplier; break;
- case TIMELIB_HOUR: s->time->relative.h += amount * relunit->multiplier; break;
- case TIMELIB_DAY: s->time->relative.d += amount * relunit->multiplier; break;
- case TIMELIB_MONTH: s->time->relative.m += amount * relunit->multiplier; break;
- case TIMELIB_YEAR: s->time->relative.y += amount * relunit->multiplier; break;
+ case TIMELIB_MICROSEC: s->time->relative.f += (((double) amount * (double) relunit->multiplier) / 1000000); break;
+ case TIMELIB_SECOND: s->time->relative.s += amount * relunit->multiplier; break;
+ case TIMELIB_MINUTE: s->time->relative.i += amount * relunit->multiplier; break;
+ case TIMELIB_HOUR: s->time->relative.h += amount * relunit->multiplier; break;
+ case TIMELIB_DAY: s->time->relative.d += amount * relunit->multiplier; break;
+ case TIMELIB_MONTH: s->time->relative.m += amount * relunit->multiplier; break;
+ case TIMELIB_YEAR: s->time->relative.y += amount * relunit->multiplier; break;
case TIMELIB_WEEKDAY:
TIMELIB_HAVE_WEEKDAY_RELATIVE();
@@ -925,6 +939,7 @@ clf = day "/" monthabbr "/" year4 ":" hour24lz ":" minutelz ":" sec
/* Timestamp format: @1126396800 */
timestamp = "@" "-"? [0-9]+;
+timestampms = "@" "-"? [0-9]+ "." [0-9]{6};
/* To fix some ambiguities */
dateshortwithtimeshort12 = datenoyear timeshort12;
@@ -938,7 +953,7 @@ dateshortwithtimelongtz = datenoyear iso8601normtz;
*/
reltextnumber = 'first'|'second'|'third'|'fourth'|'fifth'|'sixth'|'seventh'|'eight'|'eighth'|'ninth'|'tenth'|'eleventh'|'twelfth';
reltexttext = 'next'|'last'|'previous'|'this';
-reltextunit = (('sec'|'second'|'min'|'minute'|'hour'|'day'|'fortnight'|'forthnight'|'month'|'year') 's'?) | 'weeks' | daytext;
+reltextunit = 'ms' | 'µs' | (('msec'|'millisecond'|'µsec'|'microsecond'|'usec'|'sec'|'second'|'min'|'minute'|'hour'|'day'|'fortnight'|'forthnight'|'month'|'year') 's'?) | 'weeks' | daytext;
relnumber = ([+-]*[ \t]*[0-9]+);
relative = relnumber space? (reltextunit | 'week' );
@@ -1032,6 +1047,34 @@ weekdayof = (reltextnumber|reltexttext) space (dayfull|dayabbr) space 'of
return TIMELIB_RELATIVE;
}
+ timestampms
+ {
+ timelib_ull i, ms;
+
+ TIMELIB_INIT;
+ TIMELIB_HAVE_RELATIVE();
+ TIMELIB_UNHAVE_DATE();
+ TIMELIB_UNHAVE_TIME();
+ TIMELIB_HAVE_TZ();
+
+ i = timelib_get_unsigned_nr((char **) &ptr, 24);
+ ms = timelib_get_unsigned_nr((char **) &ptr, 24);
+ s->time->y = 1970;
+ s->time->m = 1;
+ s->time->d = 1;
+ s->time->h = s->time->i = s->time->s = 0;
+ s->time->f = 0.0;
+ s->time->relative.s += i;
+ s->time->relative.f = ((double) ms) / 1000000.0;
+ s->time->is_localtime = 1;
+ s->time->zone_type = TIMELIB_ZONETYPE_OFFSET;
+ s->time->z = 0;
+ s->time->dst = 0;
+
+ TIMELIB_DEINIT;
+ return TIMELIB_RELATIVE;
+ }
+
firstdayof | lastdayof
{
DEBUG_OUTPUT("firstdayof | lastdayof");
@@ -2032,7 +2075,6 @@ timelib_time *timelib_parse_from_format(char *format, char *string, size_t len,
s->time->m = 1;
s->time->d = 1;
s->time->h = s->time->i = s->time->s = 0;
- s->time->f = 0.0;
s->time->relative.s += tmp;
s->time->is_localtime = 1;
s->time->zone_type = TIMELIB_ZONETYPE_OFFSET;
@@ -2190,13 +2232,20 @@ void timelib_fill_holes(timelib_time *parsed, timelib_time *now, int options)
parsed->s = 0;
parsed->f = 0;
}
+ if (
+ parsed->y != TIMELIB_UNSET || parsed->m != TIMELIB_UNSET || parsed->d != TIMELIB_UNSET ||
+ parsed->h != TIMELIB_UNSET || parsed->i != TIMELIB_UNSET || parsed->s != TIMELIB_UNSET
+ ) {
+ if (parsed->f == TIMELIB_UNSET) parsed->f = 0;
+ } else {
+ if (parsed->f == TIMELIB_UNSET) parsed->f = now->f != TIMELIB_UNSET ? now->f : 0;
+ }
if (parsed->y == TIMELIB_UNSET) parsed->y = now->y != TIMELIB_UNSET ? now->y : 0;
- if (parsed->d == TIMELIB_UNSET) parsed->d = now->d != TIMELIB_UNSET ? now->d : 0;
if (parsed->m == TIMELIB_UNSET) parsed->m = now->m != TIMELIB_UNSET ? now->m : 0;
+ if (parsed->d == TIMELIB_UNSET) parsed->d = now->d != TIMELIB_UNSET ? now->d : 0;
if (parsed->h == TIMELIB_UNSET) parsed->h = now->h != TIMELIB_UNSET ? now->h : 0;
if (parsed->i == TIMELIB_UNSET) parsed->i = now->i != TIMELIB_UNSET ? now->i : 0;
if (parsed->s == TIMELIB_UNSET) parsed->s = now->s != TIMELIB_UNSET ? now->s : 0;
- if (parsed->f == TIMELIB_UNSET) parsed->f = now->f != TIMELIB_UNSET ? now->f : 0;
if (parsed->z == TIMELIB_UNSET) parsed->z = now->z != TIMELIB_UNSET ? now->z : 0;
if (parsed->dst == TIMELIB_UNSET) parsed->dst = now->dst != TIMELIB_UNSET ? now->dst : 0;
diff --git a/ext/date/lib/timelib.c b/ext/date/lib/timelib.c
index b9fb66f00f..84dba3d969 100644
--- a/ext/date/lib/timelib.c
+++ b/ext/date/lib/timelib.c
@@ -231,7 +231,7 @@ void timelib_dump_date(timelib_time *d, int options)
printf("TS: %lld | %s%04lld-%02lld-%02lld %02lld:%02lld:%02lld",
d->sse, d->y < 0 ? "-" : "", TIMELIB_LLABS(d->y), d->m, d->d, d->h, d->i, d->s);
if (d->f > +0.0) {
- printf(" %.5f", d->f);
+ printf(" %.6f", d->f);
}
if (d->is_localtime) {
@@ -260,6 +260,9 @@ void timelib_dump_date(timelib_time *d, int options)
if (d->have_relative) {
printf("%3lldY %3lldM %3lldD / %3lldH %3lldM %3lldS",
d->relative.y, d->relative.m, d->relative.d, d->relative.h, d->relative.i, d->relative.s);
+ if (d->relative.f) {
+ printf(" %6f", d->relative.f);
+ }
if (d->relative.first_last_day_of != 0) {
switch (d->relative.first_last_day_of) {
case 1:
diff --git a/ext/date/lib/timelib.h b/ext/date/lib/timelib.h
index 9a59d89770..e58a92a5e6 100644
--- a/ext/date/lib/timelib.h
+++ b/ext/date/lib/timelib.h
@@ -38,8 +38,8 @@
# define timelib_free free
#endif
-#define TIMELIB_VERSION 201602
-#define TIMELIB_ASCII_VERSION "2016.02"
+#define TIMELIB_VERSION 201605
+#define TIMELIB_ASCII_VERSION "2016.05"
#define TIMELIB_NONE 0x00
#define TIMELIB_OVERRIDE_TIME 0x01
@@ -139,6 +139,7 @@ void timelib_time_set_option(timelib_time* tm, int option, void* option_value);
void timelib_time_dtor(timelib_time* t);
timelib_time* timelib_time_clone(timelib_time* orig);
int timelib_time_compare(timelib_time *t1, timelib_time *t2);
+void timelib_set_fraction_from_timeval(timelib_time *t, struct timeval tp);
timelib_time_offset* timelib_time_offset_ctor(void);
void timelib_time_offset_dtor(timelib_time_offset* t);
diff --git a/ext/date/lib/timelib.m4 b/ext/date/lib/timelib.m4
index c7255727f2..99bf9fad20 100644
--- a/ext/date/lib/timelib.m4
+++ b/ext/date/lib/timelib.m4
@@ -77,4 +77,4 @@ stdlib.h
])
dnl Check for strtoll, atoll
-AC_CHECK_FUNCS(strtoll atoll strftime)
+AC_CHECK_FUNCS(strtoll atoll strftime gettimeofday)
diff --git a/ext/date/lib/timelib_structs.h b/ext/date/lib/timelib_structs.h
index d38175753a..4256dd4f6f 100644
--- a/ext/date/lib/timelib_structs.h
+++ b/ext/date/lib/timelib_structs.h
@@ -125,7 +125,7 @@ typedef unsigned __int64 uint64_t;
#include <strings.h>
#endif
-#if defined(__x86_64__) || defined(__LP64__) || defined(_LP64) || defined(_WIN64)
+#if (defined(__x86_64__) || defined(__LP64__) || defined(_LP64) || defined(_WIN64)) && !defined(TIMELIB_FORCE_LONG32)
typedef int64_t timelib_long;
typedef uint64_t timelib_ulong;
# define TIMELIB_LONG_MAX INT64_MAX
@@ -216,6 +216,7 @@ typedef struct timelib_special {
typedef struct timelib_rel_time {
timelib_sll y, m, d; /* Years, Months and Days */
timelib_sll h, i, s; /* Hours, mInutes and Seconds */
+ double f; /* Fraction */
int weekday; /* Stores the day in 'next monday' */
int weekday_behavior; /* 0: the current day should *not* be counted when advancing forwards; 1: the current day *should* be counted */
diff --git a/ext/date/lib/timezonemap.h b/ext/date/lib/timezonemap.h
index 41471909ac..d8898bf33d 100644
--- a/ext/date/lib/timezonemap.h
+++ b/ext/date/lib/timezonemap.h
@@ -5,10 +5,16 @@
{ "acdt", 1, 37800, "Australia/South" },
{ "acdt", 1, 37800, "Australia/Yancowinna" },
{ "acst", 1, -14400, "America/Porto_Acre" },
+ { "acst", 0, 32400, "Australia/Adelaide" },
{ "acst", 0, 34200, "Australia/Adelaide" },
{ "acst", 1, -14400, "America/Eirunepe" },
{ "acst", 1, -14400, "America/Rio_Branco" },
{ "acst", 1, -14400, "Brazil/Acre" },
+ { "acst", 0, 32400, "Australia/Broken_Hill" },
+ { "acst", 0, 32400, "Australia/Darwin" },
+ { "acst", 0, 32400, "Australia/North" },
+ { "acst", 0, 32400, "Australia/South" },
+ { "acst", 0, 32400, "Australia/Yancowinna" },
{ "acst", 0, 34200, "Asia/Jayapura" },
{ "acst", 0, 34200, "Australia/Broken_Hill" },
{ "acst", 0, 34200, "Australia/Darwin" },
@@ -71,23 +77,16 @@
{ "ahst", 0, -36000, "America/Atka" },
{ "akdt", 1, -28800, "America/Anchorage" },
{ "akdt", 1, -28800, "America/Juneau" },
+ { "akdt", 1, -28800, "America/Metlakatla" },
{ "akdt", 1, -28800, "America/Nome" },
{ "akdt", 1, -28800, "America/Sitka" },
{ "akdt", 1, -28800, "America/Yakutat" },
{ "akst", 0, -32400, "America/Anchorage" },
{ "akst", 0, -32400, "America/Juneau" },
+ { "akst", 0, -32400, "America/Metlakatla" },
{ "akst", 0, -32400, "America/Nome" },
{ "akst", 0, -32400, "America/Sitka" },
{ "akst", 0, -32400, "America/Yakutat" },
- { "aktst", 1, 21600, "Asia/Aqtobe" },
- { "aktt", 0, 21600, "Asia/Aqtobe" },
- { "aktt", 0, 14400, "Asia/Aqtobe" },
- { "aktt", 0, 18000, "Asia/Aqtobe" },
- { "almst", 1, 25200, "Asia/Almaty" },
- { "almt", 0, 21600, "Asia/Almaty" },
- { "almt", 0, 18000, "Asia/Almaty" },
- { "amst", 1, 18000, "Asia/Yerevan" },
- { "amst", 1, 14400, "Asia/Yerevan" },
{ "amst", 1, -10800, "America/Boa_Vista" },
{ "amst", 1, -10800, "America/Campo_Grande" },
{ "amst", 1, -10800, "America/Cuiaba" },
@@ -95,8 +94,6 @@
{ "amst", 1, -10800, "America/Porto_Velho" },
{ "amst", 1, -10800, "America/Santarem" },
{ "amst", 1, -10800, "Brazil/West" },
- { "amt", 0, 14400, "Asia/Yerevan" },
- { "amt", 0, 10800, "Asia/Yerevan" },
{ "amt", 0, -13840, "America/Asuncion" },
{ "amt", 0, -14400, "America/Boa_Vista" },
{ "amt", 0, -14400, "America/Campo_Grande" },
@@ -111,12 +108,6 @@
{ "amt", 0, -14400, "Brazil/West" },
{ "amt", 0, 1172, "Europe/Amsterdam" },
{ "amt", 0, 5692, "Europe/Athens" },
- { "anast", 1, 50400, "Asia/Anadyr" },
- { "anast", 1, 43200, "Asia/Anadyr" },
- { "anast", 1, 46800, "Asia/Anadyr" },
- { "anat", 0, 46800, "Asia/Anadyr" },
- { "anat", 0, 39600, "Asia/Anadyr" },
- { "anat", 0, 43200, "Asia/Anadyr" },
{ "ant", 0, -16200, "America/Curacao" },
{ "ant", 0, -16200, "America/Aruba" },
{ "ant", 0, -16200, "America/Kralendijk" },
@@ -128,12 +119,6 @@
{ "apt", 1, -10800, "America/Pangnirtung" },
{ "apt", 1, -10800, "America/Puerto_Rico" },
{ "apt", 1, -10800, "Canada/Atlantic" },
- { "aqtst", 1, 21600, "Asia/Aqtau" },
- { "aqtst", 1, 18000, "Asia/Aqtau" },
- { "aqtst", 1, 21600, "Asia/Aqtobe" },
- { "aqtt", 0, 18000, "Asia/Aqtau" },
- { "aqtt", 0, 14400, "Asia/Aqtau" },
- { "aqtt", 0, 18000, "Asia/Aqtobe" },
{ "arst", 1, -7200, "America/Buenos_Aires" },
{ "arst", 1, -10800, "America/Buenos_Aires" },
{ "arst", 1, -10800, "America/Argentina/Buenos_Aires" },
@@ -214,14 +199,6 @@
{ "art", 0, -14400, "America/Mendoza" },
{ "art", 0, -14400, "America/Rosario" },
{ "art", 0, -14400, "Antarctica/Palmer" },
- { "ashst", 1, 21600, "Asia/Ashkhabad" },
- { "ashst", 1, 18000, "Asia/Ashkhabad" },
- { "ashst", 1, 18000, "Asia/Ashgabat" },
- { "ashst", 1, 21600, "Asia/Ashgabat" },
- { "asht", 0, 18000, "Asia/Ashkhabad" },
- { "asht", 0, 14400, "Asia/Ashkhabad" },
- { "asht", 0, 14400, "Asia/Ashgabat" },
- { "asht", 0, 18000, "Asia/Ashgabat" },
{ "ast", 0, 10800, "Asia/Riyadh" },
{ "ast", 0, -14400, "America/Anguilla" },
{ "ast", 0, -14400, "America/Antigua" },
@@ -265,7 +242,6 @@
{ "awdt", 1, 32400, "Australia/Perth" },
{ "awdt", 1, 32400, "Australia/West" },
{ "awst", 0, 28800, "Australia/Perth" },
- { "awst", 0, 28800, "Antarctica/Casey" },
{ "awst", 0, 28800, "Australia/West" },
{ "awt", 1, -10800, "America/Halifax" },
{ "awt", 1, -10800, "America/Blanc-Sablon" },
@@ -279,14 +255,6 @@
{ "azost", 1, -3600, "Atlantic/Azores" },
{ "azot", 0, -3600, "Atlantic/Azores" },
{ "azot", 0, -7200, "Atlantic/Azores" },
- { "azst", 1, 18000, "Asia/Baku" },
- { "azst", 1, 14400, "Asia/Baku" },
- { "azt", 0, 14400, "Asia/Baku" },
- { "azt", 0, 10800, "Asia/Baku" },
- { "bakst", 1, 18000, "Asia/Baku" },
- { "bakst", 1, 14400, "Asia/Baku" },
- { "bakt", 0, 14400, "Asia/Baku" },
- { "bakt", 0, 10800, "Asia/Baku" },
{ "bdst", 1, 7200, "Europe/London" },
{ "bdst", 1, 25200, "Asia/Dacca" },
{ "bdst", 1, 25200, "Asia/Dhaka" },
@@ -326,7 +294,11 @@
{ "bmt", 0, -14309, "America/Barbados" },
{ "bmt", 0, 6264, "Europe/Tiraspol" },
{ "bmt", 0, -17776, "America/Bogota" },
+ { "bmt", 0, 1050, "Europe/Brussels" },
{ "bmt", 0, 10656, "Asia/Baghdad" },
+ { "bmt", 0, 1786, "Europe/Busingen" },
+ { "bmt", 0, 1786, "Europe/Vaduz" },
+ { "bmt", 0, 1786, "Europe/Zurich" },
{ "bmt", 0, 24124, "Asia/Bangkok" },
{ "bmt", 0, 24124, "Asia/Phnom_Penh" },
{ "bmt", 0, 24124, "Asia/Vientiane" },
@@ -385,12 +357,12 @@
{ "burt", 0, 23400, "Asia/Dacca" },
{ "burt", 0, 23400, "Asia/Dhaka" },
{ "burt", 0, 23400, "Asia/Rangoon" },
+ { "burt", 0, 23400, "Asia/Yangon" },
{ "cant", 0, -3600, "Atlantic/Canary" },
{ "capt", 1, -32400, "America/Anchorage" },
{ "cast", 0, 34200, "Australia/Adelaide" },
{ "cast", 1, 10800, "Africa/Juba" },
{ "cast", 1, 10800, "Africa/Khartoum" },
- { "cast", 0, 39600, "Antarctica/Casey" },
{ "cat", 0, -36000, "America/Anchorage" },
{ "cat", 0, 7200, "Africa/Khartoum" },
{ "cat", 0, 7200, "Africa/Blantyre" },
@@ -466,7 +438,6 @@
{ "cest", 1, 7200, "Africa/Ceuta" },
{ "cest", 1, 7200, "Africa/Tripoli" },
{ "cest", 1, 7200, "Africa/Tunis" },
- { "cest", 1, 7200, "Antarctica/Troll" },
{ "cest", 1, 7200, "Arctic/Longyearbyen" },
{ "cest", 1, 7200, "Atlantic/Jan_Mayen" },
{ "cest", 1, 7200, "Europe/Amsterdam" },
@@ -588,10 +559,7 @@
{ "clst", 1, -10800, "Chile/Continental" },
{ "clst", 1, -14400, "Chile/Continental" },
{ "clt", 0, -14400, "America/Santiago" },
- { "clt", 0, -10800, "America/Santiago" },
{ "clt", 0, -18000, "America/Santiago" },
- { "clt", 0, -10800, "Antarctica/Palmer" },
- { "clt", 0, -10800, "Chile/Continental" },
{ "clt", 0, -14400, "Antarctica/Palmer" },
{ "clt", 0, -14400, "Chile/Continental" },
{ "clt", 0, -18000, "Chile/Continental" },
@@ -618,6 +586,7 @@
{ "cmt", 0, -16356, "America/La_Paz" },
{ "cmt", 0, -19176, "America/Cayman" },
{ "cmt", 0, -19176, "America/Panama" },
+ { "cmt", 0, 3020, "Europe/Copenhagen" },
{ "cmt", 0, 6900, "Europe/Chisinau" },
{ "cmt", 0, 6900, "Europe/Tiraspol" },
{ "cost", 1, -14400, "America/Bogota" },
@@ -738,22 +707,13 @@
{ "chst", 0, 36000, "Pacific/Saipan" },
{ "dact", 0, 21600, "Asia/Dacca" },
{ "dact", 0, 21600, "Asia/Dhaka" },
- { "davt", 0, 25200, "Antarctica/Davis" },
- { "davt", 0, 18000, "Antarctica/Davis" },
- { "ddut", 0, 36000, "Antarctica/DumontDUrville" },
{ "dmt", 0, -1521, "Europe/Dublin" },
- { "dusst", 1, 25200, "Asia/Dushanbe" },
- { "dusst", 1, 21600, "Asia/Dushanbe" },
- { "dust", 0, 21600, "Asia/Dushanbe" },
- { "dust", 0, 18000, "Asia/Dushanbe" },
{ "easst", 1, -21600, "Chile/EasterIsland" },
{ "easst", 1, -18000, "Chile/EasterIsland" },
{ "easst", 1, -18000, "Pacific/Easter" },
{ "easst", 1, -21600, "Pacific/Easter" },
- { "east", 0, -18000, "Chile/EasterIsland" },
{ "east", 0, -21600, "Chile/EasterIsland" },
{ "east", 0, -25200, "Chile/EasterIsland" },
- { "east", 0, -18000, "Pacific/Easter" },
{ "east", 0, -21600, "Pacific/Easter" },
{ "east", 0, -25200, "Pacific/Easter" },
{ "eat", 0, 10800, "Africa/Khartoum" },
@@ -819,7 +779,6 @@
{ "eest", 1, 10800, "Europe/Moscow" },
{ "eest", 1, 10800, "Europe/Nicosia" },
{ "eest", 1, 10800, "Europe/Riga" },
- { "eest", 1, 10800, "Europe/Samara" },
{ "eest", 1, 10800, "Europe/Simferopol" },
{ "eest", 1, 10800, "Europe/Sofia" },
{ "eest", 1, 10800, "Europe/Tallinn" },
@@ -921,8 +880,6 @@
{ "ewt", 1, -14400, "America/Thunder_Bay" },
{ "ewt", 1, -14400, "America/Toronto" },
{ "ewt", 1, -14400, "Canada/Eastern" },
- { "fet", 0, 10800, "Europe/Kaliningrad" },
- { "fet", 0, 10800, "Europe/Minsk" },
{ "ffmt", 0, -14660, "America/Martinique" },
{ "fjst", 1, 46800, "Pacific/Fiji" },
{ "fjt", 0, 43200, "Pacific/Fiji" },
@@ -936,19 +893,9 @@
{ "fnst", 1, -3600, "Brazil/DeNoronha" },
{ "fnt", 0, -7200, "America/Noronha" },
{ "fnt", 0, -7200, "Brazil/DeNoronha" },
- { "fort", 0, 18000, "Asia/Aqtau" },
- { "fort", 0, 14400, "Asia/Aqtau" },
- { "frust", 1, 25200, "Asia/Bishkek" },
- { "frust", 1, 21600, "Asia/Bishkek" },
- { "frut", 0, 21600, "Asia/Bishkek" },
- { "frut", 0, 18000, "Asia/Bishkek" },
{ "galt", 0, -21600, "Pacific/Galapagos" },
{ "gamt", 0, -32400, "Pacific/Gambier" },
{ "gbgt", 0, -13500, "America/Guyana" },
- { "gest", 1, 14400, "Asia/Tbilisi" },
- { "gest", 1, 18000, "Asia/Tbilisi" },
- { "get", 0, 14400, "Asia/Tbilisi" },
- { "get", 0, 10800, "Asia/Tbilisi" },
{ "gft", 0, -14400, "America/Cayenne" },
{ "gft", 0, -10800, "America/Cayenne" },
{ "ghst", 1, 1200, "Africa/Accra" },
@@ -990,11 +937,9 @@
{ "gyt", 0, -14400, "America/Guyana" },
{ "gyt", 0, -10800, "America/Guyana" },
{ "gyt", 0, -13500, "America/Guyana" },
- { "hadt", 1, -32400, "America/Adak" },
- { "hadt", 1, -32400, "America/Atka" },
- { "hast", 0, -36000, "America/Adak" },
- { "hast", 0, -36000, "America/Atka" },
{ "hdt", 1, -34200, "Pacific/Honolulu" },
+ { "hdt", 1, -32400, "America/Adak" },
+ { "hdt", 1, -32400, "America/Atka" },
{ "hdt", 1, -34200, "Pacific/Johnston" },
{ "hkst", 1, 32400, "Asia/Hong_Kong" },
{ "hkt", 0, 28800, "Asia/Hong_Kong" },
@@ -1011,6 +956,8 @@
{ "hovt", 0, 21600, "Asia/Hovd" },
{ "hst", 0, -36000, "Pacific/Honolulu" },
{ "hst", 0, -37800, "Pacific/Honolulu" },
+ { "hst", 0, -36000, "America/Adak" },
+ { "hst", 0, -36000, "America/Atka" },
{ "hst", 0, -36000, "Pacific/Johnston" },
{ "hst", 0, -37800, "Pacific/Johnston" },
{ "ict", 0, 25200, "Asia/Bangkok" },
@@ -1030,16 +977,11 @@
{ "imt", 0, 25025, "Asia/Irkutsk" },
{ "imt", 0, 7016, "Asia/Istanbul" },
{ "imt", 0, 7016, "Europe/Istanbul" },
+ { "imt", 0, 7016, "Europe/Sofia" },
{ "iot", 0, 21600, "Indian/Chagos" },
{ "iot", 0, 18000, "Indian/Chagos" },
{ "irdt", 1, 16200, "Asia/Tehran" },
{ "irdt", 1, 18000, "Asia/Tehran" },
- { "irkst", 1, 32400, "Asia/Irkutsk" },
- { "irkst", 1, 28800, "Asia/Irkutsk" },
- { "irkt", 0, 28800, "Asia/Irkutsk" },
- { "irkt", 0, 25200, "Asia/Irkutsk" },
- { "irkt", 0, 32400, "Asia/Irkutsk" },
- { "irkt", 0, 28800, "Asia/Chita" },
{ "irst", 0, 12600, "Asia/Tehran" },
{ "irst", 0, 14400, "Asia/Tehran" },
{ "isst", 1, 0, "Atlantic/Reykjavik" },
@@ -1067,7 +1009,6 @@
{ "ist", 0, 7200, "Asia/Tel_Aviv" },
{ "javt", 0, 26400, "Asia/Jakarta" },
{ "jcst", 0, 32400, "Asia/Pyongyang" },
- { "jcst", 0, 32400, "Asia/Sakhalin" },
{ "jcst", 0, 32400, "Asia/Seoul" },
{ "jcst", 0, 32400, "Asia/Tokyo" },
{ "jcst", 0, 32400, "ROK" },
@@ -1087,11 +1028,11 @@
{ "jst", 0, 32400, "Asia/Pyongyang" },
{ "jst", 0, 32400, "Asia/Rangoon" },
{ "jst", 0, 32400, "Asia/Saigon" },
- { "jst", 0, 32400, "Asia/Sakhalin" },
{ "jst", 0, 32400, "Asia/Seoul" },
{ "jst", 0, 32400, "Asia/Singapore" },
{ "jst", 0, 32400, "Asia/Taipei" },
{ "jst", 0, 32400, "Asia/Ujung_Pandang" },
+ { "jst", 0, 32400, "Asia/Yangon" },
{ "jst", 0, 32400, "Pacific/Bougainville" },
{ "jst", 0, 32400, "Pacific/Nauru" },
{ "jst", 0, 32400, "ROC" },
@@ -1103,37 +1044,18 @@
{ "kdt", 1, 34200, "Asia/Seoul" },
{ "kdt", 1, 34200, "ROK" },
{ "kdt", 1, 36000, "ROK" },
- { "kgst", 1, 21600, "Asia/Bishkek" },
- { "kgt", 0, 18000, "Asia/Bishkek" },
- { "kgt", 0, 21600, "Asia/Bishkek" },
- { "kizst", 1, 21600, "Asia/Qyzylorda" },
- { "kizt", 0, 21600, "Asia/Qyzylorda" },
- { "kizt", 0, 14400, "Asia/Qyzylorda" },
- { "kizt", 0, 18000, "Asia/Qyzylorda" },
{ "kmt", 0, 5736, "Europe/Vilnius" },
{ "kmt", 0, -18431, "America/Grand_Turk" },
{ "kmt", 0, -18431, "America/Jamaica" },
{ "kmt", 0, 7324, "Europe/Kiev" },
{ "kost", 0, 43200, "Pacific/Kosrae" },
{ "kost", 0, 39600, "Pacific/Kosrae" },
- { "krast", 1, 28800, "Asia/Krasnoyarsk" },
- { "krast", 1, 25200, "Asia/Krasnoyarsk" },
- { "krast", 1, 25200, "Asia/Novokuznetsk" },
- { "krast", 1, 28800, "Asia/Novokuznetsk" },
- { "krat", 0, 25200, "Asia/Krasnoyarsk" },
- { "krat", 0, 21600, "Asia/Krasnoyarsk" },
- { "krat", 0, 28800, "Asia/Krasnoyarsk" },
- { "krat", 0, 21600, "Asia/Novokuznetsk" },
- { "krat", 0, 25200, "Asia/Novokuznetsk" },
{ "kst", 0, 30600, "Asia/Seoul" },
{ "kst", 0, 32400, "Asia/Pyongyang" },
{ "kst", 0, 32400, "Asia/Seoul" },
{ "kst", 0, 30600, "Asia/Pyongyang" },
{ "kst", 0, 30600, "ROK" },
{ "kst", 0, 32400, "ROK" },
- { "kuyst", 1, 18000, "Europe/Samara" },
- { "kuyt", 0, 14400, "Europe/Samara" },
- { "kuyt", 0, 10800, "Europe/Samara" },
{ "kwat", 0, -43200, "Pacific/Kwajalein" },
{ "lhdt", 1, 39600, "Australia/LHI" },
{ "lhdt", 1, 39600, "Australia/Lord_Howe" },
@@ -1151,21 +1073,6 @@
{ "madmt", 1, 3600, "Atlantic/Madeira" },
{ "madst", 1, 0, "Atlantic/Madeira" },
{ "madt", 0, -3600, "Atlantic/Madeira" },
- { "magst", 1, 43200, "Asia/Magadan" },
- { "magst", 1, 39600, "Asia/Magadan" },
- { "magst", 1, 39600, "Asia/Srednekolymsk" },
- { "magst", 1, 39600, "Asia/Ust-Nera" },
- { "magst", 1, 43200, "Asia/Srednekolymsk" },
- { "magst", 1, 43200, "Asia/Ust-Nera" },
- { "magt", 0, 36000, "Asia/Magadan" },
- { "magt", 0, 39600, "Asia/Magadan" },
- { "magt", 0, 43200, "Asia/Magadan" },
- { "magt", 0, 36000, "Asia/Srednekolymsk" },
- { "magt", 0, 36000, "Asia/Ust-Nera" },
- { "magt", 0, 39600, "Asia/Srednekolymsk" },
- { "magt", 0, 39600, "Asia/Ust-Nera" },
- { "magt", 0, 43200, "Asia/Srednekolymsk" },
- { "magt", 0, 43200, "Asia/Ust-Nera" },
{ "malst", 1, 26400, "Asia/Singapore" },
{ "malst", 1, 26400, "Asia/Kuala_Lumpur" },
{ "malt", 0, 27000, "Asia/Singapore" },
@@ -1175,8 +1082,6 @@
{ "malt", 0, 26400, "Asia/Kuala_Lumpur" },
{ "malt", 0, 27000, "Asia/Kuala_Lumpur" },
{ "mart", 0, -34200, "Pacific/Marquesas" },
- { "mawt", 0, 21600, "Antarctica/Mawson" },
- { "mawt", 0, 18000, "Antarctica/Mawson" },
{ "mddt", 1, -18000, "America/Cambridge_Bay" },
{ "mddt", 1, -18000, "America/Yellowknife" },
{ "mdst", 1, 16279, "Europe/Moscow" },
@@ -1202,6 +1107,8 @@
{ "mdt", 1, -21600, "Canada/Mountain" },
{ "mdt", 1, -21600, "Canada/Saskatchewan" },
{ "mdt", 1, -21600, "Mexico/BajaSur" },
+ { "mest", 1, 7200, "MET" },
+ { "met", 0, 3600, "MET" },
{ "mht", 0, 43200, "Pacific/Kwajalein" },
{ "mht", 0, 39600, "Pacific/Kwajalein" },
{ "mht", 0, 39600, "Pacific/Majuro" },
@@ -1215,6 +1122,7 @@
{ "mmt", 0, 17640, "Indian/Maldives" },
{ "mmt", 0, 19172, "Asia/Colombo" },
{ "mmt", 0, 23400, "Asia/Rangoon" },
+ { "mmt", 0, 23400, "Asia/Yangon" },
{ "mmt", 0, 28656, "Asia/Makassar" },
{ "mmt", 0, 28656, "Asia/Ujung_Pandang" },
{ "mmt", 0, 6600, "Europe/Minsk" },
@@ -1242,13 +1150,11 @@
{ "msd", 1, 14400, "Europe/Kiev" },
{ "msd", 1, 14400, "Europe/Minsk" },
{ "msd", 1, 14400, "Europe/Riga" },
- { "msd", 1, 14400, "Europe/Samara" },
{ "msd", 1, 14400, "Europe/Simferopol" },
{ "msd", 1, 14400, "Europe/Tallinn" },
{ "msd", 1, 14400, "Europe/Tiraspol" },
{ "msd", 1, 14400, "Europe/Uzhgorod" },
{ "msd", 1, 14400, "Europe/Vilnius" },
- { "msd", 1, 14400, "Europe/Volgograd" },
{ "msd", 1, 14400, "Europe/Zaporozhye" },
{ "msk", 0, 10800, "Europe/Moscow" },
{ "msk", 0, 14400, "Europe/Moscow" },
@@ -1257,17 +1163,13 @@
{ "msk", 0, 10800, "Europe/Kiev" },
{ "msk", 0, 10800, "Europe/Minsk" },
{ "msk", 0, 10800, "Europe/Riga" },
- { "msk", 0, 10800, "Europe/Samara" },
{ "msk", 0, 10800, "Europe/Simferopol" },
{ "msk", 0, 10800, "Europe/Tallinn" },
{ "msk", 0, 10800, "Europe/Tiraspol" },
{ "msk", 0, 10800, "Europe/Uzhgorod" },
{ "msk", 0, 10800, "Europe/Vilnius" },
- { "msk", 0, 10800, "Europe/Volgograd" },
{ "msk", 0, 10800, "Europe/Zaporozhye" },
{ "msk", 0, 14400, "Europe/Simferopol" },
- { "msk", 0, 14400, "Europe/Volgograd" },
- { "msm", 1, 18000, "Europe/Moscow" },
{ "mst", 0, -25200, "America/Denver" },
{ "mst", 0, -25200, "America/Bahia_Banderas" },
{ "mst", 0, -25200, "America/Boise" },
@@ -1277,6 +1179,7 @@
{ "mst", 0, -25200, "America/Dawson_Creek" },
{ "mst", 0, -25200, "America/Edmonton" },
{ "mst", 0, -25200, "America/Ensenada" },
+ { "mst", 0, -25200, "America/Fort_Nelson" },
{ "mst", 0, -25200, "America/Hermosillo" },
{ "mst", 0, -25200, "America/Inuvik" },
{ "mst", 0, -25200, "America/Mazatlan" },
@@ -1332,15 +1235,10 @@
{ "negt", 0, -12600, "America/Paramaribo" },
{ "nest", 1, 4800, "Europe/Amsterdam" },
{ "net", 0, 1200, "Europe/Amsterdam" },
+ { "nfst", 1, 45000, "Pacific/Norfolk" },
{ "nft", 0, 41400, "Pacific/Norfolk" },
+ { "nft", 0, 39600, "Pacific/Norfolk" },
{ "nmt", 0, 40320, "Pacific/Norfolk" },
- { "novst", 1, 25200, "Asia/Novosibirsk" },
- { "novst", 1, 28800, "Asia/Novosibirsk" },
- { "novst", 1, 25200, "Asia/Novokuznetsk" },
- { "novt", 0, 21600, "Asia/Novosibirsk" },
- { "novt", 0, 25200, "Asia/Novosibirsk" },
- { "novt", 0, 21600, "Asia/Novokuznetsk" },
- { "novt", 0, 25200, "Asia/Novokuznetsk" },
{ "npt", 1, -9000, "America/St_Johns" },
{ "npt", 0, 20700, "Asia/Katmandu" },
{ "npt", 1, -36000, "America/Adak" },
@@ -1393,20 +1291,13 @@
{ "nzst", 1, 45000, "Antarctica/McMurdo" },
{ "nzst", 1, 45000, "Antarctica/South_Pole" },
{ "nzst", 1, 45000, "NZ" },
- { "omsst", 1, 25200, "Asia/Omsk" },
- { "omsst", 1, 21600, "Asia/Omsk" },
- { "omst", 0, 21600, "Asia/Omsk" },
- { "omst", 0, 18000, "Asia/Omsk" },
- { "omst", 0, 25200, "Asia/Omsk" },
- { "orast", 1, 18000, "Asia/Oral" },
- { "orat", 0, 18000, "Asia/Oral" },
- { "orat", 0, 14400, "Asia/Oral" },
{ "pddt", 1, -21600, "America/Inuvik" },
{ "pdt", 1, -25200, "America/Los_Angeles" },
{ "pdt", 1, -25200, "America/Boise" },
{ "pdt", 1, -25200, "America/Dawson" },
{ "pdt", 1, -25200, "America/Dawson_Creek" },
{ "pdt", 1, -25200, "America/Ensenada" },
+ { "pdt", 1, -25200, "America/Fort_Nelson" },
{ "pdt", 1, -25200, "America/Juneau" },
{ "pdt", 1, -25200, "America/Metlakatla" },
{ "pdt", 1, -25200, "America/Santa_Isabel" },
@@ -1418,10 +1309,6 @@
{ "pdt", 1, -25200, "Canada/Yukon" },
{ "pdt", 1, -25200, "Mexico/BajaNorte" },
{ "pest", 1, -14400, "America/Lima" },
- { "petst", 1, 46800, "Asia/Kamchatka" },
- { "petst", 1, 43200, "Asia/Kamchatka" },
- { "pett", 0, 43200, "Asia/Kamchatka" },
- { "pett", 0, 39600, "Asia/Kamchatka" },
{ "pet", 0, -18000, "America/Lima" },
{ "pgt", 0, 36000, "Pacific/Bougainville" },
{ "pgt", 0, 36000, "Pacific/Port_Moresby" },
@@ -1435,12 +1322,14 @@
{ "plmt", 0, 25590, "Asia/Ho_Chi_Minh" },
{ "plmt", 0, 25590, "Asia/Saigon" },
{ "pmdt", 1, -7200, "America/Miquelon" },
+ { "pmmt", 0, 35312, "Pacific/Bougainville" },
{ "pmst", 0, -10800, "America/Miquelon" },
{ "pmt", 0, -13236, "America/Paramaribo" },
{ "pmt", 0, -13252, "America/Paramaribo" },
- { "pmt", 0, 36000, "Antarctica/DumontDUrville" },
{ "pmt", 0, 13505, "Asia/Yekaterinburg" },
{ "pmt", 0, 26240, "Asia/Pontianak" },
+ { "pmt", 0, 3464, "Europe/Bratislava" },
+ { "pmt", 0, 3464, "Europe/Prague" },
{ "pmt", 0, 561, "Africa/Algiers" },
{ "pmt", 0, 561, "Africa/Tunis" },
{ "pmt", 0, 561, "Europe/Monaco" },
@@ -1452,6 +1341,7 @@
{ "ppt", 1, -25200, "America/Los_Angeles" },
{ "ppt", 1, -25200, "America/Dawson_Creek" },
{ "ppt", 1, -25200, "America/Ensenada" },
+ { "ppt", 1, -25200, "America/Fort_Nelson" },
{ "ppt", 1, -25200, "America/Juneau" },
{ "ppt", 1, -25200, "America/Metlakatla" },
{ "ppt", 1, -25200, "America/Santa_Isabel" },
@@ -1467,6 +1357,7 @@
{ "pst", 0, -28800, "America/Dawson" },
{ "pst", 0, -28800, "America/Dawson_Creek" },
{ "pst", 0, -28800, "America/Ensenada" },
+ { "pst", 0, -28800, "America/Fort_Nelson" },
{ "pst", 0, -28800, "America/Hermosillo" },
{ "pst", 0, -28800, "America/Inuvik" },
{ "pst", 0, -28800, "America/Juneau" },
@@ -1485,6 +1376,7 @@
{ "pwt", 1, -25200, "America/Los_Angeles" },
{ "pwt", 1, -25200, "America/Dawson_Creek" },
{ "pwt", 1, -25200, "America/Ensenada" },
+ { "pwt", 1, -25200, "America/Fort_Nelson" },
{ "pwt", 1, -25200, "America/Juneau" },
{ "pwt", 1, -25200, "America/Metlakatla" },
{ "pwt", 1, -25200, "America/Santa_Isabel" },
@@ -1498,24 +1390,13 @@
{ "pyt", 0, -14400, "America/Asuncion" },
{ "pyt", 0, -10800, "America/Asuncion" },
{ "qmt", 0, -18840, "America/Guayaquil" },
- { "qyzst", 1, 25200, "Asia/Qyzylorda" },
- { "qyzt", 0, 21600, "Asia/Qyzylorda" },
- { "qyzt", 0, 18000, "Asia/Qyzylorda" },
{ "ret", 0, 14400, "Indian/Reunion" },
{ "rmt", 0, 5794, "Europe/Riga" },
{ "rmt", 0, 23080, "Asia/Rangoon" },
- { "rott", 0, -10800, "Antarctica/Rothera" },
- { "sakst", 1, 39600, "Asia/Sakhalin" },
- { "sakst", 1, 43200, "Asia/Sakhalin" },
- { "sakt", 0, 36000, "Asia/Sakhalin" },
- { "sakt", 0, 39600, "Asia/Sakhalin" },
- { "samst", 1, 21600, "Asia/Samarkand" },
- { "samst", 1, 14400, "Europe/Samara" },
- { "samst", 1, 18000, "Europe/Samara" },
- { "samt", 0, 18000, "Asia/Samarkand" },
- { "samt", 0, 14400, "Asia/Samarkand" },
- { "samt", 0, 10800, "Europe/Samara" },
- { "samt", 0, 14400, "Europe/Samara" },
+ { "rmt", 0, 23080, "Asia/Yangon" },
+ { "rmt", 0, 2996, "Europe/Rome" },
+ { "rmt", 0, 2996, "Europe/San_Marino" },
+ { "rmt", 0, 2996, "Europe/Vatican" },
{ "sast", 0, 7200, "Africa/Johannesburg" },
{ "sast", 1, 10800, "Africa/Johannesburg" },
{ "sast", 0, 5400, "Africa/Johannesburg" },
@@ -1531,11 +1412,9 @@
{ "sct", 0, 14400, "Indian/Mahe" },
{ "sdmt", 0, -16800, "America/Santo_Domingo" },
{ "sdt", 1, -36000, "Pacific/Apia" },
+ { "set", 0, 3614, "Europe/Stockholm" },
{ "sgt", 0, 28800, "Asia/Singapore" },
{ "sgt", 0, 27000, "Asia/Singapore" },
- { "shest", 1, 21600, "Asia/Aqtau" },
- { "shet", 0, 21600, "Asia/Aqtau" },
- { "shet", 0, 18000, "Asia/Aqtau" },
{ "sjmt", 0, -20173, "America/Costa_Rica" },
{ "smt", 0, -13884, "Atlantic/Stanley" },
{ "smt", 0, -16966, "America/Santiago" },
@@ -1543,53 +1422,24 @@
{ "smt", 0, 24925, "Asia/Kuala_Lumpur" },
{ "smt", 0, 24925, "Asia/Singapore" },
{ "smt", 0, 8160, "Europe/Simferopol" },
- { "sret", 0, 39600, "Asia/Srednekolymsk" },
{ "srt", 0, -10800, "America/Paramaribo" },
{ "srt", 0, -12600, "America/Paramaribo" },
{ "sst", 0, -39600, "Pacific/Samoa" },
{ "sst", 0, -39600, "Pacific/Apia" },
{ "sst", 0, -39600, "Pacific/Midway" },
{ "sst", 0, -39600, "Pacific/Pago_Pago" },
- { "stat", 0, 10800, "Europe/Volgograd" },
- { "stat", 0, 14400, "Europe/Volgograd" },
- { "svest", 1, 21600, "Asia/Yekaterinburg" },
- { "svest", 1, 18000, "Asia/Yekaterinburg" },
- { "svet", 0, 18000, "Asia/Yekaterinburg" },
- { "svet", 0, 14400, "Asia/Yekaterinburg" },
{ "swat", 0, 5400, "Africa/Windhoek" },
- { "syot", 0, 10800, "Antarctica/Syowa" },
{ "taht", 0, -36000, "Pacific/Tahiti" },
- { "tasst", 1, 25200, "Asia/Samarkand" },
- { "tasst", 1, 21600, "Asia/Tashkent" },
- { "tasst", 1, 25200, "Asia/Tashkent" },
- { "tast", 0, 21600, "Asia/Samarkand" },
- { "tast", 0, 18000, "Asia/Tashkent" },
- { "tast", 0, 21600, "Asia/Tashkent" },
- { "tbist", 1, 18000, "Asia/Tbilisi" },
- { "tbist", 1, 14400, "Asia/Tbilisi" },
- { "tbit", 0, 14400, "Asia/Tbilisi" },
- { "tbit", 0, 10800, "Asia/Tbilisi" },
{ "tbmt", 0, 10751, "Asia/Tbilisi" },
- { "tft", 0, 18000, "Indian/Kerguelen" },
- { "tjt", 0, 18000, "Asia/Dushanbe" },
{ "tkt", 0, -39600, "Pacific/Fakaofo" },
{ "tkt", 0, 46800, "Pacific/Fakaofo" },
{ "tlt", 0, 32400, "Asia/Dili" },
{ "tlt", 0, 28800, "Asia/Dili" },
{ "tmt", 0, 12344, "Asia/Tehran" },
{ "tmt", 0, 5940, "Europe/Tallinn" },
- { "tmt", 0, 14400, "Asia/Ashgabat" },
- { "tmt", 0, 14400, "Asia/Ashkhabad" },
- { "tmt", 0, 18000, "Asia/Ashgabat" },
- { "tmt", 0, 18000, "Asia/Ashkhabad" },
{ "tost", 1, 50400, "Pacific/Tongatapu" },
{ "tot", 0, 46800, "Pacific/Tongatapu" },
{ "tot", 0, 44400, "Pacific/Tongatapu" },
- { "trst", 1, 14400, "Europe/Istanbul" },
- { "trst", 1, 14400, "Asia/Istanbul" },
- { "trt", 0, 10800, "Europe/Istanbul" },
- { "trt", 0, 10800, "Asia/Istanbul" },
- { "tsat", 0, 10800, "Europe/Volgograd" },
{ "tvt", 0, 43200, "Pacific/Funafuti" },
{ "uct", 0, 0, "Etc/UCT" },
{ "ulast", 1, 32400, "Asia/Ulaanbaatar" },
@@ -1600,12 +1450,6 @@
{ "ulat", 0, 25200, "Asia/Ulan_Bator" },
{ "ulat", 0, 28800, "Asia/Choibalsan" },
{ "ulat", 0, 28800, "Asia/Ulan_Bator" },
- { "urast", 1, 21600, "Asia/Oral" },
- { "urast", 1, 18000, "Asia/Oral" },
- { "urat", 0, 21600, "Asia/Oral" },
- { "urat", 0, 14400, "Asia/Oral" },
- { "urat", 0, 18000, "Asia/Oral" },
- { "utc", 0, 0, "Antarctica/Troll" },
{ "utc", 0, 0, "Etc/Universal" },
{ "utc", 0, 0, "Etc/UTC" },
{ "utc", 0, 0, "Etc/Zulu" },
@@ -1616,27 +1460,8 @@
{ "uyst", 1, -7200, "America/Montevideo" },
{ "uyt", 0, -10800, "America/Montevideo" },
{ "uyt", 0, -12600, "America/Montevideo" },
- { "uzst", 1, 21600, "Asia/Samarkand" },
- { "uzst", 1, 21600, "Asia/Tashkent" },
- { "uzt", 0, 18000, "Asia/Samarkand" },
- { "uzt", 0, 18000, "Asia/Tashkent" },
{ "vet", 0, -16200, "America/Caracas" },
{ "vet", 0, -14400, "America/Caracas" },
- { "vlast", 1, 39600, "Asia/Vladivostok" },
- { "vlast", 1, 36000, "Asia/Vladivostok" },
- { "vlast", 1, 39600, "Asia/Khandyga" },
- { "vlat", 0, 36000, "Asia/Vladivostok" },
- { "vlat", 0, 32400, "Asia/Vladivostok" },
- { "vlat", 0, 39600, "Asia/Vladivostok" },
- { "vlat", 0, 36000, "Asia/Khandyga" },
- { "vlat", 0, 36000, "Asia/Ust-Nera" },
- { "vlat", 0, 39600, "Asia/Khandyga" },
- { "vlat", 0, 39600, "Asia/Ust-Nera" },
- { "volst", 1, 14400, "Europe/Volgograd" },
- { "volst", 1, 18000, "Europe/Volgograd" },
- { "volt", 0, 10800, "Europe/Volgograd" },
- { "volt", 0, 14400, "Europe/Volgograd" },
- { "vost", 0, 21600, "Antarctica/Vostok" },
{ "vust", 1, 43200, "Pacific/Efate" },
{ "vut", 0, 39600, "Pacific/Efate" },
{ "wakt", 0, 43200, "Pacific/Wake" },
@@ -1737,23 +1562,6 @@
{ "wsst", 0, 46800, "Pacific/Apia" },
{ "xjt", 0, 21600, "Asia/Kashgar" },
{ "xjt", 0, 21600, "Asia/Urumqi" },
- { "yakst", 1, 36000, "Asia/Yakutsk" },
- { "yakst", 1, 32400, "Asia/Yakutsk" },
- { "yakst", 1, 32400, "Asia/Chita" },
- { "yakst", 1, 32400, "Asia/Khandyga" },
- { "yakst", 1, 36000, "Asia/Chita" },
- { "yakst", 1, 36000, "Asia/Khandyga" },
- { "yakt", 0, 32400, "Asia/Yakutsk" },
- { "yakt", 0, 28800, "Asia/Yakutsk" },
- { "yakt", 0, 36000, "Asia/Yakutsk" },
- { "yakt", 0, 28800, "Asia/Chita" },
- { "yakt", 0, 28800, "Asia/Khandyga" },
- { "yakt", 0, 28800, "Asia/Ust-Nera" },
- { "yakt", 0, 32400, "Asia/Chita" },
- { "yakt", 0, 32400, "Asia/Khandyga" },
- { "yakt", 0, 32400, "Asia/Ust-Nera" },
- { "yakt", 0, 36000, "Asia/Chita" },
- { "yakt", 0, 36000, "Asia/Khandyga" },
{ "yddt", 1, -25200, "America/Dawson" },
{ "yddt", 1, -25200, "America/Whitehorse" },
{ "yddt", 1, -25200, "Canada/Yukon" },
@@ -1762,13 +1570,6 @@
{ "ydt", 1, -28800, "America/Whitehorse" },
{ "ydt", 1, -28800, "America/Yakutat" },
{ "ydt", 1, -28800, "Canada/Yukon" },
- { "yekst", 1, 21600, "Asia/Yekaterinburg" },
- { "yekt", 0, 18000, "Asia/Yekaterinburg" },
- { "yekt", 0, 21600, "Asia/Yekaterinburg" },
- { "yerst", 1, 18000, "Asia/Yerevan" },
- { "yerst", 1, 14400, "Asia/Yerevan" },
- { "yert", 0, 14400, "Asia/Yerevan" },
- { "yert", 0, 10800, "Asia/Yerevan" },
{ "ypt", 1, -28800, "America/Dawson" },
{ "ypt", 1, -28800, "America/Whitehorse" },
{ "ypt", 1, -28800, "America/Yakutat" },
@@ -1809,22 +1610,4 @@
{ "w", 0, -36000, NULL },
{ "x", 0, -39600, NULL },
{ "y", 0, -43200, NULL },
- { "zzz", 0, 0, "Antarctica/Davis" },
- { "zzz", 0, 0, "America/Cambridge_Bay" },
- { "zzz", 0, 0, "America/Inuvik" },
- { "zzz", 0, 0, "America/Iqaluit" },
- { "zzz", 0, 0, "America/Pangnirtung" },
- { "zzz", 0, 0, "America/Rankin_Inlet" },
- { "zzz", 0, 0, "America/Resolute" },
- { "zzz", 0, 0, "America/Yellowknife" },
- { "zzz", 0, 0, "Antarctica/Casey" },
- { "zzz", 0, 0, "Antarctica/DumontDUrville" },
- { "zzz", 0, 0, "Antarctica/Macquarie" },
- { "zzz", 0, 0, "Antarctica/Mawson" },
- { "zzz", 0, 0, "Antarctica/Palmer" },
- { "zzz", 0, 0, "Antarctica/Rothera" },
- { "zzz", 0, 0, "Antarctica/Syowa" },
- { "zzz", 0, 0, "Antarctica/Troll" },
- { "zzz", 0, 0, "Antarctica/Vostok" },
- { "zzz", 0, 0, "Indian/Kerguelen" },
{ "z", 0, 0, NULL },
diff --git a/ext/date/lib/tm2unixtime.c b/ext/date/lib/tm2unixtime.c
index 83ff40b385..d8597f14f9 100644
--- a/ext/date/lib/tm2unixtime.c
+++ b/ext/date/lib/tm2unixtime.c
@@ -32,6 +32,18 @@ static int month_tab[12] = { 0, 31, 59, 90, 120, 151, 181, 212, 24
static int days_in_month_leap[13] = { 31, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
static int days_in_month[13] = { 31, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+static void do_range_limit_fraction(double *fraction, timelib_sll *seconds)
+{
+ if (*fraction < 0) {
+ *fraction += 1;
+ *seconds -= 1;
+ }
+ if (*fraction > 1) {
+ *fraction -= 1;
+ *seconds += 1;
+ }
+}
+
static void do_range_limit(timelib_sll start, timelib_sll end, timelib_sll adj, timelib_sll *a, timelib_sll *b)
{
if (*a < start) {
@@ -192,6 +204,7 @@ void timelib_do_rel_normalize(timelib_time *base, timelib_rel_time *rt)
void timelib_do_normalize(timelib_time* time)
{
+ if (time->f != TIMELIB_UNSET) do_range_limit_fraction(&time->f, &time->s);
if (time->s != TIMELIB_UNSET) do_range_limit(0, 60, 60, &time->s, &time->i);
if (time->s != TIMELIB_UNSET) do_range_limit(0, 60, 60, &time->i, &time->h);
if (time->s != TIMELIB_UNSET) do_range_limit(0, 24, 24, &time->h, &time->d);
@@ -209,6 +222,8 @@ static void do_adjust_relative(timelib_time* time)
timelib_do_normalize(time);
if (time->have_relative) {
+ time->f += time->relative.f;
+
time->s += time->relative.s;
time->i += time->relative.i;
time->h += time->relative.h;
diff --git a/ext/date/lib/unixtime2tm.c b/ext/date/lib/unixtime2tm.c
index bdef26defc..e46a0af57d 100644
--- a/ext/date/lib/unixtime2tm.c
+++ b/ext/date/lib/unixtime2tm.c
@@ -295,3 +295,10 @@ int timelib_apply_localtime(timelib_time *t, unsigned int localtime)
}
return 0;
}
+
+#if HAVE_GETTIMEOFDAY
+void timelib_set_fraction_from_timeval(timelib_time *t, struct timeval tp)
+{
+ t->f = (double) tp.tv_usec / 1000000;
+}
+#endif
diff --git a/ext/date/php_date.c b/ext/date/php_date.c
index f671f4cba0..dcc71a402b 100644
--- a/ext/date/php_date.c
+++ b/ext/date/php_date.c
@@ -29,7 +29,11 @@
#include "php_date.h"
#include "zend_interfaces.h"
#include "lib/timelib.h"
+#ifndef PHP_WIN32
#include <time.h>
+#else
+#include "win32/time.h"
+#endif
#ifdef PHP_WIN32
static __inline __int64 php_date_llabs( __int64 i ) { return i >= 0? i: -i; }
@@ -253,12 +257,14 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_date_time_set, 0, 0, 3)
ZEND_ARG_INFO(0, hour)
ZEND_ARG_INFO(0, minute)
ZEND_ARG_INFO(0, second)
+ ZEND_ARG_INFO(0, microseconds)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_date_method_time_set, 0, 0, 2)
ZEND_ARG_INFO(0, hour)
ZEND_ARG_INFO(0, minute)
ZEND_ARG_INFO(0, second)
+ ZEND_ARG_INFO(0, microseconds)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_date_date_set, 0, 0, 4)
@@ -585,6 +591,11 @@ PHPAPI zend_class_entry *php_date_get_immutable_ce(void)
return date_ce_immutable;
}
+PHPAPI zend_class_entry *php_date_get_interface_ce(void)
+{
+ return date_ce_interface;
+}
+
PHPAPI zend_class_entry *php_date_get_timezone_ce(void)
{
return date_ce_timezone;
@@ -1077,7 +1088,8 @@ char *php_date_short_day_name(timelib_sll y, timelib_sll m, timelib_sll d)
static zend_string *date_format(char *format, size_t format_len, timelib_time *t, int localtime)
{
smart_str string = {0};
- int i, length = 0;
+ size_t i;
+ int length = 0;
char buffer[97];
timelib_time_offset *offset = NULL;
timelib_sll isoweek, isoyear;
@@ -2385,8 +2397,11 @@ static zend_object *date_object_clone_interval(zval *this_ptr) /* {{{ */
php_interval_obj *new_obj = php_interval_obj_from_obj(date_object_new_interval_ex(old_obj->std.ce, 0));
zend_objects_clone_members(&new_obj->std, &old_obj->std);
+ new_obj->initialized = old_obj->initialized;
+ if (old_obj->diff) {
+ new_obj->diff = timelib_rel_time_clone(old_obj->diff);
+ }
- /** FIX ME ADD CLONE STUFF **/
return &new_obj->std;
} /* }}} */
@@ -2422,6 +2437,8 @@ static HashTable *date_object_get_properties_interval(zval *object) /* {{{ */
PHP_DATE_INTERVAL_ADD_PROPERTY("h", h);
PHP_DATE_INTERVAL_ADD_PROPERTY("i", i);
PHP_DATE_INTERVAL_ADD_PROPERTY("s", s);
+ ZVAL_DOUBLE(&zv, (double)intervalobj->diff->f);
+ zend_hash_str_update(props, "f", sizeof("f") - 1, &zv);
PHP_DATE_INTERVAL_ADD_PROPERTY("weekday", weekday);
PHP_DATE_INTERVAL_ADD_PROPERTY("weekday_behavior", weekday_behavior);
PHP_DATE_INTERVAL_ADD_PROPERTY("first_last_day_of", first_last_day_of);
@@ -2467,8 +2484,23 @@ static zend_object *date_object_clone_period(zval *this_ptr) /* {{{ */
php_period_obj *new_obj = php_period_obj_from_obj(date_object_new_period_ex(old_obj->std.ce, 0));
zend_objects_clone_members(&new_obj->std, &old_obj->std);
-
- /** FIX ME ADD CLONE STUFF **/
+ new_obj->initialized = old_obj->initialized;
+ new_obj->recurrences = old_obj->recurrences;
+ new_obj->include_start_date = old_obj->include_start_date;
+ new_obj->start_ce = old_obj->start_ce;
+
+ if (old_obj->start) {
+ new_obj->start = timelib_time_clone(old_obj->start);
+ }
+ if (old_obj->current) {
+ new_obj->current = timelib_time_clone(old_obj->current);
+ }
+ if (old_obj->end) {
+ new_obj->end = timelib_time_clone(old_obj->end);
+ }
+ if (old_obj->interval) {
+ new_obj->interval = timelib_rel_time_clone(old_obj->interval);
+ }
return &new_obj->std;
} /* }}} */
@@ -2539,6 +2571,25 @@ static void update_errors_warnings(timelib_error_container *last_errors) /* {{{
DATEG(last_errors) = last_errors;
} /* }}} */
+static void php_date_set_time_fraction(timelib_time *time, int microseconds)
+{
+ time->f = (double) microseconds / 1000000;
+}
+
+static void php_date_get_current_time_with_fraction(time_t *sec, suseconds_t *usec)
+{
+#if HAVE_GETTIMEOFDAY
+ struct timeval tp = {0}; /* For setting microseconds */
+
+ gettimeofday(&tp, NULL);
+ *sec = tp.tv_sec;
+ *usec = tp.tv_usec;
+#else
+ *sec = time(NULL);
+ *usec = 0;
+#endif
+}
+
PHPAPI int php_date_initialize(php_date_obj *dateobj, /*const*/ char *time_str, size_t time_str_len, char *format, zval *timezone_object, int ctor) /* {{{ */
{
timelib_time *now;
@@ -2547,6 +2598,8 @@ PHPAPI int php_date_initialize(php_date_obj *dateobj, /*const*/ char *time_str,
int type = TIMELIB_ZONETYPE_ID, new_dst = 0;
char *new_abbr = NULL;
timelib_sll new_offset = 0;
+ time_t sec;
+ suseconds_t usec;
if (dateobj->time) {
timelib_time_dtor(dateobj->time);
@@ -2611,8 +2664,9 @@ PHPAPI int php_date_initialize(php_date_obj *dateobj, /*const*/ char *time_str,
now->tz_abbr = new_abbr;
break;
}
- timelib_unixtime2local(now, (timelib_sll) time(NULL));
-
+ php_date_get_current_time_with_fraction(&sec, &usec);
+ timelib_unixtime2local(now, (timelib_sll) sec);
+ php_date_set_time_fraction(now, usec);
timelib_fill_holes(dateobj->time, now, TIMELIB_NO_CLONE);
timelib_update_ts(dateobj->time, tzi);
timelib_update_from_sse(dateobj->time);
@@ -2844,7 +2898,7 @@ PHP_METHOD(DateTime, __set_state)
php_date_instantiate(date_ce_date, return_value);
dateobj = Z_PHPDATE_P(return_value);
if (!php_date_initialize_from_hash(&dateobj, myht)) {
- php_error(E_ERROR, "Invalid serialization data for DateTime object");
+ zend_throw_error(NULL, "Invalid serialization data for DateTime object");
}
}
/* }}} */
@@ -2866,7 +2920,7 @@ PHP_METHOD(DateTimeImmutable, __set_state)
php_date_instantiate(date_ce_immutable, return_value);
dateobj = Z_PHPDATE_P(return_value);
if (!php_date_initialize_from_hash(&dateobj, myht)) {
- php_error(E_ERROR, "Invalid serialization data for DateTimeImmutable object");
+ zend_throw_error(NULL, "Invalid serialization data for DateTimeImmutable object");
}
}
/* }}} */
@@ -2884,7 +2938,7 @@ PHP_METHOD(DateTime, __wakeup)
myht = Z_OBJPROP_P(object);
if (!php_date_initialize_from_hash(&dateobj, myht)) {
- php_error(E_ERROR, "Invalid serialization data for DateTime object");
+ zend_throw_error(NULL, "Invalid serialization data for DateTime object");
}
}
/* }}} */
@@ -3108,6 +3162,11 @@ static int php_date_modify(zval *object, char *modify, size_t modify_len) /* {{{
dateobj->time->s = 0;
}
}
+
+ if (tmp_time->f != -99999) {
+ dateobj->time->f = tmp_time->f;
+ }
+
timelib_time_dtor(tmp_time);
timelib_update_ts(dateobj->time, NULL);
@@ -3157,7 +3216,7 @@ PHP_METHOD(DateTimeImmutable, modify)
RETURN_FALSE;
}
- ZVAL_COPY_VALUE(return_value, &new_object);
+ ZVAL_OBJ(return_value, Z_OBJ(new_object));
}
/* }}} */
@@ -3208,7 +3267,7 @@ PHP_METHOD(DateTimeImmutable, add)
date_clone_immutable(object, &new_object);
php_date_add(&new_object, interval, return_value);
- ZVAL_COPY_VALUE(return_value, &new_object);
+ ZVAL_OBJ(return_value, Z_OBJ(new_object));
}
/* }}} */
@@ -3264,7 +3323,7 @@ PHP_METHOD(DateTimeImmutable, sub)
date_clone_immutable(object, &new_object);
php_date_sub(&new_object, interval, return_value);
- ZVAL_COPY_VALUE(return_value, &new_object);
+ ZVAL_OBJ(return_value, Z_OBJ(new_object));
}
/* }}} */
@@ -3368,7 +3427,7 @@ PHP_METHOD(DateTimeImmutable, setTimezone)
date_clone_immutable(object, &new_object);
php_date_timezone_set(&new_object, timezone_object, return_value);
- ZVAL_COPY_VALUE(return_value, &new_object);
+ ZVAL_OBJ(return_value, Z_OBJ(new_object));
}
/* }}} */
@@ -3407,7 +3466,7 @@ PHP_FUNCTION(date_offset_get)
}
/* }}} */
-static void php_date_time_set(zval *object, zend_long h, zend_long i, zend_long s, zval *return_value) /* {{{ */
+static void php_date_time_set(zval *object, zend_long h, zend_long i, zend_long s, zend_long ms, zval *return_value) /* {{{ */
{
php_date_obj *dateobj;
@@ -3416,22 +3475,23 @@ static void php_date_time_set(zval *object, zend_long h, zend_long i, zend_long
dateobj->time->h = h;
dateobj->time->i = i;
dateobj->time->s = s;
+ dateobj->time->f = ((double) ms) / 1000000;
timelib_update_ts(dateobj->time, NULL);
} /* }}} */
-/* {{{ proto DateTime date_time_set(DateTime object, long hour, long minute[, long second])
+/* {{{ proto DateTime date_time_set(DateTime object, long hour, long minute[, long second[, long microseconds]])
Sets the time.
*/
PHP_FUNCTION(date_time_set)
{
zval *object;
- zend_long h, i, s = 0;
+ zend_long h, i, s = 0, ms = 0;
- if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Oll|l", &object, date_ce_date, &h, &i, &s) == FAILURE) {
+ if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Oll|ll", &object, date_ce_date, &h, &i, &s, &ms) == FAILURE) {
RETURN_FALSE;
}
- php_date_time_set(object, h, i, s, return_value);
+ php_date_time_set(object, h, i, s, ms, return_value);
Z_ADDREF_P(object);
ZVAL_COPY_VALUE(return_value, object);
@@ -3443,16 +3503,16 @@ PHP_FUNCTION(date_time_set)
PHP_METHOD(DateTimeImmutable, setTime)
{
zval *object, new_object;
- zend_long h, i, s = 0;
+ zend_long h, i, s = 0, ms = 0;
- if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Oll|l", &object, date_ce_immutable, &h, &i, &s) == FAILURE) {
+ if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Oll|ll", &object, date_ce_immutable, &h, &i, &s, &ms) == FAILURE) {
RETURN_FALSE;
}
date_clone_immutable(object, &new_object);
- php_date_time_set(&new_object, h, i, s, return_value);
+ php_date_time_set(&new_object, h, i, s, ms, return_value);
- ZVAL_COPY_VALUE(return_value, &new_object);
+ ZVAL_OBJ(return_value, Z_OBJ(new_object));
}
/* }}} */
@@ -3501,7 +3561,7 @@ PHP_METHOD(DateTimeImmutable, setDate)
date_clone_immutable(object, &new_object);
php_date_date_set(&new_object, y, m, d, return_value);
- ZVAL_COPY_VALUE(return_value, &new_object);
+ ZVAL_OBJ(return_value, Z_OBJ(new_object));
}
/* }}} */
@@ -3554,7 +3614,7 @@ PHP_METHOD(DateTimeImmutable, setISODate)
date_clone_immutable(object, &new_object);
php_date_isodate_set(&new_object, y, w, d, return_value);
- ZVAL_COPY_VALUE(return_value, &new_object);
+ ZVAL_OBJ(return_value, Z_OBJ(new_object));
}
/* }}} */
@@ -3566,6 +3626,7 @@ static void php_date_timestamp_set(zval *object, zend_long timestamp, zval *retu
DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
timelib_unixtime2local(dateobj->time, (timelib_sll)timestamp);
timelib_update_ts(dateobj->time, NULL);
+ php_date_set_time_fraction(dateobj->time, 0);
} /* }}} */
/* {{{ proto DateTime date_timestamp_set(DateTime object, long unixTimestamp)
@@ -3601,7 +3662,7 @@ PHP_METHOD(DateTimeImmutable, setTimestamp)
date_clone_immutable(object, &new_object);
php_date_timestamp_set(&new_object, timestamp, return_value);
- ZVAL_COPY_VALUE(return_value, &new_object);
+ ZVAL_OBJ(return_value, Z_OBJ(new_object));
}
/* }}} */
@@ -3757,7 +3818,7 @@ PHP_METHOD(DateTimeZone, __set_state)
HashTable *myht;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &array) == FAILURE) {
- RETURN_FALSE;
+ return;
}
myht = Z_ARRVAL_P(array);
@@ -3765,7 +3826,8 @@ PHP_METHOD(DateTimeZone, __set_state)
php_date_instantiate(date_ce_timezone, return_value);
tzobj = Z_PHPTIMEZONE_P(return_value);
if(php_date_timezone_initialize_from_hash(&return_value, &tzobj, myht) != SUCCESS) {
- php_error_docref(NULL, E_ERROR, "Timezone initialization failed");
+ zend_throw_error(NULL, "Timezone initialization failed");
+ zval_dtor(return_value);
}
}
/* }}} */
@@ -3783,7 +3845,7 @@ PHP_METHOD(DateTimeZone, __wakeup)
myht = Z_OBJPROP_P(object);
if(php_date_timezone_initialize_from_hash(&return_value, &tzobj, myht) != SUCCESS) {
- php_error_docref(NULL, E_ERROR, "Timezone initialization failed");
+ zend_throw_error(NULL, "Timezone initialization failed");
}
}
/* }}} */
@@ -4030,6 +4092,7 @@ zval *date_interval_read_property(zval *object, zval *member, int type, void **c
zval *retval;
zval tmp_member;
timelib_sll value = -1;
+ double fvalue = -1;
if (Z_TYPE_P(member) != IS_STRING) {
tmp_member = *member;
@@ -4061,6 +4124,10 @@ zval *date_interval_read_property(zval *object, zval *member, int type, void **c
GET_VALUE_FROM_STRUCT(h, "h");
GET_VALUE_FROM_STRUCT(i, "i");
GET_VALUE_FROM_STRUCT(s, "s");
+ if (strcmp(Z_STRVAL_P(member), "f") == 0) {
+ fvalue = obj->diff->f;
+ break;
+ }
GET_VALUE_FROM_STRUCT(invert, "invert");
GET_VALUE_FROM_STRUCT(days, "days");
/* didn't find any */
@@ -4075,7 +4142,9 @@ zval *date_interval_read_property(zval *object, zval *member, int type, void **c
retval = rv;
- if (value != -99999) {
+ if (fvalue != -1) {
+ ZVAL_DOUBLE(retval, fvalue);
+ } else if (value != -99999) {
ZVAL_LONG(retval, value);
} else {
ZVAL_FALSE(retval);
@@ -4126,6 +4195,10 @@ void date_interval_write_property(zval *object, zval *member, zval *value, void
SET_VALUE_FROM_STRUCT(h, "h");
SET_VALUE_FROM_STRUCT(i, "i");
SET_VALUE_FROM_STRUCT(s, "s");
+ if (strcmp(Z_STRVAL_P(member), "f") == 0) {
+ obj->diff->f = zval_get_double(value);
+ break;
+ }
SET_VALUE_FROM_STRUCT(invert, "invert");
/* didn't find any */
(zend_get_std_object_handlers())->write_property(object, member, value, cache_slot);
@@ -4190,12 +4263,23 @@ static int php_date_interval_initialize_from_hash(zval **return_value, php_inter
} \
} while (0);
+#define PHP_DATE_INTERVAL_READ_PROPERTY_DOUBLE(element, member, def) \
+ do { \
+ zval *z_arg = zend_hash_str_find(myht, element, sizeof(element) - 1); \
+ if (z_arg) { \
+ (*intobj)->diff->member = (double)zval_get_double(z_arg); \
+ } else { \
+ (*intobj)->diff->member = (double)def; \
+ } \
+ } while (0);
+
PHP_DATE_INTERVAL_READ_PROPERTY("y", y, timelib_sll, -1)
PHP_DATE_INTERVAL_READ_PROPERTY("m", m, timelib_sll, -1)
PHP_DATE_INTERVAL_READ_PROPERTY("d", d, timelib_sll, -1)
PHP_DATE_INTERVAL_READ_PROPERTY("h", h, timelib_sll, -1)
PHP_DATE_INTERVAL_READ_PROPERTY("i", i, timelib_sll, -1)
PHP_DATE_INTERVAL_READ_PROPERTY("s", s, timelib_sll, -1)
+ PHP_DATE_INTERVAL_READ_PROPERTY_DOUBLE("f", f, -1)
PHP_DATE_INTERVAL_READ_PROPERTY("weekday", weekday, int, -1)
PHP_DATE_INTERVAL_READ_PROPERTY("weekday_behavior", weekday_behavior, int, -1)
PHP_DATE_INTERVAL_READ_PROPERTY("first_last_day_of", first_last_day_of, int, -1)
@@ -4276,7 +4360,8 @@ PHP_FUNCTION(date_interval_create_from_date_string)
static zend_string *date_interval_format(char *format, size_t format_len, timelib_rel_time *t)
{
smart_str string = {0};
- int i, length, have_format_spec = 0;
+ size_t i;
+ int length, have_format_spec = 0;
char buffer[33];
if (!format_len) {
@@ -4304,6 +4389,9 @@ static zend_string *date_interval_format(char *format, size_t format_len, timeli
case 'S': length = slprintf(buffer, 32, "%02" ZEND_LONG_FMT_SPEC, (zend_long) t->s); break;
case 's': length = slprintf(buffer, 32, ZEND_LONG_FMT, (zend_long) t->s); break;
+ case 'F': length = slprintf(buffer, 32, "%06" ZEND_LONG_FMT_SPEC, (zend_long) (t->f * 1000000)); break;
+ case 'f': length = slprintf(buffer, 32, ZEND_LONG_FMT, (zend_long) (t->f * 1000000)); break;
+
case 'a': {
if ((int) t->days != -99999) {
length = slprintf(buffer, 32, "%d", (int) t->days);
@@ -5063,7 +5151,7 @@ PHP_METHOD(DatePeriod, __set_state)
object_init_ex(return_value, date_ce_period);
period_obj = Z_PHPPERIOD_P(return_value);
if (!php_date_period_initialize_from_hash(period_obj, myht)) {
- php_error(E_ERROR, "Invalid serialization data for DatePeriod object");
+ zend_throw_error(NULL, "Invalid serialization data for DatePeriod object");
}
}
/* }}} */
@@ -5081,7 +5169,7 @@ PHP_METHOD(DatePeriod, __wakeup)
myht = Z_OBJPROP_P(object);
if (!php_date_period_initialize_from_hash(period_obj, myht)) {
- php_error(E_ERROR, "Invalid serialization data for DatePeriod object");
+ zend_throw_error(NULL, "Invalid serialization data for DatePeriod object");
}
}
/* }}} */
@@ -5091,7 +5179,8 @@ static zval *date_period_read_property(zval *object, zval *member, int type, voi
{
zval *zv;
if (type != BP_VAR_IS && type != BP_VAR_R) {
- php_error_docref(NULL, E_ERROR, "Retrieval of DatePeriod properties for modification is unsupported");
+ zend_throw_error(NULL, "Retrieval of DatePeriod properties for modification is unsupported");
+ return &EG(uninitialized_zval);
}
Z_OBJPROP_P(object); /* build properties hash table */
@@ -5109,7 +5198,7 @@ static zval *date_period_read_property(zval *object, zval *member, int type, voi
/* {{{ date_period_write_property */
static void date_period_write_property(zval *object, zval *member, zval *value, void **cache_slot)
{
- php_error_docref(NULL, E_ERROR, "Writing to DatePeriod properties is unsupported");
+ zend_throw_error(NULL, "Writing to DatePeriod properties is unsupported");
}
/* }}} */
diff --git a/ext/date/php_date.h b/ext/date/php_date.h
index 77ca7252c0..827d0b9deb 100644
--- a/ext/date/php_date.h
+++ b/ext/date/php_date.h
@@ -221,6 +221,7 @@ PHPAPI timelib_tzinfo *get_timezone_info(void);
/* Grabbing CE's so that other exts can use the date objects too */
PHPAPI zend_class_entry *php_date_get_date_ce(void);
PHPAPI zend_class_entry *php_date_get_immutable_ce(void);
+PHPAPI zend_class_entry *php_date_get_interface_ce(void);
PHPAPI zend_class_entry *php_date_get_timezone_ce(void);
/* Functions for creating DateTime objects, and initializing them from a string */
diff --git a/ext/date/tests/010.phpt b/ext/date/tests/010.phpt
index 8b39229703..ff42de5dea 100644
--- a/ext/date/tests/010.phpt
+++ b/ext/date/tests/010.phpt
@@ -8,7 +8,7 @@ date_default_timezone_set('UTC');
echo "Done\n";
?>
--EXPECTF--
-array(6) {
+array(5) {
[0]=>
array(3) {
["dst"]=>
@@ -16,18 +16,9 @@ array(6) {
["offset"]=>
int(0)
["timezone_id"]=>
- string(16) "Antarctica/Troll"
- }
- [1]=>
- array(3) {
- ["dst"]=>
- bool(false)
- ["offset"]=>
- int(0)
- ["timezone_id"]=>
string(13) "Etc/Universal"
}
- [2]=>
+ [1]=>
array(3) {
["dst"]=>
bool(false)
@@ -36,7 +27,7 @@ array(6) {
["timezone_id"]=>
string(7) "Etc/UTC"
}
- [3]=>
+ [2]=>
array(3) {
["dst"]=>
bool(false)
@@ -45,7 +36,7 @@ array(6) {
["timezone_id"]=>
string(8) "Etc/Zulu"
}
- [4]=>
+ [3]=>
array(3) {
["dst"]=>
bool(false)
@@ -54,7 +45,7 @@ array(6) {
["timezone_id"]=>
string(3) "UTC"
}
- [5]=>
+ [4]=>
array(3) {
["dst"]=>
bool(false)
diff --git a/ext/date/tests/DateTimeZone_getLocation.phpt b/ext/date/tests/DateTimeZone_getLocation.phpt
index 8e6e33bd17..2dec10ad57 100644
--- a/ext/date/tests/DateTimeZone_getLocation.phpt
+++ b/ext/date/tests/DateTimeZone_getLocation.phpt
@@ -1,6 +1,5 @@
--TEST--
-DateTimeZone::getLocation -- timezone_location_get — Returns location information for a timezone
-public array DateTimeZone::getLocation ( void ) ;
+DateTimeZone::getLocation -- timezone_location_get — Returns location information for a timezone public array DateTimeZone::getLocation ( void ) ;
--CREDITS--
marcosptf - <marcosptf@yahoo.com.br> - #phparty7 - @phpsp - novatec/2015 - sao paulo - br
--SKIPIF--
@@ -66,17 +65,3 @@ Array
[longitude] => %f
[comments] => %s
)
-Array
-(
- [country_code] => %s
- [latitude] => %f
- [longitude] => %f
- [comments] => %s
-)
-Array
-(
- [country_code] => %s
- [latitude] => %f
- [longitude] => %f
- [comments] =>
-)
diff --git a/ext/date/tests/DateTimeZone_listAbbreviations_basic1.phpt b/ext/date/tests/DateTimeZone_listAbbreviations_basic1.phpt
index e4a5dbd175..1cbaaef377 100644
--- a/ext/date/tests/DateTimeZone_listAbbreviations_basic1.phpt
+++ b/ext/date/tests/DateTimeZone_listAbbreviations_basic1.phpt
@@ -29,7 +29,7 @@ string(5) "array"
int(%d)
-- Format a sample entry --
-array(11) {
+array(17) {
[0]=>
array(3) {
["dst"]=>
@@ -44,20 +44,29 @@ array(11) {
["dst"]=>
bool(false)
["offset"]=>
- int(34200)
+ int(32400)
["timezone_id"]=>
string(18) "Australia/Adelaide"
}
[2]=>
array(3) {
["dst"]=>
+ bool(false)
+ ["offset"]=>
+ int(34200)
+ ["timezone_id"]=>
+ string(18) "Australia/Adelaide"
+ }
+ [3]=>
+ array(3) {
+ ["dst"]=>
bool(true)
["offset"]=>
int(-14400)
["timezone_id"]=>
string(16) "America/Eirunepe"
}
- [3]=>
+ [4]=>
array(3) {
["dst"]=>
bool(true)
@@ -66,7 +75,7 @@ array(11) {
["timezone_id"]=>
string(18) "America/Rio_Branco"
}
- [4]=>
+ [5]=>
array(3) {
["dst"]=>
bool(true)
@@ -75,7 +84,52 @@ array(11) {
["timezone_id"]=>
string(11) "Brazil/Acre"
}
- [5]=>
+ [6]=>
+ array(3) {
+ ["dst"]=>
+ bool(false)
+ ["offset"]=>
+ int(32400)
+ ["timezone_id"]=>
+ string(21) "Australia/Broken_Hill"
+ }
+ [7]=>
+ array(3) {
+ ["dst"]=>
+ bool(false)
+ ["offset"]=>
+ int(32400)
+ ["timezone_id"]=>
+ string(16) "Australia/Darwin"
+ }
+ [8]=>
+ array(3) {
+ ["dst"]=>
+ bool(false)
+ ["offset"]=>
+ int(32400)
+ ["timezone_id"]=>
+ string(15) "Australia/North"
+ }
+ [9]=>
+ array(3) {
+ ["dst"]=>
+ bool(false)
+ ["offset"]=>
+ int(32400)
+ ["timezone_id"]=>
+ string(15) "Australia/South"
+ }
+ [10]=>
+ array(3) {
+ ["dst"]=>
+ bool(false)
+ ["offset"]=>
+ int(32400)
+ ["timezone_id"]=>
+ string(20) "Australia/Yancowinna"
+ }
+ [11]=>
array(3) {
["dst"]=>
bool(false)
@@ -84,7 +138,7 @@ array(11) {
["timezone_id"]=>
string(13) "Asia/Jayapura"
}
- [6]=>
+ [12]=>
array(3) {
["dst"]=>
bool(false)
@@ -93,7 +147,7 @@ array(11) {
["timezone_id"]=>
string(21) "Australia/Broken_Hill"
}
- [7]=>
+ [13]=>
array(3) {
["dst"]=>
bool(false)
@@ -102,7 +156,7 @@ array(11) {
["timezone_id"]=>
string(16) "Australia/Darwin"
}
- [8]=>
+ [14]=>
array(3) {
["dst"]=>
bool(false)
@@ -111,7 +165,7 @@ array(11) {
["timezone_id"]=>
string(15) "Australia/North"
}
- [9]=>
+ [15]=>
array(3) {
["dst"]=>
bool(false)
@@ -120,7 +174,7 @@ array(11) {
["timezone_id"]=>
string(15) "Australia/South"
}
- [10]=>
+ [16]=>
array(3) {
["dst"]=>
bool(false)
diff --git a/ext/date/tests/DateTime_setTime_error.phpt b/ext/date/tests/DateTime_setTime_error.phpt
index eaf1555d1d..a6b5c5edea 100644
--- a/ext/date/tests/DateTime_setTime_error.phpt
+++ b/ext/date/tests/DateTime_setTime_error.phpt
@@ -25,7 +25,8 @@ echo "\n-- Testing DateTime::setTime() function with more than expected no. of a
$min = 15;
$sec = 30;
$extra_arg = 10;
-var_dump( $datetime->setTime($hour, $min, $sec, $extra_arg) );
+$microseconds = 123123;
+var_dump( $datetime->setTime($hour, $min, $sec, $microseconds, $extra_arg) );
?>
===DONE===
@@ -44,6 +45,6 @@ bool(false)
-- Testing DateTime::setTime() function with more than expected no. of arguments --
-Warning: DateTime::setTime() expects at most 3 parameters, 4 given in %s on line %d
+Warning: DateTime::setTime() expects at most 4 parameters, 5 given in %s on line %d
bool(false)
===DONE===
diff --git a/ext/date/tests/bug45682.phpt b/ext/date/tests/bug45682.phpt
index ea8fa94706..324f64867d 100644
--- a/ext/date/tests/bug45682.phpt
+++ b/ext/date/tests/bug45682.phpt
@@ -12,7 +12,7 @@ $diff = date_diff($date, $other);
var_dump($diff);
--EXPECTF--
-object(DateInterval)#%d (15) {
+object(DateInterval)#%d (16) {
["y"]=>
int(0)
["m"]=>
@@ -25,6 +25,8 @@ object(DateInterval)#%d (15) {
int(0)
["s"]=>
int(0)
+ ["f"]=>
+ float(0)
["weekday"]=>
int(0)
["weekday_behavior"]=>
diff --git a/ext/date/tests/bug48678.phpt b/ext/date/tests/bug48678.phpt
index 253cb84ce6..9565cb2dfb 100644
--- a/ext/date/tests/bug48678.phpt
+++ b/ext/date/tests/bug48678.phpt
@@ -15,6 +15,7 @@ DateInterval Object
[h] => 12
[i] => 30
[s] => 5
+ [f] => 0
[weekday] => 0
[weekday_behavior] => 0
[first_last_day_of] => 0
@@ -33,6 +34,7 @@ DateInterval Object
[h] => 12
[i] => 30
[s] => 5
+ [f] => 0
[weekday] => 0
[weekday_behavior] => 0
[first_last_day_of] => 0
diff --git a/ext/date/tests/bug49081.phpt b/ext/date/tests/bug49081.phpt
index 31f7351481..b5d17a6c26 100644
--- a/ext/date/tests/bug49081.phpt
+++ b/ext/date/tests/bug49081.phpt
@@ -17,6 +17,7 @@ DateInterval Object
[h] => 4
[i] => 0
[s] => 0
+ [f] => 0
[weekday] => 0
[weekday_behavior] => 0
[first_last_day_of] => 0
diff --git a/ext/date/tests/bug49778.phpt b/ext/date/tests/bug49778.phpt
index 2062d69168..56ce135179 100644
--- a/ext/date/tests/bug49778.phpt
+++ b/ext/date/tests/bug49778.phpt
@@ -7,8 +7,8 @@ var_dump($i);
echo $i->format("%d"), "\n";
echo $i->format("%a"), "\n";
?>
---EXPECT--
-object(DateInterval)#1 (15) {
+--EXPECTF--
+object(DateInterval)#%d (16) {
["y"]=>
int(0)
["m"]=>
@@ -21,6 +21,8 @@ object(DateInterval)#1 (15) {
int(0)
["s"]=>
int(0)
+ ["f"]=>
+ float(0)
["weekday"]=>
int(0)
["weekday_behavior"]=>
diff --git a/ext/date/tests/bug52113.phpt b/ext/date/tests/bug52113.phpt
index 62c2fca94e..dfc7d6112d 100644
--- a/ext/date/tests/bug52113.phpt
+++ b/ext/date/tests/bug52113.phpt
@@ -23,6 +23,7 @@ $unser = DateInterval::__set_state(array(
'h' => 4,
'i' => 3,
's' => 2,
+ 'f' => 0.876543,
'invert' => 1,
'days' => 2400,
));
@@ -32,7 +33,7 @@ var_dump($unser, $p);
?>
--EXPECT--
-object(DateInterval)#3 (15) {
+object(DateInterval)#3 (16) {
["y"]=>
int(0)
["m"]=>
@@ -45,6 +46,8 @@ object(DateInterval)#3 (15) {
int(0)
["s"]=>
int(0)
+ ["f"]=>
+ float(0)
["weekday"]=>
int(0)
["weekday_behavior"]=>
@@ -64,7 +67,7 @@ object(DateInterval)#3 (15) {
["have_special_relative"]=>
int(0)
}
-string(320) "O:12:"DateInterval":15:{s:1:"y";i:0;s:1:"m";i:0;s:1:"d";i:0;s:1:"h";i:4;s:1:"i";i:0;s:1:"s";i:0;s:7:"weekday";i:0;s:16:"weekday_behavior";i:0;s:17:"first_last_day_of";i:0;s:6:"invert";i:0;s:4:"days";i:0;s:12:"special_type";i:0;s:14:"special_amount";i:0;s:21:"have_weekday_relative";i:0;s:21:"have_special_relative";i:0;}"
+string(332) "O:12:"DateInterval":16:{s:1:"y";i:0;s:1:"m";i:0;s:1:"d";i:0;s:1:"h";i:4;s:1:"i";i:0;s:1:"s";i:0;s:1:"f";d:0;s:7:"weekday";i:0;s:16:"weekday_behavior";i:0;s:17:"first_last_day_of";i:0;s:6:"invert";i:0;s:4:"days";i:0;s:12:"special_type";i:0;s:14:"special_amount";i:0;s:21:"have_weekday_relative";i:0;s:21:"have_special_relative";i:0;}"
DateInterval::__set_state(array(
'y' => 0,
'm' => 0,
@@ -72,6 +75,7 @@ DateInterval::__set_state(array(
'h' => 4,
'i' => 0,
's' => 0,
+ 'f' => 0.0,
'weekday' => 0,
'weekday_behavior' => 0,
'first_last_day_of' => 0,
@@ -81,7 +85,7 @@ DateInterval::__set_state(array(
'special_amount' => 0,
'have_weekday_relative' => 0,
'have_special_relative' => 0,
-))object(DateInterval)#5 (15) {
+))object(DateInterval)#5 (16) {
["y"]=>
int(0)
["m"]=>
@@ -94,6 +98,8 @@ DateInterval::__set_state(array(
int(0)
["s"]=>
int(0)
+ ["f"]=>
+ float(0)
["weekday"]=>
int(0)
["weekday_behavior"]=>
@@ -128,7 +134,7 @@ object(DatePeriod)#6 (6) {
["end"]=>
NULL
["interval"]=>
- object(DateInterval)#7 (15) {
+ object(DateInterval)#7 (16) {
["y"]=>
int(0)
["m"]=>
@@ -141,6 +147,8 @@ object(DatePeriod)#6 (6) {
int(0)
["s"]=>
int(0)
+ ["f"]=>
+ float(0)
["weekday"]=>
int(0)
["weekday_behavior"]=>
@@ -165,7 +173,7 @@ object(DatePeriod)#6 (6) {
["include_start_date"]=>
bool(true)
}
-object(DateInterval)#8 (15) {
+object(DateInterval)#8 (16) {
["y"]=>
int(7)
["m"]=>
@@ -178,6 +186,8 @@ object(DateInterval)#8 (15) {
int(3)
["s"]=>
int(2)
+ ["f"]=>
+ float(0.876543)
["weekday"]=>
int(-1)
["weekday_behavior"]=>
@@ -212,7 +222,7 @@ object(DatePeriod)#9 (6) {
["end"]=>
NULL
["interval"]=>
- object(DateInterval)#7 (15) {
+ object(DateInterval)#7 (16) {
["y"]=>
int(0)
["m"]=>
@@ -225,6 +235,8 @@ object(DatePeriod)#9 (6) {
int(0)
["s"]=>
int(0)
+ ["f"]=>
+ float(0)
["weekday"]=>
int(0)
["weekday_behavior"]=>
diff --git a/ext/date/tests/bug52738.phpt b/ext/date/tests/bug52738.phpt
index ea219f7c7c..6ed72af1c1 100644
--- a/ext/date/tests/bug52738.phpt
+++ b/ext/date/tests/bug52738.phpt
@@ -27,6 +27,7 @@ di Object
[h] => 0
[i] => 0
[s] => 0
+ [f] => 0
[weekday] => 0
[weekday_behavior] => 0
[first_last_day_of] => 0
diff --git a/ext/date/tests/bug52808.phpt b/ext/date/tests/bug52808.phpt
index 1f0fc84cd7..810874858f 100644
--- a/ext/date/tests/bug52808.phpt
+++ b/ext/date/tests/bug52808.phpt
@@ -25,7 +25,7 @@ foreach($intervals as $iv) {
echo "==DONE==\n";
?>
--EXPECTF--
-object(DateInterval)#%d (15) {
+object(DateInterval)#%d (16) {
["y"]=>
int(1)
["m"]=>
@@ -38,6 +38,8 @@ object(DateInterval)#%d (15) {
int(30)
["s"]=>
int(0)
+ ["f"]=>
+ float(0)
["weekday"]=>
int(0)
["weekday_behavior"]=>
@@ -57,7 +59,7 @@ object(DateInterval)#%d (15) {
["have_special_relative"]=>
int(0)
}
-object(DateInterval)#%d (15) {
+object(DateInterval)#%d (16) {
["y"]=>
int(0)
["m"]=>
@@ -70,6 +72,8 @@ object(DateInterval)#%d (15) {
int(30)
["s"]=>
int(0)
+ ["f"]=>
+ float(0)
["weekday"]=>
int(0)
["weekday_behavior"]=>
@@ -89,7 +93,7 @@ object(DateInterval)#%d (15) {
["have_special_relative"]=>
int(0)
}
-object(DateInterval)#%d (15) {
+object(DateInterval)#%d (16) {
["y"]=>
int(0)
["m"]=>
@@ -102,6 +106,8 @@ object(DateInterval)#%d (15) {
int(30)
["s"]=>
int(0)
+ ["f"]=>
+ float(0)
["weekday"]=>
int(0)
["weekday_behavior"]=>
diff --git a/ext/date/tests/bug53437.phpt b/ext/date/tests/bug53437.phpt
index f82a4879b3..0be9691a14 100644
--- a/ext/date/tests/bug53437.phpt
+++ b/ext/date/tests/bug53437.phpt
@@ -51,7 +51,7 @@ object(DatePeriod)#1 (6) {
["end"]=>
NULL
["interval"]=>
- object(DateInterval)#5 (15) {
+ object(DateInterval)#5 (16) {
["y"]=>
int(0)
["m"]=>
@@ -64,6 +64,8 @@ object(DatePeriod)#1 (6) {
int(0)
["s"]=>
int(0)
+ ["f"]=>
+ float(0)
["weekday"]=>
int(0)
["weekday_behavior"]=>
@@ -110,7 +112,7 @@ object(DatePeriod)#5 (6) {
["end"]=>
NULL
["interval"]=>
- object(DateInterval)#8 (15) {
+ object(DateInterval)#8 (16) {
["y"]=>
int(0)
["m"]=>
@@ -123,6 +125,8 @@ object(DatePeriod)#5 (6) {
int(0)
["s"]=>
int(0)
+ ["f"]=>
+ float(0)
["weekday"]=>
int(0)
["weekday_behavior"]=>
diff --git a/ext/date/tests/bug53437_var1.phpt b/ext/date/tests/bug53437_var1.phpt
index f1f9843d5e..938439abe8 100644
--- a/ext/date/tests/bug53437_var1.phpt
+++ b/ext/date/tests/bug53437_var1.phpt
@@ -10,4 +10,9 @@ var_dump($dp);
?>
==DONE==
--EXPECTF--
-Fatal error: Invalid serialization data for DatePeriod object in %sbug53437_var1.php on line %d
+Fatal error: Uncaught Error: Invalid serialization data for DatePeriod object in %sbug53437_var1.php:%d
+Stack trace:
+#0 [internal function]: DatePeriod->__wakeup()
+#1 %sbug53437_var1.php(%d): unserialize('O:10:"DatePerio...')
+#2 {main}
+ thrown in %sbug53437_var1.php on line %d
diff --git a/ext/date/tests/bug53437_var2.phpt b/ext/date/tests/bug53437_var2.phpt
index 50aebda57c..2ef21e738e 100644
--- a/ext/date/tests/bug53437_var2.phpt
+++ b/ext/date/tests/bug53437_var2.phpt
@@ -13,7 +13,7 @@ var_dump($di0, $di1);
?>
==DONE==
--EXPECT--
-object(DateInterval)#1 (15) {
+object(DateInterval)#1 (16) {
["y"]=>
int(2)
["m"]=>
@@ -26,6 +26,8 @@ object(DateInterval)#1 (15) {
int(8)
["s"]=>
int(0)
+ ["f"]=>
+ float(0)
["weekday"]=>
int(0)
["weekday_behavior"]=>
@@ -45,7 +47,7 @@ object(DateInterval)#1 (15) {
["have_special_relative"]=>
int(0)
}
-object(DateInterval)#2 (15) {
+object(DateInterval)#2 (16) {
["y"]=>
int(2)
["m"]=>
@@ -58,6 +60,8 @@ object(DateInterval)#2 (15) {
int(8)
["s"]=>
int(0)
+ ["f"]=>
+ float(0)
["weekday"]=>
int(0)
["weekday_behavior"]=>
diff --git a/ext/date/tests/bug53437_var3.phpt b/ext/date/tests/bug53437_var3.phpt
index 82b90f559b..8f48b1b644 100644
--- a/ext/date/tests/bug53437_var3.phpt
+++ b/ext/date/tests/bug53437_var3.phpt
@@ -12,7 +12,7 @@ var_dump($di);
?>
==DONE==
--EXPECTF--
-object(DateInterval)#%d (15) {
+object(DateInterval)#%d (16) {
["y"]=>
int(2)
["m"]=>
@@ -43,5 +43,7 @@ object(DateInterval)#%d (15) {
int(9)
["have_special_relative"]=>
int(0)
+ ["f"]=>
+ float(-1)
}
==DONE==
diff --git a/ext/date/tests/bug53437_var4.phpt b/ext/date/tests/bug53437_var4.phpt
index 88fd81c9db..189c15cb1b 100644
--- a/ext/date/tests/bug53437_var4.phpt
+++ b/ext/date/tests/bug53437_var4.phpt
@@ -15,13 +15,14 @@ var_dump($df,
$df->h,
$df->i,
$df->s,
+ $df->f,
$df->invert,
$df->days);
?>
==DONE==
--EXPECTF--
-object(DateInterval)#%d (15) {
+object(DateInterval)#%d (16) {
["y"]=>
int(0)
["m"]=>
@@ -34,6 +35,8 @@ object(DateInterval)#%d (15) {
int(0)
["s"]=>
int(0)
+ ["f"]=>
+ float(0)
["weekday"]=>
int(0)
["weekday_behavior"]=>
@@ -59,6 +62,7 @@ int(2)
int(0)
int(0)
int(0)
+float(0)
int(0)
int(2)
==DONE==
diff --git a/ext/date/tests/bug53437_var5.phpt b/ext/date/tests/bug53437_var5.phpt
index e95fcdae96..38783b1545 100644
--- a/ext/date/tests/bug53437_var5.phpt
+++ b/ext/date/tests/bug53437_var5.phpt
@@ -12,7 +12,7 @@ var_dump($di);
?>
==DONE==
--EXPECTF--
-object(DateInterval)#%d (15) {
+object(DateInterval)#%d (16) {
["y"]=>
int(2)
["m"]=>
@@ -43,5 +43,7 @@ object(DateInterval)#%d (15) {
int(9)
["have_special_relative"]=>
int(0)
+ ["f"]=>
+ float(-1)
}
==DONE==
diff --git a/ext/date/tests/bug53437_var6.phpt b/ext/date/tests/bug53437_var6.phpt
new file mode 100644
index 0000000000..633fcb3f38
--- /dev/null
+++ b/ext/date/tests/bug53437_var6.phpt
@@ -0,0 +1,49 @@
+--TEST--
+Bug #53437 DateInterval unserialize bad data, 64 bit
+--SKIPIF--
+<?php if (PHP_INT_SIZE != 8) { die('skip true 64 bit only'); } ?>
+--FILE--
+<?php
+$s = 'O:12:"DateInterval":16:{s:1:"y";s:1:"2";s:1:"m";s:1:"0";s:1:"d";s:3:"bla";s:1:"h";s:1:"6";s:1:"i";s:1:"8";s:1:"s";s:1:"0";s:1:"f";d:0.123654;s:7:"weekday";i:10;s:16:"weekday_behavior";i:10;s:17:"first_last_day_of";i:0;s:6:"invert";i:0;s:4:"days";s:4:"aoeu";s:12:"special_type";i:0;s:14:"special_amount";s:21:"234523452345234532455";s:21:"have_weekday_relative";i:21474836489;s:21:"have_special_relative";s:3:"bla";}';
+
+$di = unserialize($s);
+var_dump($di);
+
+?>
+==DONE==
+--EXPECTF--
+object(DateInterval)#%d (16) {
+ ["y"]=>
+ int(2)
+ ["m"]=>
+ int(0)
+ ["d"]=>
+ int(0)
+ ["h"]=>
+ int(6)
+ ["i"]=>
+ int(8)
+ ["s"]=>
+ int(0)
+ ["f"]=>
+ float(0.123654)
+ ["weekday"]=>
+ int(10)
+ ["weekday_behavior"]=>
+ int(10)
+ ["first_last_day_of"]=>
+ int(0)
+ ["invert"]=>
+ int(0)
+ ["days"]=>
+ int(0)
+ ["special_type"]=>
+ int(0)
+ ["special_amount"]=>
+ int(9223372036854775807)
+ ["have_weekday_relative"]=>
+ int(9)
+ ["have_special_relative"]=>
+ int(0)
+}
+==DONE==
diff --git a/ext/date/tests/bug55397.phpt b/ext/date/tests/bug55397.phpt
index 7c9bbb01c1..2ce1257710 100644
--- a/ext/date/tests/bug55397.phpt
+++ b/ext/date/tests/bug55397.phpt
@@ -7,4 +7,9 @@ date_default_timezone_set('Europe/Prague');
var_dump(unserialize('O:8:"DateTime":0:{}') == new DateTime);
?>
--EXPECTF--
-Fatal error: Invalid serialization data for DateTime object in %sbug55397.php on line %d
+Fatal error: Uncaught Error: Invalid serialization data for DateTime object in %sbug55397.php:%d
+Stack trace:
+#0 [internal function]: DateTime->__wakeup()
+#1 %sbug55397.php(%d): unserialize('O:8:"DateTime":...')
+#2 {main}
+ thrown in %sbug55397.php on line %d
diff --git a/ext/date/tests/bug60774.phpt b/ext/date/tests/bug60774.phpt
index 7045cd7781..0a9c4223c3 100644
--- a/ext/date/tests/bug60774.phpt
+++ b/ext/date/tests/bug60774.phpt
@@ -21,6 +21,8 @@ object(DateInterval)#1 (%d) {
int(0)
["s"]=>
int(0)
+ ["f"]=>
+ float(0)
["weekday"]=>
int(0)
["weekday_behavior"]=>
diff --git a/ext/date/tests/bug62852.phpt b/ext/date/tests/bug62852.phpt
index 7013a3f97c..a1b5190281 100644
--- a/ext/date/tests/bug62852.phpt
+++ b/ext/date/tests/bug62852.phpt
@@ -11,4 +11,9 @@ try {
} catch ( Exception $e ) {}
--EXPECTF--
-Fatal error: Invalid serialization data for DateTime object in %sbug62852.php on line %d
+Fatal error: Uncaught Error: Invalid serialization data for DateTime object in %sbug62852.php:%d
+Stack trace:
+#0 [internal function]: DateTime->__wakeup()
+#1 %sbug62852.php(%d): unserialize('O:8:"DateTime":...')
+#2 {main}
+ thrown in %sbug62852.php on line %d
diff --git a/ext/date/tests/bug62852_var2.phpt b/ext/date/tests/bug62852_var2.phpt
index f93ba28ab1..9d742d9363 100644
--- a/ext/date/tests/bug62852_var2.phpt
+++ b/ext/date/tests/bug62852_var2.phpt
@@ -22,4 +22,10 @@ try {
var_dump( $foo );
--EXPECTF--
-Fatal error: Invalid serialization data for DateTime object in %sbug62852_var2.php on line %d
+Fatal error: Uncaught Error: Invalid serialization data for DateTime object in %sbug62852_var2.php:%d
+Stack trace:
+#0 %sbug62852_var2.php(%d): DateTime->__wakeup()
+#1 [internal function]: Foo->__wakeup()
+#2 %sbug62852_var2.php(%d): unserialize('O:3:"Foo":3:{s:...')
+#3 {main}
+ thrown in %sbug62852_var2.php on line %d
diff --git a/ext/date/tests/bug62852_var3.phpt b/ext/date/tests/bug62852_var3.phpt
index 5a644b5470..bef8d4ec6b 100644
--- a/ext/date/tests/bug62852_var3.phpt
+++ b/ext/date/tests/bug62852_var3.phpt
@@ -22,4 +22,10 @@ try {
var_dump( $foo );
--EXPECTF--
-Fatal error: Invalid serialization data for DateTime object in %sbug62852_var3.php on line %d
+Fatal error: Uncaught Error: Invalid serialization data for DateTime object in %sbug62852_var3.php:%d
+Stack trace:
+#0 %sbug62852_var3.php(%d): DateTime->__wakeup()
+#1 [internal function]: Foo->__wakeup()
+#2 %sbug62852_var3.php(%d): unserialize('O:3:"Foo":3:{s:...')
+#3 {main}
+ thrown in %sbug62852_var3.php on line %d
diff --git a/ext/date/tests/bug64887.phpt b/ext/date/tests/bug64887.phpt
new file mode 100644
index 0000000000..dba1921eac
--- /dev/null
+++ b/ext/date/tests/bug64887.phpt
@@ -0,0 +1,46 @@
+--TEST--
+Bug #64887: Allow DateTime modification with subsecond items
+--INI--
+date.timezone=UTC
+--FILE--
+<?php
+$tests = [
+ '+1 ms',
+ '-2 msec',
+ '+3 msecs',
+ '-4 millisecond',
+ '+5 milliseconds',
+
+ '-6 usec',
+ '+7 usecs',
+ '-8 microsecond',
+ '+9 microseconds',
+ '-10 µs',
+ '+11 µsec',
+ '-12 µsecs',
+
+ '+8 msec -2 µsec',
+];
+
+$datetime = new DateTimeImmutable( "2016-10-07 13:25:50" );
+
+foreach ( $tests as $test )
+{
+ echo $datetime->modify( $test )->format( 'Y-m-d H:i:s.u' ), "\n";
+}
+
+?>
+--EXPECT--
+2016-10-07 13:25:50.001000
+2016-10-07 13:25:49.998000
+2016-10-07 13:25:50.003000
+2016-10-07 13:25:49.996000
+2016-10-07 13:25:50.005000
+2016-10-07 13:25:49.999994
+2016-10-07 13:25:50.000007
+2016-10-07 13:25:49.999992
+2016-10-07 13:25:50.000009
+2016-10-07 13:25:49.999990
+2016-10-07 13:25:50.000011
+2016-10-07 13:25:49.999988
+2016-10-07 13:25:50.007998
diff --git a/ext/date/tests/bug66721.phpt b/ext/date/tests/bug66721.phpt
index 4806712437..9effb7ca56 100644
--- a/ext/date/tests/bug66721.phpt
+++ b/ext/date/tests/bug66721.phpt
@@ -8,4 +8,9 @@ $y = 'O:8:"DateTime":3:{s:4:"date";s:19:"2014-02-15 02:00:51";s:13:"timezone_typ
var_dump(unserialize($y));
?>
--EXPECTF--
-Fatal error: Invalid serialization data for DateTime object in %s on line %d
+Fatal error: Uncaught Error: Invalid serialization data for DateTime object in %sbug66721.php:%d
+Stack trace:
+#0 [internal function]: DateTime->__wakeup()
+#1 %sbug66721.php(%d): unserialize('O:8:"DateTime":...')
+#2 {main}
+ thrown in %sbug66721.php on line %d
diff --git a/ext/date/tests/bug68942.phpt b/ext/date/tests/bug68942.phpt
index a26ce8867b..9a9a5cfb88 100644
--- a/ext/date/tests/bug68942.phpt
+++ b/ext/date/tests/bug68942.phpt
@@ -6,4 +6,9 @@ $data = unserialize('a:2:{i:0;O:12:"DateTimeZone":2:{s:13:"timezone_type";a:2:{i
var_dump($data);
?>
--EXPECTF--
-Fatal error: DateTimeZone::__wakeup(): Timezone initialization failed in %s%ebug68942.php on line %d
+Fatal error: Uncaught Error: Timezone initialization failed in %s:%d
+Stack trace:
+#0 [internal function]: DateTimeZone->__wakeup()
+#1 %s(%d): unserialize('a:2:{i:0;O:12:"...')
+#2 {main}
+ thrown in %s on line %d
diff --git a/ext/date/tests/bug68942_2.phpt b/ext/date/tests/bug68942_2.phpt
index 54ffdb535e..9870bbce5c 100644
--- a/ext/date/tests/bug68942_2.phpt
+++ b/ext/date/tests/bug68942_2.phpt
@@ -6,4 +6,9 @@ $data = unserialize('a:2:{i:0;O:8:"DateTime":3:{s:4:"date";s:26:"2000-01-01 00:0
var_dump($data);
?>
--EXPECTF--
-Fatal error: Invalid serialization data for DateTime object in %s%ebug68942_2.php on line %d
+Fatal error: Uncaught Error: Invalid serialization data for DateTime object in %sbug68942_2.php:%d
+Stack trace:
+#0 [internal function]: DateTime->__wakeup()
+#1 %sbug68942_2.php(%d): unserialize('a:2:{i:0;O:8:"D...')
+#2 {main}
+ thrown in %sbug68942_2.php on line %d
diff --git a/ext/date/tests/bug73091.phpt b/ext/date/tests/bug73091.phpt
index 668ef505d8..c051573297 100644
--- a/ext/date/tests/bug73091.phpt
+++ b/ext/date/tests/bug73091.phpt
@@ -12,7 +12,7 @@ class foo {
var_dump(unserialize('O:12:"DateInterval":1:{s:4:"days";O:3:"foo":0:{}}'));
?>
--EXPECTF--
-object(DateInterval)#%d (15) {
+object(DateInterval)#%d (16) {
["days"]=>
int(-1)
["y"]=>
@@ -27,6 +27,8 @@ object(DateInterval)#%d (15) {
int(-1)
["s"]=>
int(-1)
+ ["f"]=>
+ float(-1)
["weekday"]=>
int(-1)
["weekday_behavior"]=>
diff --git a/ext/date/tests/bug73426.phpt b/ext/date/tests/bug73426.phpt
new file mode 100644
index 0000000000..6f3b19e05d
--- /dev/null
+++ b/ext/date/tests/bug73426.phpt
@@ -0,0 +1,32 @@
+--TEST--
+Bug #73426 (createFromFormat with 'z' format char results in incorrect time)
+--INI--
+date.timezone=UTC
+--FILE--
+<?php
+$date = '2016 12:00:00 15';
+$format = 'Y H:i:s z';
+var_dump(DateTime::createFromFormat($format, $date));
+
+$date = '16 12:00:00 2016';
+$format = 'z H:i:s Y';
+var_dump(DateTime::createFromFormat($format, $date));
+
+?>
+--EXPECTF--
+object(DateTime)#%d (%d) {
+ ["date"]=>
+ string(26) "2016-01-16 12:00:00.000000"
+ ["timezone_type"]=>
+ int(3)
+ ["timezone"]=>
+ string(3) "UTC"
+}
+object(DateTime)#%d (%d) {
+ ["date"]=>
+ string(26) "2016-01-17 12:00:00.000000"
+ ["timezone_type"]=>
+ int(3)
+ ["timezone"]=>
+ string(3) "UTC"
+}
diff --git a/ext/date/tests/bug73837.phpt b/ext/date/tests/bug73837.phpt
new file mode 100644
index 0000000000..b14feecd93
--- /dev/null
+++ b/ext/date/tests/bug73837.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Bug #73837: Milliseconds in DateTime()
+--FILE--
+<?php
+$collect = [];
+
+for ( $i = 0; $i < 1000; $i++ )
+{
+ $a = new DateTime();
+ $key = "s" . $a->format( "u" );
+ $collect[$key] = true;
+}
+
+var_dump($n = count( $collect ));
+echo ( $n > 700 ) ? "microseconds differ\n" : "microseconds do not differ enough ($n)\n";
+?>
+--EXPECTF--
+int(%d)
+microseconds differ
diff --git a/ext/date/tests/bug74639.phpt b/ext/date/tests/bug74639.phpt
new file mode 100644
index 0000000000..43eccc974d
--- /dev/null
+++ b/ext/date/tests/bug74639.phpt
@@ -0,0 +1,33 @@
+--TEST--
+Bug #74639 Cloning DatePeriod leads to segfault
+--FILE--
+<?php
+
+$start = new DateTime('2017-05-22 09:00:00');
+$end = new DateTime('2017-08-24 18:00:00');
+$interval = $start->diff($end);
+
+$period = new DatePeriod($start, $interval, $end);
+$clonedPeriod = clone $period;
+$clonedInterval = clone $interval;
+
+if ($period->getStartDate() != $clonedPeriod->getStartDate()) {
+ echo "failure\n";
+}
+
+if ($period->getEndDate() != $clonedPeriod->getEndDate()) {
+ echo "failure\n";
+}
+
+if ($period->getDateInterval() != $clonedPeriod->getDateInterval()) {
+ echo "failure\n";
+}
+
+if ($interval->format('Y-m-d H:i:s') != $clonedInterval->format('Y-m-d H:i:s')) {
+ echo "failure\n";
+}
+
+echo 'success';
+?>
+--EXPECT--
+success
diff --git a/ext/date/tests/bug74652.phpt b/ext/date/tests/bug74652.phpt
new file mode 100644
index 0000000000..029464be4c
--- /dev/null
+++ b/ext/date/tests/bug74652.phpt
@@ -0,0 +1,30 @@
+--TEST--
+Test for bug #74652: Incomplete dates
+--INI--
+date.timezone=UTC
+--FILE--
+<?php
+$formats = [
+ '2017-03-25 10:52:09',
+ '2017-03-25 10:52',
+ '2017-03-25 10am',
+ '2017-03-25',
+ '2017-03',
+ '2017.042',
+ '2017043',
+];
+
+foreach ( $formats as $format )
+{
+ $dt = new DateTimeImmutable( $format );
+ echo $dt->format( 'Y-m-d H:i:s' ), "\n";
+}
+?>
+--EXPECT--
+2017-03-25 10:52:09
+2017-03-25 10:52:00
+2017-03-25 10:00:00
+2017-03-25 00:00:00
+2017-03-01 00:00:00
+2017-02-11 00:00:00
+2017-02-12 00:00:00
diff --git a/ext/date/tests/date_diff1.phpt b/ext/date/tests/date_diff1.phpt
index fefffcde52..2b73eae6a8 100644
--- a/ext/date/tests/date_diff1.phpt
+++ b/ext/date/tests/date_diff1.phpt
@@ -11,7 +11,7 @@ var_dump($start);
var_dump($end);
var_dump($int);
?>
---EXPECT--
+--EXPECTF--
object(DateTime)#1 (3) {
["date"]=>
string(26) "2010-10-04 02:18:48.000000"
@@ -28,7 +28,7 @@ object(DateTime)#2 (3) {
["timezone"]=>
string(3) "EDT"
}
-object(DateInterval)#3 (15) {
+object(DateInterval)#%d (16) {
["y"]=>
int(0)
["m"]=>
@@ -41,6 +41,8 @@ object(DateInterval)#3 (15) {
int(19)
["s"]=>
int(40)
+ ["f"]=>
+ float(0)
["weekday"]=>
int(0)
["weekday_behavior"]=>
diff --git a/ext/date/tests/date_interval_create_from_date_string_nullparam.phpt b/ext/date/tests/date_interval_create_from_date_string_nullparam.phpt
index 369db5714a..526b474e47 100644
--- a/ext/date/tests/date_interval_create_from_date_string_nullparam.phpt
+++ b/ext/date/tests/date_interval_create_from_date_string_nullparam.phpt
@@ -8,7 +8,7 @@ $i = date_interval_create_from_date_string(null); //returns a empty object
var_dump($i);
?>
--EXPECTF--
-object(DateInterval)#1 (15) {
+object(DateInterval)#%d (16) {
["y"]=>
int(0)
["m"]=>
@@ -21,6 +21,8 @@ object(DateInterval)#1 (15) {
int(0)
["s"]=>
int(0)
+ ["f"]=>
+ float(0)
["weekday"]=>
int(0)
["weekday_behavior"]=>
@@ -39,4 +41,4 @@ object(DateInterval)#1 (15) {
int(0)
["have_special_relative"]=>
int(0)
-} \ No newline at end of file
+}
diff --git a/ext/date/tests/date_interval_create_from_date_string_wrongparam_002.phpt b/ext/date/tests/date_interval_create_from_date_string_wrongparam_002.phpt
index a3407c3967..89cea6abf3 100644
--- a/ext/date/tests/date_interval_create_from_date_string_wrongparam_002.phpt
+++ b/ext/date/tests/date_interval_create_from_date_string_wrongparam_002.phpt
@@ -1,9 +1,8 @@
--TEST--
Test date_interval_create_from_date_string() function : with 2 parameters (wrong).
-Rodrigo Prado de Jesus <royopa [at] gmail [dot] com>
--FILE--
<?php
$i = date_interval_create_from_date_string('1 year', 'wrong');
?>
--EXPECTF--
-Warning: date_interval_create_from_date_string() expects exactly 1 parameter, 2 given in %s on line %d \ No newline at end of file
+Warning: date_interval_create_from_date_string() expects exactly 1 parameter, 2 given in %s on line %d
diff --git a/ext/date/tests/date_sun_info_003.phpt b/ext/date/tests/date_sun_info_003.phpt
new file mode 100644
index 0000000000..3ff7a06df1
--- /dev/null
+++ b/ext/date/tests/date_sun_info_003.phpt
@@ -0,0 +1,42 @@
+--TEST--
+Test basic date_sun_info()
+--CREDITS--
+edgarsandi - <edgar.r.sandi@gmail.com>
+--FILE--
+<?php
+date_default_timezone_set('America/Sao_Paulo');
+$sun_info = date_sun_info(strtotime("2015-01-12 00:00:00 UTC"), 89.00, 1.00);
+foreach ($sun_info as $key => $elem ) {
+ echo "$key: " . date("H:i:s", $elem) . "\n";
+}
+
+echo "\n";
+
+$sun_info = date_sun_info(strtotime("2015-09-12 00:00:00 UTC"), 89.00, 1.00);
+foreach ($sun_info as $key => $elem ) {
+ echo "$key: " . date("H:i:s", $elem) . "\n";
+}
+
+echo "Done\n";
+?>
+--EXPECTF--
+sunrise: 21:00:00
+sunset: 21:00:00
+transit: 10:04:02
+civil_twilight_begin: 21:00:00
+civil_twilight_end: 21:00:00
+nautical_twilight_begin: 21:00:00
+nautical_twilight_end: 21:00:00
+astronomical_twilight_begin: 21:00:00
+astronomical_twilight_end: 21:00:00
+
+sunrise: 21:00:01
+sunset: 21:00:01
+transit: 08:52:30
+civil_twilight_begin: 21:00:01
+civil_twilight_end: 21:00:01
+nautical_twilight_begin: 21:00:01
+nautical_twilight_end: 21:00:01
+astronomical_twilight_begin: 21:00:01
+astronomical_twilight_end: 21:00:01
+Done \ No newline at end of file
diff --git a/ext/date/tests/date_time_fractions.phpt b/ext/date/tests/date_time_fractions.phpt
new file mode 100644
index 0000000000..c58ed4f431
--- /dev/null
+++ b/ext/date/tests/date_time_fractions.phpt
@@ -0,0 +1,99 @@
+--TEST--
+Fractions with DateTime objects
+--INI--
+date.timezone=UTC
+--FILE--
+<?php
+/* This will go wrong, once in a million times */
+$ms = date_create()->format('u');
+echo ($ms = 0) ? "microseconds = false\n" : "microseconds = true\n";
+
+/* Normal creation */
+echo date_create( "2016-10-03 12:47:18.819313" )->format( "Y-m-d H:i:s.u" ), "\n\n";
+
+/* With modifications */
+$dt = new DateTimeImmutable( "2016-10-03 12:47:18.819210" );
+echo $dt->modify( "+1 day" )->format( "Y-m-d H:i:s.u" ), "\n";
+
+$dt = new DateTimeImmutable( "2016-10-03 12:47:18.081921" );
+echo $dt->modify( "-3 months" )->format( "Y-m-d H:i:s.u" ), "\n";
+
+echo "\n";
+
+/* These should reset the time (and hence fraction) to 0 */
+$dt = new DateTimeImmutable( "2016-10-03 12:47:18.081921" );
+echo $dt->modify( "yesterday" )->format( "Y-m-d H:i:s.u" ), "\n";
+
+$dt = new DateTimeImmutable( "2016-10-03 12:47:18.081921" );
+echo $dt->modify( "noon" )->format( "Y-m-d H:i:s.u" ), "\n";
+
+$dt = new DateTimeImmutable( "2016-10-03 12:47:18.081921" );
+echo $dt->modify( "10 weekday" )->format( "Y-m-d H:i:s.u" ), "\n";
+
+/* Interval containing fractions */
+
+$dt1 = new DateTimeImmutable( "2016-10-03 13:20:07.103123" );
+$dt2 = new DateTimeImmutable( "2016-10-03 13:20:07.481312" );
+$diff = $dt1->diff( $dt2 );
+
+var_dump( $diff );
+
+$dt0 = $dt1->sub( $diff );
+$dt3 = $dt2->add( $diff );
+$dt4 = $dt3->add( $diff );
+
+echo $dt0->format( "Y-m-d H:i:s.u" ), "\n";
+echo $dt1->format( "Y-m-d H:i:s.u" ), "\n";
+echo $dt2->format( "Y-m-d H:i:s.u" ), "\n";
+echo $dt3->format( "Y-m-d H:i:s.u" ), "\n";
+echo $dt4->format( "Y-m-d H:i:s.u" ), "\n";
+?>
+--EXPECTF--
+microseconds = true
+2016-10-03 12:47:18.819313
+
+2016-10-04 12:47:18.819210
+2016-07-03 12:47:18.081921
+
+2016-10-02 00:00:00.000000
+2016-10-03 12:00:00.000000
+2016-10-17 00:00:00.000000
+object(DateInterval)#%d (16) {
+ ["y"]=>
+ int(0)
+ ["m"]=>
+ int(0)
+ ["d"]=>
+ int(0)
+ ["h"]=>
+ int(0)
+ ["i"]=>
+ int(0)
+ ["s"]=>
+ int(0)
+ ["f"]=>
+ float(0.378189)
+ ["weekday"]=>
+ int(0)
+ ["weekday_behavior"]=>
+ int(0)
+ ["first_last_day_of"]=>
+ int(0)
+ ["invert"]=>
+ int(0)
+ ["days"]=>
+ int(0)
+ ["special_type"]=>
+ int(0)
+ ["special_amount"]=>
+ int(0)
+ ["have_weekday_relative"]=>
+ int(0)
+ ["have_special_relative"]=>
+ int(0)
+}
+2016-10-03 13:20:06.724934
+2016-10-03 13:20:07.103123
+2016-10-03 13:20:07.481312
+2016-10-03 13:20:07.859501
+2016-10-03 13:20:08.237690
diff --git a/ext/date/tests/date_time_fractions_create_from_format.phpt b/ext/date/tests/date_time_fractions_create_from_format.phpt
new file mode 100644
index 0000000000..c598f174ad
--- /dev/null
+++ b/ext/date/tests/date_time_fractions_create_from_format.phpt
@@ -0,0 +1,29 @@
+--TEST--
+Fractions with DateTime objects (create_from_format)
+--INI--
+date.timezone=UTC
+--FILE--
+<?php
+$dt = date_create_from_format( "Y-m-d H:i:s.u", "2016-10-03 12:47:18.819313" );
+var_dump( $dt );
+
+$dt = date_create_from_format( "U.u", "1475500799.176312" );
+var_dump( $dt );
+?>
+--EXPECTF--
+object(DateTime)#%d (%d) {
+ ["date"]=>
+ string(26) "2016-10-03 12:47:18.819313"
+ ["timezone_type"]=>
+ int(3)
+ ["timezone"]=>
+ string(3) "UTC"
+}
+object(DateTime)#%d (%d) {
+ ["date"]=>
+ string(26) "2016-10-03 13:19:59.176312"
+ ["timezone_type"]=>
+ int(1)
+ ["timezone"]=>
+ string(6) "+00:00"
+}
diff --git a/ext/date/tests/date_time_fractions_serialize.phpt b/ext/date/tests/date_time_fractions_serialize.phpt
new file mode 100644
index 0000000000..4931bb02b3
--- /dev/null
+++ b/ext/date/tests/date_time_fractions_serialize.phpt
@@ -0,0 +1,25 @@
+--TEST--
+Fractions with DateTime objects (Serialization)
+--INI--
+date.timezone=UTC
+--FILE--
+<?php
+/* Normal creation */
+$dt = date_create( "2016-10-03 12:47:18.819313" );
+
+$s = serialize( $dt );
+echo $s, "\n";
+
+$u = unserialize( $s );
+var_dump( $u );
+?>
+--EXPECTF--
+O:8:"DateTime":3:{s:4:"date";s:26:"2016-10-03 12:47:18.819313";s:13:"timezone_type";i:3;s:8:"timezone";s:3:"UTC";}
+object(DateTime)#2 (%d) {
+ ["date"]=>
+ string(26) "2016-10-03 12:47:18.819313"
+ ["timezone_type"]=>
+ int(3)
+ ["timezone"]=>
+ string(3) "UTC"
+}
diff --git a/ext/date/tests/date_time_set_error.phpt b/ext/date/tests/date_time_set_error.phpt
index 8e5c855bcd..5c5ddaf4d2 100644
--- a/ext/date/tests/date_time_set_error.phpt
+++ b/ext/date/tests/date_time_set_error.phpt
@@ -24,8 +24,9 @@ var_dump( date_time_set($datetime, $hour) );
echo "\n-- Testing date_time_set() function with more than expected no. of arguments --\n";
$min = 15;
$sec = 30;
+$microseconds = 123123;
$extra_arg = 10;
-var_dump( date_time_set($datetime, $hour, $min, $sec, $extra_arg) );
+var_dump( date_time_set($datetime, $hour, $min, $sec, $microseconds, $extra_arg) );
echo "\n-- Testing date_time_set() function with an invalid values for \$object argument --\n";
$invalid_obj = new stdClass();
@@ -54,7 +55,7 @@ bool(false)
-- Testing date_time_set() function with more than expected no. of arguments --
-Warning: date_time_set() expects at most 4 parameters, 5 given in %s on line %d
+Warning: date_time_set() expects at most 5 parameters, 6 given in %s on line %d
bool(false)
-- Testing date_time_set() function with an invalid values for $object argument --
diff --git a/ext/date/tests/timezone_abbreviations_list_basic1.phpt b/ext/date/tests/timezone_abbreviations_list_basic1.phpt
index 73af2acf3d..7a0dcdee98 100644
--- a/ext/date/tests/timezone_abbreviations_list_basic1.phpt
+++ b/ext/date/tests/timezone_abbreviations_list_basic1.phpt
@@ -29,7 +29,7 @@ string(5) "array"
int(%d)
-- Format a sample entry --
-array(11) {
+array(17) {
[0]=>
array(3) {
["dst"]=>
@@ -44,20 +44,29 @@ array(11) {
["dst"]=>
bool(false)
["offset"]=>
- int(34200)
+ int(32400)
["timezone_id"]=>
string(18) "Australia/Adelaide"
}
[2]=>
array(3) {
["dst"]=>
+ bool(false)
+ ["offset"]=>
+ int(34200)
+ ["timezone_id"]=>
+ string(18) "Australia/Adelaide"
+ }
+ [3]=>
+ array(3) {
+ ["dst"]=>
bool(true)
["offset"]=>
int(-14400)
["timezone_id"]=>
string(16) "America/Eirunepe"
}
- [3]=>
+ [4]=>
array(3) {
["dst"]=>
bool(true)
@@ -66,7 +75,7 @@ array(11) {
["timezone_id"]=>
string(18) "America/Rio_Branco"
}
- [4]=>
+ [5]=>
array(3) {
["dst"]=>
bool(true)
@@ -75,7 +84,52 @@ array(11) {
["timezone_id"]=>
string(11) "Brazil/Acre"
}
- [5]=>
+ [6]=>
+ array(3) {
+ ["dst"]=>
+ bool(false)
+ ["offset"]=>
+ int(32400)
+ ["timezone_id"]=>
+ string(21) "Australia/Broken_Hill"
+ }
+ [7]=>
+ array(3) {
+ ["dst"]=>
+ bool(false)
+ ["offset"]=>
+ int(32400)
+ ["timezone_id"]=>
+ string(16) "Australia/Darwin"
+ }
+ [8]=>
+ array(3) {
+ ["dst"]=>
+ bool(false)
+ ["offset"]=>
+ int(32400)
+ ["timezone_id"]=>
+ string(15) "Australia/North"
+ }
+ [9]=>
+ array(3) {
+ ["dst"]=>
+ bool(false)
+ ["offset"]=>
+ int(32400)
+ ["timezone_id"]=>
+ string(15) "Australia/South"
+ }
+ [10]=>
+ array(3) {
+ ["dst"]=>
+ bool(false)
+ ["offset"]=>
+ int(32400)
+ ["timezone_id"]=>
+ string(20) "Australia/Yancowinna"
+ }
+ [11]=>
array(3) {
["dst"]=>
bool(false)
@@ -84,7 +138,7 @@ array(11) {
["timezone_id"]=>
string(13) "Asia/Jayapura"
}
- [6]=>
+ [12]=>
array(3) {
["dst"]=>
bool(false)
@@ -93,7 +147,7 @@ array(11) {
["timezone_id"]=>
string(21) "Australia/Broken_Hill"
}
- [7]=>
+ [13]=>
array(3) {
["dst"]=>
bool(false)
@@ -102,7 +156,7 @@ array(11) {
["timezone_id"]=>
string(16) "Australia/Darwin"
}
- [8]=>
+ [14]=>
array(3) {
["dst"]=>
bool(false)
@@ -111,7 +165,7 @@ array(11) {
["timezone_id"]=>
string(15) "Australia/North"
}
- [9]=>
+ [15]=>
array(3) {
["dst"]=>
bool(false)
@@ -120,7 +174,7 @@ array(11) {
["timezone_id"]=>
string(15) "Australia/South"
}
- [10]=>
+ [16]=>
array(3) {
["dst"]=>
bool(false)
diff --git a/ext/date/tests/timezone_name_from_abbr_basic1.phpt b/ext/date/tests/timezone_name_from_abbr_basic1.phpt
index b591fe28f9..492c623eb1 100644
--- a/ext/date/tests/timezone_name_from_abbr_basic1.phpt
+++ b/ext/date/tests/timezone_name_from_abbr_basic1.phpt
@@ -24,11 +24,7 @@ var_dump( timezone_name_from_abbr("EDT") );
echo "-- Lookup with name and offset--\n";
var_dump( timezone_name_from_abbr("ADT", -10800) );
var_dump( timezone_name_from_abbr("ADT", 14400) );
-var_dump( timezone_name_from_abbr("AKTT", 14400) );
-var_dump( timezone_name_from_abbr("aktt", 18000) );
-var_dump( timezone_name_from_abbr("Aktt", 21600) );
var_dump( timezone_name_from_abbr("AMST", -10800) );
-var_dump( timezone_name_from_abbr("amst", 180000) );
echo "-- Tests without valid name - uses gmtOffset and isdst to find match --\n";
var_dump( timezone_name_from_abbr("", 3600, 1) );
@@ -52,11 +48,7 @@ string(16) "America/New_York"
-- Lookup with name and offset--
string(15) "America/Halifax"
string(12) "Asia/Baghdad"
-string(11) "Asia/Aqtobe"
-string(11) "Asia/Aqtobe"
-string(11) "Asia/Aqtobe"
string(17) "America/Boa_Vista"
-string(12) "Asia/Yerevan"
-- Tests without valid name - uses gmtOffset and isdst to find match --
string(13) "Europe/London"
string(17) "America/Sao_Paulo"
@@ -65,4 +57,4 @@ string(15) "America/Halifax"
-- Tests with invalid offsets --
bool(false)
bool(false)
-===DONE=== \ No newline at end of file
+===DONE===
diff --git a/ext/dba/dba.c b/ext/dba/dba.c
index 4335a861a1..60060a5f10 100644
--- a/ext/dba/dba.c
+++ b/ext/dba/dba.c
@@ -208,7 +208,7 @@ static size_t php_dba_make_key(zval *key, char **key_str, char **key_free)
size_t len;
if (zend_hash_num_elements(Z_ARRVAL_P(key)) != 2) {
- php_error_docref(NULL, E_RECOVERABLE_ERROR, "Key does not have exactly two elements: (key, name)");
+ zend_throw_error(NULL, "Key does not have exactly two elements: (key, name)");
return 0;
}
zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(key), &pos);
@@ -363,7 +363,7 @@ static dba_handler handler[] = {
#if DBA_TCADB
DBA_HND(tcadb, DBA_LOCK_ALL)
#endif
- { NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
+ { NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
};
#if DBA_FLATFILE
diff --git a/ext/dba/libinifile/inifile.c b/ext/dba/libinifile/inifile.c
index e909e1ba8a..5e1e7a2890 100644
--- a/ext/dba/libinifile/inifile.c
+++ b/ext/dba/libinifile/inifile.c
@@ -543,7 +543,7 @@ static int inifile_delete_replace_append(inifile *dba, const key_type *key, cons
php_stream_seek(fp_tmp, 0, SEEK_SET);
php_stream_seek(dba->fp, 0, SEEK_END);
if (SUCCESS != php_stream_copy_to_stream_ex(fp_tmp, dba->fp, PHP_STREAM_COPY_ALL, NULL)) {
- php_error_docref(NULL, E_RECOVERABLE_ERROR, "Could not copy from temporary stream - ini file truncated");
+ zend_throw_error(NULL, "Could not copy from temporary stream - ini file truncated");
ret = FAILURE;
}
}
diff --git a/ext/dba/tests/dba013.phpt b/ext/dba/tests/dba013.phpt
index bf95642e9d..8e37b26315 100644
--- a/ext/dba/tests/dba013.phpt
+++ b/ext/dba/tests/dba013.phpt
@@ -24,4 +24,8 @@ require(dirname(__FILE__) .'/clean.inc');
--EXPECTF--
database handler: %s
-Catchable fatal error: dba_insert(): Key does not have exactly two elements: (key, name) in %sdba013.php on line %d
+Fatal error: Uncaught Error: Key does not have exactly two elements: (key, name) in %sdba013.php:6
+Stack trace:
+#0 %sdba013.php(6): dba_insert(Array, '%s', Resource id #%d)
+#1 {main}
+ thrown in %sdba013.php on line 6
diff --git a/ext/dba/tests/dba014.phpt b/ext/dba/tests/dba014.phpt
index 7e52be7c3a..f08fff25d7 100644
--- a/ext/dba/tests/dba014.phpt
+++ b/ext/dba/tests/dba014.phpt
@@ -24,4 +24,8 @@ require(dirname(__FILE__) .'/clean.inc');
--EXPECTF--
database handler: %s
-Catchable fatal error: dba_insert(): Key does not have exactly two elements: (key, name) in %sdba014.php on line %d
+Fatal error: Uncaught Error: Key does not have exactly two elements: (key, name) in %sdba014.php:6
+Stack trace:
+#0 %sdba014.php(6): dba_insert(Array, '%s', Resource id #%d)
+#1 {main}
+ thrown in %sdba014.php on line 6
diff --git a/ext/dom/comment.c b/ext/dom/comment.c
index 71df35b605..5587dbad30 100644
--- a/ext/dom/comment.c
+++ b/ext/dom/comment.c
@@ -43,7 +43,7 @@ ZEND_END_ARG_INFO();
const zend_function_entry php_dom_comment_class_functions[] = {
PHP_ME(domcomment, __construct, arginfo_dom_comment_construct, ZEND_ACC_PUBLIC)
- {NULL, NULL, NULL}
+ PHP_FE_END
};
/* {{{ proto void DOMComment::__construct([string value]); */
diff --git a/ext/dom/document.c b/ext/dom/document.c
index a884087f5f..cac6830077 100644
--- a/ext/dom/document.c
+++ b/ext/dom/document.c
@@ -1856,7 +1856,7 @@ static void _dom_document_schema_validate(INTERNAL_FUNCTION_PARAMETERS, int type
vptr = xmlSchemaNewValidCtxt(sptr);
if (!vptr) {
xmlSchemaFree(sptr);
- php_error(E_ERROR, "Invalid Schema Validation Context");
+ zend_throw_error(NULL, "Invalid Schema Validation Context");
RETURN_FALSE;
}
@@ -1956,7 +1956,7 @@ static void _dom_document_relaxNG_validate(INTERNAL_FUNCTION_PARAMETERS, int typ
vptr = xmlRelaxNGNewValidCtxt(sptr);
if (!vptr) {
xmlRelaxNGFree(sptr);
- php_error(E_ERROR, "Invalid RelaxNG Validation Context");
+ zend_throw_error(NULL, "Invalid RelaxNG Validation Context");
RETURN_FALSE;
}
@@ -2244,15 +2244,11 @@ PHP_METHOD(domdocument, registerNodeClass)
if (ce == NULL || instanceof_function(ce, basece)) {
DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
-
- if (dom_set_doc_classmap(intern->document, basece, ce) == FAILURE) {
- php_error_docref(NULL, E_ERROR, "Class %s could not be registered.", ZSTR_VAL(ce->name));
- }
+ dom_set_doc_classmap(intern->document, basece, ce);
RETURN_TRUE;
- } else {
- php_error_docref(NULL, E_ERROR, "Class %s is not derived from %s.", ZSTR_VAL(ce->name), ZSTR_VAL(basece->name));
}
-
+
+ zend_throw_error(NULL, "Class %s is not derived from %s.", ZSTR_VAL(ce->name), ZSTR_VAL(basece->name));
RETURN_FALSE;
}
/* }}} */
diff --git a/ext/dom/dom_iterators.c b/ext/dom/dom_iterators.c
index f08d8a4749..02f1e3c472 100644
--- a/ext/dom/dom_iterators.c
+++ b/ext/dom/dom_iterators.c
@@ -249,6 +249,7 @@ zend_object_iterator_funcs php_dom_iterator_funcs = {
php_dom_iterator_current_data,
php_dom_iterator_current_key,
php_dom_iterator_move_forward,
+ NULL,
NULL
};
diff --git a/ext/dom/domimplementationlist.c b/ext/dom/domimplementationlist.c
index b82483b6ce..dac673cae0 100644
--- a/ext/dom/domimplementationlist.c
+++ b/ext/dom/domimplementationlist.c
@@ -42,7 +42,7 @@ ZEND_END_ARG_INFO();
const zend_function_entry php_dom_domimplementationlist_class_functions[] = {
PHP_FALIAS(item, dom_domimplementationlist_item, arginfo_dom_implementationlist_item)
- {NULL, NULL, NULL}
+ PHP_FE_END
};
/* {{{ attribute protos, not implemented yet */
diff --git a/ext/dom/node.c b/ext/dom/node.c
index b4a081ebe9..a9499a96aa 100644
--- a/ext/dom/node.c
+++ b/ext/dom/node.c
@@ -867,7 +867,7 @@ int dom_node_text_content_write(dom_object *obj, zval *newval)
str = zval_get_string(newval);
/* we have to use xmlNodeAddContent() to get the same behavior as with xmlNewText() */
xmlNodeSetContent(nodep, (xmlChar *) "");
- xmlNodeAddContent(nodep, ZSTR_VAL(str));
+ xmlNodeAddContent(nodep, (xmlChar *) ZSTR_VAL(str));
zend_string_release(str);
return SUCCESS;
diff --git a/ext/dom/php_dom.c b/ext/dom/php_dom.c
index e10f209491..25d57f4ea8 100644
--- a/ext/dom/php_dom.c
+++ b/ext/dom/php_dom.c
@@ -206,7 +206,7 @@ static void dom_copy_doc_props(php_libxml_ref_obj *source_doc, php_libxml_ref_ob
}
}
-int dom_set_doc_classmap(php_libxml_ref_obj *document, zend_class_entry *basece, zend_class_entry *ce)
+void dom_set_doc_classmap(php_libxml_ref_obj *document, zend_class_entry *basece, zend_class_entry *ce)
{
dom_doc_propsptr doc_props;
@@ -214,7 +214,7 @@ int dom_set_doc_classmap(php_libxml_ref_obj *document, zend_class_entry *basece,
doc_props = dom_get_doc_props(document);
if (doc_props->classmap == NULL) {
if (ce == NULL) {
- return SUCCESS;
+ return;
}
ALLOC_HASHTABLE(doc_props->classmap);
zend_hash_init(doc_props->classmap, 0, NULL, NULL, 0);
@@ -225,7 +225,6 @@ int dom_set_doc_classmap(php_libxml_ref_obj *document, zend_class_entry *basece,
zend_hash_del(doc_props->classmap, basece->name);
}
}
- return SUCCESS;
}
zend_class_entry *dom_get_doc_classmap(php_libxml_ref_obj *document, zend_class_entry *basece)
@@ -286,7 +285,7 @@ PHP_DOM_EXPORT dom_object *php_dom_object_get_data(xmlNodePtr obj)
/* {{{ dom_read_na */
static int dom_read_na(dom_object *obj, zval *retval)
{
- php_error_docref(NULL, E_ERROR, "Cannot read property");
+ zend_throw_error(NULL, "Cannot read property");
return FAILURE;
}
/* }}} */
@@ -294,7 +293,7 @@ static int dom_read_na(dom_object *obj, zval *retval)
/* {{{ dom_write_na */
static int dom_write_na(dom_object *obj, zval *newval)
{
- php_error_docref(NULL, E_ERROR, "Cannot write property");
+ zend_throw_error(NULL, "Cannot write property");
return FAILURE;
}
/* }}} */
@@ -420,7 +419,7 @@ static HashTable* dom_get_debug_info_helper(zval *object, int *is_temp) /* {{{ *
*std_props;
zend_string *string_key;
dom_prop_handler *entry;
- zval object_value;
+ zend_string *object_str;
*is_temp = 1;
@@ -431,7 +430,7 @@ static HashTable* dom_get_debug_info_helper(zval *object, int *is_temp) /* {{{ *
return debug_info;
}
- ZVAL_STRING(&object_value, "(object value omitted)");
+ object_str = zend_string_init("(object value omitted)", sizeof("(object value omitted)")-1, 0);
ZEND_HASH_FOREACH_STR_KEY_PTR(prop_handlers, string_key, entry) {
zval value;
@@ -442,13 +441,14 @@ static HashTable* dom_get_debug_info_helper(zval *object, int *is_temp) /* {{{ *
if (Z_TYPE(value) == IS_OBJECT) {
zval_dtor(&value);
- ZVAL_COPY(&value, &object_value);
+ ZVAL_NEW_STR(&value, object_str);
+ zend_string_addref(object_str);
}
zend_hash_add(debug_info, string_key, &value);
} ZEND_HASH_FOREACH_END();
- zval_dtor(&object_value);
+ zend_string_release(object_str);
return debug_info;
}
diff --git a/ext/dom/php_dom.h b/ext/dom/php_dom.h
index 76e7a87e68..30d143c351 100644
--- a/ext/dom/php_dom.h
+++ b/ext/dom/php_dom.h
@@ -125,7 +125,7 @@ xmlNodePtr create_notation(const xmlChar *name, const xmlChar *ExternalID, const
xmlNode *php_dom_libxml_hash_iter(xmlHashTable *ht, int index);
xmlNode *php_dom_libxml_notation_iter(xmlHashTable *ht, int index);
zend_object_iterator *php_dom_get_iterator(zend_class_entry *ce, zval *object, int by_ref);
-int dom_set_doc_classmap(php_libxml_ref_obj *document, zend_class_entry *basece, zend_class_entry *ce);
+void dom_set_doc_classmap(php_libxml_ref_obj *document, zend_class_entry *basece, zend_class_entry *ce);
zval *dom_nodelist_read_dimension(zval *object, zval *offset, int type, zval *rv);
int dom_nodelist_has_dimension(zval *object, zval *member, int check_empty);
diff --git a/ext/dom/xpath.c b/ext/dom/xpath.c
index 4d7d1e2d16..068ca61bfe 100644
--- a/ext/dom/xpath.c
+++ b/ext/dom/xpath.c
@@ -171,7 +171,6 @@ static void dom_xpath_ext_function_php(xmlXPathParserContextPtr ctxt, int nargs,
}
fci.size = sizeof(fci);
- fci.function_table = EG(function_table);
obj = valuePop(ctxt);
if (obj->stringval == NULL) {
@@ -188,7 +187,6 @@ static void dom_xpath_ext_function_php(xmlXPathParserContextPtr ctxt, int nargs,
ZVAL_STRING(&fci.function_name, (char *) obj->stringval);
xmlXPathFreeObject(obj);
- fci.symbol_table = NULL;
fci.object = NULL;
fci.retval = &retval;
fci.no_separation = 0;
@@ -474,7 +472,7 @@ static void php_xpath_eval(INTERNAL_FUNCTION_PARAMETERS, int type) /* {{{ */
break;
case XPATH_NUMBER:
- RETVAL_DOUBLE(xpathobjp->floatval)
+ RETVAL_DOUBLE(xpathobjp->floatval);
break;
case XPATH_STRING:
diff --git a/ext/enchant/enchant.c b/ext/enchant/enchant.c
index c51c51d6f2..77f6840161 100644
--- a/ext/enchant/enchant.c
+++ b/ext/enchant/enchant.c
@@ -587,11 +587,7 @@ PHP_FUNCTION(enchant_broker_request_pwl_dict)
RETURN_FALSE;
}
-#if PHP_API_VERSION < 20100412
- if ((PG(safe_mode) && (!php_checkuid(pwl, NULL, CHECKUID_CHECK_FILE_AND_DIR))) || php_check_open_basedir(pwl)) {
-#else
if (php_check_open_basedir(pwl)) {
-#endif
RETURN_FALSE;
}
diff --git a/ext/exif/exif.c b/ext/exif/exif.c
index 5de48bd00a..4525fb3553 100644
--- a/ext/exif/exif.c
+++ b/ext/exif/exif.c
@@ -2710,7 +2710,8 @@ static int exif_process_unicode(image_info_type *ImageInfo, xp_field_type *xp_fi
* Process nested IFDs directories in Maker Note. */
static int exif_process_IFD_in_MAKERNOTE(image_info_type *ImageInfo, char * value_ptr, int value_len, char *offset_base, size_t IFDlength, size_t displacement)
{
- int de, i=0, section_index = SECTION_MAKERNOTE;
+ size_t i;
+ int de, section_index = SECTION_MAKERNOTE;
int NumDirEntries, old_motorola_intel, offset_diff;
const maker_note_type *maker_note;
char *dir_start;
@@ -3348,11 +3349,11 @@ static int exif_scan_JPEG_header(image_info_type *ImageInfo)
}
/* Read the length of the section. */
- if ((lh = php_stream_getc(ImageInfo->infile)) == EOF) {
+ if ((lh = php_stream_getc(ImageInfo->infile)) == (unsigned int)EOF) {
EXIF_ERRLOG_CORRUPT(ImageInfo)
return FALSE;
}
- if ((ll = php_stream_getc(ImageInfo->infile)) == EOF) {
+ if ((ll = php_stream_getc(ImageInfo->infile)) == (unsigned int)EOF) {
EXIF_ERRLOG_CORRUPT(ImageInfo)
return FALSE;
}
diff --git a/ext/exif/tests/bug34704-mb.phpt b/ext/exif/tests/bug34704-mb.phpt
new file mode 100644
index 0000000000..05c50b3a5a
--- /dev/null
+++ b/ext/exif/tests/bug34704-mb.phpt
@@ -0,0 +1,42 @@
+--TEST--
+Bug #34704 (Infinite recursion due to corrupt JPEG)
+--SKIPIF--
+<?php if (!extension_loaded('exif')) print 'skip exif extension not available';?>
+--INI--
+output_handler=
+zlib.output_compression=0
+--FILE--
+<?php
+$infile = dirname(__FILE__).'/bug34704私はガラスを食べられます.jpg';
+var_dump(exif_read_data($infile));
+?>
+===DONE===
+--EXPECTF--
+array(7) {
+ ["FileName"]=>
+ string(48) "bug34704私はガラスを食べられます.jpg"
+ ["FileDateTime"]=>
+ int(%d)
+ ["FileSize"]=>
+ int(9976)
+ ["FileType"]=>
+ int(2)
+ ["MimeType"]=>
+ string(10) "image/jpeg"
+ ["SectionsFound"]=>
+ string(4) "IFD0"
+ ["COMPUTED"]=>
+ array(5) {
+ ["html"]=>
+ string(24) "width="386" height="488""
+ ["Height"]=>
+ int(488)
+ ["Width"]=>
+ int(386)
+ ["IsColor"]=>
+ int(1)
+ ["ByteOrderMotorola"]=>
+ int(0)
+ }
+}
+===DONE===
diff --git a/ext/exif/tests/bug34704私はガラスを食べられます.jpg b/ext/exif/tests/bug34704私はガラスを食べられます.jpg
new file mode 100644
index 0000000000..42b14c1908
--- /dev/null
+++ b/ext/exif/tests/bug34704私はガラスを食べられます.jpg
Binary files differ
diff --git a/ext/exif/tests/bug68113-mb.phpt b/ext/exif/tests/bug68113-mb.phpt
new file mode 100644
index 0000000000..cc01f817c2
--- /dev/null
+++ b/ext/exif/tests/bug68113-mb.phpt
@@ -0,0 +1,17 @@
+--TEST--
+Bug #68113 (Heap corruption in exif_thumbnail())
+--SKIPIF--
+<?php
+extension_loaded("exif") or die("skip need exif");
+?>
+--FILE--
+<?php
+var_dump(exif_thumbnail(__DIR__."/bug68113私はガラスを食べられます.jpg"));
+?>
+Done
+--EXPECTF--
+Warning: exif_thumbnail(bug68113私はガラスを食べられます.jpg): File structure corrupted in %s%ebug68113-mb.php on line 2
+
+Warning: exif_thumbnail(bug68113私はガラスを食べられます.jpg): Invalid JPEG file in %s%ebug68113-mb.php on line 2
+bool(false)
+Done
diff --git a/ext/exif/tests/bug68113私はガラスを食べられます.jpg b/ext/exif/tests/bug68113私はガラスを食べられます.jpg
new file mode 100644
index 0000000000..3ce7a620fb
--- /dev/null
+++ b/ext/exif/tests/bug68113私はガラスを食べられます.jpg
Binary files differ
diff --git a/ext/exif/tests/exif_imagetype_basic-mb.phpt b/ext/exif/tests/exif_imagetype_basic-mb.phpt
new file mode 100644
index 0000000000..936e25777a
--- /dev/null
+++ b/ext/exif/tests/exif_imagetype_basic-mb.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Check for exif_imagetype default behaviour
+--SKIPIF--
+<?php if (!extension_loaded('exif')) print 'skip exif extension not available';?>
+--INI--
+output_handler=
+zlib.output_compression=0
+--FILE--
+<?php
+
+/* Prototype : int exif_imagetype ( string $filename )
+ * Description: Determine the type of an image
+ * Source code: ext/exif/exif.c
+*/
+echo "*** Testing exif_imagetype() : basic functionality ***\n";
+
+var_dump(exif_imagetype(dirname(__FILE__).'/test2私はガラスを食べられます.jpg'));
+?>
+===Done===
+--EXPECT--
+*** Testing exif_imagetype() : basic functionality ***
+int(2)
+===Done=== \ No newline at end of file
diff --git a/ext/exif/tests/exif_read_exif_data_basic-mb.phpt b/ext/exif/tests/exif_read_exif_data_basic-mb.phpt
new file mode 100644
index 0000000000..2f6a1a4103
--- /dev/null
+++ b/ext/exif/tests/exif_read_exif_data_basic-mb.phpt
@@ -0,0 +1,62 @@
+--TEST--
+Check for read_exif_data default behaviour
+--SKIPIF--
+<?php if (!extension_loaded('exif')) print 'skip exif extension not available';?>
+--INI--
+output_handler=
+zlib.output_compression=0
+--FILE--
+<?php
+
+/* Prototype : array read_exif_data ( string $filename [, string $sections [, bool $arrays [, bool $thumbnail ]]] )
+ * Description: Alias of exif_read_data()
+ * Source code: ext/exif/exif.c
+*/
+echo "*** Testing read_exif_data() : basic functionality ***\n";
+
+print_r(read_exif_data(dirname(__FILE__).'/test2私はガラスを食べられます.jpg'));
+?>
+===Done===
+--EXPECTF--
+*** Testing read_exif_data() : basic functionality ***
+Array
+(
+ [FileName] => test2私はガラスを食べられます.jpg
+ [FileDateTime] => %d
+ [FileSize] => 1240
+ [FileType] => 2
+ [MimeType] => image/jpeg
+ [SectionsFound] => ANY_TAG, IFD0, THUMBNAIL, COMMENT
+ [COMPUTED] => Array
+ (
+ [html] => width="1" height="1"
+ [Height] => 1
+ [Width] => 1
+ [IsColor] => 1
+ [ByteOrderMotorola] => 1
+ [UserComment] => Exif test image.
+ [UserCommentEncoding] => ASCII
+ [Copyright] => Photo (c) M.Boerger, Edited by M.Boerger.
+ [Copyright.Photographer] => Photo (c) M.Boerger
+ [Copyright.Editor] => Edited by M.Boerger.
+ [Thumbnail.FileType] => 2
+ [Thumbnail.MimeType] => image/jpeg
+ )
+
+ [Copyright] => Photo (c) M.Boerger
+ [UserComment] => ASCII
+ [THUMBNAIL] => Array
+ (
+ [JPEGInterchangeFormat] => 134
+ [JPEGInterchangeFormatLength] => 523
+ )
+
+ [COMMENT] => Array
+ (
+ [0] => Comment #1.
+ [1] => Comment #2.
+ [2] => Comment #3end
+ )
+
+)
+===Done===
diff --git a/ext/exif/tests/test2私はガラスを食べられます.jpg b/ext/exif/tests/test2私はガラスを食べられます.jpg
new file mode 100644
index 0000000000..f60ecded6f
--- /dev/null
+++ b/ext/exif/tests/test2私はガラスを食べられます.jpg
Binary files differ
diff --git a/ext/ext_skel b/ext/ext_skel
index a1c64640ae..f5b9b2578d 100755
--- a/ext/ext_skel
+++ b/ext/ext_skel
@@ -299,12 +299,12 @@ if test -n "$proto"; then
warning="
NOTE! Because some arguments to functions were resources, the code generated
cannot yet be compiled without editing. Please consider this to be step 4.5
-in the instructions above.
+in the instructions above.
"
fi
fi
-find . -type f | xargs chmod 644
+find . -type f | xargs chmod 644
find . -type d | xargs chmod 755
fi
diff --git a/ext/fileinfo/fileinfo.c b/ext/fileinfo/fileinfo.c
index 36d54756e2..e5fe25b31c 100644
--- a/ext/fileinfo/fileinfo.c
+++ b/ext/fileinfo/fileinfo.c
@@ -173,7 +173,7 @@ zend_function_entry finfo_class_functions[] = {
#define FINFO_SET_OPTION(magic, options) \
if (magic_setflags(magic, options) == -1) { \
- php_error_docref(NULL, E_WARNING, "Failed to set option '%pd' %d:%s", \
+ php_error_docref(NULL, E_WARNING, "Failed to set option '" ZEND_LONG_FMT "' %d:%s", \
options, magic_errno(magic), magic_error(magic)); \
RETURN_FALSE; \
}
@@ -203,7 +203,7 @@ zend_function_entry fileinfo_functions[] = {
PHP_FE(finfo_file, arginfo_finfo_file)
PHP_FE(finfo_buffer, arginfo_finfo_buffer)
PHP_FE(mime_content_type, arginfo_mime_content_type)
- {NULL, NULL, NULL}
+ PHP_FE_END
};
/* }}} */
@@ -340,7 +340,7 @@ PHP_FUNCTION(finfo_open)
if (finfo->magic == NULL) {
efree(finfo);
- php_error_docref(NULL, E_WARNING, "Invalid mode '%pd'.", options);
+ php_error_docref(NULL, E_WARNING, "Invalid mode '" ZEND_LONG_FMT "'.", options);
if (object) {
zend_restore_error_handling(&zeh);
if (!EG(exception)) {
@@ -548,11 +548,7 @@ static void _php_finfo_get_type(INTERNAL_FUNCTION_PARAMETERS, int mode, int mime
}
#endif
-#if PHP_API_VERSION < 20100412
- stream = php_stream_open_wrapper_ex(buffer, "rb", ENFORCE_SAFE_MODE | REPORT_ERRORS, NULL, context);
-#else
stream = php_stream_open_wrapper_ex(buffer, "rb", REPORT_ERRORS, NULL, context);
-#endif
if (!stream) {
RETVAL_FALSE;
diff --git a/ext/fileinfo/tests/67647私はガラスを食べられます.mov b/ext/fileinfo/tests/67647私はガラスを食べられます.mov
new file mode 100644
index 0000000000..e119013fb1
--- /dev/null
+++ b/ext/fileinfo/tests/67647私はガラスを食べられます.mov
Binary files differ
diff --git a/ext/fileinfo/tests/bug61964-mb.phpt b/ext/fileinfo/tests/bug61964-mb.phpt
new file mode 100644
index 0000000000..1b1c95ecfa
--- /dev/null
+++ b/ext/fileinfo/tests/bug61964-mb.phpt
@@ -0,0 +1,73 @@
+--TEST--
+Bug #61964 (finfo_open with directory cause invalid free)
+--SKIPIF--
+<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
+--FILE--
+<?php
+
+$magic_file = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'magic私はガラスを食べられます';
+
+$ret = @finfo_open(FILEINFO_NONE, $magic_file . ".non-exits私はガラスを食べられます");
+var_dump($ret);
+
+$dir = __DIR__ . "/test-folder";
+@mkdir($dir);
+
+$magic_file_copy = $dir . "/magic私はガラスを食べられます.copy";
+$magic_file_copy2 = $magic_file_copy . "2";
+copy($magic_file, $magic_file_copy);
+copy($magic_file, $magic_file_copy2);
+
+$ret = finfo_open(FILEINFO_NONE, $dir);
+var_dump($ret);
+
+$ret = @finfo_open(FILEINFO_NONE, $dir);
+var_dump($ret);
+
+$ret = @finfo_open(FILEINFO_NONE, $dir. "/non-exits-dir私はガラスを食べられます");
+var_dump($ret);
+
+// write some test files to test folder
+file_put_contents($dir . "/test1.txt", "string\n> Core\n> Me");
+file_put_contents($dir . "/test2.txt", "a\nb\n");
+@mkdir($dir . "/test-inner-folder私はガラスを食べられます");
+
+finfo_open(FILEINFO_NONE, $dir);
+echo "DONE: testing dir with files\n";
+
+rmdir($dir . "/test-inner-folder私はガラスを食べられます");
+unlink($dir . "/test1.txt");
+unlink($dir . "/test2.txt");
+
+unlink($magic_file_copy);
+unlink($magic_file_copy2);
+rmdir($dir);
+?>
+===DONE===
+--EXPECTF--
+bool(false)
+resource(%d) of type (file_info)
+resource(%d) of type (file_info)
+bool(false)
+
+Notice: finfo_open(): Warning: offset `string' invalid in %sbug61964-mb.php on line %d
+
+Notice: finfo_open(): Warning: offset ` Core' invalid in %sbug61964-mb.php on line %d
+
+Notice: finfo_open(): Warning: type `Core' invalid in %sbug61964-mb.php on line %d
+
+Notice: finfo_open(): Warning: offset ` Me' invalid in %sbug61964-mb.php on line %d
+
+Notice: finfo_open(): Warning: type `Me' invalid in %sbug61964-mb.php on line %d
+
+Notice: finfo_open(): Warning: offset `a' invalid in %sbug61964-mb.php on line %d
+
+Notice: finfo_open(): Warning: type `a' invalid in %sbug61964-mb.php on line %d
+
+Notice: finfo_open(): Warning: offset `b' invalid in %sbug61964-mb.php on line %d
+
+Notice: finfo_open(): Warning: type `b' invalid in %sbug61964-mb.php on line %d
+
+Warning: finfo_open(): Failed to load magic database at '%stest-folder'. in %sbug61964-mb.php on line %d
+DONE: testing dir with files
+===DONE===
diff --git a/ext/fileinfo/tests/bug67647-mb.phpt b/ext/fileinfo/tests/bug67647-mb.phpt
new file mode 100644
index 0000000000..611c4ab4d6
--- /dev/null
+++ b/ext/fileinfo/tests/bug67647-mb.phpt
@@ -0,0 +1,17 @@
+--TEST--
+Bug #67647: Bundled libmagic 5.17 does not detect quicktime files correctly
+--SKIPIF--
+<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
+--FILE--
+<?php
+
+$f = dirname(__FILE__) . DIRECTORY_SEPARATOR . "67647私はガラスを食べられます.mov";
+
+$fi = new finfo(FILEINFO_MIME_TYPE);
+var_dump($fi->file($f));
+?>
++++DONE+++
+--EXPECT--
+string(15) "video/quicktime"
++++DONE+++
+
diff --git a/ext/fileinfo/tests/bug71527-mb.phpt b/ext/fileinfo/tests/bug71527-mb.phpt
new file mode 100644
index 0000000000..a3e5680cb3
--- /dev/null
+++ b/ext/fileinfo/tests/bug71527-mb.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Bug #71527 Buffer over-write in finfo_open with malformed magic file
+--SKIPIF--
+<?php
+if (!class_exists('finfo'))
+ die('skip no fileinfo extension');
+--ENV--
+USE_ZEND_ALLOC=0
+--FILE--
+<?php
+ $finfo = finfo_open(FILEINFO_NONE, dirname(__FILE__) . DIRECTORY_SEPARATOR . "bug71527私はガラスを食べられます.magic");
+ $info = finfo_file($finfo, __FILE__);
+ var_dump($info);
+?>
+--EXPECTF--
+Warning: finfo_open(): Failed to load magic database at '%sbug71527私はガラスを食べられます.magic'. in %sbug71527-mb.php on line %d
+
+Warning: finfo_file() expects parameter 1 to be resource, boolean given in %sbug71527-mb.php on line %d
+bool(false)
diff --git a/ext/fileinfo/tests/bug71527私はガラスを食べられます.magic b/ext/fileinfo/tests/bug71527私はガラスを食べられます.magic
new file mode 100644
index 0000000000..14d77817be
--- /dev/null
+++ b/ext/fileinfo/tests/bug71527私はガラスを食べられます.magic
@@ -0,0 +1 @@
+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> \ No newline at end of file
diff --git a/ext/fileinfo/tests/cve-2014-1943-mb.phpt b/ext/fileinfo/tests/cve-2014-1943-mb.phpt
new file mode 100644
index 0000000000..939518855b
--- /dev/null
+++ b/ext/fileinfo/tests/cve-2014-1943-mb.phpt
@@ -0,0 +1,39 @@
+--TEST--
+Bug #66731: file: infinite recursion
+--SKIPIF--
+<?php
+if (!class_exists('finfo'))
+ die('skip no fileinfo extension');
+--FILE--
+<?php
+$fd = __DIR__.'/cve-2014-1943私はガラスを食べられます.data';
+$fm = __DIR__.'/cve-2014-1943私はガラスを食べられます.magic';
+
+$a = "\105\122\000\000\000\000\000";
+$b = str_repeat("\001", 250000);
+$m = "0 byte x\n".
+ ">(1.b) indirect x\n";
+
+file_put_contents($fd, $a);
+$fi = finfo_open(FILEINFO_NONE);
+var_dump(finfo_file($fi, $fd));
+finfo_close($fi);
+
+file_put_contents($fd, $b);
+file_put_contents($fm, $m);
+$fi = finfo_open(FILEINFO_NONE, $fm);
+var_dump(finfo_file($fi, $fd));
+finfo_close($fi);
+?>
+Done
+--CLEAN--
+<?php
+@unlink(__DIR__.'/cve-2014-1943.data');
+@unlink(__DIR__.'/cve-2014-1943.magic');
+?>
+--EXPECTF--
+string(%d) "%s"
+
+Warning: finfo_file(): Failed identify data 0:indirect recursion nesting (%d) exceeded in %s on line %d
+bool(false)
+Done
diff --git a/ext/fileinfo/tests/cve-2014-1943私はガラスを食べられます.data b/ext/fileinfo/tests/cve-2014-1943私はガラスを食べられます.data
new file mode 100644
index 0000000000..c4416f4eb4
--- /dev/null
+++ b/ext/fileinfo/tests/cve-2014-1943私はガラスを食べられます.data
@@ -0,0 +1 @@
+ \ No newline at end of file
diff --git a/ext/fileinfo/tests/cve-2014-1943私はガラスを食べられます.magic b/ext/fileinfo/tests/cve-2014-1943私はガラスを食べられます.magic
new file mode 100644
index 0000000000..75a8a70f97
--- /dev/null
+++ b/ext/fileinfo/tests/cve-2014-1943私はガラスを食べられます.magic
@@ -0,0 +1,2 @@
+0 byte x
+>(1.b) indirect x
diff --git a/ext/fileinfo/tests/cve-2014-3538-mb.phpt b/ext/fileinfo/tests/cve-2014-3538-mb.phpt
new file mode 100644
index 0000000000..62d8e43d9b
--- /dev/null
+++ b/ext/fileinfo/tests/cve-2014-3538-mb.phpt
@@ -0,0 +1,35 @@
+--TEST--
+Bug #66731: file: extensive backtraking
+--SKIPIF--
+<?php
+if (!class_exists('finfo'))
+ die('skip no fileinfo extension');
+--FILE--
+<?php
+$fd = __DIR__.'/cve-2014-3538私はガラスを食べられます.data';
+
+file_put_contents($fd,
+ 'try:' .
+ str_repeat("\n", 1000000));
+
+$fi = finfo_open(FILEINFO_NONE);
+$t = microtime(true);
+var_dump(finfo_file($fi, $fd));
+$t = microtime(true) - $t;
+finfo_close($fi);
+if ($t < 1) {
+ echo "Ok\n";
+} else {
+ printf("Failed, time=%.2f\n", $t);
+}
+
+?>
+Done
+--CLEAN--
+<?php
+@unlink(__DIR__.'/cve-2014-3538.data');
+?>
+--EXPECTF--
+string(%d) "%s"
+Ok
+Done \ No newline at end of file
diff --git a/ext/fileinfo/tests/cve-2014-3538私はガラスを食べられます.data b/ext/fileinfo/tests/cve-2014-3538私はガラスを食べられます.data
new file mode 100644
index 0000000000..c5a77dc013
--- /dev/null
+++ b/ext/fileinfo/tests/cve-2014-3538私はガラスを食べられます.data
@@ -0,0 +1,1000000 @@
+try:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ext/fileinfo/tests/finfo_buffer_basic-mb.phpt b/ext/fileinfo/tests/finfo_buffer_basic-mb.phpt
new file mode 100644
index 0000000000..e56062a534
--- /dev/null
+++ b/ext/fileinfo/tests/finfo_buffer_basic-mb.phpt
@@ -0,0 +1,55 @@
+--TEST--
+Test finfo_buffer() function : basic functionality
+--SKIPIF--
+<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
+--FILE--
+<?php
+/* Prototype : string finfo_buffer(resource finfo, char *string [, int options [, resource context]])
+ * Description: Return infromation about a string buffer.
+ * Source code: ext/fileinfo/fileinfo.c
+ * Alias to functions:
+ */
+
+$magicFile = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'magic私はガラスを食べられます';
+
+$options = array(
+ FILEINFO_NONE,
+ FILEINFO_MIME,
+);
+
+$buffers = array(
+ "Regular string here",
+ "\177ELF",
+ "\000\000\0001\000\000\0000\000\000\0000\000\000\0002\000\000\0000\000\000\0000\000\000\0003",
+ "\x55\x7A\x6E\x61",
+ "id=ImageMagick",
+ "RIFFüîò^BAVI LISTv",
+);
+
+echo "*** Testing finfo_buffer() : basic functionality ***\n";
+
+foreach( $options as $option ) {
+ $finfo = finfo_open( $option, $magicFile );
+ foreach( $buffers as $string ) {
+ var_dump( finfo_buffer( $finfo, $string, $option ) );
+ }
+ finfo_close( $finfo );
+}
+
+?>
+===DONE===
+--EXPECTF--
+*** Testing finfo_buffer() : basic functionality ***
+string(36) "ASCII text, with no line terminators"
+string(3) "ELF"
+string(22) "old ACE/gr binary file"
+string(12) "xo65 object,"
+string(15) "MIFF image data"
+string(25) "RIFF (little-endian) data"
+string(28) "text/plain; charset=us-ascii"
+string(26) "text/plain; charset=ebcdic"
+string(40) "application/octet-stream; charset=binary"
+string(28) "text/plain; charset=us-ascii"
+string(28) "text/plain; charset=us-ascii"
+string(25) "text/plain; charset=utf-8"
+===DONE===
diff --git a/ext/fileinfo/tests/finfo_buffer_variation1-mb.phpt b/ext/fileinfo/tests/finfo_buffer_variation1-mb.phpt
new file mode 100644
index 0000000000..5179fd4497
--- /dev/null
+++ b/ext/fileinfo/tests/finfo_buffer_variation1-mb.phpt
@@ -0,0 +1,54 @@
+--TEST--
+Test finfo_buffer() function : basic functionality
+--SKIPIF--
+<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
+--FILE--
+<?php
+/* Prototype : string finfo_buffer(resource finfo, char *string [, int options [, resource context]])
+ * Description: Return infromation about a string buffer.
+ * Source code: ext/fileinfo/fileinfo.c
+ * Alias to functions:
+ */
+
+$magicFile = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'magic私はガラスを食べられます';
+
+$options = array(
+ FILEINFO_NONE,
+ FILEINFO_MIME,
+);
+
+$buffers = array(
+ "Regular string here",
+ "\177ELF",
+ "\000\000\0001\000\000\0000\000\000\0000\000\000\0002\000\000\0000\000\000\0000\000\000\0003",
+ "\x55\x7A\x6E\x61",
+ "id=ImageMagick",
+ "RIFFüîò^BAVI LISTv",
+);
+
+echo "*** Testing finfo_buffer() : variation functionality with oo interface ***\n";
+
+foreach( $options as $option ) {
+ $finfo = new finfo( $option, $magicFile );
+ foreach( $buffers as $string ) {
+ var_dump( $finfo->buffer( $string, $option ) );
+ }
+}
+
+?>
+===DONE===
+--EXPECTF--
+*** Testing finfo_buffer() : variation functionality with oo interface ***
+string(36) "ASCII text, with no line terminators"
+string(3) "ELF"
+string(22) "old ACE/gr binary file"
+string(12) "xo65 object,"
+string(15) "MIFF image data"
+string(25) "RIFF (little-endian) data"
+string(28) "text/plain; charset=us-ascii"
+string(26) "text/plain; charset=ebcdic"
+string(40) "application/octet-stream; charset=binary"
+string(28) "text/plain; charset=us-ascii"
+string(28) "text/plain; charset=us-ascii"
+string(25) "text/plain; charset=utf-8"
+===DONE===
diff --git a/ext/fileinfo/tests/finfo_set_flags_basic-mb.phpt b/ext/fileinfo/tests/finfo_set_flags_basic-mb.phpt
new file mode 100644
index 0000000000..40b788b316
--- /dev/null
+++ b/ext/fileinfo/tests/finfo_set_flags_basic-mb.phpt
@@ -0,0 +1,42 @@
+--TEST--
+Test finfo_set_flags() function : basic functionality
+--SKIPIF--
+<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
+--FILE--
+<?php
+/* Prototype : bool finfo_set_flags(resource finfo, int options)
+ * Description: Set libmagic configuration options.
+ * Source code: ext/fileinfo/fileinfo.c
+ * Alias to functions:
+ */
+
+$magicFile = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'magic私はガラスを食べられます';
+$finfo = finfo_open( FILEINFO_MIME, $magicFile );
+
+echo "*** Testing finfo_set_flags() : basic functionality ***\n";
+
+var_dump( finfo_set_flags( $finfo, FILEINFO_NONE ) );
+var_dump( finfo_set_flags( $finfo, FILEINFO_SYMLINK ) );
+var_dump( finfo_set_flags() );
+
+finfo_close( $finfo );
+
+// OO way
+$finfo = new finfo( FILEINFO_NONE, $magicFile );
+var_dump( $finfo->set_flags( FILEINFO_MIME ) );
+var_dump( $finfo->set_flags() );
+
+?>
+===DONE===
+--EXPECTF--
+*** Testing finfo_set_flags() : basic functionality ***
+bool(true)
+bool(true)
+
+Warning: finfo_set_flags() expects exactly 2 parameters, 0 given in %s on line %d
+bool(false)
+bool(true)
+
+Warning: finfo::set_flags() expects exactly 1 parameter, 0 given in %s on line %d
+bool(false)
+===DONE===
diff --git a/ext/fileinfo/tests/magic私はガラスを食べられます b/ext/fileinfo/tests/magic私はガラスを食べられます
new file mode 100644
index 0000000000..37078c5ccc
--- /dev/null
+++ b/ext/fileinfo/tests/magic私はガラスを食べられます
@@ -0,0 +1,21766 @@
+# Magic data for file(1) command.
+# Format is described in magic(files), where:
+# files is 5 on V7 and BSD, 4 on SV, and ?? on SVID.
+# Don't edit this file, edit /etc/magic or send your magic improvements
+# to the maintainers, at file@mx.gw.com
+
+#------------------------------------------------------------------------------
+# Localstuff: file(1) magic for locally observed files
+#
+# $File: Localstuff,v 1.4 2003/03/23 04:17:27 christos Exp $
+# Add any locally observed files here. Remember:
+# text if readable, executable if runnable binary, data if unreadable.
+
+#------------------------------------------------------------------------------
+# $File$
+# acorn: file(1) magic for files found on Acorn systems
+#
+
+# RISC OS Chunk File Format
+# From RISC OS Programmer's Reference Manual, Appendix D
+# We guess the file type from the type of the first chunk.
+0 lelong 0xc3cbc6c5 RISC OS Chunk data
+>12 string OBJ_ \b, AOF object
+>12 string LIB_ \b, ALF library
+
+# RISC OS AIF, contains "SWI OS_Exit" at offset 16.
+16 lelong 0xef000011 RISC OS AIF executable
+
+# RISC OS Draw files
+# From RISC OS Programmer's Reference Manual, Appendix E
+0 string Draw RISC OS Draw file data
+
+# RISC OS new format font files
+# From RISC OS Programmer's Reference Manual, Appendix E
+0 string FONT\0 RISC OS outline font data,
+>5 byte x version %d
+0 string FONT\1 RISC OS 1bpp font data,
+>5 byte x version %d
+0 string FONT\4 RISC OS 4bpp font data
+>5 byte x version %d
+
+# RISC OS Music files
+# From RISC OS Programmer's Reference Manual, Appendix E
+0 string Maestro\r RISC OS music file
+>8 byte x version %d
+
+>8 byte x type %d
+
+# Digital Symphony data files
+# From: Bernard Jungen (bern8817@euphonynet.be)
+0 string \x02\x01\x13\x13\x13\x01\x0d\x10 Digital Symphony sound sample (RISC OS),
+>8 byte x version %d,
+>9 pstring x named "%s",
+>(9.b+19) byte =0 8-bit logarithmic
+>(9.b+19) byte =1 LZW-compressed linear
+>(9.b+19) byte =2 8-bit linear signed
+>(9.b+19) byte =3 16-bit linear signed
+>(9.b+19) byte =4 SigmaDelta-compressed linear
+>(9.b+19) byte =5 SigmaDelta-compressed logarithmic
+>(9.b+19) byte >5 unknown format
+
+0 string \x02\x01\x13\x13\x14\x12\x01\x0b Digital Symphony song (RISC OS),
+>8 byte x version %d,
+>9 byte =1 1 voice,
+>9 byte !1 %d voices,
+>10 leshort =1 1 track,
+>10 leshort !1 %d tracks,
+>12 leshort =1 1 pattern
+>12 leshort !1 %d patterns
+
+0 string \x02\x01\x13\x13\x10\x14\x12\x0e
+>9 byte =0 Digital Symphony sequence (RISC OS),
+>>8 byte x version %d,
+>>10 byte =1 1 line,
+>>10 byte !1 %d lines,
+>>11 leshort =1 1 position
+>>11 leshort !1 %d positions
+>9 byte =1 Digital Symphony pattern data (RISC OS),
+>>8 byte x version %d,
+>>10 leshort =1 1 pattern
+>>10 leshort !1 %d patterns
+
+#------------------------------------------------------------------------------
+# $File$
+# adi: file(1) magic for ADi's objects
+# From Gregory McGarry <g.mcgarry@ieee.org>
+#
+0 leshort 0x521c COFF DSP21k
+>18 lelong &02 executable,
+>18 lelong ^02
+>>18 lelong &01 static object,
+>>18 lelong ^01 relocatable object,
+>18 lelong &010 stripped
+>18 lelong ^010 not stripped
+
+#------------------------------------------------------------------------------
+# $File: adventure,v 1.13 2010/12/31 16:32:54 christos Exp $
+# adventure: file(1) magic for Adventure game files
+#
+# from Allen Garvin <earendil@faeryland.tamu-commerce.edu>
+# Edited by Dave Chapeskie <dchapes@ddm.on.ca> Jun 28, 1998
+# Edited by Chris Chittleborough <cchittleborough@yahoo.com.au>, March 2002
+#
+# ALAN
+# I assume there are other, lower versions, but these are the only ones I
+# saw in the archive.
+0 beshort 0x0206 ALAN game data
+>2 byte <10 version 2.6%d
+
+
+# Infocom (see z-machine)
+#------------------------------------------------------------------------------
+# Z-machine: file(1) magic for Z-machine binaries.
+# Updated by Adam Buchbinder <adam.buchbinder@gmail.com>
+#
+#http://www.gnelson.demon.co.uk/zspec/sect11.html
+#http://www.jczorkmid.net/~jpenney/ZSpec11-latest.txt
+#http://en.wikipedia.org/wiki/Z-machine
+# The first byte is the Z-machine revision; it is always between 1 and 8. We
+# had false matches (for instance, inbig5.ocp from the Omega TeX extension as
+# well as an occasional MP3 file), so we sanity-check the version number.
+#
+# It might be possible to sanity-check the release number as well, as it seems
+# (at least in classic Infocom games) to always be a relatively small number,
+# always under 150 or so, but as this isn't rigorous, we'll wait on that until
+# it becomes clear that it's needed.
+#
+0 ubyte >0
+>0 ubyte <9
+>>16 belong&0xfe00f0f0 0x3030
+>>>0 ubyte < 10
+>>>>2 ubeshort < 10
+>>>>>18 regex [0-9][0-9][0-9][0-9][0-9][0-9]
+>>>>>>0 ubyte < 10 Infocom (Z-machine %d,
+>>>>>>>2 ubeshort < 10 Release %d /
+>>>>>>>>18 string >\0 Serial %.6s)
+!:strength + 40
+
+#------------------------------------------------------------------------------
+# Glulx: file(1) magic for Glulx binaries.
+#
+# I haven't checked for false matches yet.
+#
+0 string Glul Glulx game data
+>4 beshort x (Version %d
+>>6 byte x \b.%d
+>>8 byte x \b.%d)
+>36 string Info Compiled by Inform
+
+
+
+# For Quetzal and blorb magic see iff
+
+
+# TADS (Text Adventure Development System) version 2
+# All files are machine-independent (games compile to byte-code) and are tagged
+# with a version string of the form "V2.<digit>.<digit>\0".
+# Game files start with "TADS2 bin\n\r\032\0" then the compiler version.
+0 string TADS2\ bin TADS
+>9 belong !0x0A0D1A00 game data, CORRUPTED
+>9 belong 0x0A0D1A00
+>>13 string >\0 %s game data
+# Resource files start with "TADS2 rsc\n\r\032\0" then the compiler version.
+0 string TADS2\ rsc TADS
+>9 belong !0x0A0D1A00 resource data, CORRUPTED
+>9 belong 0x0A0D1A00
+>>13 string >\0 %s resource data
+# Some saved game files start with "TADS2 save/g\n\r\032\0", a little-endian
+# 2-byte length N, the N-char name of the game file *without* a NUL (darn!),
+# "TADS2 save\n\r\032\0" and the interpreter version.
+0 string TADS2\ save/g TADS
+>12 belong !0x0A0D1A00 saved game data, CORRUPTED
+>12 belong 0x0A0D1A00
+>>(16.s+32) string >\0 %s saved game data
+# Other saved game files start with "TADS2 save\n\r\032\0" and the interpreter
+# version.
+0 string TADS2\ save TADS
+>10 belong !0x0A0D1A00 saved game data, CORRUPTED
+>10 belong 0x0A0D1A00
+>>14 string >\0 %s saved game data
+
+# TADS (Text Adventure Development System) version 3
+# Game files start with "T3-image\015\012\032"
+0 string T3-image\015\012\032
+>11 leshort x TADS 3 game data (format version %d)
+# Saved game files start with "T3-state-v####\015\012\032"
+# where #### is a format version number
+0 string T3-state-v
+>14 string \015\012\032 TADS 3 saved game data (format version
+>>10 byte x %c
+>>11 byte x \b%c
+>>12 byte x \b%c
+>>13 byte x \b%c)
+
+# Danny Milosavljevic <danny.milo@gmx.net>
+# this are adrift (adventure game standard) game files, extension .taf
+# depending on version magic continues with 0x93453E6139FA (V 4.0)
+# 0x9445376139FA (V 3.90)
+# 0x9445366139FA (V 3.80)
+# this is from source (http://www.adrift.org.uk/) and I have some taf
+# files, and checked them.
+#0 belong 0x3C423FC9
+#>4 belong 0x6A87C2CF Adrift game file
+#!:mime application/x-adrift
+
+#------------------------------------------------------------------------------
+# $File$
+# allegro: file(1) magic for Allegro datafiles
+# Toby Deshane <hac@shoelace.digivill.net>
+#
+0 belong 0x736C6821 Allegro datafile (packed)
+0 belong 0x736C682E Allegro datafile (not packed/autodetect)
+0 belong 0x736C682B Allegro datafile (appended exe data)
+
+#------------------------------------------------------------------------------
+# $File$
+# alliant: file(1) magic for Alliant FX series a.out files
+#
+# If the FX series is the one that had a processor with a 68K-derived
+# instruction set, the "short" should probably become "beshort" and the
+# "long" should probably become "belong".
+# If it's the i860-based one, they should probably become either the
+# big-endian or little-endian versions, depending on the mode they ran
+# the 860 in....
+#
+0 short 0420 0420 Alliant virtual executable
+>2 short &0x0020 common library
+>16 long >0 not stripped
+0 short 0421 0421 Alliant compact executable
+>2 short &0x0020 common library
+>16 long >0 not stripped
+
+#------------------------------------------------------------------------------
+# $File$
+# alpha architecture description
+#
+
+0 leshort 0603 COFF format alpha
+>22 leshort&030000 !020000 executable
+>24 leshort 0410 pure
+>24 leshort 0413 paged
+>22 leshort&020000 !0 dynamically linked
+>16 lelong !0 not stripped
+>16 lelong 0 stripped
+>22 leshort&030000 020000 shared library
+>24 leshort 0407 object
+>27 byte x - version %d
+>26 byte x .%d
+>28 byte x -%d
+
+# Basic recognition of Digital UNIX core dumps - Mike Bremford <mike@opac.bl.uk>
+#
+# The actual magic number is just "Core", followed by a 2-byte version
+# number; however, treating any file that begins with "Core" as a Digital
+# UNIX core dump file may produce too many false hits, so we include one
+# byte of the version number as well; DU 5.0 appears only to be up to
+# version 2.
+#
+0 string Core\001 Alpha COFF format core dump (Digital UNIX)
+>24 string >\0 \b, from '%s'
+0 string Core\002 Alpha COFF format core dump (Digital UNIX)
+>24 string >\0 \b, from '%s'
+
+
+#------------------------------------------------------------------------------
+# $File$
+# amanda: file(1) magic for amanda file format
+#
+0 string AMANDA:\ AMANDA
+>8 string TAPESTART\ DATE tape header file,
+>>23 string X
+>>>25 string >\ Unused %s
+>>23 string >\ DATE %s
+>8 string FILE\ dump file,
+>>13 string >\ DATE %s
+
+#------------------------------------------------------------------------------
+# $File: amigaos,v 1.14 2009/09/19 16:28:07 christos Exp $
+# amigaos: file(1) magic for AmigaOS binary formats:
+
+#
+# From ignatios@cs.uni-bonn.de (Ignatios Souvatzis)
+#
+0 belong 0x000003fa AmigaOS shared library
+0 belong 0x000003f3 AmigaOS loadseg()ble executable/binary
+0 belong 0x000003e7 AmigaOS object/library data
+#
+0 beshort 0xe310 Amiga Workbench
+>2 beshort 1
+>>48 byte 1 disk icon
+>>48 byte 2 drawer icon
+>>48 byte 3 tool icon
+>>48 byte 4 project icon
+>>48 byte 5 garbage icon
+>>48 byte 6 device icon
+>>48 byte 7 kickstart icon
+>>48 byte 8 workbench application icon
+>2 beshort >1 icon, vers. %d
+#
+# various sound formats from the Amiga
+# G=F6tz Waschk <waschk@informatik.uni-rostock.de>
+#
+0 string FC14 Future Composer 1.4 Module sound file
+0 string SMOD Future Composer 1.3 Module sound file
+0 string AON4artofnoise Art Of Noise Module sound file
+1 string MUGICIAN/SOFTEYES Mugician Module sound file
+58 string SIDMON\ II\ -\ THE Sidmon 2.0 Module sound file
+0 string Synth4.0 Synthesis Module sound file
+0 string ARP. The Holy Noise Module sound file
+0 string BeEp\0 JamCracker Module sound file
+0 string COSO\0 Hippel-COSO Module sound file
+# Too simple (short, pure ASCII, deep), MPi
+#26 string V.3 Brian Postma's Soundmon Module sound file v3
+#26 string BPSM Brian Postma's Soundmon Module sound file v3
+#26 string V.2 Brian Postma's Soundmon Module sound file v2
+
+# The following are from: "Stefan A. Haubenthal" <polluks@web.de>
+0 beshort 0x0f00 AmigaOS bitmap font
+0 beshort 0x0f03 AmigaOS outline font
+0 belong 0x80001001 AmigaOS outline tag
+0 string ##\ version catalog translation
+0 string EMOD\0 Amiga E module
+8 string ECXM\0 ECX module
+0 string/c @database AmigaGuide file
+
+# Amiga disk types
+#
+0 string RDSK Rigid Disk Block
+>160 string x on %.24s
+0 string DOS\0 Amiga DOS disk
+0 string DOS\1 Amiga FFS disk
+0 string DOS\2 Amiga Inter DOS disk
+0 string DOS\3 Amiga Inter FFS disk
+0 string DOS\4 Amiga Fastdir DOS disk
+0 string DOS\5 Amiga Fastdir FFS disk
+0 string KICK Kickstart disk
+
+# From: Alex Beregszaszi <alex@fsn.hu>
+0 string LZX LZX compressed archive (Amiga)
+
+# From: Przemek Kramarczyk <pkramarczyk@gmail.com>
+0 string .KEY AmigaDOS script
+0 string .key AmigaDOS script
+
+#------------------------------------------------------------
+# $File: android,v 1.6 2014/08/04 06:00:36 christos Exp $
+# Various android related magic entries
+#------------------------------------------------------------
+
+# Dalvik .dex format. http://retrodev.com/android/dexformat.html
+# From <mkf@google.com> "Mike Fleming"
+# Fixed to avoid regexec 17 errors on some dex files
+# From <diff@lookout.com> "Tim Strazzere"
+0 string dex\n
+>0 regex dex\n[0-9]{2}\0 Dalvik dex file
+>4 string >000 version %s
+0 string dey\n
+>0 regex dey\n[0-9]{2}\0 Dalvik dex file (optimized for host)
+>4 string >000 version %s
+
+# Android bootimg format
+# From https://android.googlesource.com/\
+# platform/system/core/+/master/mkbootimg/bootimg.h
+0 string ANDROID! Android bootimg
+>1024 string LOKI\01 \b, LOKI'd
+>8 lelong >0 \b, kernel
+>>12 lelong >0 \b (0x%x)
+>16 lelong >0 \b, ramdisk
+>>20 lelong >0 \b (0x%x)
+>24 lelong >0 \b, second stage
+>>28 lelong >0 \b (0x%x)
+>36 lelong >0 \b, page size: %d
+>38 string >0 \b, name: %s
+>64 string >0 \b, cmdline (%s)
+
+# Android Backup archive
+# From: Ariel Shkedi
+# File extension: .ab
+# No mime-type defined
+# URL: https://github.com/android/platform_frameworks_base/blob/\
+# 0bacfd2ba68d21a68a3df345b830bc2a1e515b5a/services/java/com/\
+# android/server/BackupManagerService.java#L2367
+# After the header comes a tar file
+# If compressed, the entire tar file is compressed with JAVA deflate
+#
+# Include the version number hardcoded with the magic string to avoid
+# false positives
+0 string/b ANDROID\ BACKUP\n1\n Android Backup
+>17 string 0\n \b, Not-Compressed
+>17 string 1\n \b, Compressed
+# any string as long as it's not the word none (which is matched below)
+>>19 regex/1l \^([^n\n]|n[^o]|no[^n]|non[^e]|none.+).* \b, Encrypted (%s)
+>>19 string none\n \b, Not-Encrypted
+# Commented out because they don't seem useful to print
+# (but they are part of the header - the tar file comes after them):
+#>>>&1 regex/1l .* \b, Password salt: %s
+#>>>>&1 regex/1l .* \b, Master salt: %s
+#>>>>>&1 regex/1l .* \b, PBKDF2 rounds: %s
+#>>>>>>&1 regex/1l .* \b, IV: %s
+#>>>>>>>&1 regex/1l .* \b, Key: %s
+
+# *.pit files by Joerg Jenderek
+# http://forum.xda-developers.com/showthread.php?p=9122369
+# http://forum.xda-developers.com/showthread.php?t=816449
+# Partition Information Table for Samsung's smartphone with Android
+# used by flash software Odin
+0 ulelong 0x12349876
+# 1st pit entry marker
+>0x01C ulequad&0xFFFFFFFCFFFFFFFC =0x0000000000000000
+# minimal 13 and maximal 18 PIT entries found
+>>4 ulelong <128 Partition Information Table for Samsung smartphone
+>>>4 ulelong x \b, %d entries
+# 1. pit entry
+>>>4 ulelong >0 \b; #1
+>>>0x01C use PIT-entry
+>>>4 ulelong >1 \b; #2
+>>>0x0A0 use PIT-entry
+>>>4 ulelong >2 \b; #3
+>>>0x124 use PIT-entry
+>>>4 ulelong >3 \b; #4
+>>>0x1A8 use PIT-entry
+>>>4 ulelong >4 \b; #5
+>>>0x22C use PIT-entry
+>>>4 ulelong >5 \b; #6
+>>>0x2B0 use PIT-entry
+>>>4 ulelong >6 \b; #7
+>>>0x334 use PIT-entry
+>>>4 ulelong >7 \b; #8
+>>>0x3B8 use PIT-entry
+>>>4 ulelong >8 \b; #9
+>>>0x43C use PIT-entry
+>>>4 ulelong >9 \b; #10
+>>>0x4C0 use PIT-entry
+>>>4 ulelong >10 \b; #11
+>>>0x544 use PIT-entry
+>>>4 ulelong >11 \b; #12
+>>>0x5C8 use PIT-entry
+>>>4 ulelong >12 \b; #13
+>>>>0x64C use PIT-entry
+# 14. pit entry
+>>>4 ulelong >13 \b; #14
+>>>>0x6D0 use PIT-entry
+>>>4 ulelong >14 \b; #15
+>>>0x754 use PIT-entry
+>>>4 ulelong >15 \b; #16
+>>>0x7D8 use PIT-entry
+>>>4 ulelong >16 \b; #17
+>>>0x85C use PIT-entry
+# 18. pit entry
+>>>4 ulelong >17 \b; #18
+>>>0x8E0 use PIT-entry
+
+0 name PIT-entry
+# garbage value implies end of pit entries
+>0x00 ulequad&0xFFFFFFFCFFFFFFFC =0x0000000000000000
+# skip empty partition name
+>>0x24 ubyte !0
+# partition name
+>>>0x24 string >\0 %-.32s
+# flags
+>>>0x0C ulelong&0x00000002 2 \b+RW
+# partition ID:
+# 0~IPL,MOVINAND,GANG;1~PIT,GPT;2~HIDDEN;3~SBL,HIDDEN;4~SBL2,HIDDEN;5~BOOT;6~KENREl,RECOVER,misc;7~RECOVER
+# ;11~MODEM;20~efs;21~PARAM;22~FACTORY,SYSTEM;23~DBDATAFS,USERDATA;24~CACHE;80~BOOTLOADER;81~TZSW
+>>>0x08 ulelong x (0x%x)
+# filename
+>>>0x44 string >\0 "%-.64s"
+#>>>0x18 ulelong >0
+# blocksize in 512 byte units ?
+#>>>>0x18 ulelong x \b, %db
+# partition size in blocks ?
+#>>>>0x22 ulelong x \b*%d
+
+# Android bootimg format
+# From https://android.googlesource.com/\
+# platform/system/core/+/master/libsparse/sparse_format.h
+0 lelong 0xed26ff3a Android sparse image
+>4 leshort x \b, version: %d
+>6 leshort x \b.%d
+>16 lelong x \b, Total of %d
+>12 lelong x \b %d-byte output blocks in
+>20 lelong x \b %d input chunks.
+
+#------------------------------------------------------------------------------
+# $File: animation,v 1.55 2014/09/13 14:29:51 christos Exp $
+# animation: file(1) magic for animation/movie formats
+#
+# animation formats
+# MPEG, FLI, DL originally from vax@ccwf.cc.utexas.edu (VaX#n8)
+# FLC, SGI, Apple originally from Daniel Quinlan (quinlan@yggdrasil.com)
+
+# SGI and Apple formats
+0 string MOVI Silicon Graphics movie file
+!:mime video/x-sgi-movie
+4 string moov Apple QuickTime
+!:mime video/quicktime
+>12 string mvhd \b movie (fast start)
+>12 string mdra \b URL
+>12 string cmov \b movie (fast start, compressed header)
+>12 string rmra \b multiple URLs
+4 string mdat Apple QuickTime movie (unoptimized)
+!:mime video/quicktime
+#4 string wide Apple QuickTime movie (unoptimized)
+#!:mime video/quicktime
+#4 string skip Apple QuickTime movie (modified)
+#!:mime video/quicktime
+#4 string free Apple QuickTime movie (modified)
+#!:mime video/quicktime
+4 string idsc Apple QuickTime image (fast start)
+!:mime image/x-quicktime
+#4 string idat Apple QuickTime image (unoptimized)
+#!:mime image/x-quicktime
+4 string pckg Apple QuickTime compressed archive
+!:mime application/x-quicktime-player
+4 string/W jP JPEG 2000 image
+!:mime image/jp2
+# http://www.ftyps.com/ with local additions
+4 string ftyp ISO Media
+>8 string 3g2 \b, MPEG v4 system, 3GPP2
+!:mime video/3gpp2
+>>11 byte 4 \b v4 (H.263/AMR GSM 6.10)
+>>11 byte 5 \b v5 (H.263/AMR GSM 6.10)
+>>11 byte 6 \b v6 (ITU H.264/AMR GSM 6.10)
+>>11 byte a \b C.S0050-0 V1.0
+>>11 byte b \b C.S0050-0-A V1.0.0
+>>11 byte c \b C.S0050-0-B V1.0
+>8 string 3ge \b, MPEG v4 system, 3GPP
+!:mime video/3gpp
+>>11 byte 6 \b, Release 6 MBMS Extended Presentations
+>>11 byte 7 \b, Release 7 MBMS Extended Presentations
+>8 string 3gg \b, MPEG v4 system, 3GPP
+>11 byte 6 \b, Release 6 General Profile
+!:mime video/3gpp
+>8 string 3gp \b, MPEG v4 system, 3GPP
+>11 byte 1 \b, Release %d (non existent)
+>11 byte 2 \b, Release %d (non existent)
+>11 byte 3 \b, Release %d (non existent)
+>11 byte 4 \b, Release %d
+>11 byte 5 \b, Release %d
+>11 byte 6 \b, Release %d
+>11 byte 7 \b, Release %d Streaming Servers
+!:mime video/3gpp
+>8 string 3gs \b, MPEG v4 system, 3GPP
+>11 byte 7 \b, Release %d Streaming Servers
+!:mime video/3gpp
+>8 string avc1 \b, MPEG v4 system, 3GPP JVT AVC [ISO 14496-12:2005]
+!:mime video/mp4
+>8 string/W qt \b, Apple QuickTime movie
+!:mime video/quicktime
+>8 string CAEP \b, Canon Digital Camera
+>8 string caqv \b, Casio Digital Camera
+>8 string CDes \b, Convergent Design
+>8 string da0a \b, DMB MAF w/ MPEG Layer II aud, MOT slides, DLS, JPG/PNG/MNG
+>8 string da0b \b, DMB MAF, ext DA0A, with 3GPP timed text, DID, TVA, REL, IPMP
+>8 string da1a \b, DMB MAF audio with ER-BSAC audio, JPG/PNG/MNG images
+>8 string da1b \b, DMB MAF, ext da1a, with 3GPP timed text, DID, TVA, REL, IPMP
+>8 string da2a \b, DMB MAF aud w/ HE-AAC v2 aud, MOT slides, DLS, JPG/PNG/MNG
+>8 string da2b \b, DMB MAF, ext da2a, with 3GPP timed text, DID, TVA, REL, IPMP
+>8 string da3a \b, DMB MAF aud with HE-AAC aud, JPG/PNG/MNG images
+>8 string da3b \b, DMB MAF, ext da3a w/ BIFS, 3GPP, DID, TVA, REL, IPMP
+>8 string dmb1 \b, DMB MAF supporting all the components defined in the spec
+>8 string dmpf \b, Digital Media Project
+>8 string drc1 \b, Dirac (wavelet compression), encap in ISO base media (MP4)
+>8 string dv1a \b, DMB MAF vid w/ AVC vid, ER-BSAC aud, BIFS, JPG/PNG/MNG, TS
+>8 string dv1b \b, DMB MAF, ext dv1a, with 3GPP timed text, DID, TVA, REL, IPMP
+>8 string dv2a \b, DMB MAF vid w/ AVC vid, HE-AAC v2 aud, BIFS, JPG/PNG/MNG, TS
+>8 string dv2b \b, DMB MAF, ext dv2a, with 3GPP timed text, DID, TVA, REL, IPMP
+>8 string dv3a \b, DMB MAF vid w/ AVC vid, HE-AAC aud, BIFS, JPG/PNG/MNG, TS
+>8 string dv3b \b, DMB MAF, ext dv3a, with 3GPP timed text, DID, TVA, REL, IPMP
+>8 string dvr1 \b, DVB (.DVB) over RTP
+!:mime video/vnd.dvb.file
+>8 string dvt1 \b, DVB (.DVB) over MPEG-2 Transport Stream
+!:mime video/vnd.dvb.file
+>8 string F4V \b, Video for Adobe Flash Player 9+ (.F4V)
+!:mime video/mp4
+>8 string F4P \b, Protected Video for Adobe Flash Player 9+ (.F4P)
+!:mime video/mp4
+>8 string F4A \b, Audio for Adobe Flash Player 9+ (.F4A)
+!:mime audio/mp4
+>8 string F4B \b, Audio Book for Adobe Flash Player 9+ (.F4B)
+!:mime audio/mp4
+>8 string isc2 \b, ISMACryp 2.0 Encrypted File
+# ?/enc-isoff-generic
+>8 string iso2 \b, MP4 Base Media v2 [ISO 14496-12:2005]
+!:mime video/mp4
+>8 string isom \b, MP4 Base Media v1 [IS0 14496-12:2003]
+!:mime video/mp4
+>8 string/W jp2 \b, JPEG 2000
+!:mime image/jp2
+>8 string JP2 \b, JPEG 2000 Image (.JP2) [ISO 15444-1 ?]
+!:mime image/jp2
+>8 string JP20 \b, Unknown, from GPAC samples (prob non-existent)
+>8 string jpm \b, JPEG 2000 Compound Image (.JPM) [ISO 15444-6]
+!:mime image/jpm
+>8 string jpx \b, JPEG 2000 w/ extensions (.JPX) [ISO 15444-2]
+!:mime image/jpx
+>8 string KDDI \b, 3GPP2 EZmovie for KDDI 3G cellphones
+!:mime video/3gpp2
+>8 string M4A \b, Apple iTunes ALAC/AAC-LC (.M4A) Audio
+!:mime audio/x-m4a
+>8 string M4B \b, Apple iTunes ALAC/AAC-LC (.M4B) Audio Book
+!:mime audio/mp4
+>8 string M4P \b, Apple iTunes ALAC/AAC-LC (.M4P) AES Protected Audio
+!:mime video/mp4
+>8 string M4V \b, Apple iTunes Video (.M4V) Video
+!:mime video/x-m4v
+>8 string M4VH \b, Apple TV (.M4V)
+!:mime video/x-m4v
+>8 string M4VP \b, Apple iPhone (.M4V)
+!:mime video/x-m4v
+>8 string mj2s \b, Motion JPEG 2000 [ISO 15444-3] Simple Profile
+!:mime video/mj2
+>8 string mjp2 \b, Motion JPEG 2000 [ISO 15444-3] General Profile
+!:mime video/mj2
+>8 string mmp4 \b, MPEG-4/3GPP Mobile Profile (.MP4 / .3GP) (for NTT)
+!:mime video/mp4
+>8 string mobi \b, MPEG-4, MOBI format
+!:mime video/mp4
+>8 string mp21 \b, MPEG-21 [ISO/IEC 21000-9]
+>8 string mp41 \b, MP4 v1 [ISO 14496-1:ch13]
+!:mime video/mp4
+>8 string mp42 \b, MP4 v2 [ISO 14496-14]
+!:mime video/mp4
+>8 string mp71 \b, MP4 w/ MPEG-7 Metadata [per ISO 14496-12]
+>8 string mp7t \b, MPEG v4 system, MPEG v7 XML
+>8 string mp7b \b, MPEG v4 system, MPEG v7 binary XML
+>8 string mmp4 \b, MPEG v4 system, 3GPP Mobile
+!:mime video/mp4
+>8 string MPPI \b, Photo Player, MAF [ISO/IEC 23000-3]
+>8 string mqt \b, Sony / Mobile QuickTime (.MQV) US Pat 7,477,830
+!:mime video/quicktime
+>8 string MSNV \b, MPEG-4 (.MP4) for SonyPSP
+!:mime audio/mp4
+>8 string NDAS \b, MP4 v2 [ISO 14496-14] Nero Digital AAC Audio
+!:mime audio/mp4
+>8 string NDSC \b, MPEG-4 (.MP4) Nero Cinema Profile
+!:mime video/mp4
+>8 string NDSH \b, MPEG-4 (.MP4) Nero HDTV Profile
+!:mime video/mp4
+>8 string NDSM \b, MPEG-4 (.MP4) Nero Mobile Profile
+!:mime video/mp4
+>8 string NDSP \b, MPEG-4 (.MP4) Nero Portable Profile
+!:mime video/mp4
+>8 string NDSS \b, MPEG-4 (.MP4) Nero Standard Profile
+!:mime video/mp4
+>8 string NDXC \b, H.264/MPEG-4 AVC (.MP4) Nero Cinema Profile
+!:mime video/mp4
+>8 string NDXH \b, H.264/MPEG-4 AVC (.MP4) Nero HDTV Profile
+!:mime video/mp4
+>8 string NDXM \b, H.264/MPEG-4 AVC (.MP4) Nero Mobile Profile
+!:mime video/mp4
+>8 string NDXP \b, H.264/MPEG-4 AVC (.MP4) Nero Portable Profile
+!:mime video/mp4
+>8 string NDXS \b, H.264/MPEG-4 AVC (.MP4) Nero Standard Profile
+!:mime video/mp4
+>8 string odcf \b, OMA DCF DRM Format 2.0 (OMA-TS-DRM-DCF-V2_0-20060303-A)
+>8 string opf2 \b, OMA PDCF DRM Format 2.1 (OMA-TS-DRM-DCF-V2_1-20070724-C)
+>8 string opx2 \b, OMA PDCF DRM + XBS ext (OMA-TS-DRM_XBS-V1_0-20070529-C)
+>8 string pana \b, Panasonic Digital Camera
+>8 string qt \b, Apple QuickTime (.MOV/QT)
+!:mime video/quicktime
+>8 string ROSS \b, Ross Video
+>8 string sdv \b, SD Memory Card Video
+>8 string ssc1 \b, Samsung stereo, single stream (patent pending)
+>8 string ssc2 \b, Samsung stereo, dual stream (patent pending)
+
+# MPEG sequences
+# Scans for all common MPEG header start codes
+0 belong 0x00000001
+>4 byte&0x1F 0x07 JVT NAL sequence, H.264 video
+>>5 byte 66 \b, baseline
+>>5 byte 77 \b, main
+>>5 byte 88 \b, extended
+>>7 byte x \b @ L %u
+0 belong&0xFFFFFF00 0x00000100
+>3 byte 0xBA MPEG sequence
+!:mime video/mpeg
+>>4 byte &0x40 \b, v2, program multiplex
+>>4 byte ^0x40 \b, v1, system multiplex
+>3 byte 0xBB MPEG sequence, v1/2, multiplex (missing pack header)
+>3 byte&0x1F 0x07 MPEG sequence, H.264 video
+>>4 byte 66 \b, baseline
+>>4 byte 77 \b, main
+>>4 byte 88 \b, extended
+>>6 byte x \b @ L %u
+# GRR too general as it catches also FoxPro Memo example NG.FPT
+>3 byte 0xB0 MPEG sequence, v4
+# TODO: maybe this extra line exclude FoxPro Memo example NG.FPT starting with 000001b0 00000100 00000000
+#>>4 byte !0 MPEG sequence, v4
+!:mime video/mpeg4-generic
+>>5 belong 0x000001B5
+>>>9 byte &0x80
+>>>>10 byte&0xF0 16 \b, video
+>>>>10 byte&0xF0 32 \b, still texture
+>>>>10 byte&0xF0 48 \b, mesh
+>>>>10 byte&0xF0 64 \b, face
+>>>9 byte&0xF8 8 \b, video
+>>>9 byte&0xF8 16 \b, still texture
+>>>9 byte&0xF8 24 \b, mesh
+>>>9 byte&0xF8 32 \b, face
+>>4 byte 1 \b, simple @ L1
+>>4 byte 2 \b, simple @ L2
+>>4 byte 3 \b, simple @ L3
+>>4 byte 4 \b, simple @ L0
+>>4 byte 17 \b, simple scalable @ L1
+>>4 byte 18 \b, simple scalable @ L2
+>>4 byte 33 \b, core @ L1
+>>4 byte 34 \b, core @ L2
+>>4 byte 50 \b, main @ L2
+>>4 byte 51 \b, main @ L3
+>>4 byte 53 \b, main @ L4
+>>4 byte 66 \b, n-bit @ L2
+>>4 byte 81 \b, scalable texture @ L1
+>>4 byte 97 \b, simple face animation @ L1
+>>4 byte 98 \b, simple face animation @ L2
+>>4 byte 99 \b, simple face basic animation @ L1
+>>4 byte 100 \b, simple face basic animation @ L2
+>>4 byte 113 \b, basic animation text @ L1
+>>4 byte 114 \b, basic animation text @ L2
+>>4 byte 129 \b, hybrid @ L1
+>>4 byte 130 \b, hybrid @ L2
+>>4 byte 145 \b, advanced RT simple @ L!
+>>4 byte 146 \b, advanced RT simple @ L2
+>>4 byte 147 \b, advanced RT simple @ L3
+>>4 byte 148 \b, advanced RT simple @ L4
+>>4 byte 161 \b, core scalable @ L1
+>>4 byte 162 \b, core scalable @ L2
+>>4 byte 163 \b, core scalable @ L3
+>>4 byte 177 \b, advanced coding efficiency @ L1
+>>4 byte 178 \b, advanced coding efficiency @ L2
+>>4 byte 179 \b, advanced coding efficiency @ L3
+>>4 byte 180 \b, advanced coding efficiency @ L4
+>>4 byte 193 \b, advanced core @ L1
+>>4 byte 194 \b, advanced core @ L2
+>>4 byte 209 \b, advanced scalable texture @ L1
+>>4 byte 210 \b, advanced scalable texture @ L2
+>>4 byte 211 \b, advanced scalable texture @ L3
+>>4 byte 225 \b, simple studio @ L1
+>>4 byte 226 \b, simple studio @ L2
+>>4 byte 227 \b, simple studio @ L3
+>>4 byte 228 \b, simple studio @ L4
+>>4 byte 229 \b, core studio @ L1
+>>4 byte 230 \b, core studio @ L2
+>>4 byte 231 \b, core studio @ L3
+>>4 byte 232 \b, core studio @ L4
+>>4 byte 240 \b, advanced simple @ L0
+>>4 byte 241 \b, advanced simple @ L1
+>>4 byte 242 \b, advanced simple @ L2
+>>4 byte 243 \b, advanced simple @ L3
+>>4 byte 244 \b, advanced simple @ L4
+>>4 byte 245 \b, advanced simple @ L5
+>>4 byte 247 \b, advanced simple @ L3b
+>>4 byte 248 \b, FGS @ L0
+>>4 byte 249 \b, FGS @ L1
+>>4 byte 250 \b, FGS @ L2
+>>4 byte 251 \b, FGS @ L3
+>>4 byte 252 \b, FGS @ L4
+>>4 byte 253 \b, FGS @ L5
+>3 byte 0xB5 MPEG sequence, v4
+!:mime video/mpeg4-generic
+>>4 byte &0x80
+>>>5 byte&0xF0 16 \b, video (missing profile header)
+>>>5 byte&0xF0 32 \b, still texture (missing profile header)
+>>>5 byte&0xF0 48 \b, mesh (missing profile header)
+>>>5 byte&0xF0 64 \b, face (missing profile header)
+>>4 byte&0xF8 8 \b, video (missing profile header)
+>>4 byte&0xF8 16 \b, still texture (missing profile header)
+>>4 byte&0xF8 24 \b, mesh (missing profile header)
+>>4 byte&0xF8 32 \b, face (missing profile header)
+>3 byte 0xB3 MPEG sequence
+!:mime video/mpeg
+>>12 belong 0x000001B8 \b, v1, progressive Y'CbCr 4:2:0 video
+>>12 belong 0x000001B2 \b, v1, progressive Y'CbCr 4:2:0 video
+>>12 belong 0x000001B5 \b, v2,
+>>>16 byte&0x0F 1 \b HP
+>>>16 byte&0x0F 2 \b Spt
+>>>16 byte&0x0F 3 \b SNR
+>>>16 byte&0x0F 4 \b MP
+>>>16 byte&0x0F 5 \b SP
+>>>17 byte&0xF0 64 \b@HL
+>>>17 byte&0xF0 96 \b@H-14
+>>>17 byte&0xF0 128 \b@ML
+>>>17 byte&0xF0 160 \b@LL
+>>>17 byte &0x08 \b progressive
+>>>17 byte ^0x08 \b interlaced
+>>>17 byte&0x06 2 \b Y'CbCr 4:2:0 video
+>>>17 byte&0x06 4 \b Y'CbCr 4:2:2 video
+>>>17 byte&0x06 6 \b Y'CbCr 4:4:4 video
+>>11 byte &0x02
+>>>75 byte &0x01
+>>>>140 belong 0x000001B8 \b, v1, progressive Y'CbCr 4:2:0 video
+>>>>140 belong 0x000001B2 \b, v1, progressive Y'CbCr 4:2:0 video
+>>>>140 belong 0x000001B5 \b, v2,
+>>>>>144 byte&0x0F 1 \b HP
+>>>>>144 byte&0x0F 2 \b Spt
+>>>>>144 byte&0x0F 3 \b SNR
+>>>>>144 byte&0x0F 4 \b MP
+>>>>>144 byte&0x0F 5 \b SP
+>>>>>145 byte&0xF0 64 \b@HL
+>>>>>145 byte&0xF0 96 \b@H-14
+>>>>>145 byte&0xF0 128 \b@ML
+>>>>>145 byte&0xF0 160 \b@LL
+>>>>>145 byte &0x08 \b progressive
+>>>>>145 byte ^0x08 \b interlaced
+>>>>>145 byte&0x06 2 \b Y'CbCr 4:2:0 video
+>>>>>145 byte&0x06 4 \b Y'CbCr 4:2:2 video
+>>>>>145 byte&0x06 6 \b Y'CbCr 4:4:4 video
+>>76 belong 0x000001B8 \b, v1, progressive Y'CbCr 4:2:0 video
+>>76 belong 0x000001B2 \b, v1, progressive Y'CbCr 4:2:0 video
+>>76 belong 0x000001B5 \b, v2,
+>>>80 byte&0x0F 1 \b HP
+>>>80 byte&0x0F 2 \b Spt
+>>>80 byte&0x0F 3 \b SNR
+>>>80 byte&0x0F 4 \b MP
+>>>80 byte&0x0F 5 \b SP
+>>>81 byte&0xF0 64 \b@HL
+>>>81 byte&0xF0 96 \b@H-14
+>>>81 byte&0xF0 128 \b@ML
+>>>81 byte&0xF0 160 \b@LL
+>>>81 byte &0x08 \b progressive
+>>>81 byte ^0x08 \b interlaced
+>>>81 byte&0x06 2 \b Y'CbCr 4:2:0 video
+>>>81 byte&0x06 4 \b Y'CbCr 4:2:2 video
+>>>81 byte&0x06 6 \b Y'CbCr 4:4:4 video
+>>4 belong&0xFFFFFF00 0x78043800 \b, HD-TV 1920P
+>>>7 byte&0xF0 0x10 \b, 16:9
+>>4 belong&0xFFFFFF00 0x50002D00 \b, SD-TV 1280I
+>>>7 byte&0xF0 0x10 \b, 16:9
+>>4 belong&0xFFFFFF00 0x30024000 \b, PAL Capture
+>>>7 byte&0xF0 0x10 \b, 4:3
+>>4 beshort&0xFFF0 0x2C00 \b, 4CIF
+>>>5 beshort&0x0FFF 0x01E0 \b NTSC
+>>>5 beshort&0x0FFF 0x0240 \b PAL
+>>>7 byte&0xF0 0x20 \b, 4:3
+>>>7 byte&0xF0 0x30 \b, 16:9
+>>>7 byte&0xF0 0x40 \b, 11:5
+>>>7 byte&0xF0 0x80 \b, PAL 4:3
+>>>7 byte&0xF0 0xC0 \b, NTSC 4:3
+>>4 belong&0xFFFFFF00 0x2801E000 \b, LD-TV 640P
+>>>7 byte&0xF0 0x10 \b, 4:3
+>>4 belong&0xFFFFFF00 0x1400F000 \b, 320x240
+>>>7 byte&0xF0 0x10 \b, 4:3
+>>4 belong&0xFFFFFF00 0x0F00A000 \b, 240x160
+>>>7 byte&0xF0 0x10 \b, 4:3
+>>4 belong&0xFFFFFF00 0x0A007800 \b, 160x120
+>>>7 byte&0xF0 0x10 \b, 4:3
+>>4 beshort&0xFFF0 0x1600 \b, CIF
+>>>5 beshort&0x0FFF 0x00F0 \b NTSC
+>>>5 beshort&0x0FFF 0x0120 \b PAL
+>>>7 byte&0xF0 0x20 \b, 4:3
+>>>7 byte&0xF0 0x30 \b, 16:9
+>>>7 byte&0xF0 0x40 \b, 11:5
+>>>7 byte&0xF0 0x80 \b, PAL 4:3
+>>>7 byte&0xF0 0xC0 \b, NTSC 4:3
+>>>5 beshort&0x0FFF 0x0240 \b PAL 625
+>>>>7 byte&0xF0 0x20 \b, 4:3
+>>>>7 byte&0xF0 0x30 \b, 16:9
+>>>>7 byte&0xF0 0x40 \b, 11:5
+>>4 beshort&0xFFF0 0x2D00 \b, CCIR/ITU
+>>>5 beshort&0x0FFF 0x01E0 \b NTSC 525
+>>>5 beshort&0x0FFF 0x0240 \b PAL 625
+>>>7 byte&0xF0 0x20 \b, 4:3
+>>>7 byte&0xF0 0x30 \b, 16:9
+>>>7 byte&0xF0 0x40 \b, 11:5
+>>4 beshort&0xFFF0 0x1E00 \b, SVCD
+>>>5 beshort&0x0FFF 0x01E0 \b NTSC 525
+>>>5 beshort&0x0FFF 0x0240 \b PAL 625
+>>>7 byte&0xF0 0x20 \b, 4:3
+>>>7 byte&0xF0 0x30 \b, 16:9
+>>>7 byte&0xF0 0x40 \b, 11:5
+>>7 byte&0x0F 1 \b, 23.976 fps
+>>7 byte&0x0F 2 \b, 24 fps
+>>7 byte&0x0F 3 \b, 25 fps
+>>7 byte&0x0F 4 \b, 29.97 fps
+>>7 byte&0x0F 5 \b, 30 fps
+>>7 byte&0x0F 6 \b, 50 fps
+>>7 byte&0x0F 7 \b, 59.94 fps
+>>7 byte&0x0F 8 \b, 60 fps
+>>11 byte &0x04 \b, Constrained
+
+# MPEG ADTS Audio (*.mpx/mxa/aac)
+# from dreesen@math.fu-berlin.de
+# modified to fully support MPEG ADTS
+
+# MP3, M1A
+# modified by Joerg Jenderek
+# GRR the original test are too common for many DOS files
+# so don't accept as MP3 until we've tested the rate
+0 beshort&0xFFFE 0xFFFA
+# rates
+>2 byte&0xF0 0x10 MPEG ADTS, layer III, v1, 32 kbps
+!:mime audio/mpeg
+>2 byte&0xF0 0x20 MPEG ADTS, layer III, v1, 40 kbps
+!:mime audio/mpeg
+>2 byte&0xF0 0x30 MPEG ADTS, layer III, v1, 48 kbps
+!:mime audio/mpeg
+>2 byte&0xF0 0x40 MPEG ADTS, layer III, v1, 56 kbps
+!:mime audio/mpeg
+>2 byte&0xF0 0x50 MPEG ADTS, layer III, v1, 64 kbps
+!:mime audio/mpeg
+>2 byte&0xF0 0x60 MPEG ADTS, layer III, v1, 80 kbps
+!:mime audio/mpeg
+>2 byte&0xF0 0x70 MPEG ADTS, layer III, v1, 96 kbps
+!:mime audio/mpeg
+>2 byte&0xF0 0x80 MPEG ADTS, layer III, v1, 112 kbps
+!:mime audio/mpeg
+>2 byte&0xF0 0x90 MPEG ADTS, layer III, v1, 128 kbps
+!:mime audio/mpeg
+>2 byte&0xF0 0xA0 MPEG ADTS, layer III, v1, 160 kbps
+!:mime audio/mpeg
+>2 byte&0xF0 0xB0 MPEG ADTS, layer III, v1, 192 kbps
+!:mime audio/mpeg
+>2 byte&0xF0 0xC0 MPEG ADTS, layer III, v1, 224 kbps
+!:mime audio/mpeg
+>2 byte&0xF0 0xD0 MPEG ADTS, layer III, v1, 256 kbps
+!:mime audio/mpeg
+>2 byte&0xF0 0xE0 MPEG ADTS, layer III, v1, 320 kbps
+!:mime audio/mpeg
+# timing
+>2 byte&0x0C 0x00 \b, 44.1 kHz
+>2 byte&0x0C 0x04 \b, 48 kHz
+>2 byte&0x0C 0x08 \b, 32 kHz
+# channels/options
+>3 byte&0xC0 0x00 \b, Stereo
+>3 byte&0xC0 0x40 \b, JntStereo
+>3 byte&0xC0 0x80 \b, 2x Monaural
+>3 byte&0xC0 0xC0 \b, Monaural
+#>1 byte ^0x01 \b, Data Verify
+#>2 byte &0x02 \b, Packet Pad
+#>2 byte &0x01 \b, Custom Flag
+#>3 byte &0x08 \b, Copyrighted
+#>3 byte &0x04 \b, Original Source
+#>3 byte&0x03 1 \b, NR: 50/15 ms
+#>3 byte&0x03 3 \b, NR: CCIT J.17
+
+# MP2, M1A
+0 beshort&0xFFFE 0xFFFC MPEG ADTS, layer II, v1
+!:mime audio/mpeg
+# rates
+>2 byte&0xF0 0x10 \b, 32 kbps
+>2 byte&0xF0 0x20 \b, 48 kbps
+>2 byte&0xF0 0x30 \b, 56 kbps
+>2 byte&0xF0 0x40 \b, 64 kbps
+>2 byte&0xF0 0x50 \b, 80 kbps
+>2 byte&0xF0 0x60 \b, 96 kbps
+>2 byte&0xF0 0x70 \b, 112 kbps
+>2 byte&0xF0 0x80 \b, 128 kbps
+>2 byte&0xF0 0x90 \b, 160 kbps
+>2 byte&0xF0 0xA0 \b, 192 kbps
+>2 byte&0xF0 0xB0 \b, 224 kbps
+>2 byte&0xF0 0xC0 \b, 256 kbps
+>2 byte&0xF0 0xD0 \b, 320 kbps
+>2 byte&0xF0 0xE0 \b, 384 kbps
+# timing
+>2 byte&0x0C 0x00 \b, 44.1 kHz
+>2 byte&0x0C 0x04 \b, 48 kHz
+>2 byte&0x0C 0x08 \b, 32 kHz
+# channels/options
+>3 byte&0xC0 0x00 \b, Stereo
+>3 byte&0xC0 0x40 \b, JntStereo
+>3 byte&0xC0 0x80 \b, 2x Monaural
+>3 byte&0xC0 0xC0 \b, Monaural
+#>1 byte ^0x01 \b, Data Verify
+#>2 byte &0x02 \b, Packet Pad
+#>2 byte &0x01 \b, Custom Flag
+#>3 byte &0x08 \b, Copyrighted
+#>3 byte &0x04 \b, Original Source
+#>3 byte&0x03 1 \b, NR: 50/15 ms
+#>3 byte&0x03 3 \b, NR: CCIT J.17
+
+# MPA, M1A
+# updated by Joerg Jenderek
+# GRR the original test are too common for many DOS files, so test 32 <= kbits <= 448
+# GRR this test is still too general as it catches a BOM of UTF-16 files (0xFFFE)
+# FIXME: Almost all little endian UTF-16 text with BOM are clobbered by these entries
+#0 beshort&0xFFFE 0xFFFE
+#>2 ubyte&0xF0 >0x0F
+#>>2 ubyte&0xF0 <0xE1 MPEG ADTS, layer I, v1
+## rate
+#>>>2 byte&0xF0 0x10 \b, 32 kbps
+#>>>2 byte&0xF0 0x20 \b, 64 kbps
+#>>>2 byte&0xF0 0x30 \b, 96 kbps
+#>>>2 byte&0xF0 0x40 \b, 128 kbps
+#>>>2 byte&0xF0 0x50 \b, 160 kbps
+#>>>2 byte&0xF0 0x60 \b, 192 kbps
+#>>>2 byte&0xF0 0x70 \b, 224 kbps
+#>>>2 byte&0xF0 0x80 \b, 256 kbps
+#>>>2 byte&0xF0 0x90 \b, 288 kbps
+#>>>2 byte&0xF0 0xA0 \b, 320 kbps
+#>>>2 byte&0xF0 0xB0 \b, 352 kbps
+#>>>2 byte&0xF0 0xC0 \b, 384 kbps
+#>>>2 byte&0xF0 0xD0 \b, 416 kbps
+#>>>2 byte&0xF0 0xE0 \b, 448 kbps
+## timing
+#>>>2 byte&0x0C 0x00 \b, 44.1 kHz
+#>>>2 byte&0x0C 0x04 \b, 48 kHz
+#>>>2 byte&0x0C 0x08 \b, 32 kHz
+## channels/options
+#>>>3 byte&0xC0 0x00 \b, Stereo
+#>>>3 byte&0xC0 0x40 \b, JntStereo
+#>>>3 byte&0xC0 0x80 \b, 2x Monaural
+#>>>3 byte&0xC0 0xC0 \b, Monaural
+##>1 byte ^0x01 \b, Data Verify
+##>2 byte &0x02 \b, Packet Pad
+##>2 byte &0x01 \b, Custom Flag
+##>3 byte &0x08 \b, Copyrighted
+##>3 byte &0x04 \b, Original Source
+##>3 byte&0x03 1 \b, NR: 50/15 ms
+##>3 byte&0x03 3 \b, NR: CCIT J.17
+
+# MP3, M2A
+0 beshort&0xFFFE 0xFFF2 MPEG ADTS, layer III, v2
+!:mime audio/mpeg
+# rate
+>2 byte&0xF0 0x10 \b, 8 kbps
+>2 byte&0xF0 0x20 \b, 16 kbps
+>2 byte&0xF0 0x30 \b, 24 kbps
+>2 byte&0xF0 0x40 \b, 32 kbps
+>2 byte&0xF0 0x50 \b, 40 kbps
+>2 byte&0xF0 0x60 \b, 48 kbps
+>2 byte&0xF0 0x70 \b, 56 kbps
+>2 byte&0xF0 0x80 \b, 64 kbps
+>2 byte&0xF0 0x90 \b, 80 kbps
+>2 byte&0xF0 0xA0 \b, 96 kbps
+>2 byte&0xF0 0xB0 \b, 112 kbps
+>2 byte&0xF0 0xC0 \b, 128 kbps
+>2 byte&0xF0 0xD0 \b, 144 kbps
+>2 byte&0xF0 0xE0 \b, 160 kbps
+# timing
+>2 byte&0x0C 0x00 \b, 22.05 kHz
+>2 byte&0x0C 0x04 \b, 24 kHz
+>2 byte&0x0C 0x08 \b, 16 kHz
+# channels/options
+>3 byte&0xC0 0x00 \b, Stereo
+>3 byte&0xC0 0x40 \b, JntStereo
+>3 byte&0xC0 0x80 \b, 2x Monaural
+>3 byte&0xC0 0xC0 \b, Monaural
+#>1 byte ^0x01 \b, Data Verify
+#>2 byte &0x02 \b, Packet Pad
+#>2 byte &0x01 \b, Custom Flag
+#>3 byte &0x08 \b, Copyrighted
+#>3 byte &0x04 \b, Original Source
+#>3 byte&0x03 1 \b, NR: 50/15 ms
+#>3 byte&0x03 3 \b, NR: CCIT J.17
+
+# MP2, M2A
+0 beshort&0xFFFE 0xFFF4 MPEG ADTS, layer II, v2
+!:mime audio/mpeg
+# rate
+>2 byte&0xF0 0x10 \b, 8 kbps
+>2 byte&0xF0 0x20 \b, 16 kbps
+>2 byte&0xF0 0x30 \b, 24 kbps
+>2 byte&0xF0 0x40 \b, 32 kbps
+>2 byte&0xF0 0x50 \b, 40 kbps
+>2 byte&0xF0 0x60 \b, 48 kbps
+>2 byte&0xF0 0x70 \b, 56 kbps
+>2 byte&0xF0 0x80 \b, 64 kbps
+>2 byte&0xF0 0x90 \b, 80 kbps
+>2 byte&0xF0 0xA0 \b, 96 kbps
+>2 byte&0xF0 0xB0 \b, 112 kbps
+>2 byte&0xF0 0xC0 \b, 128 kbps
+>2 byte&0xF0 0xD0 \b, 144 kbps
+>2 byte&0xF0 0xE0 \b, 160 kbps
+# timing
+>2 byte&0x0C 0x00 \b, 22.05 kHz
+>2 byte&0x0C 0x04 \b, 24 kHz
+>2 byte&0x0C 0x08 \b, 16 kHz
+# channels/options
+>3 byte&0xC0 0x00 \b, Stereo
+>3 byte&0xC0 0x40 \b, JntStereo
+>3 byte&0xC0 0x80 \b, 2x Monaural
+>3 byte&0xC0 0xC0 \b, Monaural
+#>1 byte ^0x01 \b, Data Verify
+#>2 byte &0x02 \b, Packet Pad
+#>2 byte &0x01 \b, Custom Flag
+#>3 byte &0x08 \b, Copyrighted
+#>3 byte &0x04 \b, Original Source
+#>3 byte&0x03 1 \b, NR: 50/15 ms
+#>3 byte&0x03 3 \b, NR: CCIT J.17
+
+# MPA, M2A
+0 beshort&0xFFFE 0xFFF6 MPEG ADTS, layer I, v2
+!:mime audio/mpeg
+# rate
+>2 byte&0xF0 0x10 \b, 32 kbps
+>2 byte&0xF0 0x20 \b, 48 kbps
+>2 byte&0xF0 0x30 \b, 56 kbps
+>2 byte&0xF0 0x40 \b, 64 kbps
+>2 byte&0xF0 0x50 \b, 80 kbps
+>2 byte&0xF0 0x60 \b, 96 kbps
+>2 byte&0xF0 0x70 \b, 112 kbps
+>2 byte&0xF0 0x80 \b, 128 kbps
+>2 byte&0xF0 0x90 \b, 144 kbps
+>2 byte&0xF0 0xA0 \b, 160 kbps
+>2 byte&0xF0 0xB0 \b, 176 kbps
+>2 byte&0xF0 0xC0 \b, 192 kbps
+>2 byte&0xF0 0xD0 \b, 224 kbps
+>2 byte&0xF0 0xE0 \b, 256 kbps
+# timing
+>2 byte&0x0C 0x00 \b, 22.05 kHz
+>2 byte&0x0C 0x04 \b, 24 kHz
+>2 byte&0x0C 0x08 \b, 16 kHz
+# channels/options
+>3 byte&0xC0 0x00 \b, Stereo
+>3 byte&0xC0 0x40 \b, JntStereo
+>3 byte&0xC0 0x80 \b, 2x Monaural
+>3 byte&0xC0 0xC0 \b, Monaural
+#>1 byte ^0x01 \b, Data Verify
+#>2 byte &0x02 \b, Packet Pad
+#>2 byte &0x01 \b, Custom Flag
+#>3 byte &0x08 \b, Copyrighted
+#>3 byte &0x04 \b, Original Source
+#>3 byte&0x03 1 \b, NR: 50/15 ms
+#>3 byte&0x03 3 \b, NR: CCIT J.17
+
+# MP3, M25A
+0 beshort&0xFFFE 0xFFE2 MPEG ADTS, layer III, v2.5
+!:mime audio/mpeg
+# rate
+>2 byte&0xF0 0x10 \b, 8 kbps
+>2 byte&0xF0 0x20 \b, 16 kbps
+>2 byte&0xF0 0x30 \b, 24 kbps
+>2 byte&0xF0 0x40 \b, 32 kbps
+>2 byte&0xF0 0x50 \b, 40 kbps
+>2 byte&0xF0 0x60 \b, 48 kbps
+>2 byte&0xF0 0x70 \b, 56 kbps
+>2 byte&0xF0 0x80 \b, 64 kbps
+>2 byte&0xF0 0x90 \b, 80 kbps
+>2 byte&0xF0 0xA0 \b, 96 kbps
+>2 byte&0xF0 0xB0 \b, 112 kbps
+>2 byte&0xF0 0xC0 \b, 128 kbps
+>2 byte&0xF0 0xD0 \b, 144 kbps
+>2 byte&0xF0 0xE0 \b, 160 kbps
+# timing
+>2 byte&0x0C 0x00 \b, 11.025 kHz
+>2 byte&0x0C 0x04 \b, 12 kHz
+>2 byte&0x0C 0x08 \b, 8 kHz
+# channels/options
+>3 byte&0xC0 0x00 \b, Stereo
+>3 byte&0xC0 0x40 \b, JntStereo
+>3 byte&0xC0 0x80 \b, 2x Monaural
+>3 byte&0xC0 0xC0 \b, Monaural
+#>1 byte ^0x01 \b, Data Verify
+#>2 byte &0x02 \b, Packet Pad
+#>2 byte &0x01 \b, Custom Flag
+#>3 byte &0x08 \b, Copyrighted
+#>3 byte &0x04 \b, Original Source
+#>3 byte&0x03 1 \b, NR: 50/15 ms
+#>3 byte&0x03 3 \b, NR: CCIT J.17
+
+# AAC (aka MPEG-2 NBC audio) and MPEG-4 audio
+
+# Stored AAC streams (instead of the MP4 format)
+0 string ADIF MPEG ADIF, AAC
+!:mime audio/x-hx-aac-adif
+>4 byte &0x80
+>>13 byte &0x10 \b, VBR
+>>13 byte ^0x10 \b, CBR
+>>16 byte&0x1E 0x02 \b, single stream
+>>16 byte&0x1E 0x04 \b, 2 streams
+>>16 byte&0x1E 0x06 \b, 3 streams
+>>16 byte &0x08 \b, 4 or more streams
+>>16 byte &0x10 \b, 8 or more streams
+>>4 byte &0x80 \b, Copyrighted
+>>13 byte &0x40 \b, Original Source
+>>13 byte &0x20 \b, Home Flag
+>4 byte ^0x80
+>>4 byte &0x10 \b, VBR
+>>4 byte ^0x10 \b, CBR
+>>7 byte&0x1E 0x02 \b, single stream
+>>7 byte&0x1E 0x04 \b, 2 streams
+>>7 byte&0x1E 0x06 \b, 3 streams
+>>7 byte &0x08 \b, 4 or more streams
+>>7 byte &0x10 \b, 8 or more streams
+>>4 byte &0x40 \b, Original Stream(s)
+>>4 byte &0x20 \b, Home Source
+
+# Live or stored single AAC stream (used with MPEG-2 systems)
+0 beshort&0xFFF6 0xFFF0 MPEG ADTS, AAC
+!:mime audio/x-hx-aac-adts
+>1 byte &0x08 \b, v2
+>1 byte ^0x08 \b, v4
+# profile
+>>2 byte &0xC0 \b LTP
+>2 byte&0xc0 0x00 \b Main
+>2 byte&0xc0 0x40 \b LC
+>2 byte&0xc0 0x80 \b SSR
+# timing
+>2 byte&0x3c 0x00 \b, 96 kHz
+>2 byte&0x3c 0x04 \b, 88.2 kHz
+>2 byte&0x3c 0x08 \b, 64 kHz
+>2 byte&0x3c 0x0c \b, 48 kHz
+>2 byte&0x3c 0x10 \b, 44.1 kHz
+>2 byte&0x3c 0x14 \b, 32 kHz
+>2 byte&0x3c 0x18 \b, 24 kHz
+>2 byte&0x3c 0x1c \b, 22.05 kHz
+>2 byte&0x3c 0x20 \b, 16 kHz
+>2 byte&0x3c 0x24 \b, 12 kHz
+>2 byte&0x3c 0x28 \b, 11.025 kHz
+>2 byte&0x3c 0x2c \b, 8 kHz
+# channels
+>2 beshort&0x01c0 0x0040 \b, monaural
+>2 beshort&0x01c0 0x0080 \b, stereo
+>2 beshort&0x01c0 0x00c0 \b, stereo + center
+>2 beshort&0x01c0 0x0100 \b, stereo+center+LFE
+>2 beshort&0x01c0 0x0140 \b, surround
+>2 beshort&0x01c0 0x0180 \b, surround + LFE
+>2 beshort &0x01C0 \b, surround + side
+#>1 byte ^0x01 \b, Data Verify
+#>2 byte &0x02 \b, Custom Flag
+#>3 byte &0x20 \b, Original Stream
+#>3 byte &0x10 \b, Home Source
+#>3 byte &0x08 \b, Copyrighted
+
+# Live MPEG-4 audio streams (instead of RTP FlexMux)
+0 beshort&0xFFE0 0x56E0 MPEG-4 LOAS
+!:mime audio/x-mp4a-latm
+#>1 beshort&0x1FFF x \b, %hu byte packet
+>3 byte&0xE0 0x40
+>>4 byte&0x3C 0x04 \b, single stream
+>>4 byte&0x3C 0x08 \b, 2 streams
+>>4 byte&0x3C 0x0C \b, 3 streams
+>>4 byte &0x08 \b, 4 or more streams
+>>4 byte &0x20 \b, 8 or more streams
+>3 byte&0xC0 0
+>>4 byte&0x78 0x08 \b, single stream
+>>4 byte&0x78 0x10 \b, 2 streams
+>>4 byte&0x78 0x18 \b, 3 streams
+>>4 byte &0x20 \b, 4 or more streams
+>>4 byte &0x40 \b, 8 or more streams
+# This magic isn't strong enough (matches plausible ISO-8859-1 text)
+#0 beshort 0x4DE1 MPEG-4 LO-EP audio stream
+#!:mime audio/x-mp4a-latm
+
+# Summary: FLI animation format
+# Created by: Daniel Quinlan <quinlan@yggdrasil.com>
+# Modified by (1): Abel Cheung <abelcheung@gmail.com> (avoid over-generic detection)
+4 leshort 0xAF11
+# standard FLI always has 320x200 resolution and 8 bit color
+>8 leshort 320
+>>10 leshort 200
+>>>12 leshort 8 FLI animation, 320x200x8
+!:mime video/x-fli
+>>>>6 leshort x \b, %d frames
+# frame speed is multiple of 1/70s
+>>>>16 leshort x \b, %d/70s per frame
+
+# Summary: FLC animation format
+# Created by: Daniel Quinlan <quinlan@yggdrasil.com>
+# Modified by (1): Abel Cheung <abelcheung@gmail.com> (avoid over-generic detection)
+4 leshort 0xAF12
+# standard FLC always use 8 bit color
+>12 leshort 8 FLC animation
+!:mime video/x-flc
+>>8 leshort x \b, %d
+>>10 leshort x \bx%dx8
+>>6 uleshort x \b, %d frames
+>>16 uleshort x \b, %dms per frame
+
+# DL animation format
+# XXX - collision with most `mips' magic
+#
+# I couldn't find a real magic number for these, however, this
+# -appears- to work. Note that it might catch other files, too, so be
+# careful!
+#
+# Note that title and author appear in the two 20-byte chunks
+# at decimal offsets 2 and 22, respectively, but they are XOR'ed with
+# 255 (hex FF)! The DL format is really bad.
+#
+#0 byte 1 DL version 1, medium format (160x100, 4 images/screen)
+#!:mime video/x-unknown
+#>42 byte x - %d screens,
+#>43 byte x %d commands
+#0 byte 2 DL version 2
+#!:mime video/x-unknown
+#>1 byte 1 - large format (320x200,1 image/screen),
+#>1 byte 2 - medium format (160x100,4 images/screen),
+#>1 byte >2 - unknown format,
+#>42 byte x %d screens,
+#>43 byte x %d commands
+# Based on empirical evidence, DL version 3 have several nulls following the
+# \003. Most of them start with non-null values at hex offset 0x34 or so.
+#0 string \3\0\0\0\0\0\0\0\0\0\0\0 DL version 3
+
+# iso 13818 transport stream
+#
+# from Oskar Schirmer <schirmer@scara.com> Feb 3, 2001 (ISO 13818.1)
+# syncbyte 8 bit 0x47
+# error_ind 1 bit -
+# payload_start 1 bit 1
+# priority 1 bit -
+# PID 13 bit 0x0000
+# scrambling 2 bit -
+# adaptfld_ctrl 2 bit 1 or 3
+# conti_count 4 bit -
+0 belong&0xFF5FFF10 0x47400010
+>188 byte 0x47 MPEG transport stream data
+
+# DIF digital video file format <mpruett@sgi.com>
+0 belong&0xffffff00 0x1f070000 DIF
+>4 byte &0x01 (DVCPRO) movie file
+>4 byte ^0x01 (DV) movie file
+>3 byte &0x80 (PAL)
+>3 byte ^0x80 (NTSC)
+
+# Microsoft Advanced Streaming Format (ASF) <mpruett@sgi.com>
+0 belong 0x3026b275 Microsoft ASF
+!:mime video/x-ms-asf
+
+# MNG Video Format, <URL:http://www.libpng.org/pub/mng/spec/>
+0 string \x8aMNG MNG video data,
+!:mime video/x-mng
+>4 belong !0x0d0a1a0a CORRUPTED,
+>4 belong 0x0d0a1a0a
+>>16 belong x %d x
+>>20 belong x %d
+
+# JNG Video Format, <URL:http://www.libpng.org/pub/mng/spec/>
+0 string \x8bJNG JNG video data,
+!:mime video/x-jng
+>4 belong !0x0d0a1a0a CORRUPTED,
+>4 belong 0x0d0a1a0a
+>>16 belong x %d x
+>>20 belong x %d
+
+# Vivo video (Wolfram Kleff)
+3 string \x0D\x0AVersion:Vivo Vivo video data
+
+# VRML (Virtual Reality Modelling Language)
+0 string/w #VRML\ V1.0\ ascii VRML 1 file
+!:mime model/vrml
+0 string/w #VRML\ V2.0\ utf8 ISO/IEC 14772 VRML 97 file
+!:mime model/vrml
+
+# X3D (Extensible 3D) [http://www.web3d.org/specifications/x3d-3.0.dtd]
+# From Michel Briand <michelbriand@free.fr>
+0 string/t \<?xml\ version="
+!:strength +1
+>20 search/1000/cw \<!DOCTYPE\ X3D X3D (Extensible 3D) model xml text
+!:mime model/x3d
+
+#---------------------------------------------------------------------------
+# HVQM4: compressed movie format designed by Hudson for Nintendo GameCube
+# From Mark Sheppard <msheppard@climax.co.uk>, 2002-10-03
+#
+0 string HVQM4 %s
+>6 string >\0 v%s
+>0 byte x GameCube movie,
+>0x34 ubeshort x %d x
+>0x36 ubeshort x %d,
+>0x26 ubeshort x %dus,
+>0x42 ubeshort 0 no audio
+>0x42 ubeshort >0 %dHz audio
+
+# From: "Stefan A. Haubenthal" <polluks@web.de>
+0 string DVDVIDEO-VTS Video title set,
+>0x21 byte x v%x
+0 string DVDVIDEO-VMG Video manager,
+>0x21 byte x v%x
+
+# From: Behan Webster <behanw@websterwood.com>
+# NuppelVideo used by Mythtv (*.nuv)
+# Note: there are two identical stanzas here differing only in the
+# initial string matched. It used to be done with a regex, but we're
+# trying to get rid of those.
+0 string NuppelVideo MythTV NuppelVideo
+>12 string x v%s
+>20 lelong x (%d
+>24 lelong x \bx%d),
+>36 string P \bprogressive,
+>36 string I \binterlaced,
+>40 ledouble x \baspect:%.2f,
+>48 ledouble x \bfps:%.2f
+0 string MythTV MythTV NuppelVideo
+>12 string x v%s
+>20 lelong x (%d
+>24 lelong x \bx%d),
+>36 string P \bprogressive,
+>36 string I \binterlaced,
+>40 ledouble x \baspect:%.2f,
+>48 ledouble x \bfps:%.2f
+
+# MPEG file
+# MPEG sequences
+# FIXME: This section is from the old magic.mime file and needs
+# integrating with the rest
+#0 belong 0x000001BA
+#>4 byte &0x40
+#!:mime video/mp2p
+#>4 byte ^0x40
+#!:mime video/mpeg
+#0 belong 0x000001BB
+#!:mime video/mpeg
+#0 belong 0x000001B0
+#!:mime video/mp4v-es
+#0 belong 0x000001B5
+#!:mime video/mp4v-es
+#0 belong 0x000001B3
+#!:mime video/mpv
+#0 belong&0xFF5FFF10 0x47400010
+#!:mime video/mp2t
+#0 belong 0x00000001
+#>4 byte&0x1F 0x07
+#!:mime video/h264
+
+# Type: Bink Video
+# Extension: .bik
+# URL: http://wiki.multimedia.cx/index.php?title=Bink_Container
+# From: <hoehle@users.sourceforge.net> 2008-07-18
+0 string BIK Bink Video
+>3 regex =[a-z] rev.%s
+#>4 ulelong x size %d
+>20 ulelong x \b, %d
+>24 ulelong x \bx%d
+>8 ulelong x \b, %d frames
+>32 ulelong x at rate %d/
+>28 ulelong >1 \b%d
+>40 ulelong =0 \b, no audio
+>40 ulelong !0 \b, %d audio track
+>>40 ulelong !1 \bs
+# follow properties of the first audio track only
+>>48 uleshort x %dHz
+>>51 byte&0x20 0 mono
+>>51 byte&0x20 !0 stereo
+#>>51 byte&0x10 0 FFT
+#>>51 byte&0x10 !0 DCT
+
+# Type: NUT Container
+# URL: http://wiki.multimedia.cx/index.php?title=NUT
+# From: Adam Buchbinder <adam.buchbinder@gmail.com>
+0 string nut/multimedia\ container\0 NUT multimedia container
+
+# Type: Nullsoft Video (NSV)
+# URL: http://wiki.multimedia.cx/index.php?title=Nullsoft_Video
+# From: Mike Melanson <mike@multimedia.cx>
+0 string NSVf Nullsoft Video
+
+# Type: REDCode Video
+# URL: http://www.red.com/ ; http://wiki.multimedia.cx/index.php?title=REDCode
+# From: Mike Melanson <mike@multimedia.cx>
+4 string RED1 REDCode Video
+
+# Type: MTV Multimedia File
+# URL: http://wiki.multimedia.cx/index.php?title=MTV
+# From: Mike Melanson <mike@multimedia.cx>
+0 string AMVS MTV Multimedia File
+
+# Type: ARMovie
+# URL: http://wiki.multimedia.cx/index.php?title=ARMovie
+# From: Mike Melanson <mike@multimedia.cx>
+0 string ARMovie\012 ARMovie
+
+# Type: Interplay MVE Movie
+# URL: http://wiki.multimedia.cx/index.php?title=Interplay_MVE
+# From: Mike Melanson <mike@multimedia.cx>
+0 string Interplay\040MVE\040File\032 Interplay MVE Movie
+
+# Type: Windows Television DVR File
+# URL: http://wiki.multimedia.cx/index.php?title=WTV
+# From: Mike Melanson <mike@mutlimedia.cx>
+# This takes the form of a Windows-style GUID
+0 bequad 0xB7D800203749DA11
+>8 bequad 0xA64E0007E95EAD8D Windows Television DVR Media
+
+# Type: Sega FILM/CPK Multimedia
+# URL: http://wiki.multimedia.cx/index.php?title=Sega_FILM
+# From: Mike Melanson <mike@multimedia.cx>
+0 string FILM Sega FILM/CPK Multimedia,
+>32 belong x %d x
+>28 belong x %d
+
+# Type: Nintendo THP Multimedia
+# URL: http://wiki.multimedia.cx/index.php?title=THP
+# From: Mike Melanson <mike@multimedia.cx>
+0 string THP\0 Nintendo THP Multimedia
+
+# Type: BBC Dirac Video
+# URL: http://wiki.multimedia.cx/index.php?title=Dirac
+# From: Mike Melanson <mike@multimedia.cx>
+0 string BBCD BBC Dirac Video
+
+# Type: RAD Game Tools Smacker Multimedia
+# URL: http://wiki.multimedia.cx/index.php?title=Smacker
+# From: Mike Melanson <mike@multimedia.cx>
+0 string SMK RAD Game Tools Smacker Multimedia
+>3 byte x version %c,
+>4 lelong x %d x
+>8 lelong x %d,
+>12 lelong x %d frames
+
+#------------------------------------------------------------------------------
+# $File$
+# aout: file(1) magic for a.out executable/object/etc entries that
+# handle executables on multiple platforms.
+#
+
+#
+# Little-endian 32-bit-int a.out, merged from bsdi (for BSD/OS, from
+# BSDI), netbsd, and vax (for UNIX/32V and BSD)
+#
+# XXX - is there anything we can look at to distinguish BSD/OS 386 from
+# NetBSD 386 from various VAX binaries? The BSD/OS shared library flag
+# works only for binaries using shared libraries. Grabbing the entry
+# point from the a.out header, using it to find the first code executed
+# in the program, and looking at that might help.
+#
+0 lelong 0407 a.out little-endian 32-bit executable
+>16 lelong >0 not stripped
+>32 byte 0x6a (uses BSD/OS shared libs)
+
+0 lelong 0410 a.out little-endian 32-bit pure executable
+>16 lelong >0 not stripped
+>32 byte 0x6a (uses BSD/OS shared libs)
+
+0 lelong 0413 a.out little-endian 32-bit demand paged pure executable
+>16 lelong >0 not stripped
+>32 byte 0x6a (uses BSD/OS shared libs)
+
+#
+# Big-endian 32-bit-int a.out, merged from sun (for old 68010 SunOS a.out),
+# mips (for old 68020(!) SGI a.out), and netbsd (for old big-endian a.out).
+#
+# XXX - is there anything we can look at to distinguish old SunOS 68010
+# from old 68020 IRIX from old NetBSD? Again, I guess we could look at
+# the first instruction or instructions in the program.
+#
+0 belong 0407 a.out big-endian 32-bit executable
+>16 belong >0 not stripped
+
+0 belong 0410 a.out big-endian 32-bit pure executable
+>16 belong >0 not stripped
+
+0 belong 0413 a.out big-endian 32-bit demand paged executable
+>16 belong >0 not stripped
+
+
+#------------------------------------------------------------------------------
+# $File$
+# apl: file(1) magic for APL (see also "pdp" and "vax" for other APL
+# workspaces)
+#
+0 long 0100554 APL workspace (Ken's original?)
+
+#------------------------------------------------------------------------------
+# $File: apple,v 1.28 2014/04/28 12:04:50 christos Exp $
+# apple: file(1) magic for Apple file formats
+#
+0 search/1/t FiLeStArTfIlEsTaRt binscii (apple ][) text
+0 string \x0aGL Binary II (apple ][) data
+0 string \x76\xff Squeezed (apple ][) data
+0 string NuFile NuFile archive (apple ][) data
+0 string N\xf5F\xe9l\xe5 NuFile archive (apple ][) data
+0 belong 0x00051600 AppleSingle encoded Macintosh file
+0 belong 0x00051607 AppleDouble encoded Macintosh file
+
+# Type: Apple Emulator 2IMG format
+# From: Radek Vokal <rvokal@redhat.com>
+0 string 2IMG Apple ][ 2IMG Disk Image
+>4 string XGS! \b, XGS
+>4 string CTKG \b, Catakig
+>4 string ShIm \b, Sheppy's ImageMaker
+>4 string WOOF \b, Sweet 16
+>4 string B2TR \b, Bernie ][ the Rescue
+>4 string !nfc \b, ASIMOV2
+>4 string x \b, Unknown Format
+>0xc byte 00 \b, DOS 3.3 sector order
+>>0x10 byte 00 \b, Volume 254
+>>0x10 byte&0x7f x \b, Volume %u
+>0xc byte 01 \b, ProDOS sector order
+>>0x14 short x \b, %u Blocks
+>0xc byte 02 \b, NIB data
+
+# magic for Newton PDA package formats
+# from Ruda Moura <ruda@helllabs.org>
+0 string package0 Newton package, NOS 1.x,
+>12 belong &0x80000000 AutoRemove,
+>12 belong &0x40000000 CopyProtect,
+>12 belong &0x10000000 NoCompression,
+>12 belong &0x04000000 Relocation,
+>12 belong &0x02000000 UseFasterCompression,
+>16 belong x version %d
+
+0 string package1 Newton package, NOS 2.x,
+>12 belong &0x80000000 AutoRemove,
+>12 belong &0x40000000 CopyProtect,
+>12 belong &0x10000000 NoCompression,
+>12 belong &0x04000000 Relocation,
+>12 belong &0x02000000 UseFasterCompression,
+>16 belong x version %d
+
+0 string package4 Newton package,
+>8 byte 8 NOS 1.x,
+>8 byte 9 NOS 2.x,
+>12 belong &0x80000000 AutoRemove,
+>12 belong &0x40000000 CopyProtect,
+>12 belong &0x10000000 NoCompression,
+
+# The following entries for the Apple II are for files that have
+# been transferred as raw binary data from an Apple, without having
+# been encapsulated by any of the above archivers.
+#
+# In general, Apple II formats are hard to identify because Apple DOS
+# and especially Apple ProDOS have strong typing in the file system and
+# therefore programmers never felt much need to include type information
+# in the files themselves.
+#
+# Eric Fischer <enf@pobox.com>
+
+# AppleWorks word processor:
+#
+# This matches the standard tab stops for an AppleWorks file, but if
+# a file has a tab stop set in the first four columns this will fail.
+#
+# The "O" is really the magic number, but that's so common that it's
+# necessary to check the tab stops that follow it to avoid false positives.
+
+4 string O==== AppleWorks word processor data
+>85 byte&0x01 >0 \b, zoomed
+>90 byte&0x01 >0 \b, paginated
+>92 byte&0x01 >0 \b, with mail merge
+#>91 byte x \b, left margin %d
+
+# AppleWorks database:
+#
+# This isn't really a magic number, but it's the closest thing to one
+# that I could find. The 1 and 2 really mean "order in which you defined
+# categories" and "left to right, top to bottom," respectively; the D and R
+# mean that the cursor should move either down or right when you press Return.
+
+#30 string \x01D AppleWorks database data
+#30 string \x02D AppleWorks database data
+#30 string \x01R AppleWorks database data
+#30 string \x02R AppleWorks database data
+
+# AppleWorks spreadsheet:
+#
+# Likewise, this isn't really meant as a magic number. The R or C means
+# row- or column-order recalculation; the A or M means automatic or manual
+# recalculation.
+
+#131 string RA AppleWorks spreadsheet data
+#131 string RM AppleWorks spreadsheet data
+#131 string CA AppleWorks spreadsheet data
+#131 string CM AppleWorks spreadsheet data
+
+# Applesoft BASIC:
+#
+# This is incredibly sloppy, but will be true if the program was
+# written at its usual memory location of 2048 and its first line
+# number is less than 256. Yuck.
+# update by Joerg Jenderek at Feb 2013
+
+# GRR: this test is still too general as it catches also Gujin BOOT144.SYS (0xfa080000)
+#0 belong&0xff00ff 0x80000 Applesoft BASIC program data
+0 belong&0x00ff00ff 0x00080000
+# assuming that line number must be positive
+>2 leshort >0 Applesoft BASIC program data, first line number %d
+#>2 leshort x \b, first line number %d
+
+# ORCA/EZ assembler:
+#
+# This will not identify ORCA/M source files, since those have
+# some sort of date code instead of the two zero bytes at 6 and 7
+# XXX Conflicts with ELF
+#4 belong&0xff00ffff 0x01000000 ORCA/EZ assembler source data
+#>5 byte x \b, build number %d
+
+# Broderbund Fantavision
+#
+# I don't know what these values really mean, but they seem to recur.
+# Will they cause too many conflicts?
+
+# Probably :-)
+#2 belong&0xFF00FF 0x040008 Fantavision movie data
+
+# Some attempts at images.
+#
+# These are actually just bit-for-bit dumps of the frame buffer, so
+# there's really no reasonably way to distinguish them except for their
+# address (if preserved) -- 8192 or 16384 -- and their length -- 8192
+# or, occasionally, 8184.
+#
+# Nevertheless this will manage to catch a lot of images that happen
+# to have a solid-colored line at the bottom of the screen.
+
+# GRR: Magic too weak
+#8144 string \x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F Apple II image with white background
+#8144 string \x55\x2A\x55\x2A\x55\x2A\x55\x2A Apple II image with purple background
+#8144 string \x2A\x55\x2A\x55\x2A\x55\x2A\x55 Apple II image with green background
+#8144 string \xD5\xAA\xD5\xAA\xD5\xAA\xD5\xAA Apple II image with blue background
+#8144 string \xAA\xD5\xAA\xD5\xAA\xD5\xAA\xD5 Apple II image with orange background
+
+# Beagle Bros. Apple Mechanic fonts
+
+0 belong&0xFF00FFFF 0x6400D000 Apple Mechanic font
+
+# Apple Universal Disk Image Format (UDIF) - dmg files.
+# From Johan Gade.
+# These entries are disabled for now until we fix the following issues.
+#
+# Note there might be some problems with the "VAX COFF executable"
+# entry. Note this entry should be placed before the mac filesystem section,
+# particularly the "Apple Partition data" entry.
+#
+# The intended meaning of these tests is, that the file is only of the
+# specified type if both of the lines are correct - i.e. if the first
+# line matches and the second doesn't then it is not of that type.
+#
+#0 long 0x7801730d
+#>4 long 0x62626060 UDIF read-only zlib-compressed image (UDZO)
+#
+# Note that this entry is recognized correctly by the "Apple Partition
+# data" entry - however since this entry is more specific - this
+# information seems to be more useful.
+#0 long 0x45520200
+#>0x410 string disk\ image UDIF read/write image (UDRW)
+
+# From: Toby Peterson <toby@apple.com>
+0 string bplist00 Apple binary property list
+
+# Apple binary property list (bplist)
+# Assumes version bytes are hex.
+# Provides content hints for version 0 files. Assumes that the root
+# object is the first object (true for CoreFoundation implementation).
+# From: David Remahl <dremahl@apple.com>
+0 string bplist
+>6 byte x \bCoreFoundation binary property list data, version 0x%c
+>>7 byte x \b%c
+>6 string 00 \b
+>>8 byte&0xF0 0x00 \b
+>>>8 byte&0x0F 0x00 \b, root type: null
+>>>8 byte&0x0F 0x08 \b, root type: false boolean
+>>>8 byte&0x0F 0x09 \b, root type: true boolean
+>>8 byte&0xF0 0x10 \b, root type: integer
+>>8 byte&0xF0 0x20 \b, root type: real
+>>8 byte&0xF0 0x30 \b, root type: date
+>>8 byte&0xF0 0x40 \b, root type: data
+>>8 byte&0xF0 0x50 \b, root type: ascii string
+>>8 byte&0xF0 0x60 \b, root type: unicode string
+>>8 byte&0xF0 0x80 \b, root type: uid (CORRUPT)
+>>8 byte&0xF0 0xa0 \b, root type: array
+>>8 byte&0xF0 0xd0 \b, root type: dictionary
+
+# Apple/NeXT typedstream data
+# Serialization format used by NeXT and Apple for various
+# purposes in YellowStep/Cocoa, including some nib files.
+# From: David Remahl <dremahl@apple.com>
+2 string typedstream NeXT/Apple typedstream data, big endian
+>0 byte x \b, version %d
+>0 byte <5 \b
+>>13 byte 0x81 \b
+>>>14 ubeshort x \b, system %d
+2 string streamtyped NeXT/Apple typedstream data, little endian
+>0 byte x \b, version %d
+>0 byte <5 \b
+>>13 byte 0x81 \b
+>>>14 uleshort x \b, system %d
+
+#------------------------------------------------------------------------------
+# CAF: Apple CoreAudio File Format
+#
+# Container format for high-end audio purposes.
+# From: David Remahl <dremahl@apple.com>
+#
+0 string caff CoreAudio Format audio file
+>4 beshort <10 version %d
+>6 beshort x
+
+
+#------------------------------------------------------------------------------
+# Keychain database files
+0 string kych Mac OS X Keychain File
+
+#------------------------------------------------------------------------------
+# Code Signing related file types
+0 belong 0xfade0c00 Mac OS X Code Requirement
+>8 belong 1 (opExpr)
+>4 belong x - %d bytes
+
+0 belong 0xfade0c01 Mac OS X Code Requirement Set
+>8 belong >1 containing %d items
+>4 belong x - %d bytes
+
+0 belong 0xfade0c02 Mac OS X Code Directory
+>8 belong x version %x
+>12 belong >0 flags 0x%x
+>4 belong x - %d bytes
+
+0 belong 0xfade0cc0 Mac OS X Detached Code Signature (non-executable)
+>4 belong x - %d bytes
+
+0 belong 0xfade0cc1 Mac OS X Detached Code Signature
+>8 belong >1 (%d elements)
+>4 belong x - %d bytes
+
+# From: "Nelson A. de Oliveira" <naoliv@gmail.com>
+# .vdi
+4 string innotek\ VirtualBox\ Disk\ Image %s
+
+# Apple disk partition stuff, strengthen the magic using byte 4
+0 beshort 0x4552
+>4 byte 0 Apple Driver Map
+>>2 beshort x \b, blocksize %d
+>>4 belong x \b, blockcount %d
+>>10 beshort x \b, devtype %d
+>>12 beshort x \b, devid %d
+>>20 beshort x \b, descriptors %d
+# Assume 8 partitions each at a multiple of the sector size.
+# We could glean this from the partition descriptors, but they are empty!?!?
+>>(2.S*1) indirect \b, contains[@0x%x]:
+>>(2.S*2) indirect \b, contains[@0x%x]:
+>>(2.S*3) indirect \b, contains[@0x%x]:
+>>(2.S*4) indirect \b, contains[@0x%x]:
+>>(2.S*5) indirect \b, contains[@0x%x]:
+>>(2.S*6) indirect \b, contains[@0x%x]:
+>>(2.S*7) indirect \b, contains[@0x%x]:
+>>(2.S*8) indirect \b, contains[@0x%x]:
+
+# Yes, the 3rd and 4th bytes are reserved, but we use them to make the
+# magic stronger.
+0 belong 0x504d0000 Apple Partition Map
+>4 belong x \b, map block count %d
+>8 belong x \b, start block %d
+>12 belong x \b, block count %d
+>16 string >0 \b, name %s
+>48 string >0 \b, type %s
+>124 string >0 \b, processor %s
+>140 string >0 \b, boot arguments %s
+>92 belong & 1 \b, valid
+>92 belong & 2 \b, allocated
+>92 belong & 4 \b, in use
+>92 belong & 8 \b, has boot info
+>92 belong & 16 \b, readable
+>92 belong & 32 \b, writable
+>92 belong & 64 \b, pic boot code
+>92 belong & 128 \b, chain compatible driver
+>92 belong & 256 \b, real driver
+>92 belong & 512 \b, chain driver
+>92 belong & 1024 \b, mount at startup
+>92 belong & 2048 \b, is the startup partition
+
+#http://wiki.mozilla.org/DS_Store_File_Format`
+#http://en.wikipedia.org/wiki/.DS_Store
+0 string \0\0\0\1Bud1\0 Apple Desktop Services Store
+
+#------------------------------------------------------------------------------
+# $File$
+# applix: file(1) magic for Applixware
+# From: Peter Soos <sp@osb.hu>
+#
+0 string *BEGIN Applixware
+>7 string WORDS Words Document
+>7 string GRAPHICS Graphic
+>7 string RASTER Bitmap
+>7 string SPREADSHEETS Spreadsheet
+>7 string MACRO Macro
+>7 string BUILDER Builder Object
+#------------------------------------------------------------------------------
+# $File: archive,v 1.87 2014/06/03 19:15:58 christos Exp $
+# archive: file(1) magic for archive formats (see also "msdos" for self-
+# extracting compressed archives)
+#
+# cpio, ar, arc, arj, hpack, lha/lharc, rar, squish, uc2, zip, zoo, etc.
+# pre-POSIX "tar" archives are handled in the C code.
+
+# POSIX tar archives
+257 string ustar\0 POSIX tar archive
+!:mime application/x-tar # encoding: posix
+257 string ustar\040\040\0 GNU tar archive
+!:mime application/x-tar # encoding: gnu
+
+# Incremental snapshot gnu-tar format from:
+# http://www.gnu.org/software/tar/manual/html_node/Snapshot-Files.html
+0 string GNU\ tar- GNU tar incremental snapshot data
+>&0 regex [0-9]\.[0-9]+-[0-9]+ version %s
+
+# cpio archives
+#
+# Yes, the top two "cpio archive" formats *are* supposed to just be "short".
+# The idea is to indicate archives produced on machines with the same
+# byte order as the machine running "file" with "cpio archive", and
+# to indicate archives produced on machines with the opposite byte order
+# from the machine running "file" with "byte-swapped cpio archive".
+#
+# The SVR4 "cpio(4)" hints that there are additional formats, but they
+# are defined as "short"s; I think all the new formats are
+# character-header formats and thus are strings, not numbers.
+0 short 070707 cpio archive
+!:mime application/x-cpio
+0 short 0143561 byte-swapped cpio archive
+!:mime application/x-cpio # encoding: swapped
+0 string 070707 ASCII cpio archive (pre-SVR4 or odc)
+0 string 070701 ASCII cpio archive (SVR4 with no CRC)
+0 string 070702 ASCII cpio archive (SVR4 with CRC)
+
+#
+# Various archive formats used by various versions of the "ar"
+# command.
+#
+
+#
+# Original UNIX archive formats.
+# They were written with binary values in host byte order, and
+# the magic number was a host "int", which might have been 16 bits
+# or 32 bits. We don't say "PDP-11" or "VAX", as there might have
+# been ports to little-endian 16-bit-int or 32-bit-int platforms
+# (x86?) using some of those formats; if none existed, feel free
+# to use "PDP-11" for little-endian 16-bit and "VAX" for little-endian
+# 32-bit. There might have been big-endian ports of that sort as
+# well.
+#
+0 leshort 0177555 very old 16-bit-int little-endian archive
+0 beshort 0177555 very old 16-bit-int big-endian archive
+0 lelong 0177555 very old 32-bit-int little-endian archive
+0 belong 0177555 very old 32-bit-int big-endian archive
+
+0 leshort 0177545 old 16-bit-int little-endian archive
+>2 string __.SYMDEF random library
+0 beshort 0177545 old 16-bit-int big-endian archive
+>2 string __.SYMDEF random library
+0 lelong 0177545 old 32-bit-int little-endian archive
+>4 string __.SYMDEF random library
+0 belong 0177545 old 32-bit-int big-endian archive
+>4 string __.SYMDEF random library
+
+#
+# From "pdp" (but why a 4-byte quantity?)
+#
+0 lelong 0x39bed PDP-11 old archive
+0 lelong 0x39bee PDP-11 4.0 archive
+
+#
+# XXX - what flavor of APL used this, and was it a variant of
+# some ar archive format? It's similar to, but not the same
+# as, the APL workspace magic numbers in pdp.
+#
+0 long 0100554 apl workspace
+
+#
+# System V Release 1 portable(?) archive format.
+#
+0 string =<ar> System V Release 1 ar archive
+!:mime application/x-archive
+
+#
+# Debian package; it's in the portable archive format, and needs to go
+# before the entry for regular portable archives, as it's recognized as
+# a portable archive whose first member has a name beginning with
+# "debian".
+#
+0 string =!<arch>\ndebian
+>8 string debian-split part of multipart Debian package
+!:mime application/vnd.debian.binary-package
+>8 string debian-binary Debian binary package
+!:mime application/vnd.debian.binary-package
+>8 string !debian
+>68 string >\0 (format %s)
+# These next two lines do not work, because a bzip2 Debian archive
+# still uses gzip for the control.tar (first in the archive). Only
+# data.tar varies, and the location of its filename varies too.
+# file/libmagic does not current have support for ascii-string based
+# (offsets) as of 2005-09-15.
+#>81 string bz2 \b, uses bzip2 compression
+#>84 string gz \b, uses gzip compression
+#>136 ledate x created: %s
+
+#
+# MIPS archive; they're in the portable archive format, and need to go
+# before the entry for regular portable archives, as it's recognized as
+# a portable archive whose first member has a name beginning with
+# "__________E".
+#
+0 string =!<arch>\n__________E MIPS archive
+!:mime application/x-archive
+>20 string U with MIPS Ucode members
+>21 string L with MIPSEL members
+>21 string B with MIPSEB members
+>19 string L and an EL hash table
+>19 string B and an EB hash table
+>22 string X -- out of date
+
+0 search/1 -h- Software Tools format archive text
+
+#
+# BSD/SVR2-and-later portable archive formats.
+#
+0 string =!<arch> current ar archive
+!:mime application/x-archive
+>8 string __.SYMDEF random library
+>68 string __.SYMDEF\ SORTED random library
+
+#
+# "Thin" archive, as can be produced by GNU ar.
+#
+0 string =!<thin>\n thin archive with
+>68 belong 0 no symbol entries
+>68 belong 1 %d symbol entry
+>68 belong >1 %d symbol entries
+
+# ARC archiver, from Daniel Quinlan (quinlan@yggdrasil.com)
+#
+# The first byte is the magic (0x1a), byte 2 is the compression type for
+# the first file (0x01 through 0x09), and bytes 3 to 15 are the MS-DOS
+# filename of the first file (null terminated). Since some types collide
+# we only test some types on basis of frequency: 0x08 (83%), 0x09 (5%),
+# 0x02 (5%), 0x03 (3%), 0x04 (2%), 0x06 (2%). 0x01 collides with terminfo.
+0 lelong&0x8080ffff 0x0000081a ARC archive data, dynamic LZW
+!:mime application/x-arc
+0 lelong&0x8080ffff 0x0000091a ARC archive data, squashed
+!:mime application/x-arc
+0 lelong&0x8080ffff 0x0000021a ARC archive data, uncompressed
+!:mime application/x-arc
+0 lelong&0x8080ffff 0x0000031a ARC archive data, packed
+!:mime application/x-arc
+0 lelong&0x8080ffff 0x0000041a ARC archive data, squeezed
+!:mime application/x-arc
+0 lelong&0x8080ffff 0x0000061a ARC archive data, crunched
+!:mime application/x-arc
+# [JW] stuff taken from idarc, obviously ARC successors:
+0 lelong&0x8080ffff 0x00000a1a PAK archive data
+!:mime application/x-arc
+0 lelong&0x8080ffff 0x0000141a ARC+ archive data
+!:mime application/x-arc
+0 lelong&0x8080ffff 0x0000481a HYP archive data
+!:mime application/x-arc
+
+# Acorn archive formats (Disaster prone simpleton, m91dps@ecs.ox.ac.uk)
+# I can't create either SPARK or ArcFS archives so I have not tested this stuff
+# [GRR: the original entries collide with ARC, above; replaced with combined
+# version (not tested)]
+#0 byte 0x1a RISC OS archive (spark format)
+0 string \032archive RISC OS archive (ArcFS format)
+0 string Archive\000 RISC OS archive (ArcFS format)
+
+# All these were taken from idarc, many could not be verified. Unfortunately,
+# there were many low-quality sigs, i.e. easy to trigger false positives.
+# Please notify me of any real-world fishy/ambiguous signatures and I'll try
+# to get my hands on the actual archiver and see if I find something better. [JW]
+# probably many can be enhanced by finding some 0-byte or control char near the start
+
+# idarc calls this Crush/Uncompressed... *shrug*
+0 string CRUSH Crush archive data
+# Squeeze It (.sqz)
+0 string HLSQZ Squeeze It archive data
+# SQWEZ
+0 string SQWEZ SQWEZ archive data
+# HPack (.hpk)
+0 string HPAK HPack archive data
+# HAP
+0 string \x91\x33HF HAP archive data
+# MD/MDCD
+0 string MDmd MDCD archive data
+# LIM
+0 string LIM\x1a LIM archive data
+# SAR
+3 string LH5 SAR archive data
+# BSArc/BS2
+0 string \212\3SB\020\0 BSArc/BS2 archive data
+# Bethesda Softworks Archive (Oblivion)
+0 string BSA\0 BSArc archive data
+>4 lelong x version %d
+# MAR
+2 string =-ah MAR archive data
+# ACB
+#0 belong&0x00f800ff 0x00800000 ACB archive data
+# CPZ
+# TODO, this is what idarc says: 0 string \0\0\0 CPZ archive data
+# JRC
+0 string JRchive JRC archive data
+# Quantum
+0 string DS\0 Quantum archive data
+# ReSOF
+0 string PK\3\6 ReSOF archive data
+# QuArk
+0 string 7\4 QuArk archive data
+# YAC
+14 string YC YAC archive data
+# X1
+0 string X1 X1 archive data
+0 string XhDr X1 archive data
+# CDC Codec (.dqt)
+0 belong&0xffffe000 0x76ff2000 CDC Codec archive data
+# AMGC
+0 string \xad6" AMGC archive data
+# NuLIB
+0 string N\xc3\xb5F\xc3\xa9lx\xc3\xa5 NuLIB archive data
+# PakLeo
+0 string LEOLZW PAKLeo archive data
+# ChArc
+0 string SChF ChArc archive data
+# PSA
+0 string PSA PSA archive data
+# CrossePAC
+0 string DSIGDCC CrossePAC archive data
+# Freeze
+0 string \x1f\x9f\x4a\x10\x0a Freeze archive data
+# KBoom
+0 string \xc2\xa8MP\xc2\xa8 KBoom archive data
+# NSQ, must go after CDC Codec
+0 string \x76\xff NSQ archive data
+# DPA
+0 string Dirk\ Paehl DPA archive data
+# BA
+# TODO: idarc says "bytes 0-2 == bytes 3-5"
+# TTComp
+0 string \0\6 TTComp archive data
+# ESP, could this conflict with Easy Software Products' (e.g.ESP ghostscript) documentation?
+0 string ESP ESP archive data
+# ZPack
+0 string \1ZPK\1 ZPack archive data
+# Sky
+0 string \xbc\x40 Sky archive data
+# UFA
+0 string UFA UFA archive data
+# Dry
+0 string =-H2O DRY archive data
+# FoxSQZ
+0 string FOXSQZ FoxSQZ archive data
+# AR7
+0 string ,AR7 AR7 archive data
+# PPMZ
+0 string PPMZ PPMZ archive data
+# MS Compress
+4 string \x88\xf0\x27 MS Compress archive data
+# updated by Joerg Jenderek
+>9 string \0
+>>0 string KWAJ
+>>>7 string \321\003 MS Compress archive data
+>>>>14 ulong >0 \b, original size: %d bytes
+>>>>18 ubyte >0x65
+>>>>>18 string x \b, was %.8s
+>>>>>(10.b-4) string x \b.%.3s
+# MP3 (archiver, not lossy audio compression)
+0 string MP3\x1a MP3-Archiver archive data
+# ZET
+0 string OZ\xc3\x9d ZET archive data
+# TSComp
+0 string \x65\x5d\x13\x8c\x08\x01\x03\x00 TSComp archive data
+# ARQ
+0 string gW\4\1 ARQ archive data
+# Squash
+3 string OctSqu Squash archive data
+# Terse
+0 string \5\1\1\0 Terse archive data
+# PUCrunch
+0 string \x01\x08\x0b\x08\xef\x00\x9e\x32\x30\x36\x31 PUCrunch archive data
+# UHarc
+0 string UHA UHarc archive data
+# ABComp
+0 string \2AB ABComp archive data
+0 string \3AB2 ABComp archive data
+# CMP
+0 string CO\0 CMP archive data
+# Splint
+0 string \x93\xb9\x06 Splint archive data
+# InstallShield
+0 string \x13\x5d\x65\x8c InstallShield Z archive Data
+# Gather
+1 string GTH Gather archive data
+# BOA
+0 string BOA BOA archive data
+# RAX
+0 string ULEB\xa RAX archive data
+# Xtreme
+0 string ULEB\0 Xtreme archive data
+# Pack Magic
+0 string @\xc3\xa2\1\0 Pack Magic archive data
+# BTS
+0 belong&0xfeffffff 0x1a034465 BTS archive data
+# ELI 5750
+0 string Ora\ ELI 5750 archive data
+# QFC
+0 string \x1aFC\x1a QFC archive data
+0 string \x1aQF\x1a QFC archive data
+# PRO-PACK
+0 string RNC PRO-PACK archive data
+# 777
+0 string 777 777 archive data
+# LZS221
+0 string sTaC LZS221 archive data
+# HPA
+0 string HPA HPA archive data
+# Arhangel
+0 string LG Arhangel archive data
+# EXP1, uses bzip2
+0 string 0123456789012345BZh EXP1 archive data
+# IMP
+0 string IMP\xa IMP archive data
+# NRV
+0 string \x00\x9E\x6E\x72\x76\xFF NRV archive data
+# Squish
+0 string \x73\xb2\x90\xf4 Squish archive data
+# Par
+0 string PHILIPP Par archive data
+0 string PAR Par archive data
+# HIT
+0 string UB HIT archive data
+# SBX
+0 belong&0xfffff000 0x53423000 SBX archive data
+# NaShrink
+0 string NSK NaShrink archive data
+# SAPCAR
+0 string #\ CAR\ archive\ header SAPCAR archive data
+0 string CAR\ 2.00RG SAPCAR archive data
+# Disintegrator
+0 string DST Disintegrator archive data
+# ASD
+0 string ASD ASD archive data
+# InstallShield CAB
+0 string ISc( InstallShield CAB
+# TOP4
+0 string T4\x1a TOP4 archive data
+# BatComp left out: sig looks like COM executable
+# so TODO: get real 4dos batcomp file and find sig
+# BlakHole
+0 string BH\5\7 BlakHole archive data
+# BIX
+0 string BIX0 BIX archive data
+# ChiefLZA
+0 string ChfLZ ChiefLZA archive data
+# Blink
+0 string Blink Blink archive data
+# Logitech Compress
+0 string \xda\xfa Logitech Compress archive data
+# ARS-Sfx (FIXME: really a SFX? then goto COM/EXE)
+1 string (C)\ STEPANYUK ARS-Sfx archive data
+# AKT/AKT32
+0 string AKT32 AKT32 archive data
+0 string AKT AKT archive data
+# NPack
+0 string MSTSM NPack archive data
+# PFT
+0 string \0\x50\0\x14 PFT archive data
+# SemOne
+0 string SEM SemOne archive data
+# PPMD
+0 string \x8f\xaf\xac\x84 PPMD archive data
+# FIZ
+0 string FIZ FIZ archive data
+# MSXiE
+0 belong&0xfffff0f0 0x4d530000 MSXiE archive data
+# DeepFreezer
+0 belong&0xfffffff0 0x797a3030 DeepFreezer archive data
+# DC
+0 string =<DC- DC archive data
+# TPac
+0 string \4TPAC\3 TPac archive data
+# Ai
+0 string Ai\1\1\0 Ai archive data
+0 string Ai\1\0\0 Ai archive data
+# Ai32
+0 string Ai\2\0 Ai32 archive data
+0 string Ai\2\1 Ai32 archive data
+# SBC
+0 string SBC SBC archive data
+# Ybs
+0 string YBS Ybs archive data
+# DitPack
+0 string \x9e\0\0 DitPack archive data
+# DMS
+0 string DMS! DMS archive data
+# EPC
+0 string \x8f\xaf\xac\x8c EPC archive data
+# VSARC
+0 string VS\x1a VSARC archive data
+# PDZ
+0 string PDZ PDZ archive data
+# ReDuq
+0 string rdqx ReDuq archive data
+# GCA
+0 string GCAX GCA archive data
+# PPMN
+0 string pN PPMN archive data
+# WinImage
+3 string WINIMAGE WinImage archive data
+# Compressia
+0 string CMP0CMP Compressia archive data
+# UHBC
+0 string UHB UHBC archive data
+# WinHKI
+0 string \x61\x5C\x04\x05 WinHKI archive data
+# WWPack data file
+0 string WWP WWPack archive data
+# BSN (BSA, PTS-DOS)
+0 string \xffBSG BSN archive data
+1 string \xffBSG BSN archive data
+3 string \xffBSG BSN archive data
+1 string \0\xae\2 BSN archive data
+1 string \0\xae\3 BSN archive data
+1 string \0\xae\7 BSN archive data
+# AIN
+0 string \x33\x18 AIN archive data
+0 string \x33\x17 AIN archive data
+# XPA32
+0 string xpa\0\1 XPA32 archive data
+# SZip (TODO: doesn't catch all versions)
+0 string SZ\x0a\4 SZip archive data
+# XPack DiskImage
+0 string jm XPack DiskImage archive data
+# XPack Data
+0 string xpa XPack archive data
+# XPack Single Data
+0 string \xc3\x8d\ jm XPack single archive data
+
+# TODO: missing due to unknown magic/magic at end of file:
+#DWC
+#ARG
+#ZAR
+#PC/3270
+#InstallIt
+#RKive
+#RK
+#XPack Diskimage
+
+# These were inspired by idarc, but actually verified
+# Dzip archiver (.dz)
+0 string DZ Dzip archive data
+>2 byte x \b, version %i
+>3 byte x \b.%i
+# ZZip archiver (.zz)
+0 string ZZ\ \0\0 ZZip archive data
+0 string ZZ0 ZZip archive data
+# PAQ archiver (.paq)
+0 string \xaa\x40\x5f\x77\x1f\xe5\x82\x0d PAQ archive data
+0 string PAQ PAQ archive data
+>3 byte&0xf0 0x30
+>>3 byte x (v%c)
+# JAR archiver (.j), this is the successor to ARJ, not Java's JAR (which is essentially ZIP)
+0xe string \x1aJar\x1b JAR (ARJ Software, Inc.) archive data
+0 string JARCS JAR (ARJ Software, Inc.) archive data
+
+# ARJ archiver (jason@jarthur.Claremont.EDU)
+0 leshort 0xea60 ARJ archive data
+!:mime application/x-arj
+>5 byte x \b, v%d,
+>8 byte &0x04 multi-volume,
+>8 byte &0x10 slash-switched,
+>8 byte &0x20 backup,
+>34 string x original name: %s,
+>7 byte 0 os: MS-DOS
+>7 byte 1 os: PRIMOS
+>7 byte 2 os: Unix
+>7 byte 3 os: Amiga
+>7 byte 4 os: Macintosh
+>7 byte 5 os: OS/2
+>7 byte 6 os: Apple ][ GS
+>7 byte 7 os: Atari ST
+>7 byte 8 os: NeXT
+>7 byte 9 os: VAX/VMS
+>3 byte >0 %d]
+# [JW] idarc says this is also possible
+2 leshort 0xea60 ARJ archive data
+
+# HA archiver (Greg Roelofs, newt@uchicago.edu)
+# This is a really bad format. A file containing HAWAII will match this...
+#0 string HA HA archive data,
+#>2 leshort =1 1 file,
+#>2 leshort >1 %hu files,
+#>4 byte&0x0f =0 first is type CPY
+#>4 byte&0x0f =1 first is type ASC
+#>4 byte&0x0f =2 first is type HSC
+#>4 byte&0x0f =0x0e first is type DIR
+#>4 byte&0x0f =0x0f first is type SPECIAL
+# suggestion: at least identify small archives (<1024 files)
+0 belong&0xffff00fc 0x48410000 HA archive data
+>2 leshort =1 1 file,
+>2 leshort >1 %u files,
+>4 byte&0x0f =0 first is type CPY
+>4 byte&0x0f =1 first is type ASC
+>4 byte&0x0f =2 first is type HSC
+>4 byte&0x0f =0x0e first is type DIR
+>4 byte&0x0f =0x0f first is type SPECIAL
+
+# HPACK archiver (Peter Gutmann, pgut1@cs.aukuni.ac.nz)
+0 string HPAK HPACK archive data
+
+# JAM Archive volume format, by Dmitry.Kohmanyuk@UA.net
+0 string \351,\001JAM\ JAM archive,
+>7 string >\0 version %.4s
+>0x26 byte =0x27 -
+>>0x2b string >\0 label %.11s,
+>>0x27 lelong x serial %08x,
+>>0x36 string >\0 fstype %.8s
+
+# LHARC/LHA archiver (Greg Roelofs, newt@uchicago.edu)
+2 string -lh0- LHarc 1.x/ARX archive data [lh0]
+!:mime application/x-lharc
+2 string -lh1- LHarc 1.x/ARX archive data [lh1]
+!:mime application/x-lharc
+2 string -lz4- LHarc 1.x archive data [lz4]
+!:mime application/x-lharc
+2 string -lz5- LHarc 1.x archive data [lz5]
+!:mime application/x-lharc
+# [never seen any but the last; -lh4- reported in comp.compression:]
+2 string -lzs- LHa/LZS archive data [lzs]
+!:mime application/x-lha
+2 string -lh\40- LHa 2.x? archive data [lh ]
+!:mime application/x-lha
+2 string -lhd- LHa 2.x? archive data [lhd]
+!:mime application/x-lha
+2 string -lh2- LHa 2.x? archive data [lh2]
+!:mime application/x-lha
+2 string -lh3- LHa 2.x? archive data [lh3]
+!:mime application/x-lha
+2 string -lh4- LHa (2.x) archive data [lh4]
+!:mime application/x-lha
+2 string -lh5- LHa (2.x) archive data [lh5]
+!:mime application/x-lha
+2 string -lh6- LHa (2.x) archive data [lh6]
+!:mime application/x-lha
+2 string -lh7- LHa (2.x)/LHark archive data [lh7]
+!:mime application/x-lha
+>20 byte x - header level %d
+# taken from idarc [JW]
+2 string -lZ PUT archive data
+2 string -lz LZS archive data
+2 string -sw1- Swag archive data
+
+# RAR archiver (Greg Roelofs, newt@uchicago.edu)
+0 string Rar! RAR archive data,
+!:mime application/x-rar
+>44 byte x v%0x,
+>10 byte >0 flags:
+>>10 byte &0x01 Archive volume,
+>>10 byte &0x02 Commented,
+>>10 byte &0x04 Locked,
+>>10 byte &0x08 Solid,
+>>10 byte &0x20 Authenticated,
+>35 byte 0 os: MS-DOS
+>35 byte 1 os: OS/2
+>35 byte 2 os: Win32
+>35 byte 3 os: Unix
+# some old version? idarc says:
+0 string RE\x7e\x5e RAR archive data
+
+# SQUISH archiver (Greg Roelofs, newt@uchicago.edu)
+0 string SQSH squished archive data (Acorn RISCOS)
+
+# UC2 archiver (Greg Roelofs, newt@uchicago.edu)
+# [JW] see exe section for self-extracting version
+0 string UC2\x1a UC2 archive data
+
+# PKZIP multi-volume archive
+0 string PK\x07\x08PK\x03\x04 Zip multi-volume archive data, at least PKZIP v2.50 to extract
+!:mime application/zip
+
+# Zip archives (Greg Roelofs, c/o zip-bugs@wkuvx1.wku.edu)
+0 string PK\005\006 Zip archive data (empty)
+0 string PK\003\004
+
+# Specialised zip formats which start with a member named 'mimetype'
+# (stored uncompressed, with no 'extra field') containing the file's MIME type.
+# Check for have 8-byte name, 0-byte extra field, name "mimetype", and
+# contents starting with "application/":
+>26 string \x8\0\0\0mimetypeapplication/
+
+# KOffice / OpenOffice & StarOffice / OpenDocument formats
+# From: Abel Cheung <abel@oaka.org>
+
+# KOffice (1.2 or above) formats
+# (mimetype contains "application/vnd.kde.<SUBTYPE>")
+>>50 string vnd.kde. KOffice (>=1.2)
+>>>58 string karbon Karbon document
+>>>58 string kchart KChart document
+>>>58 string kformula KFormula document
+>>>58 string kivio Kivio document
+>>>58 string kontour Kontour document
+>>>58 string kpresenter KPresenter document
+>>>58 string kspread KSpread document
+>>>58 string kword KWord document
+
+# OpenOffice formats (for OpenOffice 1.x / StarOffice 6/7)
+# (mimetype contains "application/vnd.sun.xml.<SUBTYPE>")
+>>50 string vnd.sun.xml. OpenOffice.org 1.x
+>>>62 string writer Writer
+>>>>68 byte !0x2e document
+>>>>68 string .template template
+>>>>68 string .global global document
+>>>62 string calc Calc
+>>>>66 byte !0x2e spreadsheet
+>>>>66 string .template template
+>>>62 string draw Draw
+>>>>66 byte !0x2e document
+>>>>66 string .template template
+>>>62 string impress Impress
+>>>>69 byte !0x2e presentation
+>>>>69 string .template template
+>>>62 string math Math document
+>>>62 string base Database file
+
+# OpenDocument formats (for OpenOffice 2.x / StarOffice >= 8)
+# http://lists.oasis-open.org/archives/office/200505/msg00006.html
+# (mimetype contains "application/vnd.oasis.opendocument.<SUBTYPE>")
+>>50 string vnd.oasis.opendocument. OpenDocument
+>>>73 string text
+>>>>77 byte !0x2d Text
+!:mime application/vnd.oasis.opendocument.text
+>>>>77 string -template Text Template
+!:mime application/vnd.oasis.opendocument.text-template
+>>>>77 string -web HTML Document Template
+!:mime application/vnd.oasis.opendocument.text-web
+>>>>77 string -master Master Document
+!:mime application/vnd.oasis.opendocument.text-master
+>>>73 string graphics
+>>>>81 byte !0x2d Drawing
+!:mime application/vnd.oasis.opendocument.graphics
+>>>>81 string -template Template
+!:mime application/vnd.oasis.opendocument.graphics-template
+>>>73 string presentation
+>>>>85 byte !0x2d Presentation
+!:mime application/vnd.oasis.opendocument.presentation
+>>>>85 string -template Template
+!:mime application/vnd.oasis.opendocument.presentation-template
+>>>73 string spreadsheet
+>>>>84 byte !0x2d Spreadsheet
+!:mime application/vnd.oasis.opendocument.spreadsheet
+>>>>84 string -template Template
+!:mime application/vnd.oasis.opendocument.spreadsheet-template
+>>>73 string chart
+>>>>78 byte !0x2d Chart
+!:mime application/vnd.oasis.opendocument.chart
+>>>>78 string -template Template
+!:mime application/vnd.oasis.opendocument.chart-template
+>>>73 string formula
+>>>>80 byte !0x2d Formula
+!:mime application/vnd.oasis.opendocument.formula
+>>>>80 string -template Template
+!:mime application/vnd.oasis.opendocument.formula-template
+>>>73 string database Database
+!:mime application/vnd.oasis.opendocument.database
+>>>73 string image
+>>>>78 byte !0x2d Image
+!:mime application/vnd.oasis.opendocument.image
+>>>>78 string -template Template
+!:mime application/vnd.oasis.opendocument.image-template
+
+# EPUB (OEBPS) books using OCF (OEBPS Container Format)
+# http://www.idpf.org/ocf/ocf1.0/download/ocf10.htm, section 4.
+# From: Ralf Brown <ralf.brown@gmail.com>
+>>50 string epub+zip EPUB document
+!:mime application/epub+zip
+
+# Catch other ZIP-with-mimetype formats
+# In a ZIP file, the bytes immediately after a member's contents are
+# always "PK". The 2 regex rules here print the "mimetype" member's
+# contents up to the first 'P'. Luckily, most MIME types don't contain
+# any capital 'P's. This is a kludge.
+# (mimetype contains "application/<OTHER>")
+>>50 string !epub+zip
+>>>50 string !vnd.oasis.opendocument.
+>>>>50 string !vnd.sun.xml.
+>>>>>50 string !vnd.kde.
+>>>>>>38 regex [!-OQ-~]+ Zip data (MIME type "%s"?)
+!:mime application/zip
+# (mimetype contents other than "application/*")
+>26 string \x8\0\0\0mimetype
+>>38 string !application/
+>>>38 regex [!-OQ-~]+ Zip data (MIME type "%s"?)
+!:mime application/zip
+
+# Java Jar files
+>(26.s+30) leshort 0xcafe Java archive data (JAR)
+!:mime application/java-archive
+
+# Generic zip archives (Greg Roelofs, c/o zip-bugs@wkuvx1.wku.edu)
+# Next line excludes specialized formats:
+>(26.s+30) leshort !0xcafe
+>>26 string !\x8\0\0\0mimetype Zip archive data
+!:mime application/zip
+>>>4 byte 0x09 \b, at least v0.9 to extract
+>>>4 byte 0x0a \b, at least v1.0 to extract
+>>>4 byte 0x0b \b, at least v1.1 to extract
+>>>4 byte 0x14 \b, at least v2.0 to extract
+>>>4 byte 0x2d \b, at least v3.0 to extract
+>>>0x161 string WINZIP \b, WinZIP self-extracting
+
+# StarView Metafile
+# From Pierre Ducroquet <pinaraf@pinaraf.info>
+0 string VCLMTF StarView MetaFile
+>6 beshort x \b, version %d
+>8 belong x \b, size %d
+
+# Zoo archiver
+20 lelong 0xfdc4a7dc Zoo archive data
+!:mime application/x-zoo
+>4 byte >48 \b, v%c.
+>>6 byte >47 \b%c
+>>>7 byte >47 \b%c
+>32 byte >0 \b, modify: v%d
+>>33 byte x \b.%d+
+>42 lelong 0xfdc4a7dc \b,
+>>70 byte >0 extract: v%d
+>>>71 byte x \b.%d+
+
+# Shell archives
+10 string #\ This\ is\ a\ shell\ archive shell archive text
+!:mime application/octet-stream
+
+#
+# LBR. NB: May conflict with the questionable
+# "binary Computer Graphics Metafile" format.
+#
+0 string \0\ \ \ \ \ \ \ \ \ \ \ \0\0 LBR archive data
+#
+# PMA (CP/M derivative of LHA)
+#
+2 string -pm0- PMarc archive data [pm0]
+2 string -pm1- PMarc archive data [pm1]
+2 string -pm2- PMarc archive data [pm2]
+2 string -pms- PMarc SFX archive (CP/M, DOS)
+5 string -pc1- PopCom compressed executable (CP/M)
+
+# From Rafael Laboissiere <rafael@laboissiere.net>
+# The Project Revision Control System (see
+# http://prcs.sourceforge.net) generates a packaged project
+# file which is recognized by the following entry:
+0 leshort 0xeb81 PRCS packaged project
+
+# Microsoft cabinets
+# by David Necas (Yeti) <yeti@physics.muni.cz>
+#0 string MSCF\0\0\0\0 Microsoft cabinet file data,
+#>25 byte x v%d
+#>24 byte x \b.%d
+# MPi: All CABs have version 1.3, so this is pointless.
+# Better magic in debian-additions.
+
+# GTKtalog catalogs
+# by David Necas (Yeti) <yeti@physics.muni.cz>
+4 string gtktalog\ GTKtalog catalog data,
+>13 string 3 version 3
+>>14 beshort 0x677a (gzipped)
+>>14 beshort !0x677a (not gzipped)
+>13 string >3 version %s
+
+############################################################################
+# Parity archive reconstruction file, the 'par' file format now used on Usenet.
+0 string PAR\0 PARity archive data
+>48 leshort =0 - Index file
+>48 leshort >0 - file number %d
+
+# Felix von Leitner <felix-file@fefe.de>
+0 string d8:announce BitTorrent file
+!:mime application/x-bittorrent
+
+# Atari MSA archive - Teemu Hukkanen <tjhukkan@iki.fi>
+0 beshort 0x0e0f Atari MSA archive data
+>2 beshort x \b, %d sectors per track
+>4 beshort 0 \b, 1 sided
+>4 beshort 1 \b, 2 sided
+>6 beshort x \b, starting track: %d
+>8 beshort x \b, ending track: %d
+
+# Alternate ZIP string (amc@arwen.cs.berkeley.edu)
+0 string PK00PK\003\004 Zip archive data
+
+# ACE archive (from http://www.wotsit.org/download.asp?f=ace)
+# by Stefan `Sec` Zehl <sec@42.org>
+7 string **ACE** ACE archive data
+>15 byte >0 version %d
+>16 byte =0x00 \b, from MS-DOS
+>16 byte =0x01 \b, from OS/2
+>16 byte =0x02 \b, from Win/32
+>16 byte =0x03 \b, from Unix
+>16 byte =0x04 \b, from MacOS
+>16 byte =0x05 \b, from WinNT
+>16 byte =0x06 \b, from Primos
+>16 byte =0x07 \b, from AppleGS
+>16 byte =0x08 \b, from Atari
+>16 byte =0x09 \b, from Vax/VMS
+>16 byte =0x0A \b, from Amiga
+>16 byte =0x0B \b, from Next
+>14 byte x \b, version %d to extract
+>5 leshort &0x0080 \b, multiple volumes,
+>>17 byte x \b (part %d),
+>5 leshort &0x0002 \b, contains comment
+>5 leshort &0x0200 \b, sfx
+>5 leshort &0x0400 \b, small dictionary
+>5 leshort &0x0800 \b, multi-volume
+>5 leshort &0x1000 \b, contains AV-String
+>>30 string \x16*UNREGISTERED\x20VERSION* (unregistered)
+>5 leshort &0x2000 \b, with recovery record
+>5 leshort &0x4000 \b, locked
+>5 leshort &0x8000 \b, solid
+# Date in MS-DOS format (whatever that is)
+#>18 lelong x Created on
+
+# sfArk : compression program for Soundfonts (sf2) by Dirk Jagdmann
+# <doj@cubic.org>
+0x1A string sfArk sfArk compressed Soundfont
+>0x15 string 2
+>>0x1 string >\0 Version %s
+>>0x2A string >\0 : %s
+
+# DR-DOS 7.03 Packed File *.??_
+0 string Packed\ File\ Personal NetWare Packed File
+>12 string x \b, was "%.12s"
+
+# EET archive
+# From: Tilman Sauerbeck <tilman@code-monkey.de>
+0 belong 0x1ee7ff00 EET archive
+!:mime application/x-eet
+
+# rzip archives
+0 string RZIP rzip compressed data
+>4 byte x - version %d
+>5 byte x \b.%d
+>6 belong x (%d bytes)
+
+# From: "Robert Dale" <robdale@gmail.com>
+0 belong 123 dar archive,
+>4 belong x label "%.8x
+>>8 belong x %.8x
+>>>12 beshort x %.4x"
+>14 byte 0x54 end slice
+>14 beshort 0x4e4e multi-part
+>14 beshort 0x4e53 multi-part, with -S
+
+# Symbian installation files
+# http://www.thouky.co.uk/software/psifs/sis.html
+# http://developer.symbian.com/main/downloads/papers/SymbianOSv91/softwareinstallsis.pdf
+8 lelong 0x10000419 Symbian installation file
+!:mime application/vnd.symbian.install
+>4 lelong 0x1000006D (EPOC release 3/4/5)
+>4 lelong 0x10003A12 (EPOC release 6)
+0 lelong 0x10201A7A Symbian installation file (Symbian OS 9.x)
+!:mime x-epoc/x-sisx-app
+
+# From "Nelson A. de Oliveira" <naoliv@gmail.com>
+0 string MPQ\032 MoPaQ (MPQ) archive
+
+# From: Dirk Jagdmann <doj@cubic.org>
+# xar archive format: http://code.google.com/p/xar/
+0 string xar! xar archive
+>6 beshort x - version %d
+
+# From: "Nelson A. de Oliveira" <naoliv@gmail.com>
+# .kgb
+0 string KGB_arch KGB Archiver file
+>10 string x with compression level %.1s
+
+# xar (eXtensible ARchiver) archive
+# From: "David Remahl" <dremahl@apple.com>
+0 string xar! xar archive
+#>4 beshort x header size %d
+>6 beshort x version %d,
+#>8 quad x compressed TOC: %d,
+#>16 quad x uncompressed TOC: %d,
+>24 belong 0 no checksum
+>24 belong 1 SHA-1 checksum
+>24 belong 2 MD5 checksum
+
+# Type: Parity Archive
+# From: Daniel van Eeden <daniel_e@dds.nl>
+0 string PAR2 Parity Archive Volume Set
+
+# Bacula volume format. (Volumes always start with a block header.)
+# URL: http://bacula.org/3.0.x-manuals/en/developers/developers/Block_Header.html
+# From: Adam Buchbinder <adam.buchbinder@gmail.com>
+12 string BB02 Bacula volume
+>20 bedate x \b, started %s
+
+# ePub is XHTML + XML inside a ZIP archive. The first member of the
+# archive must be an uncompressed file called 'mimetype' with contents
+# 'application/epub+zip'
+
+
+# From: "Michael Gorny" <mgorny@gentoo.org>
+# ZPAQ: http://mattmahoney.net/dc/zpaq.html
+0 string zPQ ZPAQ stream
+>3 byte x \b, level %d
+
+# BBeB ebook, unencrypted (LRF format)
+# URL: http://www.sven.de/librie/Librie/LrfFormat
+# From: Adam Buchbinder <adam.buchbinder@gmail.com>
+0 string L\0R\0F\0\0\0 BBeB ebook data, unencrypted
+>8 beshort x \b, version %d
+>36 byte 1 \b, front-to-back
+>36 byte 16 \b, back-to-front
+>42 beshort x \b, (%dx,
+>44 beshort x %d)
+
+# Symantec GHOST image by Joerg Jenderek at May 2014
+# http://us.norton.com/ghost/
+# http://www.garykessler.net/library/file_sigs.html
+0 ubelong&0xFFFFf7f0 0xFEEF0100 Norton GHost image
+# *.GHO
+>2 ubyte&0x08 0x00 \b, first file
+# *.GHS or *.[0-9] with cns program option
+>2 ubyte&0x08 0x08 \b, split file
+# part of split index interesting for *.ghs
+>>4 ubyte x id=0x%x
+# compression tag minus one equals numeric compression command line switch z[1-9]
+>3 ubyte 0 \b, no compression
+>3 ubyte 2 \b, fast compression (Z1)
+>3 ubyte 3 \b, medium compression (Z2)
+>3 ubyte >3
+>>3 ubyte <11 \b, compression (Z%d-1)
+>2 ubyte&0x08 0x00
+# ~ 30 byte password field only for *.gho
+>>12 ubequad !0 \b, password protected
+>>44 ubyte !1
+# 1~Image All, sector-by-sector only for *.gho
+>>>10 ubyte 1 \b, sector copy
+# 1~Image Boot track only for *.gho
+>>>43 ubyte 1 \b, boot track
+# 1~Image Disc only for *.gho implies Image Boot track and sector copy
+>>44 ubyte 1 \b, disc sector copy
+# optional image description only *.gho
+>>0xff string >\0 "%-.254s"
+# look for DOS sector end sequence
+>0xE08 search/7776 \x55\xAA
+>>&-512 indirect x \b; contains
+
+#------------------------------------------------------------------------------
+# $File: assembler,v 1.5 2013/09/17 17:33:36 christos Exp $
+# make: file(1) magic for assembler source
+#
+0 regex \^[\040\t]{0,50}\\.asciiz assembler source text
+!:mime text/x-asm
+0 regex \^[\040\t]{0,50}\\.byte assembler source text
+!:mime text/x-asm
+0 regex \^[\040\t]{0,50}\\.even assembler source text
+!:mime text/x-asm
+0 regex \^[\040\t]{0,50}\\.globl assembler source text
+!:mime text/x-asm
+0 regex \^[\040\t]{0,50}\\.text assembler source text
+!:mime text/x-asm
+0 regex \^[\040\t]{0,50}\\.file assembler source text
+!:mime text/x-asm
+0 regex \^[\040\t]{0,50}\\.type assembler source text
+!:mime text/x-asm
+
+#------------------------------------------------------------------------------
+# $File$
+# asterix: file(1) magic for Aster*x; SunOS 5.5.1 gave the 4-character
+# strings as "long" - we assume they're just strings:
+# From: guy@netapp.com (Guy Harris)
+#
+0 string *STA Aster*x
+>7 string WORD Words Document
+>7 string GRAP Graphic
+>7 string SPRE Spreadsheet
+>7 string MACR Macro
+0 string 2278 Aster*x Version 2
+>29 byte 0x36 Words Document
+>29 byte 0x35 Graphic
+>29 byte 0x32 Spreadsheet
+>29 byte 0x38 Macro
+
+
+#------------------------------------------------------------------------------
+# $File: att3b,v 1.8 2009/09/19 16:28:08 christos Exp $
+# att3b: file(1) magic for AT&T 3B machines
+#
+# The `versions' should be un-commented if they work for you.
+# (Was the problem just one of endianness?)
+#
+# 3B20
+#
+# The 3B20 conflicts with SCCS.
+#0 beshort 0550 3b20 COFF executable
+#>12 belong >0 not stripped
+#>22 beshort >0 - version %d
+#0 beshort 0551 3b20 COFF executable (TV)
+#>12 belong >0 not stripped
+#>22 beshort >0 - version %d
+#
+# WE32K
+#
+0 beshort 0560 WE32000 COFF
+>18 beshort ^00000020 object
+>18 beshort &00000020 executable
+>12 belong >0 not stripped
+>18 beshort ^00010000 N/A on 3b2/300 w/paging
+>18 beshort &00020000 32100 required
+>18 beshort &00040000 and MAU hardware required
+>20 beshort 0407 (impure)
+>20 beshort 0410 (pure)
+>20 beshort 0413 (demand paged)
+>20 beshort 0443 (target shared library)
+>22 beshort >0 - version %d
+0 beshort 0561 WE32000 COFF executable (TV)
+>12 belong >0 not stripped
+#>18 beshort &00020000 - 32100 required
+#>18 beshort &00040000 and MAU hardware required
+#>22 beshort >0 - version %d
+#
+# core file for 3b2
+0 string \000\004\036\212\200 3b2 core file
+>364 string >\0 of '%s'
+
+#------------------------------------------------------------------------------
+# $File: audio,v 1.71 2014/05/14 23:30:28 christos Exp $
+# audio: file(1) magic for sound formats (see also "iff")
+#
+# Jan Nicolai Langfeldt (janl@ifi.uio.no), Dan Quinlan (quinlan@yggdrasil.com),
+# and others
+#
+
+# Sun/NeXT audio data
+0 string .snd Sun/NeXT audio data:
+>12 belong 1 8-bit ISDN mu-law,
+!:mime audio/basic
+>12 belong 2 8-bit linear PCM [REF-PCM],
+!:mime audio/basic
+>12 belong 3 16-bit linear PCM,
+!:mime audio/basic
+>12 belong 4 24-bit linear PCM,
+!:mime audio/basic
+>12 belong 5 32-bit linear PCM,
+!:mime audio/basic
+>12 belong 6 32-bit IEEE floating point,
+!:mime audio/basic
+>12 belong 7 64-bit IEEE floating point,
+!:mime audio/basic
+>12 belong 8 Fragmented sample data,
+>12 belong 10 DSP program,
+>12 belong 11 8-bit fixed point,
+>12 belong 12 16-bit fixed point,
+>12 belong 13 24-bit fixed point,
+>12 belong 14 32-bit fixed point,
+>12 belong 18 16-bit linear with emphasis,
+>12 belong 19 16-bit linear compressed,
+>12 belong 20 16-bit linear with emphasis and compression,
+>12 belong 21 Music kit DSP commands,
+>12 belong 23 8-bit ISDN mu-law compressed (CCITT G.721 ADPCM voice enc.),
+!:mime audio/x-adpcm
+>12 belong 24 compressed (8-bit CCITT G.722 ADPCM)
+>12 belong 25 compressed (3-bit CCITT G.723.3 ADPCM),
+>12 belong 26 compressed (5-bit CCITT G.723.5 ADPCM),
+>12 belong 27 8-bit A-law (CCITT G.711),
+>20 belong 1 mono,
+>20 belong 2 stereo,
+>20 belong 4 quad,
+>16 belong >0 %d Hz
+
+# DEC systems (e.g. DECstation 5000) use a variant of the Sun/NeXT format
+# that uses little-endian encoding and has a different magic number
+0 lelong 0x0064732E DEC audio data:
+>12 lelong 1 8-bit ISDN mu-law,
+!:mime audio/x-dec-basic
+>12 lelong 2 8-bit linear PCM [REF-PCM],
+!:mime audio/x-dec-basic
+>12 lelong 3 16-bit linear PCM,
+!:mime audio/x-dec-basic
+>12 lelong 4 24-bit linear PCM,
+!:mime audio/x-dec-basic
+>12 lelong 5 32-bit linear PCM,
+!:mime audio/x-dec-basic
+>12 lelong 6 32-bit IEEE floating point,
+!:mime audio/x-dec-basic
+>12 lelong 7 64-bit IEEE floating point,
+!:mime audio/x-dec-basic
+>12 belong 8 Fragmented sample data,
+>12 belong 10 DSP program,
+>12 belong 11 8-bit fixed point,
+>12 belong 12 16-bit fixed point,
+>12 belong 13 24-bit fixed point,
+>12 belong 14 32-bit fixed point,
+>12 belong 18 16-bit linear with emphasis,
+>12 belong 19 16-bit linear compressed,
+>12 belong 20 16-bit linear with emphasis and compression,
+>12 belong 21 Music kit DSP commands,
+>12 lelong 23 8-bit ISDN mu-law compressed (CCITT G.721 ADPCM voice enc.),
+!:mime audio/x-dec-basic
+>12 belong 24 compressed (8-bit CCITT G.722 ADPCM)
+>12 belong 25 compressed (3-bit CCITT G.723.3 ADPCM),
+>12 belong 26 compressed (5-bit CCITT G.723.5 ADPCM),
+>12 belong 27 8-bit A-law (CCITT G.711),
+>20 lelong 1 mono,
+>20 lelong 2 stereo,
+>20 lelong 4 quad,
+>16 lelong >0 %d Hz
+
+# Creative Labs AUDIO stuff
+0 string MThd Standard MIDI data
+!:mime audio/midi
+>8 beshort x (format %d)
+>10 beshort x using %d track
+>10 beshort >1 \bs
+>12 beshort&0x7fff x at 1/%d
+>12 beshort&0x8000 >0 SMPTE
+
+0 string CTMF Creative Music (CMF) data
+!:mime audio/x-unknown
+0 string SBI SoundBlaster instrument data
+!:mime audio/x-unknown
+0 string Creative\ Voice\ File Creative Labs voice data
+!:mime audio/x-unknown
+# is this next line right? it came this way...
+>19 byte 0x1A
+>23 byte >0 - version %d
+>22 byte >0 \b.%d
+
+# first entry is also the string "NTRK"
+0 belong 0x4e54524b MultiTrack sound data
+>4 belong x - version %d
+
+# Extended MOD format (*.emd) (Greg Roelofs, newt@uchicago.edu); NOT TESTED
+# [based on posting 940824 by "Dirk/Elastik", husberg@lehtori.cc.tut.fi]
+0 string EMOD Extended MOD sound data,
+>4 byte&0xf0 x version %d
+>4 byte&0x0f x \b.%d,
+>45 byte x %d instruments
+>83 byte 0 (module)
+>83 byte 1 (song)
+
+# Real Audio (Magic .ra\0375)
+0 belong 0x2e7261fd RealAudio sound file
+!:mime audio/x-pn-realaudio
+0 string .RMF\0\0\0 RealMedia file
+!:mime application/vnd.rn-realmedia
+#video/x-pn-realvideo
+#video/vnd.rn-realvideo
+#application/vnd.rn-realmedia
+# sigh, there are many mimes for that but the above are the most common.
+
+# MTM/669/FAR/S3M/ULT/XM format checking [Aaron Eppert, aeppert@dialin.ind.net]
+# Oct 31, 1995
+# fixed by <doj@cubic.org> 2003-06-24
+# Too short...
+#0 string MTM MultiTracker Module sound file
+#0 string if Composer 669 Module sound data
+#0 string JN Composer 669 Module sound data (extended format)
+0 string MAS_U ULT(imate) Module sound data
+
+#0 string FAR Module sound data
+#>4 string >\15 Title: "%s"
+
+0x2c string SCRM ScreamTracker III Module sound data
+>0 string >\0 Title: "%s"
+
+# Gravis UltraSound patches
+# From <ache@nagual.ru>
+
+0 string GF1PATCH110\0ID#000002\0 GUS patch
+0 string GF1PATCH100\0ID#000002\0 Old GUS patch
+
+# mime types according to http://www.geocities.com/nevilo/mod.htm:
+# audio/it .it
+# audio/x-zipped-it .itz
+# audio/xm fasttracker modules
+# audio/x-s3m screamtracker modules
+# audio/s3m screamtracker modules
+# audio/x-zipped-mod mdz
+# audio/mod mod
+# audio/x-mod All modules (mod, s3m, 669, mtm, med, xm, it, mdz, stm, itz, xmz, s3z)
+
+#
+# Taken from loader code from mikmod version 2.14
+# by Steve McIntyre (stevem@chiark.greenend.org.uk)
+# <doj@cubic.org> added title printing on 2003-06-24
+0 string MAS_UTrack_V00
+>14 string >/0 ultratracker V1.%.1s module sound data
+!:mime audio/x-mod
+#audio/x-tracker-module
+
+0 string UN05 MikMod UNI format module sound data
+
+0 string Extended\ Module: Fasttracker II module sound data
+!:mime audio/x-mod
+#audio/x-tracker-module
+>17 string >\0 Title: "%s"
+
+21 string/c =!SCREAM! Screamtracker 2 module sound data
+!:mime audio/x-mod
+#audio/x-screamtracker-module
+21 string BMOD2STM Screamtracker 2 module sound data
+!:mime audio/x-mod
+#audio/x-screamtracker-module
+1080 string M.K. 4-channel Protracker module sound data
+!:mime audio/x-mod
+#audio/x-protracker-module
+>0 string >\0 Title: "%s"
+1080 string M!K! 4-channel Protracker module sound data
+!:mime audio/x-mod
+#audio/x-protracker-module
+>0 string >\0 Title: "%s"
+1080 string FLT4 4-channel Startracker module sound data
+!:mime audio/x-mod
+#audio/x-startracker-module
+>0 string >\0 Title: "%s"
+1080 string FLT8 8-channel Startracker module sound data
+!:mime audio/x-mod
+#audio/x-startracker-module
+>0 string >\0 Title: "%s"
+1080 string 4CHN 4-channel Fasttracker module sound data
+!:mime audio/x-mod
+#audio/x-fasttracker-module
+>0 string >\0 Title: "%s"
+1080 string 6CHN 6-channel Fasttracker module sound data
+!:mime audio/x-mod
+#audio/x-fasttracker-module
+>0 string >\0 Title: "%s"
+1080 string 8CHN 8-channel Fasttracker module sound data
+!:mime audio/x-mod
+#audio/x-fasttracker-module
+>0 string >\0 Title: "%s"
+1080 string CD81 8-channel Octalyser module sound data
+!:mime audio/x-mod
+#audio/x-octalysertracker-module
+>0 string >\0 Title: "%s"
+1080 string OKTA 8-channel Octalyzer module sound data
+!:mime audio/x-mod
+#audio/x-octalysertracker-module
+>0 string >\0 Title: "%s"
+# Not good enough.
+#1082 string CH
+#>1080 string >/0 %.2s-channel Fasttracker "oktalyzer" module sound data
+1080 string 16CN 16-channel Taketracker module sound data
+!:mime audio/x-mod
+#audio/x-taketracker-module
+>0 string >\0 Title: "%s"
+1080 string 32CN 32-channel Taketracker module sound data
+!:mime audio/x-mod
+#audio/x-taketracker-module
+>0 string >\0 Title: "%s"
+
+# TOC sound files -Trevor Johnson <trevor@jpj.net>
+#
+0 string TOC TOC sound file
+
+# sidfiles <pooka@iki.fi>
+# added name,author,(c) and new RSID type by <doj@cubic.org> 2003-06-24
+0 string SIDPLAY\ INFOFILE Sidplay info file
+
+0 string PSID PlaySID v2.2+ (AMIGA) sidtune
+>4 beshort >0 w/ header v%d,
+>14 beshort =1 single song,
+>14 beshort >1 %d songs,
+>16 beshort >0 default song: %d
+>0x16 string >\0 name: "%s"
+>0x36 string >\0 author: "%s"
+>0x56 string >\0 copyright: "%s"
+
+0 string RSID RSID sidtune PlaySID compatible
+>4 beshort >0 w/ header v%d,
+>14 beshort =1 single song,
+>14 beshort >1 %d songs,
+>16 beshort >0 default song: %d
+>0x16 string >\0 name: "%s"
+>0x36 string >\0 author: "%s"
+>0x56 string >\0 copyright: "%s"
+
+# IRCAM sound files - Michael Pruett <michael@68k.org>
+# http://www-mmsp.ece.mcgill.ca/documents/AudioFormats/IRCAM/IRCAM.html
+0 belong 0x64a30100 IRCAM file (VAX little-endian)
+0 belong 0x0001a364 IRCAM file (VAX big-endian)
+0 belong 0x64a30200 IRCAM file (Sun big-endian)
+0 belong 0x0002a364 IRCAM file (Sun little-endian)
+0 belong 0x64a30300 IRCAM file (MIPS little-endian)
+0 belong 0x0003a364 IRCAM file (MIPS big-endian)
+0 belong 0x64a30400 IRCAM file (NeXT big-endian)
+0 belong 0x64a30400 IRCAM file (NeXT big-endian)
+0 belong 0x0004a364 IRCAM file (NeXT little-endian)
+
+# NIST SPHERE <mpruett@sgi.com>
+0 string NIST_1A\n\ \ \ 1024\n NIST SPHERE file
+
+# Sample Vision <mpruett@sgi.com>
+0 string SOUND\ SAMPLE\ DATA\ Sample Vision file
+
+# Audio Visual Research <tonigonenstein@users.sourceforge.net>
+0 string 2BIT Audio Visual Research file,
+>12 beshort =0 mono,
+>12 beshort =-1 stereo,
+>14 beshort x %d bits
+>16 beshort =0 unsigned,
+>16 beshort =-1 signed,
+>22 belong&0x00ffffff x %d Hz,
+>18 beshort =0 no loop,
+>18 beshort =-1 loop,
+>21 ubyte <128 note %d,
+>22 byte =0 replay 5.485 KHz
+>22 byte =1 replay 8.084 KHz
+>22 byte =2 replay 10.971 KHz
+>22 byte =3 replay 16.168 KHz
+>22 byte =4 replay 21.942 KHz
+>22 byte =5 replay 32.336 KHz
+>22 byte =6 replay 43.885 KHz
+>22 byte =7 replay 47.261 KHz
+
+# SGI SoundTrack <mpruett@sgi.com>
+0 string _SGI_SoundTrack SGI SoundTrack project file
+# ID3 version 2 tags <waschk@informatik.uni-rostock.de>
+0 string ID3 Audio file with ID3 version 2
+>3 byte x \b.%d
+>4 byte x \b.%d
+>>5 byte &0x80 \b, unsynchronized frames
+>>5 byte &0x40 \b, extended header
+>>5 byte &0x20 \b, experimental
+>>5 byte &0x10 \b, footer present
+>(6.I+10) indirect x \b, contains:
+
+# NSF (NES sound file) magic
+0 string NESM\x1a NES Sound File
+>14 string >\0 ("%s" by
+>46 string >\0 %s, copyright
+>78 string >\0 %s),
+>5 byte x version %d,
+>6 byte x %d tracks,
+>122 byte&0x2 =1 dual PAL/NTSC
+>122 byte&0x1 =1 PAL
+>122 byte&0x1 =0 NTSC
+
+# Type: SNES SPC700 sound files
+# From: Josh Triplett <josh@freedesktop.org>
+0 string SNES-SPC700\ Sound\ File\ Data\ v SNES SPC700 sound file
+>&0 string 0.30 \b, version %s
+>>0x23 byte 0x1B \b, without ID666 tag
+>>0x23 byte 0x1A \b, with ID666 tag
+>>>0x2E string >\0 \b, song "%.32s"
+>>>0x4E string >\0 \b, game "%.32s"
+
+# Impulse tracker module (audio/x-it)
+0 string IMPM Impulse Tracker module sound data -
+!:mime audio/x-mod
+>4 string >\0 "%s"
+>40 leshort !0 compatible w/ITv%x
+>42 leshort !0 created w/ITv%x
+
+# Imago Orpheus module (audio/x-imf)
+60 string IM10 Imago Orpheus module sound data -
+>0 string >\0 "%s"
+
+# From <collver1@attbi.com>
+# These are the /etc/magic entries to decode modules, instruments, and
+# samples in Impulse Tracker's native format.
+
+0 string IMPS Impulse Tracker Sample
+>18 byte &2 16 bit
+>18 byte ^2 8 bit
+>18 byte &4 stereo
+>18 byte ^4 mono
+0 string IMPI Impulse Tracker Instrument
+>28 leshort !0 ITv%x
+>30 byte !0 %d samples
+
+# Yamaha TX Wave: file(1) magic for Yamaha TX Wave audio files
+# From <collver1@attbi.com>
+0 string LM8953 Yamaha TX Wave
+>22 byte 0x49 looped
+>22 byte 0xC9 non-looped
+>23 byte 1 33kHz
+>23 byte 2 50kHz
+>23 byte 3 16kHz
+
+# scream tracker: file(1) magic for Scream Tracker sample files
+#
+# From <collver1@attbi.com>
+76 string SCRS Scream Tracker Sample
+>0 byte 1 sample
+>0 byte 2 adlib melody
+>0 byte >2 adlib drum
+>31 byte &2 stereo
+>31 byte ^2 mono
+>31 byte &4 16bit little endian
+>31 byte ^4 8bit
+>30 byte 0 unpacked
+>30 byte 1 packed
+
+# audio
+# From: Cory Dikkers <cdikkers@swbell.net>
+0 string MMD0 MED music file, version 0
+0 string MMD1 OctaMED Pro music file, version 1
+0 string MMD3 OctaMED Soundstudio music file, version 3
+0 string OctaMEDCmpr OctaMED Soundstudio compressed file
+0 string MED MED_Song
+0 string SymM Symphonie SymMOD music file
+#
+0 string THX AHX version
+>3 byte =0 1 module data
+>3 byte =1 2 module data
+#
+0 string OKTASONG Oktalyzer module data
+#
+0 string DIGI\ Booster\ module\0 %s
+>20 byte >0 %c
+>>21 byte >0 \b%c
+>>>22 byte >0 \b%c
+>>>>23 byte >0 \b%c
+>610 string >\0 \b, "%s"
+#
+0 string DBM0 DIGI Booster Pro Module
+>4 byte >0 V%X.
+>>5 byte x \b%02X
+>16 string >\0 \b, "%s"
+#
+0 string FTMN FaceTheMusic module
+>16 string >\0d \b, "%s"
+
+# From: <doj@cubic.org> 2003-06-24
+0 string AMShdr\32 Velvet Studio AMS Module v2.2
+0 string Extreme Extreme Tracker AMS Module v1.3
+0 string DDMF Xtracker DMF Module
+>4 byte x v%i
+>0xD string >\0 Title: "%s"
+>0x2B string >\0 Composer: "%s"
+0 string DSM\32 Dynamic Studio Module DSM
+0 string SONG DigiTrekker DTM Module
+0 string DMDL DigiTrakker MDL Module
+0 string PSM\32 Protracker Studio PSM Module
+44 string PTMF Poly Tracker PTM Module
+>0 string >\32 Title: "%s"
+0 string MT20 MadTracker 2.0 Module MT2
+0 string RAD\40by\40REALiTY!! RAD Adlib Tracker Module RAD
+0 string RTMM RTM Module
+0x426 string MaDoKaN96 XMS Adlib Module
+>0 string >\0 Composer: "%s"
+0 string AMF AMF Module
+>4 string >\0 Title: "%s"
+0 string MODINFO1 Open Cubic Player Module Inforation MDZ
+0 string Extended\40Instrument: Fast Tracker II Instrument
+
+# From: Takeshi Hamasaki <hma@syd.odn.ne.jp>
+# NOA Nancy Codec file
+0 string \210NOA\015\012\032 NOA Nancy Codec Movie file
+# Yamaha SMAF format
+0 string MMMD Yamaha SMAF file
+# Sharp Jisaku Melody format for PDC
+0 string \001Sharp\040JisakuMelody SHARP Cell-Phone ringing Melody
+>20 string Ver01.00 Ver. 1.00
+>>32 byte x , %d tracks
+
+# Free lossless audio codec <http://flac.sourceforge.net>
+# From: Przemyslaw Augustyniak <silvathraec@rpg.pl>
+0 string fLaC FLAC audio bitstream data
+!:mime audio/x-flac
+>4 byte&0x7f >0 \b, unknown version
+>4 byte&0x7f 0 \b
+# some common bits/sample values
+>>20 beshort&0x1f0 0x030 \b, 4 bit
+>>20 beshort&0x1f0 0x050 \b, 6 bit
+>>20 beshort&0x1f0 0x070 \b, 8 bit
+>>20 beshort&0x1f0 0x0b0 \b, 12 bit
+>>20 beshort&0x1f0 0x0f0 \b, 16 bit
+>>20 beshort&0x1f0 0x170 \b, 24 bit
+>>20 byte&0xe 0x0 \b, mono
+>>20 byte&0xe 0x2 \b, stereo
+>>20 byte&0xe 0x4 \b, 3 channels
+>>20 byte&0xe 0x6 \b, 4 channels
+>>20 byte&0xe 0x8 \b, 5 channels
+>>20 byte&0xe 0xa \b, 6 channels
+>>20 byte&0xe 0xc \b, 7 channels
+>>20 byte&0xe 0xe \b, 8 channels
+# some common sample rates
+>>17 belong&0xfffff0 0x0ac440 \b, 44.1 kHz
+>>17 belong&0xfffff0 0x0bb800 \b, 48 kHz
+>>17 belong&0xfffff0 0x07d000 \b, 32 kHz
+>>17 belong&0xfffff0 0x056220 \b, 22.05 kHz
+>>17 belong&0xfffff0 0x05dc00 \b, 24 kHz
+>>17 belong&0xfffff0 0x03e800 \b, 16 kHz
+>>17 belong&0xfffff0 0x02b110 \b, 11.025 kHz
+>>17 belong&0xfffff0 0x02ee00 \b, 12 kHz
+>>17 belong&0xfffff0 0x01f400 \b, 8 kHz
+>>17 belong&0xfffff0 0x177000 \b, 96 kHz
+>>17 belong&0xfffff0 0x0fa000 \b, 64 kHz
+>>21 byte&0xf >0 \b, >4G samples
+>>21 byte&0xf 0 \b
+>>>22 belong >0 \b, %u samples
+>>>22 belong 0 \b, length unknown
+
+# (ISDN) VBOX voice message file (Wolfram Kleff)
+0 string VBOX VBOX voice message data
+
+# ReBorn Song Files (.rbs)
+# David J. Singer <doc@deadvirgins.org.uk>
+8 string RB40 RBS Song file
+>29 string ReBorn created by ReBorn
+>37 string Propellerhead created by ReBirth
+
+# Synthesizer Generator and Kimwitu share their file format
+0 string A#S#C#S#S#L#V#3 Synthesizer Generator or Kimwitu data
+# Kimwitu++ uses a slightly different magic
+0 string A#S#C#S#S#L#HUB Kimwitu++ data
+
+# From "Simon Hosie
+0 string TFMX-SONG TFMX module sound data
+
+# Monkey's Audio compressed audio format (.ape)
+# From danny.milo@gmx.net (Danny Milosavljevic)
+# New version from Abel Cheung <abel (@) oaka.org>
+0 string MAC\040 Monkey's Audio compressed format
+!:mime audio/x-ape
+>4 uleshort >0x0F8B version %d
+>>(0x08.l) uleshort =1000 with fast compression
+>>(0x08.l) uleshort =2000 with normal compression
+>>(0x08.l) uleshort =3000 with high compression
+>>(0x08.l) uleshort =4000 with extra high compression
+>>(0x08.l) uleshort =5000 with insane compression
+>>(0x08.l+18) uleshort =1 \b, mono
+>>(0x08.l+18) uleshort =2 \b, stereo
+>>(0x08.l+20) ulelong x \b, sample rate %d
+>4 uleshort <0x0F8C version %d
+>>6 uleshort =1000 with fast compression
+>>6 uleshort =2000 with normal compression
+>>6 uleshort =3000 with high compression
+>>6 uleshort =4000 with extra high compression
+>>6 uleshort =5000 with insane compression
+>>10 uleshort =1 \b, mono
+>>10 uleshort =2 \b, stereo
+>>12 ulelong x \b, sample rate %d
+
+# adlib sound files
+# From Gurkan Sengun <gurkan@linuks.mine.nu>, http://www.linuks.mine.nu
+0 string RAWADATA RdosPlay RAW
+
+1068 string RoR AMUSIC Adlib Tracker
+
+0 string JCH EdLib
+
+0 string mpu401tr MPU-401 Trakker
+
+0 string SAdT Surprise! Adlib Tracker
+>4 byte x Version %d
+
+0 string XAD! eXotic ADlib
+
+0 string ofTAZ! eXtra Simple Music
+
+# Spectrum 128 tunes (.ay files).
+# From: Emanuel Haupt <ehaupt@critical.ch>
+0 string ZXAYEMUL Spectrum 128 tune
+
+0 string \0BONK BONK,
+#>5 byte x version %d
+>14 byte x %d channel(s),
+>15 byte =1 lossless,
+>15 byte =0 lossy,
+>16 byte x mid-side
+
+384 string LockStream LockStream Embedded file (mostly MP3 on old Nokia phones)
+
+# format VQF (proprietary codec for sound)
+# some infos on the header file available at :
+# http://www.twinvq.org/english/technology_format.html
+0 string TWIN97012000 VQF data
+>27 short 0 \b, Mono
+>27 short 1 \b, Stereo
+>31 short >0 \b, %d kbit/s
+>35 short >0 \b, %d kHz
+
+# Nelson A. de Oliveira (naoliv@gmail.com)
+# .eqf
+0 string Winamp\ EQ\ library\ file %s
+# it will match only versions like v<digit>.<digit>
+# Since I saw only eqf files with version v1.1 I think that it's OK
+>23 string x \b%.4s
+# .preset
+0 string [Equalizer\ preset] XMMS equalizer preset
+# .m3u
+0 search/1 #EXTM3U M3U playlist text
+# .pls
+0 search/1 [playlist] PLS playlist text
+# licq.conf
+1 string [licq] LICQ configuration file
+
+# Atari ST audio files by Dirk Jagdmann <doj@cubic.org>
+0 string ICE! SNDH Atari ST music
+0 string SC68\ Music-file\ /\ (c)\ (BeN)jami sc68 Atari ST music
+
+# musepak support From: "Jiri Pejchal" <jiri.pejchal@gmail.com>
+0 string MP+ Musepack audio
+!:mime audio/x-musepack
+>3 byte 255 \b, SV pre8
+>3 byte&0xF 0x6 \b, SV 6
+>3 byte&0xF 0x8 \b, SV 8
+>3 byte&0xF 0x7 \b, SV 7
+>>3 byte&0xF0 0x0 \b.0
+>>3 byte&0xF0 0x10 \b.1
+>>3 byte&0xF0 240 \b.15
+>>10 byte&0xF0 0x0 \b, no profile
+>>10 byte&0xF0 0x10 \b, profile 'Unstable/Experimental'
+>>10 byte&0xF0 0x50 \b, quality 0
+>>10 byte&0xF0 0x60 \b, quality 1
+>>10 byte&0xF0 0x70 \b, quality 2 (Telephone)
+>>10 byte&0xF0 0x80 \b, quality 3 (Thumb)
+>>10 byte&0xF0 0x90 \b, quality 4 (Radio)
+>>10 byte&0xF0 0xA0 \b, quality 5 (Standard)
+>>10 byte&0xF0 0xB0 \b, quality 6 (Xtreme)
+>>10 byte&0xF0 0xC0 \b, quality 7 (Insane)
+>>10 byte&0xF0 0xD0 \b, quality 8 (BrainDead)
+>>10 byte&0xF0 0xE0 \b, quality 9
+>>10 byte&0xF0 0xF0 \b, quality 10
+>>27 byte 0x0 \b, Buschmann 1.7.0-9, Klemm 0.90-1.05
+>>27 byte 102 \b, Beta 1.02
+>>27 byte 104 \b, Beta 1.04
+>>27 byte 105 \b, Alpha 1.05
+>>27 byte 106 \b, Beta 1.06
+>>27 byte 110 \b, Release 1.1
+>>27 byte 111 \b, Alpha 1.11
+>>27 byte 112 \b, Beta 1.12
+>>27 byte 113 \b, Alpha 1.13
+>>27 byte 114 \b, Beta 1.14
+>>27 byte 115 \b, Alpha 1.15
+
+# IMY
+# from http://filext.com/detaillist.php?extdetail=IMY
+# http://cellphones.about.com/od/cellularfaqs/f/rf_imelody.htm
+# http://download.ncl.ie/doc/api/ie/ncl/media/music/IMelody.html
+# http://www.wx800.com/msg/download/irda/iMelody.pdf
+0 string BEGIN:IMELODY iMelody Ringtone Format
+
+# From: "Mateus Caruccio" <mateus@caruccio.com>
+# guitar pro v3,4,5 from http://filext.com/file-extension/gp3
+0 string \030FICHIER\ GUITAR\ PRO\ v3. Guitar Pro Ver. 3 Tablature
+
+# From: "Leslie P. Polzer" <leslie.polzer@gmx.net>
+60 string SONG SoundFX Module sound file
+
+# Type: Adaptive Multi-Rate Codec
+# URL: http://filext.com/detaillist.php?extdetail=AMR
+# From: Russell Coker <russell@coker.com.au>
+0 string #!AMR Adaptive Multi-Rate Codec (GSM telephony)
+
+# Type: SuperCollider 3 Synth Definition File Format
+# From: Mario Lang <mlang@debian.org>
+0 string SCgf SuperCollider3 Synth Definition file,
+>4 belong x version %d
+
+# Type: True Audio Lossless Audio
+# URL: http://wiki.multimedia.cx/index.php?title=True_Audio
+# From: Mike Melanson <mike@multimedia.cx>
+0 string TTA1 True Audio Lossless Audio
+
+# Type: WavPack Lossless Audio
+# URL: http://wiki.multimedia.cx/index.php?title=WavPack
+# From: Mike Melanson <mike@multimedia.cx>
+0 string wvpk WavPack Lossless Audio
+
+# From Fabio R. Schmidlin <frs@pop.com.br>
+# VGM music file
+0 string Vgm\
+>9 ubyte >0 VGM Video Game Music dump v
+>>9 ubyte/16 >0 \b%d
+>>9 ubyte&0x0F x \b%d
+>>8 ubyte/16 x \b.%d
+>>8 ubyte&0x0F >0 \b%d
+#Get soundchips
+>>8 ubyte x \b, soundchip(s)=
+>>0x0C ulelong >0 SN76489,
+>>0x10 ulelong >0 YM2413,
+>>0x2C ulelong >0 YM2612,
+>>0x30 ulelong >0 YM2151,
+>>0x38 ulelong >0 Sega PCM,
+>>0x34 ulelong >0xC
+>>>0x40 ulelong >0 RF5C68,
+>>0x34 ulelong >0x10
+>>>0x44 ulelong >0 YM2203,
+>>0x34 ulelong >0x14
+>>>0x48 ulelong >0 YM2608,
+>>0x34 ulelong >0x18
+>>>0x4C lelong >0 YM2610,
+>>>0x4C lelong <0 YM2610B,
+>>0x34 ulelong >0x1C
+>>>0x50 ulelong >0 YM3812,
+>>0x34 ulelong >0x20
+>>>0x54 ulelong >0 YM3526,
+>>0x34 ulelong >0x24
+>>>0x58 ulelong >0 Y8950,
+>>0x34 ulelong >0x28
+>>>0x5C ulelong >0 YMF262,
+>>0x34 ulelong >0x2C
+>>>0x60 ulelong >0 YMF278B,
+>>0x34 ulelong >0x30
+>>>0x64 ulelong >0 YMF271,
+>>0x34 ulelong >0x34
+>>>0x68 ulelong >0 YMZ280B,
+>>0x34 ulelong >0x38
+>>>0x6C ulelong >0 RF5C164,
+>>0x34 ulelong >0x3C
+>>>0x70 ulelong >0 PWM,
+>>0x34 ulelong >0x40
+>>>0x74 ulelong >0
+>>>>0x78 ubyte 0x00 AY-3-8910,
+>>>>0x78 ubyte 0x01 AY-3-8912,
+>>>>0x78 ubyte 0x02 AY-3-8913,
+>>>>0x78 ubyte 0x03 AY-3-8930,
+>>>>0x78 ubyte 0x10 YM2149,
+>>>>0x78 ubyte 0x11 YM3439,
+
+# GVOX Encore file format
+# Since this is a proprietary file format and there is no publicly available
+# format specification, this is just based on induction
+#
+0 string SCOW
+>4 byte 0xc4 GVOX Encore music, version 5.0 or above
+>4 byte 0xc2 GVOX Encore music, version < 5.0
+
+0 string ZBOT
+>4 byte 0xc5 GVOX Encore music, version < 5.0
+
+
+#----------------------------------------------------------------
+# $File$
+# basis: file(1) magic for BBx/Pro5-files
+# Oliver Dammer <dammer@olida.de> 2005/11/07
+# http://www.basis.com business-basic-files.
+#
+0 string \074\074bbx\076\076 BBx
+>7 string \000 indexed file
+>7 string \001 serial file
+>7 string \002 keyed file
+>>13 short 0 (sort)
+>7 string \004 program
+>>18 byte x (LEVEL %d)
+>>>23 string >\000 psaved
+>7 string \006 mkeyed file
+>>13 short 0 (sort)
+>>8 string \000 (mkey)
+
+#------------------------------------------------------------------------------
+# $File: bflt,v 1.4 2009/09/19 16:28:08 christos Exp $
+# bFLT: file(1) magic for BFLT uclinux binary files
+#
+# From Philippe De Muyter <phdm@macqel.be>
+#
+0 string bFLT BFLT executable
+>4 belong x - version %d
+>4 belong 4
+>>36 belong&0x1 0x1 ram
+>>36 belong&0x2 0x2 gotpic
+>>36 belong&0x4 0x4 gzip
+>>36 belong&0x8 0x8 gzdata
+
+#------------------------------------------------------------------------------
+# $File: apple,v 1.27 2013/03/09 22:36:00 christos Exp $
+# blackberry: file(1) magic for BlackBerry file formats
+#
+5 belong 0
+>8 belong 010010010 BlackBerry RIM ETP file
+>>22 string x \b for %s
+# Berkeley Lab Checkpoint Restart (BLCR) checkpoint context files
+# http://ftg.lbl.gov/checkpoint
+0 string C\0\0\0R\0\0\0 BLCR
+>16 lelong 1 x86
+>16 lelong 3 alpha
+>16 lelong 5 x86-64
+>16 lelong 7 ARM
+>8 lelong x context data (little endian, version %d)
+# Uncomment the following only of your "file" program supports "search"
+#>0 search/1024 VMA\06 for kernel
+#>>&1 byte x %d.
+#>>&2 byte x %d.
+#>>&3 byte x %d
+0 string \0\0\0C\0\0\0R BLCR
+>16 belong 2 SPARC
+>16 belong 4 ppc
+>16 belong 6 ppc64
+>16 belong 7 ARMEB
+>16 belong 8 SPARC64
+>8 belong x context data (big endian, version %d)
+# Uncomment the following only of your "file" program supports "search"
+#>0 search/1024 VMA\06 for kernel
+#>>&1 byte x %d.
+#>>&2 byte x \b%d.
+#>>&3 byte x \b%d
+
+#------------------------------------------------------------------------------
+# $File: blender,v 1.5 2009/09/19 16:28:08 christos Exp $
+# blender: file(1) magic for Blender 3D related files
+#
+# Native format rule v1.2. For questions use the developers list
+# http://lists.blender.org/mailman/listinfo/bf-committers
+# GLOB chunk was moved near start and provides subversion info since 2.42
+
+0 string =BLENDER Blender3D,
+>7 string =_ saved as 32-bits
+>>8 string =v little endian
+>>>9 byte x with version %c.
+>>>10 byte x \b%c
+>>>11 byte x \b%c
+>>>0x40 string =GLOB \b.
+>>>>0x58 leshort x \b%.4d
+>>8 string =V big endian
+>>>9 byte x with version %c.
+>>>10 byte x \b%c
+>>>11 byte x \b%c
+>>>0x40 string =GLOB \b.
+>>>>0x58 beshort x \b%.4d
+>7 string =- saved as 64-bits
+>>8 string =v little endian
+>>9 byte x with version %c.
+>>10 byte x \b%c
+>>11 byte x \b%c
+>>0x44 string =GLOB \b.
+>>>0x60 leshort x \b%.4d
+>>8 string =V big endian
+>>>9 byte x with version %c.
+>>>10 byte x \b%c
+>>>11 byte x \b%c
+>>>0x44 string =GLOB \b.
+>>>>0x60 beshort x \b%.4d
+
+# Scripts that run in the embedded Python interpreter
+0 string #!BPY Blender3D BPython script
+
+#------------------------------------------------------------------------------
+# $File$
+# blit: file(1) magic for 68K Blit stuff as seen from 680x0 machine
+#
+# Note that this 0407 conflicts with several other a.out formats...
+#
+# XXX - should this be redone with "be" and "le", so that it works on
+# little-endian machines as well? If so, what's the deal with
+# "VAX-order" and "VAX-order2"?
+#
+#0 long 0407 68K Blit (standalone) executable
+#0 short 0407 VAX-order2 68K Blit (standalone) executable
+0 short 03401 VAX-order 68K Blit (standalone) executable
+0 long 0406 68k Blit mpx/mux executable
+0 short 0406 VAX-order2 68k Blit mpx/mux executable
+0 short 03001 VAX-order 68k Blit mpx/mux executable
+# Need more values for WE32 DMD executables.
+# Note that 0520 is the same as COFF
+#0 short 0520 tty630 layers executable
+
+#------------------------------------------------------------------------------
+# $File$
+# i80960 b.out objects and archives
+#
+0 long 0x10d i960 b.out relocatable object
+>16 long >0 not stripped
+#
+# b.out archive (hp-rt on i960)
+0 string =!<bout> b.out archive
+>8 string __.SYMDEF random library
+
+#------------------------------------------------------------------------------
+# $File: bsdi,v 1.6 2013/01/09 22:37:24 christos Exp $
+# bsdi: file(1) magic for BSD/OS (from BSDI) objects
+# Some object/executable formats use the same magic numbers as are used
+# in other OSes; those are handled by entries in aout.
+#
+
+0 lelong 0314 386 compact demand paged pure executable
+>16 lelong >0 not stripped
+>32 byte 0x6a (uses shared libs)
+
+# same as in SunOS 4.x, except for static shared libraries
+0 belong&077777777 0600413 SPARC demand paged
+>0 byte &0x80
+>>20 belong <4096 shared library
+>>20 belong =4096 dynamically linked executable
+>>20 belong >4096 dynamically linked executable
+>0 byte ^0x80 executable
+>16 belong >0 not stripped
+>36 belong 0xb4100001 (uses shared libs)
+
+0 belong&077777777 0600410 SPARC pure
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80 executable
+>16 belong >0 not stripped
+>36 belong 0xb4100001 (uses shared libs)
+
+0 belong&077777777 0600407 SPARC
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80 executable
+>16 belong >0 not stripped
+>36 belong 0xb4100001 (uses shared libs)
+# Chiasmus is a encryption standard developed by the German Federal
+# Office for Information Security (Bundesamt fuer Sicherheit in der
+# Informationstechnik).
+
+# Extension: .xia
+0 string XIA1 Chiasmus encrypted data
+
+# Extension: .xis
+0 string XIS Chiasmus key
+
+#------------------------------------------------------------------------------
+# $File$
+# BTSnoop: file(1) magic for BTSnoop files
+#
+# From <marcel@holtmann.org>
+0 string btsnoop\0 BTSnoop
+>8 belong x version %d,
+>12 belong 1001 Unencapsulated HCI
+>12 belong 1002 HCI UART (H4)
+>12 belong 1003 HCI BCSP
+>12 belong 1004 HCI Serial (H5)
+>>12 belong x type %d
+
+#------------------------------------------------------------------------------
+# $File$
+# c64: file(1) magic for various commodore 64 related files
+#
+# From: Dirk Jagdmann <doj@cubic.org>
+
+0x16500 belong 0x12014100 D64 Image
+0x16500 belong 0x12014180 D71 Image
+0x61800 belong 0x28034400 D81 Image
+0 string C64\40CARTRIDGE CCS C64 Emultar Cartridge Image
+0 belong 0x43154164 X64 Image
+
+0 string GCR-1541 GCR Image
+>8 byte x version: %i
+>9 byte x tracks: %i
+
+9 string PSUR ARC archive (c64)
+2 string -LH1- LHA archive (c64)
+
+0 string C64File PC64 Emulator file
+>8 string >\0 "%s"
+0 string C64Image PC64 Freezer Image
+
+0 beshort 0x38CD C64 PCLink Image
+0 string CBM\144\0\0 Power 64 C64 Emulator Snapshot
+
+0 belong 0xFF424CFF WRAptor packer (c64)
+
+0 string C64S\x20tape\x20file T64 tape Image
+>32 leshort x Version:0x%x
+>36 leshort !0 Entries:%i
+>40 string x Name:%.24s
+
+0 string C64\x20tape\x20image\x20file\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0 T64 tape Image
+>32 leshort x Version:0x%x
+>36 leshort !0 Entries:%i
+>40 string x Name:%.24s
+
+0 string C64S\x20tape\x20image\x20file\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0 T64 tape Image
+>32 leshort x Version:0x%x
+>36 leshort !0 Entries:%i
+>40 string x Name:%.24s
+
+#------------------------------------------------------------------------------
+# $File: cad,v 1.12 2013/07/04 15:24:37 christos Exp $
+# autocad: file(1) magic for cad files
+#
+
+# Microstation DGN/CIT Files (www.bentley.com)
+# Last updated July 29, 2005 by Lester Hightower
+# DGN is the default file extension of Microstation/Intergraph CAD files.
+# CIT is the proprietary raster format (similar to TIFF) used to attach
+# raster underlays to Microstation DGN (vector) drawings.
+#
+# http://www.wotsit.org/search.asp
+# http://filext.com/detaillist.php?extdetail=DGN
+# http://filext.com/detaillist.php?extdetail=CIT
+#
+# http://www.bentley.com/products/default.cfm?objectid=97F351F5-9C35-4E5E-89C2
+# 3F86C928&method=display&p_objectid=97F351F5-9C35-4E5E-89C280A93F86C928
+# http://www.bentley.com/products/default.cfm?objectid=A5C2FD43-3AC9-4C71-B682
+# 721C479F&method=display&p_objectid=A5C2FD43-3AC9-4C71-B682C7BE721C479F
+0 string \010\011\376 Microstation
+>3 string \002
+>>30 string \026\105 DGNFile
+>>30 string \034\105 DGNFile
+>>30 string \073\107 DGNFile
+>>30 string \073\110 DGNFile
+>>30 string \106\107 DGNFile
+>>30 string \110\103 DGNFile
+>>30 string \120\104 DGNFile
+>>30 string \172\104 DGNFile
+>>30 string \172\105 DGNFile
+>>30 string \172\106 DGNFile
+>>30 string \234\106 DGNFile
+>>30 string \273\105 DGNFile
+>>30 string \306\106 DGNFile
+>>30 string \310\104 DGNFile
+>>30 string \341\104 DGNFile
+>>30 string \372\103 DGNFile
+>>30 string \372\104 DGNFile
+>>30 string \372\106 DGNFile
+>>30 string \376\103 DGNFile
+>4 string \030\000\000 CITFile
+>4 string \030\000\003 CITFile
+
+# AutoCAD
+# Merge of the different contributions and updates from http://en.wikipedia.org/wiki/Dwg
+# and http://www.iana.org/assignments/media-types/image/vnd.dwg
+0 string MC0.0 DWG AutoDesk AutoCAD Release 1.0
+!:mime image/vnd.dwg
+0 string AC1.2 DWG AutoDesk AutoCAD Release 1.2
+!:mime image/vnd.dwg
+0 string AC1.3 DWG AutoDesk AutoCAD Release 1.3
+!:mime image/vnd.dwg
+0 string AC1.40 DWG AutoDesk AutoCAD Release 1.40
+!:mime image/vnd.dwg
+0 string AC1.50 DWG AutoDesk AutoCAD Release 2.05
+!:mime image/vnd.dwg
+0 string AC2.10 DWG AutoDesk AutoCAD Release 2.10
+!:mime image/vnd.dwg
+0 string AC2.21 DWG AutoDesk AutoCAD Release 2.21
+!:mime image/vnd.dwg
+0 string AC2.22 DWG AutoDesk AutoCAD Release 2.22
+!:mime image/vnd.dwg
+0 string AC1001 DWG AutoDesk AutoCAD Release 2.22
+!:mime image/vnd.dwg
+0 string AC1002 DWG AutoDesk AutoCAD Release 2.50
+!:mime image/vnd.dwg
+0 string AC1003 DWG AutoDesk AutoCAD Release 2.60
+!:mime image/vnd.dwg
+0 string AC1004 DWG AutoDesk AutoCAD Release 9
+!:mime image/vnd.dwg
+0 string AC1006 DWG AutoDesk AutoCAD Release 10
+!:mime image/vnd.dwg
+0 string AC1009 DWG AutoDesk AutoCAD Release 11/12
+!:mime image/vnd.dwg
+# AutoCAD DWG versions R13/R14 (www.autodesk.com)
+# Written December 01, 2003 by Lester Hightower
+# Based on the DWG File Format Specifications at http://www.opendwg.org/
+# AutoCad, from Nahuel Greco
+# AutoCAD DWG versions R12/R13/R14 (www.autodesk.com)
+0 string AC1012 DWG AutoDesk AutoCAD Release 13
+!:mime image/vnd.dwg
+0 string AC1014 DWG AutoDesk AutoCAD Release 14
+!:mime image/vnd.dwg
+0 string AC1015 DWG AutoDesk AutoCAD 2000/2002
+!:mime image/vnd.dwg
+
+# A new version of AutoCAD DWG
+# Sergey Zaykov (mail_of_sergey@mail.ru, sergey_zaikov@rambler.ru,
+# ICQ 358572321)
+# From various sources like:
+# http://autodesk.blogs.com/between_the_lines/autocad-release-history.html
+0 string AC1018 DWG AutoDesk AutoCAD 2004/2005/2006
+!:mime image/vnd.dwg
+0 string AC1021 DWG AutoDesk AutoCAD 2007/2008/2009
+!:mime image/vnd.dwg
+0 string AC1024 DWG AutoDesk AutoCAD 2010/2011/2012
+!:mime image/vnd.dwg
+0 string AC1027 DWG AutoDesk AutoCAD 2013/2014
+!:mime image/vnd.dwg
+
+# KOMPAS 2D drawing from ASCON
+# This is KOMPAS 2D drawing or fragment of drawing but is not detailed nor
+# gathered nor specification
+# ASCON http://ascon.net/main/ in English,
+# http://ascon.ru/ main site in Russian
+# Extension is CDW for drawing and FRW for fragment of drawing
+# Sergey Zaykov (mail_of_sergey@mail.ru, sergey_zaikov@rambler.ru,
+# ICQ 358572321, http://vkontakte.ru/id16076543)
+# From:
+# http://sd.ascon.ru/otrs/customer.pl?Action=CustomerFAQ&CategoryID=4&ItemID=292
+# (in russian) and my experiments
+0 string KF
+>2 belong 0x4E00000C Kompas drawing 12.0 SP1
+>2 belong 0x4D00000C Kompas drawing 12.0
+>2 belong 0x3200000B Kompas drawing 11.0 SP1
+>2 belong 0x3100000B Kompas drawing 11.0
+>2 belong 0x2310000A Kompas drawing 10.0 SP1
+>2 belong 0x2110000A Kompas drawing 10.0
+>2 belong 0x08000009 Kompas drawing 9.0 SP1
+>2 belong 0x05000009 Kompas drawing 9.0
+>2 belong 0x33010008 Kompas drawing 8+
+>2 belong 0x1A000008 Kompas drawing 8.0
+>2 belong 0x2C010107 Kompas drawing 7+
+>2 belong 0x05000007 Kompas drawing 7.0
+>2 belong 0x32000006 Kompas drawing 6+
+>2 belong 0x09000006 Kompas drawing 6.0
+>2 belong 0x5C009005 Kompas drawing 5.11R03
+>2 belong 0x54009005 Kompas drawing 5.11R02
+>2 belong 0x51009005 Kompas drawing 5.11R01
+>2 belong 0x22009005 Kompas drawing 5.10R03
+>2 belong 0x22009005 Kompas drawing 5.10R02 mar
+>2 belong 0x21009005 Kompas drawing 5.10R02 febr
+>2 belong 0x19009005 Kompas drawing 5.10R01
+>2 belong 0xF4008005 Kompas drawing 5.9R01.003
+>2 belong 0x1C008005 Kompas drawing 5.9R01.002
+>2 belong 0x11008005 Kompas drawing 5.8R01.003
+
+# CAD: file(1) magic for computer aided design files
+# Phillip Griffith <phillip dot griffith at gmail dot com>
+# AutoCAD magic taken from the Open Design Alliance's OpenDWG specifications.
+#
+0 belong 0x08051700 Bentley/Intergraph MicroStation DGN cell library
+0 belong 0x0809fe02 Bentley/Intergraph MicroStation DGN vector CAD
+0 belong 0xc809fe02 Bentley/Intergraph MicroStation DGN vector CAD
+0 beshort 0x0809 Bentley/Intergraph MicroStation
+>0x02 byte 0xfe
+>>0x04 beshort 0x1800 CIT raster CAD
+
+# 3DS (3d Studio files) Conflicts with diff output 0x3d '='
+#16 beshort 0x3d3d image/x-3ds
+
+# MegaCAD 2D/3D drawing (.prt)
+# http://megacad.de/
+# From: Markus Heidelberg <markus.heidelberg@web.de>
+0 string MegaCad23\0 MegaCAD 2D/3D drawing
+
+#------------------------------------------------------------------------------
+# $File: cafebabe,v 1.17 2015/01/01 17:07:00 christos Exp $
+# Cafe Babes unite!
+#
+# Since Java bytecode and Mach-O universal binaries have the same magic number,
+# the test must be performed in the same "magic" sequence to get both right.
+# The long at offset 4 in a Mach-O universal binary tells the number of
+# architectures; the short at offset 4 in a Java bytecode file is the JVM minor
+# version and the short at offset 6 is the JVM major version. Since there are only
+# only 18 labeled Mach-O architectures at current, and the first released
+# Java class format was version 43.0, we can safely choose any number
+# between 18 and 39 to test the number of architectures against
+# (and use as a hack). Let's not use 18, because the Mach-O people
+# might add another one or two as time goes by...
+#
+### JAVA START ###
+0 belong 0xcafebabe
+>4 belong >30 compiled Java class data,
+!:mime application/x-java-applet
+>>6 beshort x version %d.
+>>4 beshort x \b%d
+# Which is which?
+#>>4 belong 0x032d (Java 1.0)
+#>>4 belong 0x032d (Java 1.1)
+>>4 belong 0x002e (Java 1.2)
+>>4 belong 0x002f (Java 1.3)
+>>4 belong 0x0030 (Java 1.4)
+>>4 belong 0x0031 (Java 1.5)
+>>4 belong 0x0032 (Java 1.6)
+>>4 belong 0x0033 (Java 1.7)
+>>4 belong 0x0034 (Java 1.8)
+
+0 belong 0xcafed00d JAR compressed with pack200,
+>5 byte x version %d.
+>4 byte x \b%d
+!:mime application/x-java-pack200
+
+
+0 belong 0xcafed00d JAR compressed with pack200,
+>5 byte x version %d.
+>4 byte x \b%d
+!:mime application/x-java-pack200
+
+### JAVA END ###
+### MACH-O START ###
+
+0 name mach-o \b [
+>0 use mach-o-cpu \b
+>(8.L) indirect \b:
+>0 belong x \b]
+
+0 belong 0xcafebabe
+>4 belong 1 Mach-O universal binary with 1 architecture:
+>>8 use mach-o \b
+>4 belong >1
+>>4 belong <20 Mach-O universal binary with %d architectures:
+>>>8 use mach-o \b
+>>>28 use mach-o \b
+>>4 belong >2
+>>>48 use mach-o \b
+>>4 belong >3
+>>>68 use mach-o \b
+
+### MACH-O END ###
+
+#------------------------------------------------------------------------------
+# $File: elf,v 1.68 2014/09/19 19:05:57 christos Exp $
+# cbor: file(1) magic for CBOR files as defined in RFC 7049
+
+0 string \xd9\xd9\xf7 Concise Binary Object Representation (CBOR) container
+!:mime application/cbor
+>3 ubyte <0x20 (positive integer)
+>3 ubyte <0x40
+>>3 ubyte >0x1f (negative integer)
+>3 ubyte <0x60
+>>3 ubyte >0x3f (byte string)
+>3 ubyte <0x80
+>>3 ubyte >0x5f (text string)
+>3 ubyte <0xa0
+>3 ubyte >0x7f (array)
+>3 ubyte <0xc0
+>>3 ubyte >0x9f (map)
+>3 ubyte <0xe0
+>>3 ubyte >0xbf (tagged)
+>3 ubyte >0xdf (other)
+
+#------------------------------------------------------------------------------
+# $File$
+# CDDB: file(1) magic for CDDB(tm) format CD text data files
+#
+# From <steve@gracenote.com>
+#
+# This is the /etc/magic entry to decode datafiles as used by
+# CDDB-enabled CD player applications.
+#
+
+0 search/1/w #\040xmcd CDDB(tm) format CD text data
+
+#------------------------------------------------------------------------------
+# $File: chord,v 1.4 2009/09/19 16:28:08 christos Exp $
+# chord: file(1) magic for Chord music sheet typesetting utility input files
+#
+# From Philippe De Muyter <phdm@macqel.be>
+# File format is actually free, but many distributed files begin with `{title'
+#
+0 string {title Chord text file
+
+# Type: PowerTab file format
+# URL: http://www.power-tab.net/
+# From: Jelmer Vernooij <jelmer@samba.org>
+0 string ptab\003\000 Power-Tab v3 Tablature File
+0 string ptab\004\000 Power-Tab v4 Tablature File
+
+#------------------------------------------------------------------------------
+# $File$
+# cisco: file(1) magic for cisco Systems routers
+#
+# Most cisco file-formats are covered by the generic elf code
+#
+# Microcode files are non-ELF, 0x8501 conflicts with NetBSD/alpha.
+0 belong&0xffffff00 0x85011400 cisco IOS microcode
+>7 string >\0 for '%s'
+0 belong&0xffffff00 0x8501cb00 cisco IOS experimental microcode
+>7 string >\0 for '%s'
+
+#------------------------------------------------------------------------------
+# $File$
+# citrus locale declaration
+#
+
+0 string RuneCT Citrus locale declaration for LC_CTYPE
+
+#------------------------------------------------------------------------------
+# $File: c-lang,v 1.18 2013/08/14 13:06:43 christos Exp $
+# c-lang: file(1) magic for C and related languages programs
+#
+
+# BCPL
+0 search/8192 "libhdr" BCPL source text
+!:mime text/x-bcpl
+0 search/8192 "LIBHDR" BCPL source text
+!:mime text/x-bcpl
+
+# C
+0 regex \^#include C source text
+!:mime text/x-c
+0 regex \^char[\ \t\n]+ C source text
+!:mime text/x-c
+0 regex \^double[\ \t\n]+ C source text
+!:mime text/x-c
+0 regex \^extern[\ \t\n]+ C source text
+!:mime text/x-c
+0 regex \^float[\ \t\n]+ C source text
+!:mime text/x-c
+0 regex \^struct[\ \t\n]+ C source text
+!:mime text/x-c
+0 regex \^union[\ \t\n]+ C source text
+!:mime text/x-c
+0 search/8192 main( C source text
+!:mime text/x-c
+
+# C++
+# The strength of these rules is increased so they beat the C rules above
+0 regex \^template[\ \t\n]+ C++ source text
+!:strength + 5
+!:mime text/x-c++
+0 regex \^virtual[\ \t\n]+ C++ source text
+!:strength + 5
+!:mime text/x-c++
+0 regex \^class[\ \t\n]+ C++ source text
+!:strength + 5
+!:mime text/x-c++
+0 regex \^public: C++ source text
+!:strength + 5
+!:mime text/x-c++
+0 regex \^private: C++ source text
+!:strength + 5
+!:mime text/x-c++
+
+# From: Mikhail Teterin <mi@aldan.algebra.com>
+0 string cscope cscope reference data
+>7 string x version %.2s
+# We skip the path here, because it is often long (so file will
+# truncate it) and mostly redundant.
+# The inverted index functionality was added some time between
+# versions 11 and 15, so look for -q if version is above 14:
+>7 string >14
+>>10 search/100 \ -q\ with inverted index
+>10 search/100 \ -c\ text (non-compressed)
+
+#------------------------------------------------------------------------------
+# $File: clarion,v 1.4 2009/09/19 16:28:08 christos Exp $
+# clarion: file(1) magic for # Clarion Personal/Professional Developer
+# (v2 and above)
+# From: Julien Blache <jb@jblache.org>
+
+# Database files
+# signature
+0 leshort 0x3343 Clarion Developer (v2 and above) data file
+# attributes
+>2 leshort &0x0001 \b, locked
+>2 leshort &0x0004 \b, encrypted
+>2 leshort &0x0008 \b, memo file exists
+>2 leshort &0x0010 \b, compressed
+>2 leshort &0x0040 \b, read only
+# number of records
+>5 lelong x \b, %d records
+
+# Memo files
+0 leshort 0x334d Clarion Developer (v2 and above) memo data
+
+# Key/Index files
+# No magic? :(
+
+# Help files
+0 leshort 0x49e0 Clarion Developer (v2 and above) help data
+
+#------------------------------------------------------------------------------
+# $File: claris,v 1.6 2012/06/20 21:19:05 christos Exp $
+# claris: file(1) magic for claris
+# "H. Nanosecond" <aldomel@ix.netcom.com>
+# Claris Works a word processor, etc.
+# Version 3.0
+
+# .pct claris works clip art files
+#0000000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000
+#*
+#0001000 #010 250 377 377 377 377 000 213 000 230 000 021 002 377 014 000
+#null to byte 1000 octal
+514 string \377\377\377\377\000
+>0 string \0\0\0\0\0\0\0\0\0\0\0\0\0 Claris clip art
+514 string \377\377\377\377\001
+>0 string \0\0\0\0\0\0\0\0\0\0\0\0\0 Claris clip art
+
+# Claris works files
+# .cwk
+0 string \002\000\210\003\102\117\102\117\000\001\206 Claris works document
+# .plt
+0 string \020\341\000\000\010\010 Claris Works palette files .plt
+
+# .msp a dictionary file I am not sure about this I have only one .msp file
+0 string \002\271\262\000\040\002\000\164 Claris works dictionary
+
+# .usp are user dictionary bits
+# I am not sure about a magic header:
+#0000000 001 123 160 146 070 125 104 040 136 123 015 012 160 157 144 151
+# soh S p f 8 U D sp ^ S cr nl p o d i
+#0000020 141 164 162 151 163 164 040 136 123 015 012 144 151 166 040 043
+# a t r i s t sp ^ S cr nl d i v sp #
+
+# .mth Thesaurus
+# starts with \0 but no magic header
+
+# .chy Hyphenation file
+# I am not sure: 000 210 034 000 000
+
+# other claris files
+#./windows/claris/useng.ndx: data
+#./windows/claris/xtndtran.l32: data
+#./windows/claris/xtndtran.lst: data
+#./windows/claris/clworks.lbl: data
+#./windows/claris/clworks.prf: data
+#./windows/claris/userd.spl: data
+
+#------------------------------------------------------------------------------
+# $File: clipper,v 1.6 2009/09/19 16:28:08 christos Exp $
+# clipper: file(1) magic for Intergraph (formerly Fairchild) Clipper.
+#
+# XXX - what byte order does the Clipper use?
+#
+# XXX - what's the "!" stuff:
+#
+# >18 short !074000,000000 C1 R1
+# >18 short !074000,004000 C2 R1
+# >18 short !074000,010000 C3 R1
+# >18 short !074000,074000 TEST
+#
+# I shall assume it's ANDing the field with the first value and
+# comparing it with the second, and rewrite it as:
+#
+# >18 short&074000 000000 C1 R1
+# >18 short&074000 004000 C2 R1
+# >18 short&074000 010000 C3 R1
+# >18 short&074000 074000 TEST
+#
+# as SVR3.1's "file" doesn't support anything of the "!074000,000000"
+# sort, nor does SunOS 4.x, so either it's something Intergraph added
+# in CLIX, or something AT&T added in SVR3.2 or later, or something
+# somebody else thought was a good idea; it's not documented in the
+# man page for this version of "magic", nor does it appear to be
+# implemented (at least not after I blew off the bogus code to turn
+# old-style "&"s into new-style "&"s, which just didn't work at all).
+#
+0 short 0575 CLIPPER COFF executable (VAX #)
+>20 short 0407 (impure)
+>20 short 0410 (5.2 compatible)
+>20 short 0411 (pure)
+>20 short 0413 (demand paged)
+>20 short 0443 (target shared library)
+>12 long >0 not stripped
+>22 short >0 - version %d
+0 short 0577 CLIPPER COFF executable
+>18 short&074000 000000 C1 R1
+>18 short&074000 004000 C2 R1
+>18 short&074000 010000 C3 R1
+>18 short&074000 074000 TEST
+>20 short 0407 (impure)
+>20 short 0410 (pure)
+>20 short 0411 (separate I&D)
+>20 short 0413 (paged)
+>20 short 0443 (target shared library)
+>12 long >0 not stripped
+>22 short >0 - version %d
+>48 long&01 01 alignment trap enabled
+>52 byte 1 -Ctnc
+>52 byte 2 -Ctsw
+>52 byte 3 -Ctpw
+>52 byte 4 -Ctcb
+>53 byte 1 -Cdnc
+>53 byte 2 -Cdsw
+>53 byte 3 -Cdpw
+>53 byte 4 -Cdcb
+>54 byte 1 -Csnc
+>54 byte 2 -Cssw
+>54 byte 3 -Cspw
+>54 byte 4 -Cscb
+4 string pipe CLIPPER instruction trace
+4 string prof CLIPPER instruction profile
+
+#------------------------------------------------------------------------------
+# $File: commands,v 1.50 2014/05/30 16:48:44 christos Exp $
+# commands: file(1) magic for various shells and interpreters
+#
+#0 string/w : shell archive or script for antique kernel text
+0 string/wt #!\ /bin/sh POSIX shell script text executable
+!:mime text/x-shellscript
+0 string/wb #!\ /bin/sh POSIX shell script executable (binary data)
+!:mime text/x-shellscript
+
+0 string/wt #!\ /bin/csh C shell script text executable
+!:mime text/x-shellscript
+
+# korn shell magic, sent by George Wu, gwu@clyde.att.com
+0 string/wt #!\ /bin/ksh Korn shell script text executable
+!:mime text/x-shellscript
+0 string/wb #!\ /bin/ksh Korn shell script executable (binary data)
+!:mime text/x-shellscript
+
+0 string/wt #!\ /bin/tcsh Tenex C shell script text executable
+!:mime text/x-shellscript
+0 string/wt #!\ /usr/bin/tcsh Tenex C shell script text executable
+!:mime text/x-shellscript
+0 string/wt #!\ /usr/local/tcsh Tenex C shell script text executable
+!:mime text/x-shellscript
+0 string/wt #!\ /usr/local/bin/tcsh Tenex C shell script text executable
+!:mime text/x-shellscript
+
+#
+# zsh/ash/ae/nawk/gawk magic from cameron@cs.unsw.oz.au (Cameron Simpson)
+0 string/wt #!\ /bin/zsh Paul Falstad's zsh script text executable
+!:mime text/x-shellscript
+0 string/wt #!\ /usr/bin/zsh Paul Falstad's zsh script text executable
+!:mime text/x-shellscript
+0 string/wt #!\ /usr/local/bin/zsh Paul Falstad's zsh script text executable
+!:mime text/x-shellscript
+0 string/wt #!\ /usr/local/bin/ash Neil Brown's ash script text executable
+!:mime text/x-shellscript
+0 string/wt #!\ /usr/local/bin/ae Neil Brown's ae script text executable
+!:mime text/x-shellscript
+0 string/wt #!\ /bin/nawk new awk script text executable
+!:mime text/x-nawk
+0 string/wt #!\ /usr/bin/nawk new awk script text executable
+!:mime text/x-nawk
+0 string/wt #!\ /usr/local/bin/nawk new awk script text executable
+!:mime text/x-nawk
+0 string/wt #!\ /bin/gawk GNU awk script text executable
+!:mime text/x-gawk
+0 string/wt #!\ /usr/bin/gawk GNU awk script text executable
+!:mime text/x-gawk
+0 string/wt #!\ /usr/local/bin/gawk GNU awk script text executable
+!:mime text/x-gawk
+#
+0 string/wt #!\ /bin/awk awk script text executable
+!:mime text/x-awk
+0 string/wt #!\ /usr/bin/awk awk script text executable
+!:mime text/x-awk
+0 regex/4096 =^\\s{0,100}BEGIN\\s{0,100}[{] awk or perl script text
+
+# AT&T Bell Labs' Plan 9 shell
+0 string/wt #!\ /bin/rc Plan 9 rc shell script text executable
+
+# bash shell magic, from Peter Tobias (tobias@server.et-inf.fho-emden.de)
+0 string/wt #!\ /bin/bash Bourne-Again shell script text executable
+!:mime text/x-shellscript
+0 string/wb #!\ /bin/bash Bourne-Again shell script executable (binary data)
+!:mime text/x-shellscript
+0 string/wt #!\ /usr/bin/bash Bourne-Again shell script text executable
+!:mime text/x-shellscript
+0 string/wb #!\ /usr/bin/bash Bourne-Again shell script executable (binary data)
+!:mime text/x-shellscript
+0 string/wt #!\ /usr/local/bash Bourne-Again shell script text executable
+!:mime text/x-shellscript
+0 string/wb #!\ /usr/local/bash Bourne-Again shell script executable (binary data)
+!:mime text/x-shellscript
+0 string/wt #!\ /usr/local/bin/bash Bourne-Again shell script text executable
+!:mime text/x-shellscript
+0 string/wb #!\ /usr/local/bin/bash Bourne-Again shell script executable (binary data)
+!:mime text/x-shellscript
+
+# PHP scripts
+# Ulf Harnhammar <ulfh@update.uu.se>
+0 search/1/c =<?php PHP script text
+!:strength + 10
+!:mime text/x-php
+0 search/1 =<?\n PHP script text
+!:mime text/x-php
+0 search/1 =<?\r PHP script text
+!:mime text/x-php
+0 search/1/w #!\ /usr/local/bin/php PHP script text executable
+!:strength + 10
+!:mime text/x-php
+0 search/1/w #!\ /usr/bin/php PHP script text executable
+!:strength + 10
+!:mime text/x-php
+# Smarty compiled template, http://www.smarty.net/
+# Elan Ruusamae <glen@delfi.ee>
+0 string =<?php\ /*\ Smarty\ version Smarty compiled template
+>24 regex [0-9.]+ \b, version %s
+!:mime text/x-php
+
+0 string Zend\x00 PHP script Zend Optimizer data
+
+0 string/t $! DCL command file
+
+# Type: Pdmenu
+# URL: http://packages.debian.org/pdmenu
+# From: Edward Betts <edward@debian.org>
+0 string #!/usr/bin/pdmenu Pdmenu configuration file text
+
+#----------------------------------------------------------------------------
+# $File$
+# communication
+
+# TTCN is the Tree and Tabular Combined Notation described in ISO 9646-3.
+# It is used for conformance testing of communication protocols.
+# Added by W. Borgert <debacle@debian.org>.
+0 string $Suite TTCN Abstract Test Suite
+>&1 string $SuiteId
+>>&1 string >\n %s
+>&2 string $SuiteId
+>>&1 string >\n %s
+>&3 string $SuiteId
+>>&1 string >\n %s
+
+# MSC (message sequence charts) are a formal description technique,
+# described in ITU-T Z.120, mainly used for communication protocols.
+# Added by W. Borgert <debacle@debian.org>.
+0 string mscdocument Message Sequence Chart (document)
+0 string msc Message Sequence Chart (chart)
+0 string submsc Message Sequence Chart (subchart)
+#------------------------------------------------------------------------------
+# $File: compress,v 1.61 2014/09/12 20:57:45 christos Exp $
+# compress: file(1) magic for pure-compression formats (no archives)
+#
+# compress, gzip, pack, compact, huf, squeeze, crunch, freeze, yabba, etc.
+#
+# Formats for various forms of compressed data
+# Formats for "compress" proper have been moved into "compress.c",
+# because it tries to uncompress it to figure out what's inside.
+
+# standard unix compress
+0 string \037\235 compress'd data
+!:mime application/x-compress
+!:apple LZIVZIVU
+>2 byte&0x80 >0 block compressed
+>2 byte&0x1f x %d bits
+
+# gzip (GNU zip, not to be confused with Info-ZIP or PKWARE zip archiver)
+# Edited by Chris Chittleborough <cchittleborough@yahoo.com.au>, March 2002
+# * Original filename is only at offset 10 if "extra field" absent
+# * Produce shorter output - notably, only report compression methods
+# other than 8 ("deflate", the only method defined in RFC 1952).
+0 string \037\213 gzip compressed data
+!:mime application/x-gzip
+!:strength * 2
+>2 byte <8 \b, reserved method
+>2 byte >8 \b, unknown method
+>3 byte &0x01 \b, ASCII
+>3 byte &0x02 \b, has CRC
+>3 byte &0x04 \b, extra field
+>3 byte&0xC =0x08
+>>10 string x \b, was "%s"
+>3 byte &0x10 \b, has comment
+>3 byte &0x20 \b, encrypted
+>4 ledate >0 \b, last modified: %s
+>8 byte 2 \b, max compression
+>8 byte 4 \b, max speed
+>9 byte =0x00 \b, from FAT filesystem (MS-DOS, OS/2, NT)
+>9 byte =0x01 \b, from Amiga
+>9 byte =0x02 \b, from VMS
+>9 byte =0x03 \b, from Unix
+>9 byte =0x04 \b, from VM/CMS
+>9 byte =0x05 \b, from Atari
+>9 byte =0x06 \b, from HPFS filesystem (OS/2, NT)
+>9 byte =0x07 \b, from MacOS
+>9 byte =0x08 \b, from Z-System
+>9 byte =0x09 \b, from CP/M
+>9 byte =0x0A \b, from TOPS/20
+>9 byte =0x0B \b, from NTFS filesystem (NT)
+>9 byte =0x0C \b, from QDOS
+>9 byte =0x0D \b, from Acorn RISCOS
+
+# packed data, Huffman (minimum redundancy) codes on a byte-by-byte basis
+0 string \037\036 packed data
+!:mime application/octet-stream
+>2 belong >1 \b, %d characters originally
+>2 belong =1 \b, %d character originally
+#
+# This magic number is byte-order-independent.
+0 short 0x1f1f old packed data
+!:mime application/octet-stream
+
+# XXX - why *two* entries for "compacted data", one of which is
+# byte-order independent, and one of which is byte-order dependent?
+#
+0 short 0x1fff compacted data
+!:mime application/octet-stream
+# This string is valid for SunOS (BE) and a matching "short" is listed
+# in the Ultrix (LE) magic file.
+0 string \377\037 compacted data
+!:mime application/octet-stream
+0 short 0145405 huf output
+!:mime application/octet-stream
+
+# bzip2
+0 string BZh bzip2 compressed data
+!:mime application/x-bzip2
+>3 byte >47 \b, block size = %c00k
+
+# lzip
+0 string LZIP lzip compressed data
+!:mime application/x-lzip
+>4 byte x \b, version: %d
+
+# squeeze and crunch
+# Michael Haardt <michael@cantor.informatik.rwth-aachen.de>
+0 beshort 0x76FF squeezed data,
+>4 string x original name %s
+0 beshort 0x76FE crunched data,
+>2 string x original name %s
+0 beshort 0x76FD LZH compressed data,
+>2 string x original name %s
+
+# Freeze
+0 string \037\237 frozen file 2.1
+0 string \037\236 frozen file 1.0 (or gzip 0.5)
+
+# SCO compress -H (LZH)
+0 string \037\240 SCO compress -H (LZH) data
+
+# European GSM 06.10 is a provisional standard for full-rate speech
+# transcoding, prI-ETS 300 036, which uses RPE/LTP (residual pulse
+# excitation/long term prediction) coding at 13 kbit/s.
+#
+# There's only a magic nibble (4 bits); that nibble repeats every 33
+# bytes. This isn't suited for use, but maybe we can use it someday.
+#
+# This will cause very short GSM files to be declared as data and
+# mismatches to be declared as data too!
+#0 byte&0xF0 0xd0 data
+#>33 byte&0xF0 0xd0
+#>66 byte&0xF0 0xd0
+#>99 byte&0xF0 0xd0
+#>132 byte&0xF0 0xd0 GSM 06.10 compressed audio
+
+# bzip a block-sorting file compressor
+# by Julian Seward <sewardj@cs.man.ac.uk> and others
+#
+#0 string BZ bzip compressed data
+#>2 byte x \b, version: %c
+#>3 string =1 \b, compression block size 100k
+#>3 string =2 \b, compression block size 200k
+#>3 string =3 \b, compression block size 300k
+#>3 string =4 \b, compression block size 400k
+#>3 string =5 \b, compression block size 500k
+#>3 string =6 \b, compression block size 600k
+#>3 string =7 \b, compression block size 700k
+#>3 string =8 \b, compression block size 800k
+#>3 string =9 \b, compression block size 900k
+
+# lzop from <markus.oberhumer@jk.uni-linz.ac.at>
+0 string \x89\x4c\x5a\x4f\x00\x0d\x0a\x1a\x0a lzop compressed data
+>9 beshort <0x0940
+>>9 byte&0xf0 =0x00 - version 0.
+>>9 beshort&0x0fff x \b%03x,
+>>13 byte 1 LZO1X-1,
+>>13 byte 2 LZO1X-1(15),
+>>13 byte 3 LZO1X-999,
+## >>22 bedate >0 last modified: %s,
+>>14 byte =0x00 os: MS-DOS
+>>14 byte =0x01 os: Amiga
+>>14 byte =0x02 os: VMS
+>>14 byte =0x03 os: Unix
+>>14 byte =0x05 os: Atari
+>>14 byte =0x06 os: OS/2
+>>14 byte =0x07 os: MacOS
+>>14 byte =0x0A os: Tops/20
+>>14 byte =0x0B os: WinNT
+>>14 byte =0x0E os: Win32
+>9 beshort >0x0939
+>>9 byte&0xf0 =0x00 - version 0.
+>>9 byte&0xf0 =0x10 - version 1.
+>>9 byte&0xf0 =0x20 - version 2.
+>>9 beshort&0x0fff x \b%03x,
+>>15 byte 1 LZO1X-1,
+>>15 byte 2 LZO1X-1(15),
+>>15 byte 3 LZO1X-999,
+## >>25 bedate >0 last modified: %s,
+>>17 byte =0x00 os: MS-DOS
+>>17 byte =0x01 os: Amiga
+>>17 byte =0x02 os: VMS
+>>17 byte =0x03 os: Unix
+>>17 byte =0x05 os: Atari
+>>17 byte =0x06 os: OS/2
+>>17 byte =0x07 os: MacOS
+>>17 byte =0x0A os: Tops/20
+>>17 byte =0x0B os: WinNT
+>>17 byte =0x0E os: Win32
+
+# 4.3BSD-Quasijarus Strong Compression
+# http://minnie.tuhs.org/Quasijarus/compress.html
+0 string \037\241 Quasijarus strong compressed data
+
+# From: Cory Dikkers <cdikkers@swbell.net>
+0 string XPKF Amiga xpkf.library compressed data
+0 string PP11 Power Packer 1.1 compressed data
+0 string PP20 Power Packer 2.0 compressed data,
+>4 belong 0x09090909 fast compression
+>4 belong 0x090A0A0A mediocre compression
+>4 belong 0x090A0B0B good compression
+>4 belong 0x090A0C0C very good compression
+>4 belong 0x090A0C0D best compression
+
+# 7-zip archiver, from Thomas Klausner (wiz@danbala.tuwien.ac.at)
+# http://www.7-zip.org or DOC/7zFormat.txt
+#
+0 string 7z\274\257\047\034 7-zip archive data,
+>6 byte x version %d
+>7 byte x \b.%d
+!:mime application/x-7z-compressed
+
+# Type: LZMA
+0 lelong&0xffffff =0x5d
+>12 leshort 0xff LZMA compressed data,
+!:mime application/x-lzma
+>>5 lequad =0xffffffffffffffff streamed
+>>5 lequad !0xffffffffffffffff non-streamed, size %lld
+>12 leshort 0 LZMA compressed data,
+>>5 lequad =0xffffffffffffffff streamed
+>>5 lequad !0xffffffffffffffff non-streamed, size %lld
+
+# http://tukaani.org/xz/xz-file-format.txt
+0 ustring \xFD7zXZ\x00 XZ compressed data
+!:mime application/x-xz
+
+# https://github.com/ckolivas/lrzip/blob/master/doc/magic.header.txt
+0 string LRZI LRZIP compressed data
+>4 byte x - version %d
+>5 byte x \b.%d
+!:mime application/x-lrzip
+
+# http://fastcompression.blogspot.fi/2013/04/lz4-streaming-format-final.html
+0 lelong 0x184d2204 LZ4 compressed data (v1.4+)
+!:mime application/x-lz4
+# Added by osm0sis@xda-developers.com
+0 lelong 0x184c2103 LZ4 compressed data (v1.0-v1.3)
+!:mime application/x-lz4
+0 lelong 0x184c2102 LZ4 compressed data (v0.1-v0.9)
+!:mime application/x-lz4
+
+# AFX compressed files (Wolfram Kleff)
+2 string -afx- AFX compressed file data
+
+# Supplementary magic data for the file(1) command to support
+# rzip(1). The format is described in magic(5).
+#
+# Copyright (C) 2003 by Andrew Tridgell. You may do whatever you want with
+# this file.
+#
+0 string RZIP rzip compressed data
+>4 byte x - version %d
+>5 byte x \b.%d
+>6 belong x (%d bytes)
+
+0 string ArC\x01 FreeArc archive <http://freearc.org>
+
+# Type: DACT compressed files
+0 long 0x444354C3 DACT compressed data
+>4 byte >-1 (version %i.
+>5 byte >-1 %i.
+>6 byte >-1 %i)
+>7 long >0 , original size: %i bytes
+>15 long >30 , block size: %i bytes
+
+# Valve Pack (VPK) files
+0 lelong 0x55aa1234 Valve Pak file
+>0x4 lelong x \b, version %u
+>0x8 lelong x \b, %u entries
+
+# Snappy framing format
+# http://code.google.com/p/snappy/source/browse/trunk/framing_format.txt
+0 string \377\006\0\0sNaPpY snappy framed data
+!:mime application/x-snappy-framed
+
+# qpress, http://www.quicklz.com/
+0 string qpress10 qpress compressed data
+!:mime application/x-qpress
+
+# Zlib https://www.ietf.org/rfc/rfc6713.txt
+0 beshort%31 =0
+>0 byte&0xf =8
+>>0 byte&0x80 =0 zlib compressed data
+!:mime application/zlib
+
+#------------------------------------------------------------------------------
+# $File: console,v 1.18 2010/09/20 19:19:17 rrt Exp $
+# Console game magic
+# Toby Deshane <hac@shoelace.digivill.net>
+# ines: file(1) magic for Marat's iNES Nintendo Entertainment System
+# ROM dump format
+
+0 string NES\032 iNES ROM dump,
+>4 byte x %dx16k PRG
+>5 byte x \b, %dx8k CHR
+>6 byte&0x01 =0x1 \b, [Vert.]
+>6 byte&0x01 =0x0 \b, [Horiz.]
+>6 byte&0x02 =0x2 \b, [SRAM]
+>6 byte&0x04 =0x4 \b, [Trainer]
+>6 byte&0x04 =0x8 \b, [4-Scr]
+
+#------------------------------------------------------------------------------
+# gameboy: file(1) magic for the Nintendo (Color) Gameboy raw ROM format
+#
+0x104 belong 0xCEED6666 Gameboy ROM:
+>0x134 string >\0 "%.16s"
+>0x146 byte 0x03 \b,[SGB]
+>0x147 byte 0x00 \b, [ROM ONLY]
+>0x147 byte 0x01 \b, [ROM+MBC1]
+>0x147 byte 0x02 \b, [ROM+MBC1+RAM]
+>0x147 byte 0x03 \b, [ROM+MBC1+RAM+BATT]
+>0x147 byte 0x05 \b, [ROM+MBC2]
+>0x147 byte 0x06 \b, [ROM+MBC2+BATTERY]
+>0x147 byte 0x08 \b, [ROM+RAM]
+>0x147 byte 0x09 \b, [ROM+RAM+BATTERY]
+>0x147 byte 0x0B \b, [ROM+MMM01]
+>0x147 byte 0x0C \b, [ROM+MMM01+SRAM]
+>0x147 byte 0x0D \b, [ROM+MMM01+SRAM+BATT]
+>0x147 byte 0x0F \b, [ROM+MBC3+TIMER+BATT]
+>0x147 byte 0x10 \b, [ROM+MBC3+TIMER+RAM+BATT]
+>0x147 byte 0x11 \b, [ROM+MBC3]
+>0x147 byte 0x12 \b, [ROM+MBC3+RAM]
+>0x147 byte 0x13 \b, [ROM+MBC3+RAM+BATT]
+>0x147 byte 0x19 \b, [ROM+MBC5]
+>0x147 byte 0x1A \b, [ROM+MBC5+RAM]
+>0x147 byte 0x1B \b, [ROM+MBC5+RAM+BATT]
+>0x147 byte 0x1C \b, [ROM+MBC5+RUMBLE]
+>0x147 byte 0x1D \b, [ROM+MBC5+RUMBLE+SRAM]
+>0x147 byte 0x1E \b, [ROM+MBC5+RUMBLE+SRAM+BATT]
+>0x147 byte 0x1F \b, [Pocket Camera]
+>0x147 byte 0xFD \b, [Bandai TAMA5]
+>0x147 byte 0xFE \b, [Hudson HuC-3]
+>0x147 byte 0xFF \b, [Hudson HuC-1]
+
+>0x148 byte 0 \b, ROM: 256Kbit
+>0x148 byte 1 \b, ROM: 512Kbit
+>0x148 byte 2 \b, ROM: 1Mbit
+>0x148 byte 3 \b, ROM: 2Mbit
+>0x148 byte 4 \b, ROM: 4Mbit
+>0x148 byte 5 \b, ROM: 8Mbit
+>0x148 byte 6 \b, ROM: 16Mbit
+>0x148 byte 0x52 \b, ROM: 9Mbit
+>0x148 byte 0x53 \b, ROM: 10Mbit
+>0x148 byte 0x54 \b, ROM: 12Mbit
+
+>0x149 byte 1 \b, RAM: 16Kbit
+>0x149 byte 2 \b, RAM: 64Kbit
+>0x149 byte 3 \b, RAM: 128Kbit
+>0x149 byte 4 \b, RAM: 1Mbit
+
+#>0x14e long x \b, CRC: %x
+
+#------------------------------------------------------------------------------
+# genesis: file(1) magic for the Sega MegaDrive/Genesis raw ROM format
+#
+0x100 string SEGA Sega MegaDrive/Genesis raw ROM dump
+>0x120 string >\0 Name: "%.16s"
+>0x110 string >\0 %.16s
+>0x1B0 string RA with SRAM
+
+#------------------------------------------------------------------------------
+# genesis: file(1) magic for the Super MegaDrive ROM dump format
+#
+0x280 string EAGN Super MagicDrive ROM dump
+>0 byte x %dx16k blocks
+>2 byte 0 \b, last in series or standalone
+>2 byte >0 \b, split ROM
+>8 byte 0xAA
+>9 byte 0xBB
+
+#------------------------------------------------------------------------------
+# genesis: file(1) alternate magic for the Super MegaDrive ROM dump format
+#
+0x280 string EAMG Super MagicDrive ROM dump
+>0 byte x %dx16k blocks
+>2 byte x \b, last in series or standalone
+>8 byte 0xAA
+>9 byte 0xBB
+
+#------------------------------------------------------------------------------
+# smsgg: file(1) magic for Sega Master System and Game Gear ROM dumps
+#
+# Does not detect all images. Very preliminary guesswork. Need more data
+# on format.
+#
+# FIXME: need a little more info...;P
+#
+#0 byte 0xF3
+#>1 byte 0xED Sega Master System/Game Gear ROM dump
+#>1 byte 0x31 Sega Master System/Game Gear ROM dump
+#>1 byte 0xDB Sega Master System/Game Gear ROM dump
+#>1 byte 0xAF Sega Master System/Game Gear ROM dump
+#>1 byte 0xC3 Sega Master System/Game Gear ROM dump
+
+#------------------------------------------------------------------------------
+# dreamcast: file(1) uncertain magic for the Sega Dreamcast VMU image format
+#
+0 belong 0x21068028 Sega Dreamcast VMU game image
+0 string LCDi Dream Animator file
+
+#------------------------------------------------------------------------------
+# v64: file(1) uncertain magic for the V64 format N64 ROM dumps
+#
+0 belong 0x37804012 V64 Nintendo 64 ROM dump
+
+# From: "Nelson A. de Oliveira" <naoliv@gmail.com>
+# Nintendo .nds
+192 string \044\377\256Qi\232 Nintendo DS Game ROM Image
+# Nintendo .gba
+0 string \056\000\000\352$\377\256Qi Nintendo Game Boy Advance ROM Image
+
+#------------------------------------------------------------------------------
+# msx: file(1) magic for MSX game cartridge dumps
+# Too simple - MPi
+#0 beshort 0x4142 MSX game cartridge dump
+
+#------------------------------------------------------------------------------
+# Sony Playstation executables (Adam Sjoegren <asjo@diku.dk>) :
+0 string PS-X\ EXE Sony Playstation executable
+# Area:
+>113 string x (%s)
+
+#------------------------------------------------------------------------------
+# Microsoft Xbox executables .xbe (Esa Hyytia <ehyytia@cc.hut.fi>)
+0 string XBEH XBE, Microsoft Xbox executable
+# probabilistic checks whether signed or not
+>0x0004 ulelong =0x0
+>>&2 ulelong =0x0
+>>>&2 ulelong =0x0 \b, not signed
+>0x0004 ulelong >0
+>>&2 ulelong >0
+>>>&2 ulelong >0 \b, signed
+# expect base address of 0x10000
+>0x0104 ulelong =0x10000
+>>(0x0118-0x0FF60) ulelong&0x80000007 0x80000007 \b, all regions
+>>(0x0118-0x0FF60) ulelong&0x80000007 !0x80000007
+>>>(0x0118-0x0FF60) ulelong >0 (regions:
+>>>>(0x0118-0x0FF60) ulelong &0x00000001 NA
+>>>>(0x0118-0x0FF60) ulelong &0x00000002 Japan
+>>>>(0x0118-0x0FF60) ulelong &0x00000004 Rest_of_World
+>>>>(0x0118-0x0FF60) ulelong &0x80000000 Manufacturer
+>>>(0x0118-0x0FF60) ulelong >0 \b)
+
+# --------------------------------
+# Microsoft Xbox data file formats
+0 string XIP0 XIP, Microsoft Xbox data
+0 string XTF0 XTF, Microsoft Xbox data
+
+# Atari Lynx cartridge dump (EXE/BLL header)
+# From: "Stefan A. Haubenthal" <polluks@web.de>
+
+# Double-check that the image type matches too, 0x8008 conflicts with
+# 8 character OMF-86 object file headers.
+0 beshort 0x8008
+>6 string BS93 Lynx homebrew cartridge
+>>2 beshort x \b, RAM start $%04x
+>6 string LYNX Lynx cartridge
+>>2 beshort x \b, RAM start $%04x
+
+# Opera file system that is used on the 3DO console
+# From: Serge van den Boom <svdb@stack.nl>
+0 string \x01ZZZZZ\x01 3DO "Opera" file system
+
+# From Gurkan Sengun <gurkan@linuks.mine.nu>, www.linuks.mine.nu
+0 string GBS Nintendo Gameboy Music/Audio Data
+12 string GameBoy\ Music\ Module Nintendo Gameboy Music Module
+
+# Playstations Patch Files from: From: Thomas Klausner <tk@giga.or.at>
+0 string PPF30 Playstation Patch File version 3.0
+>5 byte 0 \b, PPF 1.0 patch
+>5 byte 1 \b, PPF 2.0 patch
+>5 byte 2 \b, PPF 3.0 patch
+>>56 byte 0 \b, Imagetype BIN (any)
+>>56 byte 1 \b, Imagetype GI (PrimoDVD)
+>>57 byte 0 \b, Blockcheck disabled
+>>57 byte 1 \b, Blockcheck enabled
+>>58 byte 0 \b, Undo data not available
+>>58 byte 1 \b, Undo data available
+>6 string x \b, description: %s
+
+0 string PPF20 Playstation Patch File version 2.0
+>5 byte 0 \b, PPF 1.0 patch
+>5 byte 1 \b, PPF 2.0 patch
+>>56 lelong >0 \b, size of file to patch %d
+>6 string x \b, description: %s
+
+0 string PPF10 Playstation Patch File version 1.0
+>5 byte 0 \b, Simple Encoding
+>6 string x \b, description: %s
+
+# From: Daniel Dawson <ddawson@icehouse.net>
+# SNES9x .smv "movie" file format.
+0 string SMV\x1A SNES9x input recording
+>0x4 lelong x \b, version %d
+# version 4 is latest so far
+>0x4 lelong <5
+>>0x8 ledate x \b, recorded at %s
+>>0xc lelong >0 \b, rerecorded %d times
+>>0x10 lelong x \b, %d frames long
+>>0x14 byte >0 \b, data for controller(s):
+>>>0x14 byte &0x1 #1
+>>>0x14 byte &0x2 #2
+>>>0x14 byte &0x4 #3
+>>>0x14 byte &0x8 #4
+>>>0x14 byte &0x10 #5
+>>0x15 byte ^0x1 \b, begins from snapshot
+>>0x15 byte &0x1 \b, begins from reset
+>>0x15 byte ^0x2 \b, NTSC standard
+>>0x15 byte &0x2 \b, PAL standard
+>>0x17 byte &0x1 \b, settings:
+# WIP1Timing not used as of version 4
+>>>0x4 lelong <4
+>>>>0x17 byte &0x2 WIP1Timing
+>>>0x17 byte &0x4 Left+Right
+>>>0x17 byte &0x8 VolumeEnvX
+>>>0x17 byte &0x10 FakeMute
+>>>0x17 byte &0x20 SyncSound
+# New flag as of version 4
+>>>0x4 lelong >3
+>>>>0x17 byte &0x80 NoCPUShutdown
+>>0x4 lelong <4
+>>>0x18 lelong >0x23
+>>>>0x20 leshort !0
+>>>>>0x20 lestring16 x \b, metadata: "%s"
+>>0x4 lelong >3
+>>>0x24 byte >0 \b, port 1:
+>>>>0x24 byte 1 joypad
+>>>>0x24 byte 2 mouse
+>>>>0x24 byte 3 SuperScope
+>>>>0x24 byte 4 Justifier
+>>>>0x24 byte 5 multitap
+>>>0x24 byte >0 \b, port 2:
+>>>>0x25 byte 1 joypad
+>>>>0x25 byte 2 mouse
+>>>>0x25 byte 3 SuperScope
+>>>>0x25 byte 4 Justifier
+>>>>0x25 byte 5 multitap
+>>>0x18 lelong >0x43
+>>>>0x40 leshort !0
+>>>>>0x40 lestring16 x \b, metadata: "%s"
+>>0x17 byte &0x40 \b, ROM:
+>>>(0x18.l-26) lelong x CRC32 0x%08x
+>>>(0x18.l-23) string x "%s"
+
+# Type: scummVM savegame files
+# From: Sven Hartge <debian@ds9.argh.org>
+0 string SCVM ScummVM savegame
+>12 string >\0 "%s"
+
+#------------------------------------------------------------------------------
+# $File: convex,v 1.7 2009/09/19 16:28:08 christos Exp $
+# convex: file(1) magic for Convex boxes
+#
+# Convexes are big-endian.
+#
+# /*\
+# * Below are the magic numbers and tests added for Convex.
+# * Added at beginning, because they are expected to be used most.
+# \*/
+0 belong 0507 Convex old-style object
+>16 belong >0 not stripped
+0 belong 0513 Convex old-style demand paged executable
+>16 belong >0 not stripped
+0 belong 0515 Convex old-style pre-paged executable
+>16 belong >0 not stripped
+0 belong 0517 Convex old-style pre-paged, non-swapped executable
+>16 belong >0 not stripped
+0 belong 0x011257 Core file
+#
+# The following are a series of dump format magic numbers. Each one
+# corresponds to a drastically different dump format. The first on is
+# the original dump format on a 4.1 BSD or earlier file system. The
+# second marks the change between the 4.1 file system and the 4.2 file
+# system. The Third marks the changing of the block size from 1K
+# to 2K to be compatible with an IDC file system. The fourth indicates
+# a dump that is dependent on Convex Storage Manager, because data in
+# secondary storage is not physically contained within the dump.
+# The restore program uses these number to determine how the data is
+# to be extracted.
+#
+24 belong =60013 dump format, 4.2 or 4.3 BSD (IDC compatible)
+24 belong =60014 dump format, Convex Storage Manager by-reference dump
+#
+# what follows is a bunch of bit-mask checks on the flags field of the opthdr.
+# If there is no `=' sign, assume just checking for whether the bit is set?
+#
+0 belong 0601 Convex SOFF
+>88 belong&0x000f0000 =0x00000000 c1
+>88 belong &0x00010000 c2
+>88 belong &0x00020000 c2mp
+>88 belong &0x00040000 parallel
+>88 belong &0x00080000 intrinsic
+>88 belong &0x00000001 demand paged
+>88 belong &0x00000002 pre-paged
+>88 belong &0x00000004 non-swapped
+>88 belong &0x00000008 POSIX
+#
+>84 belong &0x80000000 executable
+>84 belong &0x40000000 object
+>84 belong&0x20000000 =0 not stripped
+>84 belong&0x18000000 =0x00000000 native fpmode
+>84 belong&0x18000000 =0x10000000 ieee fpmode
+>84 belong&0x18000000 =0x18000000 undefined fpmode
+#
+0 belong 0605 Convex SOFF core
+#
+0 belong 0607 Convex SOFF checkpoint
+>88 belong&0x000f0000 =0x00000000 c1
+>88 belong &0x00010000 c2
+>88 belong &0x00020000 c2mp
+>88 belong &0x00040000 parallel
+>88 belong &0x00080000 intrinsic
+>88 belong &0x00000008 POSIX
+#
+>84 belong&0x18000000 =0x00000000 native fpmode
+>84 belong&0x18000000 =0x10000000 ieee fpmode
+>84 belong&0x18000000 =0x18000000 undefined fpmode
+
+#------------------------------------------------------------------------------
+# $File$
+# cracklib: file (1) magic for cracklib v2.7
+
+0 lelong 0x70775631 Cracklib password index, little endian
+>4 long >0 (%i words)
+>4 long 0 ("64-bit")
+>>8 long >-1 (%i words)
+0 belong 0x70775631 Cracklib password index, big endian
+>4 belong >-1 (%i words)
+# really bellong 0x0000000070775631
+0 search/1 \0\0\0\0pwV1 Cracklib password index, big endian ("64-bit")
+>12 belong >0 (%i words)
+
+# ----------------------------------------------------------------------------
+# $File$
+# ctags: file (1) magic for Exuberant Ctags files
+# From: Alexander Mai <mai@migdal.ikp.physik.tu-darmstadt.de>
+0 search/1 =!_TAG Exuberant Ctags tag file text
+
+#--------------------------------------------------------------
+# ctf: file(1) magic for CTF (Common Trace Format) trace files
+#
+# Specs. available here: <http://www.efficios.com/ctf>
+#--------------------------------------------------------------
+
+# CTF trace data
+0 lelong 0xc1fc1fc1 Common Trace Format (CTF) trace data (LE)
+0 belong 0xc1fc1fc1 Common Trace Format (CTF) trace data (BE)
+
+# CTF metadata (packetized)
+0 lelong 0x75d11d57 Common Trace Format (CTF) packetized metadata (LE)
+>35 byte x \b, v%d
+>36 byte x \b.%d
+0 belong 0x75d11d57 Common Trace Format (CTF) packetized metadata (BE)
+>35 byte x \b, v%d
+>36 byte x \b.%d
+
+# CTF metadata (plain text)
+0 string /*\x20CTF\x20 Common Trace Format (CTF) plain text metadata
+!:strength + 5 # this is to make sure we beat C
+>&0 regex [0-9]+\.[0-9]+ \b, v%s
+
+#------------------------------------------------------------------------------
+# $File: cubemaps,v 1.0 2011/12/22 09:01:05 christos Exp $
+# file(1) magic(5) data for cubemaps Martin Erik Werner <martinerikwerner@gmail.com>
+#
+0 string ACMP Map file for the AssaultCube FPS game
+0 string CUBE Map file for cube and cube2 engine games
+0 string MAPZ) Map file for the Blood Frontier/Red Eclipse FPS games
+
+#------------------------------------------------------------------------------
+# $File: cups,v 1.2 2012/11/02 21:50:29 christos Exp $
+# Cups: file(1) magic for the cups raster file format
+# From: Laurent Martelli <martellilaurent@gmail.com>
+# http://www.cups.org/documentation.php/spec-raster.html
+#
+
+0 name cups-le
+>280 lelong x \b, %d
+>284 lelong x \bx%d dpi
+>376 lelong x \b, %dx
+>380 lelong x \b%d pixels
+>388 lelong x %d bits/color
+>392 lelong x %d bits/pixel
+>400 lelong 0 ColorOrder=Chunky
+>400 lelong 1 ColorOrder=Banded
+>400 lelong 2 ColorOrder=Planar
+>404 lelong 0 ColorSpace=gray
+>404 lelong 1 ColorSpace=RGB
+>404 lelong 2 ColorSpace=RGBA
+>404 lelong 3 ColorSpace=black
+>404 lelong 4 ColorSpace=CMY
+>404 lelong 5 ColorSpace=YMC
+>404 lelong 6 ColorSpace=CMYK
+>404 lelong 7 ColorSpace=YMCK
+>404 lelong 8 ColorSpace=KCMY
+>404 lelong 9 ColorSpace=KCMYcm
+>404 lelong 10 ColorSpace=GMCK
+>404 lelong 11 ColorSpace=GMCS
+>404 lelong 12 ColorSpace=WHITE
+>404 lelong 13 ColorSpace=GOLD
+>404 lelong 14 ColorSpace=SILVER
+>404 lelong 15 ColorSpace=CIE XYZ
+>404 lelong 16 ColorSpace=CIE Lab
+>404 lelong 17 ColorSpace=RGBW
+>404 lelong 18 ColorSpace=sGray
+>404 lelong 19 ColorSpace=sRGB
+>404 lelong 20 ColorSpace=AdobeRGB
+
+# Cups Raster image format, Big Endian
+0 string RaS
+>3 string t Cups Raster version 1, Big Endian
+>3 string 2 Cups Raster version 2, Big Endian
+>3 string 3 Cups Raster version 3, Big Endian
+!:mime application/vnd.cups-raster
+>0 use ^cups-le
+
+
+# Cups Raster image format, Little Endian
+1 string SaR
+>0 string t Cups Raster version 1, Little Endian
+>0 string 2 Cups Raster version 2, Little Endian
+>0 string 3 Cups Raster version 3, Little Endian
+!:mime application/vnd.cups-raster
+>0 use cups-le
+
+#------------------------------------------------------------------------------
+# $File$
+# dact: file(1) magic for DACT compressed files
+#
+0 long 0x444354C3 DACT compressed data
+>4 byte >-1 (version %i.
+>5 byte >-1 $BS%i.
+>6 byte >-1 $BS%i)
+>7 long >0 $BS, original size: %i bytes
+>15 long >30 $BS, block size: %i bytes
+
+#------------------------------------------------------------------------------
+# $File: database,v 1.42 2014/08/19 14:18:04 christos Exp $
+# database: file(1) magic for various databases
+#
+# extracted from header/code files by Graeme Wilford (eep2gw@ee.surrey.ac.uk)
+#
+#
+# GDBM magic numbers
+# Will be maintained as part of the GDBM distribution in the future.
+# <downsj@teeny.org>
+0 belong 0x13579acd GNU dbm 1.x or ndbm database, big endian, 32-bit
+!:mime application/x-gdbm
+0 belong 0x13579ace GNU dbm 1.x or ndbm database, big endian, old
+!:mime application/x-gdbm
+0 belong 0x13579acf GNU dbm 1.x or ndbm database, big endian, 64-bit
+!:mime application/x-gdbm
+0 lelong 0x13579acd GNU dbm 1.x or ndbm database, little endian, 32-bit
+!:mime application/x-gdbm
+0 lelong 0x13579ace GNU dbm 1.x or ndbm database, little endian, old
+!:mime application/x-gdbm
+0 lelong 0x13579acf GNU dbm 1.x or ndbm database, little endian, 64-bit
+!:mime application/x-gdbm
+0 string GDBM GNU dbm 2.x database
+!:mime application/x-gdbm
+#
+# Berkeley DB
+#
+# Ian Darwin's file /etc/magic files: big/little-endian version.
+#
+# Hash 1.85/1.86 databases store metadata in network byte order.
+# Btree 1.85/1.86 databases store the metadata in host byte order.
+# Hash and Btree 2.X and later databases store the metadata in host byte order.
+
+0 long 0x00061561 Berkeley DB
+!:mime application/x-dbm
+>8 belong 4321
+>>4 belong >2 1.86
+>>4 belong <3 1.85
+>>4 belong >0 (Hash, version %d, native byte-order)
+>8 belong 1234
+>>4 belong >2 1.86
+>>4 belong <3 1.85
+>>4 belong >0 (Hash, version %d, little-endian)
+
+0 belong 0x00061561 Berkeley DB
+>8 belong 4321
+>>4 belong >2 1.86
+>>4 belong <3 1.85
+>>4 belong >0 (Hash, version %d, big-endian)
+>8 belong 1234
+>>4 belong >2 1.86
+>>4 belong <3 1.85
+>>4 belong >0 (Hash, version %d, native byte-order)
+
+0 long 0x00053162 Berkeley DB 1.85/1.86
+>4 long >0 (Btree, version %d, native byte-order)
+0 belong 0x00053162 Berkeley DB 1.85/1.86
+>4 belong >0 (Btree, version %d, big-endian)
+0 lelong 0x00053162 Berkeley DB 1.85/1.86
+>4 lelong >0 (Btree, version %d, little-endian)
+
+12 long 0x00061561 Berkeley DB
+>16 long >0 (Hash, version %d, native byte-order)
+12 belong 0x00061561 Berkeley DB
+>16 belong >0 (Hash, version %d, big-endian)
+12 lelong 0x00061561 Berkeley DB
+>16 lelong >0 (Hash, version %d, little-endian)
+
+12 long 0x00053162 Berkeley DB
+>16 long >0 (Btree, version %d, native byte-order)
+12 belong 0x00053162 Berkeley DB
+>16 belong >0 (Btree, version %d, big-endian)
+12 lelong 0x00053162 Berkeley DB
+>16 lelong >0 (Btree, version %d, little-endian)
+
+12 long 0x00042253 Berkeley DB
+>16 long >0 (Queue, version %d, native byte-order)
+12 belong 0x00042253 Berkeley DB
+>16 belong >0 (Queue, version %d, big-endian)
+12 lelong 0x00042253 Berkeley DB
+>16 lelong >0 (Queue, version %d, little-endian)
+
+# From Max Bowsher.
+12 long 0x00040988 Berkeley DB
+>16 long >0 (Log, version %d, native byte-order)
+12 belong 0x00040988 Berkeley DB
+>16 belong >0 (Log, version %d, big-endian)
+12 lelong 0x00040988 Berkeley DB
+>16 lelong >0 (Log, version %d, little-endian)
+
+#
+#
+# Round Robin Database Tool by Tobias Oetiker <oetiker@ee.ethz.ch>
+0 string/b RRD\0 RRDTool DB
+>4 string/b x version %s
+
+>>10 short !0 16bit aligned
+>>>10 bedouble 8.642135e+130 big-endian
+>>>>18 short x 32bit long (m68k)
+
+>>10 short 0
+>>>12 long !0 32bit aligned
+>>>>12 bedouble 8.642135e+130 big-endian
+>>>>>20 long 0 64bit long
+>>>>>20 long !0 32bit long
+>>>>12 ledouble 8.642135e+130 little-endian
+>>>>>24 long 0 64bit long
+>>>>>24 long !0 32bit long (i386)
+>>>>12 string \x43\x2b\x1f\x5b\x2f\x25\xc0\xc7 middle-endian
+>>>>>24 short !0 32bit long (arm)
+
+>>8 quad 0 64bit aligned
+>>>16 bedouble 8.642135e+130 big-endian
+>>>>24 long 0 64bit long (s390x)
+>>>>24 long !0 32bit long (hppa/mips/ppc/s390/SPARC)
+>>>16 ledouble 8.642135e+130 little-endian
+>>>>28 long 0 64bit long (alpha/amd64/ia64)
+>>>>28 long !0 32bit long (armel/mipsel)
+
+#----------------------------------------------------------------------
+# ROOT: file(1) magic for ROOT databases
+#
+0 string root\0 ROOT file
+>4 belong x Version %d
+>33 belong x (Compression: %d)
+
+# XXX: Weak magic.
+# Alex Ott <ott@jet.msk.su>
+## Paradox file formats
+#2 leshort 0x0800 Paradox
+#>0x39 byte 3 v. 3.0
+#>0x39 byte 4 v. 3.5
+#>0x39 byte 9 v. 4.x
+#>0x39 byte 10 v. 5.x
+#>0x39 byte 11 v. 5.x
+#>0x39 byte 12 v. 7.x
+#>>0x04 byte 0 indexed .DB data file
+#>>0x04 byte 1 primary index .PX file
+#>>0x04 byte 2 non-indexed .DB data file
+#>>0x04 byte 3 non-incrementing secondary index .Xnn file
+#>>0x04 byte 4 secondary index .Ynn file
+#>>0x04 byte 5 incrementing secondary index .Xnn file
+#>>0x04 byte 6 non-incrementing secondary index .XGn file
+#>>0x04 byte 7 secondary index .YGn file
+#>>>0x04 byte 8 incrementing secondary index .XGn file
+
+## XBase database files
+# updated by Joerg Jenderek at Feb 2013
+# http://www.dbase.com/Knowledgebase/INT/db7_file_fmt.htm
+# http://www.clicketyclick.dk/databases/xbase/format/dbf.html
+# http://home.f1.htw-berlin.de/scheibl/db/intern/dBase.htm
+# inspect VVYYMMDD , where 1<= MM <= 12 and 1<= DD <= 31
+0 ubelong&0x0000FFFF <0x00000C20
+# skip Infocom game Z-machine
+>2 ubyte >0
+# skip Androids *.xml
+>>3 ubyte >0
+>>>3 ubyte <32
+# 1 < version VV
+>>>>0 ubyte >1
+# skip HELP.CA3 by test for reserved byte ( NULL )
+>>>>>27 ubyte 0
+# reserved bytes not always 0 ; also found 0x3901 (T4.DBF) ,0x7101 (T5.DBF,T6.DBF)
+#>>>>>30 ubeshort x 30NULL?%x
+# possible production flag,tag numbers(<=0x30),tag length(<=0x20), reserved (NULL)
+>>>>>>24 ubelong&0xffFFFFff >0x01302000
+# .DBF or .MDX
+>>>>>>24 ubelong&0xffFFFFff <0x01302001
+# for Xbase Database file (*.DBF) reserved (NULL) for multi-user
+>>>>>>>24 ubelong&0xffFFFFff =0
+# test for 2 reserved NULL bytes,transaction and encryption byte flag
+>>>>>>>>12 ubelong&0xFFFFfEfE 0
+# test for MDX flag
+>>>>>>>>>28 ubyte x
+>>>>>>>>>28 ubyte&0xf8 0
+# header size >= 32
+>>>>>>>>>>8 uleshort >31
+# skip PIC15736.PCX by test for language driver name or field name
+>>>>>>>>>>>32 ubyte >0
+#!:mime application/x-dbf; charset=unknown-8bit ??
+#!:mime application/x-dbase
+>>>>>>>>>>>>0 use xbase-type
+# database file
+>>>>>>>>>>>>0 ubyte x \b DBF
+>>>>>>>>>>>>4 lelong 0 \b, no records
+>>>>>>>>>>>>4 lelong >0 \b, %d record
+# plural s appended
+>>>>>>>>>>>>>4 lelong >1 \bs
+# http://www.clicketyclick.dk/databases/xbase/format/dbf_check.html#CHECK_DBF
+# 1 <= record size <= 4000 (dBase 3,4) or 32 * KB (=0x8000)
+>>>>>>>>>>>>10 uleshort x * %d
+# file size = records * record size + header size
+>>>>>>>>>>>>1 ubyte x \b, update-date
+>>>>>>>>>>>>1 use xbase-date
+# http://msdn.microsoft.com/de-de/library/cc483186(v=vs.71).aspx
+#>>>>>>>>>>>>29 ubyte =0 \b, codepage ID=0x%x
+# 2~cp850 , 3~cp1252 , 0x1b~?? ; what code page is 0x1b ?
+>>>>>>>>>>>>29 ubyte >0 \b, codepage ID=0x%x
+#>>>>>>>>>>>>28 ubyte&0x01 0 \b, no index file
+>>>>>>>>>>>>28 ubyte&0x01 1 \b, with index file .MDX
+>>>>>>>>>>>>28 ubyte&0x02 2 \b, with memo .FPT
+>>>>>>>>>>>>28 ubyte&0x04 4 \b, DataBaseContainer
+# 1st record offset + 1 = header size
+>>>>>>>>>>>>8 uleshort >0
+>>>>>>>>>>>>(8.s+1) ubyte >0
+>>>>>>>>>>>>>8 uleshort >0 \b, at offset %d
+>>>>>>>>>>>>>(8.s+1) ubyte >0
+>>>>>>>>>>>>>>&-1 string >\0 1st record "%s"
+# for multiple index files (*.MDX) Production flag,tag numbers(<=0x30),tag length(<=0x20), reserverd (NULL)
+>>>>>>>24 ubelong&0x0133f7ff >0
+# test for reserved NULL byte
+>>>>>>>>47 ubyte 0
+# test for valid TAG key format (0x10 or 0)
+>>>>>>>>>559 ubyte&0xeF 0
+# test MM <= 12
+>>>>>>>>>>45 ubeshort <0x0C20
+>>>>>>>>>>>45 ubyte >0
+>>>>>>>>>>>>46 ubyte <32
+>>>>>>>>>>>>>46 ubyte >0
+#!:mime application/x-mdx
+>>>>>>>>>>>>>>0 use xbase-type
+>>>>>>>>>>>>>>0 ubyte x \b MDX
+>>>>>>>>>>>>>>1 ubyte x \b, creation-date
+>>>>>>>>>>>>>>1 use xbase-date
+>>>>>>>>>>>>>>44 ubyte x \b, update-date
+>>>>>>>>>>>>>>44 use xbase-date
+# No.of tags in use (1,2,5,12)
+>>>>>>>>>>>>>>28 uleshort x \b, %d
+# No. of entries in tag (0x30)
+>>>>>>>>>>>>>>25 ubyte x \b/%d tags
+# Length of tag
+>>>>>>>>>>>>>>26 ubyte x * %d
+# 1st tag name_
+>>>>>>>>>>>>>548 string x \b, 1st tag "%.11s"
+# 2nd tag name
+#>>>>>>>>>>>>(26.b+548) string x \b, 2nd tag "%.11s"
+#
+# Print the xBase names of different version variants
+0 name xbase-type
+>0 ubyte <2
+# 1 < version
+>0 ubyte >1
+>>0 ubyte 0x02 FoxBase
+# FoxBase+/dBaseIII+, no memo
+>>0 ubyte 0x03 FoxBase+/dBase III
+!:mime application/x-dbf
+# dBASE IV no memo file
+>>0 ubyte 0x04 dBase IV
+!:mime application/x-dbf
+# dBASE V no memo file
+>>0 ubyte 0x05 dBase V
+!:mime application/x-dbf
+>>0 ubyte 0x30 Visual FoxPro
+!:mime application/x-dbf
+>>0 ubyte 0x31 Visual FoxPro, autoincrement
+!:mime application/x-dbf
+# Visual FoxPro, with field type Varchar or Varbinary
+>>0 ubyte 0x32 Visual FoxPro, with field type Varchar
+!:mime application/x-dbf
+# dBASE IV SQL, no memo;dbv memo var size (Flagship)
+>>0 ubyte 0x43 dBase IV, with SQL table
+!:mime application/x-dbf
+# http://msdn.microsoft.com/en-US/library/st4a0s68(v=vs.80).aspx
+#>>0 ubyte 0x62 dBase IV, with SQL table
+#!:mime application/x-dbf
+# dBASE IV, with memo!!
+>>0 ubyte 0x7b dBase IV, with memo
+!:mime application/x-dbf
+# http://msdn.microsoft.com/en-US/library/st4a0s68(v=vs.80).aspx
+#>>0 ubyte 0x82 dBase IV, with SQL system
+#!:mime application/x-dbf
+# FoxBase+/dBaseIII+ with memo .DBT!
+>>0 ubyte 0x83 FoxBase+/dBase III, with memo .DBT
+!:mime application/x-dbf
+# VISUAL OBJECTS (first 1.0 versions) for the Dbase III files (NTX clipper driver); memo file
+>>0 ubyte 0x87 VISUAL OBJECTS, with memo file
+!:mime application/x-dbf
+# http://msdn.microsoft.com/en-US/library/st4a0s68(v=vs.80).aspx
+#>>0 ubyte 0x8A FoxBase+/dBase III, with memo .DBT
+#!:mime application/x-dbf
+# dBASE IV with memo!
+>>0 ubyte 0x8B dBase IV, with memo .DBT
+!:mime application/x-dbf
+# dBase IV with SQL Table,no memo?
+>>0 ubyte 0x8E dBase IV, with SQL table
+!:mime application/x-dbf
+# .dbv and .dbt memo (Flagship)?
+>>0 ubyte 0xB3 Flagship
+# http://msdn.microsoft.com/en-US/library/st4a0s68(v=vs.80).aspx
+#>>0 ubyte 0xCA dBase IV with memo .DBT
+#!:mime application/x-dbf
+# dBASE IV with SQL table, with memo .DBT
+>>0 ubyte 0xCB dBase IV with SQL table, with memo .DBT
+!:mime application/x-dbf
+# HiPer-Six format;Clipper SIX, with SMT memo file
+>>0 ubyte 0xE5 Clipper SIX with memo
+!:mime application/x-dbf
+# http://msdn.microsoft.com/en-US/library/st4a0s68(v=vs.80).aspx
+#>>0 ubyte 0xF4 dBase IV, with SQL table, with memo
+#!:mime application/x-dbf
+>>0 ubyte 0xF5 FoxPro with memo
+!:mime application/x-dbf
+# http://msdn.microsoft.com/en-US/library/st4a0s68(v=vs.80).aspx
+#>>0 ubyte 0xFA FoxPro 2.x, with memo
+#!:mime application/x-dbf
+# unknown version (should not happen)
+>>0 default x xBase
+!:mime application/x-dbf
+>>>0 ubyte x (0x%x)
+# flags in version byte
+# DBT flag (with dBASE III memo .DBT)!!
+# >>0 ubyte&0x80 >0 DBT_FLAG=%x
+# memo flag ??
+# >>0 ubyte&0x08 >0 MEMO_FLAG=%x
+# SQL flag ??
+# >>0 ubyte&0x70 >0 SQL_FLAG=%x
+# test and print the date of xBase .DBF .MDX
+0 name xbase-date
+# inspect YYMMDD , where 1<= MM <= 12 and 1<= DD <= 31
+>0 ubelong x
+>1 ubyte <13
+>>1 ubyte >0
+>>>2 ubyte >0
+>>>>2 ubyte <32
+>>>>>0 ubyte x
+# YY is interpreted as 20YY or 19YY
+>>>>>>0 ubyte <100 \b %.2d
+# YY is interpreted 1900+YY; TODO: display yy or 20yy instead 1YY
+>>>>>>0 ubyte >99 \b %d
+>>>>>1 ubyte x \b-%d
+>>>>>2 ubyte x \b-%d
+
+# dBase memo files .DBT or .FPT
+# http://msdn.microsoft.com/en-us/library/8599s21w(v=vs.80).aspx
+16 ubyte <4
+>16 ubyte !2
+>>16 ubyte !1
+# next free block index is positive
+>>>0 ulelong >0
+# skip many JPG. ZIP, BZ2 by test for reserved bytes NULL , 0|2 , 0|1 , low byte of block size
+>>>>17 ubelong&0xFFfdFE00 0x00000000
+# skip many RAR by test for low byte 0 ,high byte 0|2|even of block size, 0|a|e|d7 , 0|64h
+>>>>>20 ubelong&0xFF01209B 0x00000000
+# dBASE III
+>>>>>>16 ubyte 3
+# dBASE III DBT
+>>>>>>>0 use dbase3-memo-print
+# dBASE III DBT without version, dBASE IV DBT , FoxPro FPT , or many ZIP , DBF garbage
+>>>>>>16 ubyte 0
+# unusual dBASE III DBT like angest.dbt, dBASE IV DBT with block size 0 , FoxPro FPT , or garbage PCX DBF
+>>>>>>>20 uleshort 0
+# FoxPro FPT , unusual dBASE III DBT like biblio.dbt or garbage
+>>>>>>>>8 ulong =0
+>>>>>>>>>6 ubeshort >0
+# skip emacs.PIF
+>>>>>>>>>>4 ushort 0
+>>>>>>>>>>>0 use foxpro-memo-print
+# dBASE III DBT , garbage
+>>>>>>>>>6 ubeshort 0
+# skip MM*DD*.bin by test for for reserved NULL byte
+>>>>>>>>>>510 ubeshort 0
+# skip TK-DOS11.img image by looking for memo text
+>>>>>>>>>>>512 ubelong <0xfeffff03
+# skip EFI executables by looking for memo text
+>>>>>>>>>>>>512 ubelong >0x1F202020
+>>>>>>>>>>>>>513 ubyte >0
+# unusual dBASE III DBT like adressen.dbt
+>>>>>>>>>>>>>>0 use dbase3-memo-print
+# dBASE III DBT like angest.dbt, or garbage PCX DBF
+>>>>>>>>8 ubelong !0
+# skip PCX and some DBF by test for for reserved NULL bytes
+>>>>>>>>>510 ubeshort 0
+# skip some DBF by test of invalid version
+>>>>>>>>>>0 ubyte >5
+>>>>>>>>>>>0 ubyte <48
+>>>>>>>>>>>>0 use dbase3-memo-print
+# dBASE IV DBT with positive block size
+>>>>>>>20 uleshort >0
+>>>>>>>>0 use dbase4-memo-print
+
+# Print the information of dBase III DBT memo file
+0 name dbase3-memo-print
+>0 ubyte x dBase III DBT
+# instead 3 as version number 0 for unusual examples like biblio.dbt
+>16 ubyte !3 \b, version number %u
+# Number of next available block for appending data
+#>0 lelong =0 \b, next free block index %u
+>0 lelong !0 \b, next free block index %u
+# no positiv block length
+#>20 uleshort =0 \b, block length %u
+>20 uleshort !0 \b, block length %u
+# dBase III memo field terminated by \032\032
+>512 string >\0 \b, 1st item "%s"
+# Print the information of dBase IV DBT memo file
+0 name dbase4-memo-print
+>0 lelong x dBase IV DBT
+# 8 character shorted main name of coresponding dBASE IV DBF file
+>8 ubelong >0x20000000
+# skip unusual like for angest.dbt
+>>20 uleshort >0
+>>>8 string >\0 \b of %-.8s.DBF
+# value 0 implies 512 as size
+#>4 ulelong =0 \b, blocks size %u
+# size of blocks not reliable like 0x2020204C in angest.dbt
+>4 ulelong !0
+>>4 ulelong&0x0000003f 0 \b, blocks size %u
+# dBase IV DBT with positive block length (found 512 , 1024)
+>20 uleshort >0 \b, block length %u
+# next available block
+#>0 lelong =0 \b, next free block index %u
+>0 lelong !0 \b, next free block index %u
+>20 uleshort >0
+>>(20.s) ubelong x
+>>>&-4 use dbase4-memofield-print
+# unusual dBase IV DBT without block length (implies 512 as length)
+>20 uleshort =0
+>>512 ubelong x
+>>>&-4 use dbase4-memofield-print
+# Print the information of dBase IV memo field
+0 name dbase4-memofield-print
+# free dBase IV memo field
+>0 ubelong !0xFFFF0800
+>>0 lelong x \b, next free block %u
+>>4 lelong x \b, next used block %u
+# used dBase IV memo field
+>0 ubelong =0xFFFF0800
+# length of memo field
+>>4 lelong x \b, field length %d
+>>>8 string >\0 \b, 1st used item "%s"
+# Print the information of FoxPro FPT memo file
+0 name foxpro-memo-print
+>0 belong x FoxPro FPT
+# Size of blocks for FoxPro ( 64,256 )
+>6 ubeshort x \b, blocks size %u
+# next available block
+#>0 belong =0 \b, next free block index %u
+>0 belong !0 \b, next free block index %u
+# field type ( 0~picture, 1~memo, 2~object )
+>512 ubelong <3 \b, field type %u
+# length of memo field
+>512 ubelong 1
+>>516 belong >0 \b, field length %d
+>>>520 string >\0 \b, 1st item "%s"
+
+# TODO:
+# DBASE index file *.NDX
+# DBASE Compound Index file *.CDX
+# dBASE IV Printer Driver *.PRF
+## End of XBase database stuff
+
+# MS Access database
+4 string Standard\ Jet\ DB Microsoft Access Database
+!:mime application/x-msaccess
+4 string Standard\ ACE\ DB Microsoft Access Database
+!:mime application/x-msaccess
+
+# TDB database from Samba et al - Martin Pool <mbp@samba.org>
+0 string TDB\ file TDB database
+>32 lelong 0x2601196D version 6, little-endian
+>>36 lelong x hash size %d bytes
+
+# SE Linux policy database
+0 lelong 0xf97cff8c SE Linux policy
+>16 lelong x v%d
+>20 lelong 1 MLS
+>24 lelong x %d symbols
+>28 lelong x %d ocons
+
+# ICE authority file data (Wolfram Kleff)
+2 string ICE ICE authority data
+
+# X11 Xauthority file (Wolfram Kleff)
+10 string MIT-MAGIC-COOKIE-1 X11 Xauthority data
+11 string MIT-MAGIC-COOKIE-1 X11 Xauthority data
+12 string MIT-MAGIC-COOKIE-1 X11 Xauthority data
+13 string MIT-MAGIC-COOKIE-1 X11 Xauthority data
+14 string MIT-MAGIC-COOKIE-1 X11 Xauthority data
+15 string MIT-MAGIC-COOKIE-1 X11 Xauthority data
+16 string MIT-MAGIC-COOKIE-1 X11 Xauthority data
+17 string MIT-MAGIC-COOKIE-1 X11 Xauthority data
+18 string MIT-MAGIC-COOKIE-1 X11 Xauthority data
+
+# From: Maxime Henrion <mux@FreeBSD.org>
+# PostgreSQL's custom dump format, Maxime Henrion <mux@FreeBSD.org>
+0 string PGDMP PostgreSQL custom database dump
+>5 byte x - v%d
+>6 byte x \b.%d
+>5 beshort <0x101 \b-0
+>5 beshort >0x100
+>>7 byte x \b-%d
+
+# Type: Advanced Data Format (ADF) database
+# URL: http://www.grc.nasa.gov/WWW/cgns/adf/
+# From: Nicolas Chauvat <nicolas.chauvat@logilab.fr>
+0 string @(#)ADF\ Database CGNS Advanced Data Format
+
+# Tokyo Cabinet magic data
+# http://tokyocabinet.sourceforge.net/index.html
+0 string ToKyO\ CaBiNeT\n Tokyo Cabinet
+>14 string x \b (%s)
+>32 byte 0 \b, Hash
+!:mime application/x-tokyocabinet-hash
+>32 byte 1 \b, B+ tree
+!:mime application/x-tokyocabinet-btree
+>32 byte 2 \b, Fixed-length
+!:mime application/x-tokyocabinet-fixed
+>32 byte 3 \b, Table
+!:mime application/x-tokyocabinet-table
+>33 byte &1 \b, [open]
+>33 byte &2 \b, [fatal]
+>34 byte x \b, apow=%d
+>35 byte x \b, fpow=%d
+>36 byte &0x01 \b, [large]
+>36 byte &0x02 \b, [deflate]
+>36 byte &0x04 \b, [bzip]
+>36 byte &0x08 \b, [tcbs]
+>36 byte &0x10 \b, [excodec]
+>40 lequad x \b, bnum=%lld
+>48 lequad x \b, rnum=%lld
+>56 lequad x \b, fsiz=%lld
+
+# Type: QDBM Quick Database Manager
+# From: Benoit Sibaud <bsibaud@april.org>
+0 string \\[depot\\]\n\f Quick Database Manager, little endian
+0 string \\[DEPOT\\]\n\f Quick Database Manager, big endian
+
+# Type: TokyoCabinet database
+# URL: http://tokyocabinet.sourceforge.net/
+# From: Benoit Sibaud <bsibaud@april.org>
+0 string ToKyO\ CaBiNeT\n TokyoCabinet database
+>14 string x (version %s)
+
+# From: Stephane Blondon http://www.yaal.fr
+# Database file for Zope (done by FileStorage)
+0 string FS21 Zope Object Database File Storage (data)
+# Cache file for the database of Zope (done by ClientStorage)
+0 string ZEC3 Zope Object Database Client Cache File (data)
+
+# IDA (Interactive Disassembler) database
+0 string IDA1 IDA (Interactive Disassembler) database
+
+#------------------------------------------------------------------------------
+# $File$
+# diamond: file(1) magic for Diamond system
+#
+# ... diamond is a multi-media mail and electronic conferencing system....
+#
+# XXX - I think it was either renamed Slate, or replaced by Slate....
+#
+# The full deal is too long...
+#0 string <list>\n<protocol\ bbn-multimedia-format> Diamond Multimedia Document
+0 string =<list>\n<protocol\ bbn-m Diamond Multimedia Document
+
+#------------------------------------------------------------------------------
+# $File: diff,v 1.13 2012/06/16 14:43:36 christos Exp $
+# diff: file(1) magic for diff(1) output
+#
+0 search/1 diff\ diff output text
+!:mime text/x-diff
+0 search/1 ***\ diff output text
+!:mime text/x-diff
+0 search/1 Only\ in\ diff output text
+!:mime text/x-diff
+0 search/1 Common\ subdirectories:\ diff output text
+!:mime text/x-diff
+
+0 search/1 Index: RCS/CVS diff output text
+!:mime text/x-diff
+
+# bsdiff: file(1) magic for bsdiff(1) output
+0 string/b BSDIFF40 bsdiff(1) patch file
+
+
+# unified diff
+0 search/4096 ---\
+>&0 search/1024 \n
+>>&0 search/1 +++\
+>>>&0 search/1024 \n
+>>>>&0 search/1 @@ unified diff output text
+!:mime text/x-diff
+!:strength + 90
+
+# librsync -- the library for network deltas
+#
+# Copyright (C) 2001 by Martin Pool. You may do whatever you want with
+# this file.
+#
+0 belong 0x72730236 rdiff network-delta data
+
+0 belong 0x72730136 rdiff network-delta signature data
+>4 belong x (block length=%d,
+>8 belong x signature strength=%d)
+
+#------------------------------------------------------------------------------
+# $File: digital,v 1.10 2011/05/03 01:44:17 christos Exp $
+# Digital UNIX - Info
+#
+0 string =!<arch>\n________64E Alpha archive
+>22 string X -- out of date
+#
+
+0 leshort 0603
+>24 leshort 0410 COFF format alpha pure
+>24 leshort 0413 COFF format alpha demand paged
+>>22 leshort&030000 !020000 executable
+>>22 leshort&020000 !0 dynamically linked
+>>16 lelong !0 not stripped
+>>16 lelong 0 stripped
+>>27 byte x - version %d
+>>26 byte x \b.%d
+>>28 byte x \b-%d
+>24 leshort 0407 COFF format alpha object
+>>22 leshort&030000 020000 shared library
+>>27 byte x - version %d
+>>26 byte x \b.%d
+>>28 byte x \b-%d
+
+# Basic recognition of Digital UNIX core dumps - Mike Bremford <mike@opac.bl.uk>
+#
+# The actual magic number is just "Core", followed by a 2-byte version
+# number; however, treating any file that begins with "Core" as a Digital
+# UNIX core dump file may produce too many false hits, so we include one
+# byte of the version number as well; DU 5.0 appears only to be up to
+# version 2.
+#
+0 string Core\001 Alpha COFF format core dump (Digital UNIX)
+>24 string >\0 \b, from '%s'
+0 string Core\002 Alpha COFF format core dump (Digital UNIX)
+>24 string >\0 \b, from '%s'
+#
+# The next is incomplete, we could tell more about this format,
+# but its not worth it.
+0 leshort 0x188 Alpha compressed COFF
+0 leshort 0x18f Alpha u-code object
+#
+#
+# Some other interesting Digital formats,
+0 string \377\377\177 ddis/ddif
+0 string \377\377\174 ddis/dots archive
+0 string \377\377\176 ddis/dtif table data
+0 string \033c\033 LN03 output
+0 long 04553207 X image
+#
+0 string =!<PDF>!\n profiling data file
+#
+# Locale data tables (MIPS and Alpha).
+#
+0 short 0x0501 locale data table
+>6 short 0x24 for MIPS
+>6 short 0x40 for Alpha
+
+#------------------------------------------------------------------------------
+# $File: dolby,v 1.6 2012/10/31 13:39:42 christos Exp $
+# ATSC A/53 aka AC-3 aka Dolby Digital <ashitaka@gmx.at>
+# from http://www.atsc.org/standards/a_52a.pdf
+# corrections, additions, etc. are always welcome!
+#
+# syncword
+0 beshort 0x0b77 ATSC A/52 aka AC-3 aka Dolby Digital stream,
+# Proposed audio/ac3 RFC/4184
+!:mime audio/vnd.dolby.dd-raw
+# fscod
+>4 byte&0xc0 = 0x00 48 kHz,
+>4 byte&0xc0 = 0x40 44.1 kHz,
+>4 byte&0xc0 = 0x80 32 kHz,
+# is this one used for 96 kHz?
+>4 byte&0xc0 = 0xc0 reserved frequency,
+#
+>5 byte&0x07 = 0x00 \b, complete main (CM)
+>5 byte&0x07 = 0x01 \b, music and effects (ME)
+>5 byte&0x07 = 0x02 \b, visually impaired (VI)
+>5 byte&0x07 = 0x03 \b, hearing impaired (HI)
+>5 byte&0x07 = 0x04 \b, dialogue (D)
+>5 byte&0x07 = 0x05 \b, commentary (C)
+>5 byte&0x07 = 0x06 \b, emergency (E)
+>5 beshort&0x07e0 0x0720 \b, voiceover (VO)
+>5 beshort&0x07e0 >0x0720 \b, karaoke
+# acmod
+>6 byte&0xe0 = 0x00 1+1 front,
+>>6 byte&0x10 = 0x10 LFE on,
+>6 byte&0xe0 = 0x20 1 front/0 rear,
+>>6 byte&0x10 = 0x10 LFE on,
+>6 byte&0xe0 = 0x40 2 front/0 rear,
+# dsurmod (for stereo only)
+>>6 byte&0x18 = 0x00 Dolby Surround not indicated
+>>6 byte&0x18 = 0x08 not Dolby Surround encoded
+>>6 byte&0x18 = 0x10 Dolby Surround encoded
+>>6 byte&0x18 = 0x18 reserved Dolby Surround mode
+>>6 byte&0x04 = 0x04 LFE on,
+>6 byte&0xe0 = 0x60 3 front/0 rear,
+>>6 byte&0x04 = 0x04 LFE on,
+>6 byte&0xe0 = 0x80 2 front/1 rear,
+>>6 byte&0x04 = 0x04 LFE on,
+>6 byte&0xe0 = 0xa0 3 front/1 rear,
+>>6 byte&0x01 = 0x01 LFE on,
+>6 byte&0xe0 = 0xc0 2 front/2 rear,
+>>6 byte&0x04 = 0x04 LFE on,
+>6 byte&0xe0 = 0xe0 3 front/2 rear,
+>>6 byte&0x01 = 0x01 LFE on,
+#
+>4 byte&0x3e = 0x00 \b, 32 kbit/s
+>4 byte&0x3e = 0x02 \b, 40 kbit/s
+>4 byte&0x3e = 0x04 \b, 48 kbit/s
+>4 byte&0x3e = 0x06 \b, 56 kbit/s
+>4 byte&0x3e = 0x08 \b, 64 kbit/s
+>4 byte&0x3e = 0x0a \b, 80 kbit/s
+>4 byte&0x3e = 0x0c \b, 96 kbit/s
+>4 byte&0x3e = 0x0e \b, 112 kbit/s
+>4 byte&0x3e = 0x10 \b, 128 kbit/s
+>4 byte&0x3e = 0x12 \b, 160 kbit/s
+>4 byte&0x3e = 0x14 \b, 192 kbit/s
+>4 byte&0x3e = 0x16 \b, 224 kbit/s
+>4 byte&0x3e = 0x18 \b, 256 kbit/s
+>4 byte&0x3e = 0x1a \b, 320 kbit/s
+>4 byte&0x3e = 0x1c \b, 384 kbit/s
+>4 byte&0x3e = 0x1e \b, 448 kbit/s
+>4 byte&0x3e = 0x20 \b, 512 kbit/s
+>4 byte&0x3e = 0x22 \b, 576 kbit/s
+>4 byte&0x3e = 0x24 \b, 640 kbit/s
+
+#------------------------------------------------------------------------------
+# $File: dump,v 1.12 2012/11/01 04:26:40 christos Exp $
+# dump: file(1) magic for dump file format--for new and old dump filesystems
+#
+# We specify both byte orders in order to recognize byte-swapped dumps.
+#
+0 name new-dump-be
+>4 bedate x Previous dump %s,
+>8 bedate x This dump %s,
+>12 belong >0 Volume %d,
+>692 belong 0 Level zero, type:
+>692 belong >0 Level %d, type:
+>0 belong 1 tape header,
+>0 belong 2 beginning of file record,
+>0 belong 3 map of inodes on tape,
+>0 belong 4 continuation of file record,
+>0 belong 5 end of volume,
+>0 belong 6 map of inodes deleted,
+>0 belong 7 end of medium (for floppy),
+>676 string >\0 Label %s,
+>696 string >\0 Filesystem %s,
+>760 string >\0 Device %s,
+>824 string >\0 Host %s,
+>888 belong >0 Flags %x
+
+0 name old-dump-be
+#>4 bedate x Previous dump %s,
+#>8 bedate x This dump %s,
+>12 belong >0 Volume %d,
+>692 belong 0 Level zero, type:
+>692 belong >0 Level %d, type:
+>0 belong 1 tape header,
+>0 belong 2 beginning of file record,
+>0 belong 3 map of inodes on tape,
+>0 belong 4 continuation of file record,
+>0 belong 5 end of volume,
+>0 belong 6 map of inodes deleted,
+>0 belong 7 end of medium (for floppy),
+>676 string >\0 Label %s,
+>696 string >\0 Filesystem %s,
+>760 string >\0 Device %s,
+>824 string >\0 Host %s,
+>888 belong >0 Flags %x
+
+0 name ufs2-dump-be
+>896 beqdate x Previous dump %s,
+>904 beqdate x This dump %s,
+>12 belong >0 Volume %d,
+>692 belong 0 Level zero, type:
+>692 belong >0 Level %d, type:
+>0 belong 1 tape header,
+>0 belong 2 beginning of file record,
+>0 belong 3 map of inodes on tape,
+>0 belong 4 continuation of file record,
+>0 belong 5 end of volume,
+>0 belong 6 map of inodes deleted,
+>0 belong 7 end of medium (for floppy),
+>676 string >\0 Label %s,
+>696 string >\0 Filesystem %s,
+>760 string >\0 Device %s,
+>824 string >\0 Host %s,
+>888 belong >0 Flags %x
+
+24 belong 60012 new-fs dump file (big endian),
+>0 use new-dump-be
+
+24 belong 60011 old-fs dump file (big endian),
+>0 use old-dump-be
+
+24 lelong 60012 new-fs dump file (little endian),
+>0 use \^new-dump-be
+
+24 lelong 60011 old-fs dump file (little endian),
+>0 use \^old-dump-be
+
+
+24 belong 0x19540119 new-fs dump file (ufs2, big endian),
+>0 use ufs2-dump-be
+
+24 lelong 0x19540119 new-fs dump file (ufs2, little endian),
+>0 use \^ufs2-dump-be
+
+18 leshort 60011 old-fs dump file (16-bit, assuming PDP-11 endianness),
+>2 medate x Previous dump %s,
+>6 medate x This dump %s,
+>10 leshort >0 Volume %d,
+>0 leshort 1 tape header.
+>0 leshort 2 beginning of file record.
+>0 leshort 3 map of inodes on tape.
+>0 leshort 4 continuation of file record.
+>0 leshort 5 end of volume.
+>0 leshort 6 map of inodes deleted.
+>0 leshort 7 end of medium (for floppy).
+
+#------------------------------------------------------------------------------
+# $File: dyadic,v 1.5 2010/09/20 18:55:20 rrt Exp $
+# Dyadic: file(1) magic for Dyalog APL.
+#
+# updated by Joerg Jenderek at Oct 2013
+# http://en.wikipedia.org/wiki/Dyalog_APL
+# http://www.dyalog.com/
+# .DXV Dyalog APL External Variable
+# .DIN Dyalog APL Input Table
+# .DOT Dyalog APL Output Table
+# .DFT Dyalog APL Format File
+0 ubeshort&0xFF60 0xaa00
+# skip biblio.dbt
+>1 byte !4
+# real Dyalog APL have non zero version numbers like 7.3 or 13.4
+>>2 ubeshort >0x0000 Dyalog APL
+>>>1 byte 0x00 aplcore
+#>>>1 byte 0x00 incomplete workspace
+# *.DCF Dyalog APL Component File
+>>>1 byte 0x01 component file 32-bit non-journaled non-checksummed
+#>>>1 byte 0x01 component file
+>>>1 byte 0x02 external variable exclusive
+#>>>1 byte 0x02 external variable
+# *.DWS Dyalog APL Workspace
+>>>1 byte 0x03 workspace
+>>>>7 byte&0x28 0x00 32-bit
+>>>>7 byte&0x28 0x20 64-bit
+>>>>7 byte&0x0c 0x00 classic
+>>>>7 byte&0x0c 0x04 unicode
+>>>>7 byte&0x88 0x00 big-endian
+>>>>7 byte&0x88 0x80 little-endian
+>>>1 byte 0x06 external variable shared
+# *.DSE Dyalog APL Session , *.DLF Dyalog APL Session Log File
+>>>1 byte 0x07 session
+>>>1 byte 0x08 mapped file 32-bit
+>>>1 byte 0x09 component file 64-bit non-journaled non-checksummed
+>>>1 byte 0x0a mapped file 64-bit
+>>>1 byte 0x0b component file 32-bit level 1 journaled non-checksummed
+>>>1 byte 0x0c component file 64-bit level 1 journaled non-checksummed
+>>>1 byte 0x0d component file 32-bit level 1 journaled checksummed
+>>>1 byte 0x0e component file 64-bit level 1 journaled checksummed
+>>>1 byte 0x0f component file 32-bit level 2 journaled checksummed
+>>>1 byte 0x10 component file 64-bit level 2 journaled checksummed
+>>>1 byte 0x11 component file 32-bit level 3 journaled checksummed
+>>>1 byte 0x12 component file 64-bit level 3 journaled checksummed
+>>>1 byte 0x13 component file 32-bit non-journaled checksummed
+>>>1 byte 0x14 component file 64-bit non-journaled checksummed
+>>>1 byte 0x80 DDB
+>>>2 byte x version %d
+>>>3 byte x \b.%d
+#>>>2 byte x type %d
+#>>>3 byte x subtype %d
+
+# *.DXF Dyalog APL Transfer File
+0 short 0x6060 Dyalog APL transfer
+
+#------------------------------------------------------------------------------
+# $File$
+# ebml: file(1) magic for various Extensible Binary Meta Language
+# http://www.matroska.org/technical/specs/index.html#track
+0 belong 0x1a45dfa3 EBML file
+>4 search/b/100 \102\202
+>>&1 string x \b, creator %.8s
+
+#------------------------------------------------------------------------------
+# $File$
+# T602 editor documents
+# by David Necas <yeti@physics.muni.cz>
+0 string @CT\ T602 document data,
+>4 string 0 Kamenicky
+>4 string 1 CP 852
+>4 string 2 KOI8-CS
+>4 string >2 unknown encoding
+
+# Vi IMproved Encrypted file
+# by David Necas <yeti@physics.muni.cz>
+0 string VimCrypt~ Vim encrypted file data
+# Vi IMproved Swap file
+# by Sven Wegener <swegener@gentoo.org>
+0 string b0VIM\ Vim swap file
+>&0 string >\0 \b, version %s
+
+#------------------------------------------------------------------------------
+# $File: efi,v 1.4 2009/09/19 16:28:09 christos Exp $
+# efi: file(1) magic for Universal EFI binaries
+
+0 lelong 0x0ef1fab9
+>4 lelong 1 Universal EFI binary with 1 architecture
+>>&0 lelong 7 \b, i386
+>>&0 lelong 0x01000007 \b, x86_64
+>4 lelong 2 Universal EFI binary with 2 architectures
+>>&0 lelong 7 \b, i386
+>>&0 lelong 0x01000007 \b, x86_64
+>>&20 lelong 7 \b, i386
+>>&20 lelong 0x01000007 \b, x86_64
+>4 lelong >2 Universal EFI binary with %d architectures
+
+#------------------------------------------------------------------------------
+# $File: elf,v 1.67 2014/06/12 13:52:48 christos Exp $
+# elf: file(1) magic for ELF executables
+#
+# We have to check the byte order flag to see what byte order all the
+# other stuff in the header is in.
+#
+# What're the correct byte orders for the nCUBE and the Fujitsu VPP500?
+#
+# Created by: unknown
+# Modified by (1): Daniel Quinlan <quinlan@yggdrasil.com>
+# Modified by (2): Peter Tobias <tobias@server.et-inf.fho-emden.de> (core support)
+# Modified by (3): Christian 'Dr. Disk' Hechelmann <drdisk@ds9.au.s.shuttle.de> (fix of core support)
+# Modified by (4): <gerardo.cacciari@gmail.com> (VMS Itanium)
+# Modified by (5): Matthias Urlichs <smurf@debian.org> (Listing of many architectures)
+
+0 name elf-le
+>16 leshort 0 no file type,
+!:mime application/octet-stream
+>16 leshort 1 relocatable,
+!:mime application/x-object
+>16 leshort 2 executable,
+!:mime application/x-executable
+>16 leshort 3 shared object,
+!:mime application/x-sharedlib
+>16 leshort 4 core file
+!:mime application/x-coredump
+# Core file detection is not reliable.
+#>>>(0x38+0xcc) string >\0 of '%s'
+#>>>(0x38+0x10) lelong >0 (signal %d),
+>16 leshort &0xff00 processor-specific,
+>18 clear x
+>18 leshort 0 no machine,
+>18 leshort 1 AT&T WE32100,
+>18 leshort 2 SPARC,
+>18 leshort 3 Intel 80386,
+>18 leshort 4 Motorola m68k,
+>>4 byte 1
+>>>36 lelong &0x01000000 68000,
+>>>36 lelong &0x00810000 CPU32,
+>>>36 lelong 0 68020,
+>18 leshort 5 Motorola m88k,
+>18 leshort 6 Intel 80486,
+>18 leshort 7 Intel 80860,
+# The official e_machine number for MIPS is now #8, regardless of endianness.
+# The second number (#10) will be deprecated later. For now, we still
+# say something if #10 is encountered, but only gory details for #8.
+>18 leshort 8 MIPS,
+>>4 byte 1
+>>>36 lelong &0x20 N32
+>18 leshort 10 MIPS,
+>>4 byte 1
+>>>36 lelong &0x20 N32
+>18 leshort 8
+# only for 32-bit
+>>4 byte 1
+>>>36 lelong&0xf0000000 0x00000000 MIPS-I
+>>>36 lelong&0xf0000000 0x10000000 MIPS-II
+>>>36 lelong&0xf0000000 0x20000000 MIPS-III
+>>>36 lelong&0xf0000000 0x30000000 MIPS-IV
+>>>36 lelong&0xf0000000 0x40000000 MIPS-V
+>>>36 lelong&0xf0000000 0x50000000 MIPS32
+>>>36 lelong&0xf0000000 0x60000000 MIPS64
+>>>36 lelong&0xf0000000 0x70000000 MIPS32 rel2
+>>>36 lelong&0xf0000000 0x80000000 MIPS64 rel2
+# only for 64-bit
+>>4 byte 2
+>>>48 lelong&0xf0000000 0x00000000 MIPS-I
+>>>48 lelong&0xf0000000 0x10000000 MIPS-II
+>>>48 lelong&0xf0000000 0x20000000 MIPS-III
+>>>48 lelong&0xf0000000 0x30000000 MIPS-IV
+>>>48 lelong&0xf0000000 0x40000000 MIPS-V
+>>>48 lelong&0xf0000000 0x50000000 MIPS32
+>>>48 lelong&0xf0000000 0x60000000 MIPS64
+>>>48 lelong&0xf0000000 0x70000000 MIPS32 rel2
+>>>48 lelong&0xf0000000 0x80000000 MIPS64 rel2
+>18 leshort 9 Amdahl,
+>18 leshort 10 MIPS (deprecated),
+>18 leshort 11 RS6000,
+>18 leshort 15 PA-RISC,
+# only for 32-bit
+>>4 byte 1
+>>>38 leshort 0x0214 2.0
+>>>36 leshort &0x0008 (LP64)
+# only for 64-bit
+>>4 byte 2
+>>>50 leshort 0x0214 2.0
+>>>48 leshort &0x0008 (LP64)
+>18 leshort 16 nCUBE,
+>18 leshort 17 Fujitsu VPP500,
+>18 leshort 18 SPARC32PLUS,
+# only for 32-bit
+>>4 byte 1
+>>>36 lelong&0xffff00 0x000100 V8+ Required,
+>>>36 lelong&0xffff00 0x000200 Sun UltraSPARC1 Extensions Required,
+>>>36 lelong&0xffff00 0x000400 HaL R1 Extensions Required,
+>>>36 lelong&0xffff00 0x000800 Sun UltraSPARC3 Extensions Required,
+>18 leshort 19 Intel 80960,
+>18 leshort 20 PowerPC or cisco 4500,
+>18 leshort 21 64-bit PowerPC or cisco 7500,
+>18 leshort 22 IBM S/390,
+>18 leshort 23 Cell SPU,
+>18 leshort 24 cisco SVIP,
+>18 leshort 25 cisco 7200,
+>18 leshort 36 NEC V800 or cisco 12000,
+>18 leshort 37 Fujitsu FR20,
+>18 leshort 38 TRW RH-32,
+>18 leshort 39 Motorola RCE,
+>18 leshort 40 ARM,
+>>4 byte 1
+>>>36 lelong&0xff000000 0x04000000 EABI4
+>>>36 lelong&0xff000000 0x05000000 EABI5
+>>>36 lelong &0x00800000 BE8
+>>>36 lelong &0x00400000 LE8
+>18 leshort 41 Alpha,
+>18 leshort 42 Renesas SH,
+>18 leshort 43 SPARC V9,
+>>4 byte 2
+>>>48 lelong&0xffff00 0x000200 Sun UltraSPARC1 Extensions Required,
+>>>48 lelong&0xffff00 0x000400 HaL R1 Extensions Required,
+>>>48 lelong&0xffff00 0x000800 Sun UltraSPARC3 Extensions Required,
+>>>48 lelong&0x3 0 total store ordering,
+>>>48 lelong&0x3 1 partial store ordering,
+>>>48 lelong&0x3 2 relaxed memory ordering,
+>18 leshort 44 Siemens Tricore Embedded Processor,
+>18 leshort 45 Argonaut RISC Core, Argonaut Technologies Inc.,
+>18 leshort 46 Renesas H8/300,
+>18 leshort 47 Renesas H8/300H,
+>18 leshort 48 Renesas H8S,
+>18 leshort 49 Renesas H8/500,
+>18 leshort 50 IA-64,
+>18 leshort 51 Stanford MIPS-X,
+>18 leshort 52 Motorola Coldfire,
+>18 leshort 53 Motorola M68HC12,
+>18 leshort 54 Fujitsu MMA,
+>18 leshort 55 Siemens PCP,
+>18 leshort 56 Sony nCPU,
+>18 leshort 57 Denso NDR1,
+>18 leshort 58 Start*Core,
+>18 leshort 59 Toyota ME16,
+>18 leshort 60 ST100,
+>18 leshort 61 Tinyj emb.,
+>18 leshort 62 x86-64,
+>18 leshort 63 Sony DSP,
+>18 leshort 64 DEC PDP-10,
+>18 leshort 65 DEC PDP-11,
+>18 leshort 66 FX66,
+>18 leshort 67 ST9+ 8/16 bit,
+>18 leshort 68 ST7 8 bit,
+>18 leshort 69 MC68HC16,
+>18 leshort 70 MC68HC11,
+>18 leshort 71 MC68HC08,
+>18 leshort 72 MC68HC05,
+>18 leshort 73 SGI SVx or Cray NV1,
+>18 leshort 74 ST19 8 bit,
+>18 leshort 75 Digital VAX,
+>18 leshort 76 Axis cris,
+>18 leshort 77 Infineon 32-bit embedded,
+>18 leshort 78 Element 14 64-bit DSP,
+>18 leshort 79 LSI Logic 16-bit DSP,
+>18 leshort 80 MMIX,
+>18 leshort 81 Harvard machine-independent,
+>18 leshort 82 SiTera Prism,
+>18 leshort 83 Atmel AVR 8-bit,
+>18 leshort 84 Fujitsu FR30,
+>18 leshort 85 Mitsubishi D10V,
+>18 leshort 86 Mitsubishi D30V,
+>18 leshort 87 NEC v850,
+>18 leshort 88 Renesas M32R,
+>18 leshort 89 Matsushita MN10300,
+>18 leshort 90 Matsushita MN10200,
+>18 leshort 91 picoJava,
+>18 leshort 92 OpenRISC,
+>18 leshort 93 ARC Cores Tangent-A5,
+>18 leshort 94 Tensilica Xtensa,
+>18 leshort 95 Alphamosaic VideoCore,
+>18 leshort 96 Thompson Multimedia,
+>18 leshort 97 NatSemi 32k,
+>18 leshort 98 Tenor Network TPC,
+>18 leshort 99 Trebia SNP 1000,
+>18 leshort 100 STMicroelectronics ST200,
+>18 leshort 101 Ubicom IP2022,
+>18 leshort 102 MAX Processor,
+>18 leshort 103 NatSemi CompactRISC,
+>18 leshort 104 Fujitsu F2MC16,
+>18 leshort 105 TI msp430,
+>18 leshort 106 Analog Devices Blackfin,
+>18 leshort 107 S1C33 Family of Seiko Epson,
+>18 leshort 108 Sharp embedded,
+>18 leshort 109 Arca RISC,
+>18 leshort 110 PKU-Unity Ltd.,
+>18 leshort 111 eXcess: 16/32/64-bit,
+>18 leshort 112 Icera Deep Execution Processor,
+>18 leshort 113 Altera Nios II,
+>18 leshort 114 NatSemi CRX,
+>18 leshort 115 Motorola XGATE,
+>18 leshort 116 Infineon C16x/XC16x,
+>18 leshort 117 Renesas M16C series,
+>18 leshort 118 Microchip dsPIC30F,
+>18 leshort 119 Freescale RISC core,
+>18 leshort 120 Renesas M32C series,
+>18 leshort 131 Altium TSK3000 core,
+>18 leshort 132 Freescale RS08,
+>18 leshort 134 Cyan Technology eCOG2,
+>18 leshort 135 Sunplus S+core7 RISC,
+>18 leshort 136 New Japan Radio (NJR) 24-bit DSP,
+>18 leshort 137 Broadcom VideoCore III,
+>18 leshort 138 LatticeMico32,
+>18 leshort 139 Seiko Epson C17 family,
+>18 leshort 140 TI TMS320C6000 DSP family,
+>18 leshort 141 TI TMS320C2000 DSP family,
+>18 leshort 142 TI TMS320C55x DSP family,
+>18 leshort 160 STMicroelectronics 64bit VLIW DSP,
+>18 leshort 161 Cypress M8C,
+>18 leshort 162 Renesas R32C series,
+>18 leshort 163 NXP TriMedia family,
+>18 leshort 164 QUALCOMM DSP6,
+>18 leshort 165 Intel 8051 and variants,
+>18 leshort 166 STMicroelectronics STxP7x family,
+>18 leshort 167 Andes embedded RISC,
+>18 leshort 168 Cyan eCOG1X family,
+>18 leshort 169 Dallas MAXQ30,
+>18 leshort 170 New Japan Radio (NJR) 16-bit DSP,
+>18 leshort 171 M2000 Reconfigurable RISC,
+>18 leshort 172 Cray NV2 vector architecture,
+>18 leshort 173 Renesas RX family,
+>18 leshort 174 META,
+>18 leshort 175 MCST Elbrus,
+>18 leshort 176 Cyan Technology eCOG16 family,
+>18 leshort 177 NatSemi CompactRISC,
+>18 leshort 178 Freescale Extended Time Processing Unit,
+>18 leshort 179 Infineon SLE9X,
+>18 leshort 180 Intel L1OM,
+>18 leshort 181 Intel K1OM,
+>18 leshort 183 ARM aarch64,
+>18 leshort 185 Atmel 32-bit family,
+>18 leshort 186 STMicroeletronics STM8 8-bit,
+>18 leshort 187 Tilera TILE64,
+>18 leshort 188 Tilera TILEPro,
+>18 leshort 189 Xilinx MicroBlaze 32-bit RISC,
+>18 leshort 190 NVIDIA CUDA architecture,
+>18 leshort 191 Tilera TILE-Gx,
+>18 leshort 197 Renesas RL78 family,
+>18 leshort 199 Renesas 78K0R,
+>18 leshort 200 Freescale 56800EX,
+>18 leshort 201 Beyond BA1,
+>18 leshort 202 Beyond BA2,
+>18 leshort 203 XMOS xCORE,
+>18 leshort 204 Microchip 8-bit PIC(r),
+>18 leshort 210 KM211 KM32,
+>18 leshort 211 KM211 KMX32,
+>18 leshort 212 KM211 KMX16,
+>18 leshort 213 KM211 KMX8,
+>18 leshort 214 KM211 KVARC,
+>18 leshort 215 Paneve CDP,
+>18 leshort 216 Cognitive Smart Memory,
+>18 leshort 217 iCelero CoolEngine,
+>18 leshort 218 Nanoradio Optimized RISC,
+>18 leshort 243 UCB RISC-V,
+>18 leshort 0x1057 AVR (unofficial),
+>18 leshort 0x1059 MSP430 (unofficial),
+>18 leshort 0x1223 Adapteva Epiphany (unofficial),
+>18 leshort 0x2530 Morpho MT (unofficial),
+>18 leshort 0x3330 FR30 (unofficial),
+>18 leshort 0x3426 OpenRISC (obsolete),
+>18 leshort 0x4688 Infineon C166 (unofficial),
+>18 leshort 0x5441 Cygnus FRV (unofficial),
+>18 leshort 0x5aa5 DLX (unofficial),
+>18 leshort 0x7650 Cygnus D10V (unofficial),
+>18 leshort 0x7676 Cygnus D30V (unofficial),
+>18 leshort 0x8217 Ubicom IP2xxx (unofficial),
+>18 leshort 0x8472 OpenRISC (obsolete),
+>18 leshort 0x9025 Cygnus PowerPC (unofficial),
+>18 leshort 0x9026 Alpha (unofficial),
+>18 leshort 0x9041 Cygnus M32R (unofficial),
+>18 leshort 0x9080 Cygnus V850 (unofficial),
+>18 leshort 0xa390 IBM S/390 (obsolete),
+>18 leshort 0xabc7 Old Xtensa (unofficial),
+>18 leshort 0xad45 xstormy16 (unofficial),
+>18 leshort 0xbaab Old MicroBlaze (unofficial),,
+>18 leshort 0xbeef Cygnus MN10300 (unofficial),
+>18 leshort 0xdead Cygnus MN10200 (unofficial),
+>18 leshort 0xf00d Toshiba MeP (unofficial),
+>18 leshort 0xfeb0 Renesas M32C (unofficial),
+>18 leshort 0xfeba Vitesse IQ2000 (unofficial),
+>18 leshort 0xfebb NIOS (unofficial),
+>18 leshort 0xfeed Moxie (unofficial),
+>18 default x
+>>18 leshort x *unknown arch 0x%x*
+>20 lelong 0 invalid version
+>20 lelong 1 version 1
+
+0 string \177ELF ELF
+!:strength *2
+>4 byte 0 invalid class
+>4 byte 1 32-bit
+>4 byte 2 64-bit
+>5 byte 0 invalid byte order
+>5 byte 1 LSB
+>>0 use elf-le
+>5 byte 2 MSB
+>>0 use \^elf-le
+# Up to now only 0, 1 and 2 are defined; I've seen a file with 0x83, it seemed
+# like proper ELF, but extracting the string had bad results.
+>4 byte <0x80
+>>8 string >\0 (%s)
+>8 string \0
+>>7 byte 0 (SYSV)
+>>7 byte 1 (HP-UX)
+>>7 byte 2 (NetBSD)
+>>7 byte 3 (GNU/Linux)
+>>7 byte 4 (GNU/Hurd)
+>>7 byte 5 (86Open)
+>>7 byte 6 (Solaris)
+>>7 byte 7 (Monterey)
+>>7 byte 8 (IRIX)
+>>7 byte 9 (FreeBSD)
+>>7 byte 10 (Tru64)
+>>7 byte 11 (Novell Modesto)
+>>7 byte 12 (OpenBSD)
+>8 string \2
+>>7 byte 13 (OpenVMS)
+>>7 byte 97 (ARM)
+>>7 byte 255 (embedded)
+
+#------------------------------------------------------------------------------
+# $File: encore,v 1.6 2009/09/19 16:28:09 christos Exp $
+# encore: file(1) magic for Encore machines
+#
+# XXX - needs to have the byte order specified (NS32K was little-endian,
+# dunno whether they run the 88K in little-endian mode or not).
+#
+0 short 0x154 Encore
+>20 short 0x107 executable
+>20 short 0x108 pure executable
+>20 short 0x10b demand-paged executable
+>20 short 0x10f unsupported executable
+>12 long >0 not stripped
+>22 short >0 - version %d
+>22 short 0 -
+#>4 date x stamp %s
+0 short 0x155 Encore unsupported executable
+>12 long >0 not stripped
+>22 short >0 - version %d
+>22 short 0 -
+#>4 date x stamp %s
+
+#------------------------------------------------------------------------------
+# $File: epoc,v 1.8 2012/06/16 14:43:36 christos Exp $
+# EPOC : file(1) magic for EPOC documents [Psion Series 5/Osaris/Geofox 1]
+# Stefan Praszalowicz <hpicollo@worldnet.fr> and Peter Breitenlohner <peb@mppmu.mpg.de>
+# Useful information for improving this file can be found at:
+# http://software.frodo.looijaard.name/psiconv/formats/Index.html
+#------------------------------------------------------------------------------
+0 lelong 0x10000037 Psion Series 5
+>4 lelong 0x10000039 font file
+>4 lelong 0x1000003A printer driver
+>4 lelong 0x1000003B clipboard
+>4 lelong 0x10000042 multi-bitmap image
+!:mime image/x-epoc-mbm
+>4 lelong 0x1000006A application information file
+>4 lelong 0x1000006D
+>>8 lelong 0x1000007D Sketch image
+!:mime image/x-epoc-sketch
+>>8 lelong 0x1000007E voice note
+>>8 lelong 0x1000007F Word file
+!:mime application/x-epoc-word
+>>8 lelong 0x10000085 OPL program (TextEd)
+!:mime application/x-epoc-opl
+>>8 lelong 0x10000087 Comms settings
+>>8 lelong 0x10000088 Sheet file
+!:mime application/x-epoc-sheet
+>>8 lelong 0x100001C4 EasyFax initialisation file
+>4 lelong 0x10000073 OPO module
+!:mime application/x-epoc-opo
+>4 lelong 0x10000074 OPL application
+!:mime application/x-epoc-app
+>4 lelong 0x1000008A exported multi-bitmap image
+>4 lelong 0x1000016D
+>>8 lelong 0x10000087 Comms names
+
+0 lelong 0x10000041 Psion Series 5 ROM multi-bitmap image
+
+0 lelong 0x10000050 Psion Series 5
+>4 lelong 0x1000006D database
+>>8 lelong 0x10000084 Agenda file
+!:mime application/x-epoc-agenda
+>>8 lelong 0x10000086 Data file
+!:mime application/x-epoc-data
+>>8 lelong 0x10000CEA Jotter file
+!:mime application/x-epoc-jotter
+>4 lelong 0x100000E4 ini file
+
+0 lelong 0x10000079 Psion Series 5 binary:
+>4 lelong 0x00000000 DLL
+>4 lelong 0x10000049 comms hardware library
+>4 lelong 0x1000004A comms protocol library
+>4 lelong 0x1000005D OPX
+>4 lelong 0x1000006C application
+>4 lelong 0x1000008D DLL
+>4 lelong 0x100000AC logical device driver
+>4 lelong 0x100000AD physical device driver
+>4 lelong 0x100000E5 file transfer protocol
+>4 lelong 0x100000E5 file transfer protocol
+>4 lelong 0x10000140 printer definition
+>4 lelong 0x10000141 printer definition
+
+0 lelong 0x1000007A Psion Series 5 executable
+
+#------------------------------------------------------------------------------
+# $File: erlang,v 1.5 2009/09/19 16:28:09 christos Exp $
+# erlang: file(1) magic for Erlang JAM and BEAM files
+# URL: http://www.erlang.org/faq/x779.html#AEN812
+
+# OTP R3-R4
+0 string \0177BEAM! Old Erlang BEAM file
+>6 short >0 - version %d
+
+# OTP R5 and onwards
+0 string FOR1
+>8 string BEAM Erlang BEAM file
+
+# 4.2 version may have a copyright notice!
+4 string Tue\ Jan\ 22\ 14:32:44\ MET\ 1991 Erlang JAM file - version 4.2
+79 string Tue\ Jan\ 22\ 14:32:44\ MET\ 1991 Erlang JAM file - version 4.2
+
+4 string 1.0\ Fri\ Feb\ 3\ 09:55:56\ MET\ 1995 Erlang JAM file - version 4.3
+
+0 bequad 0x0000000000ABCDEF Erlang DETS file
+
+#------------------------------------------------------------------------------
+# $File$
+# ESRI Shapefile format (.shp .shx .dbf=DBaseIII)
+# Based on info from
+# <URL:http://www.esri.com/library/whitepapers/pdfs/shapefile.pdf>
+0 belong 9994 ESRI Shapefile
+>4 belong =0
+>8 belong =0
+>12 belong =0
+>16 belong =0
+>20 belong =0
+>28 lelong x version %d
+>24 belong x length %d
+>32 lelong =0 type Null Shape
+>32 lelong =1 type Point
+>32 lelong =3 type PolyLine
+>32 lelong =5 type Polygon
+>32 lelong =8 type MultiPoint
+>32 lelong =11 type PointZ
+>32 lelong =13 type PolyLineZ
+>32 lelong =15 type PolygonZ
+>32 lelong =18 type MultiPointZ
+>32 lelong =21 type PointM
+>32 lelong =23 type PolyLineM
+>32 lelong =25 type PolygonM
+>32 lelong =28 type MultiPointM
+>32 lelong =31 type MultiPatch
+
+#------------------------------------------------------------------------------
+# $File$
+# fcs: file(1) magic for FCS (Flow Cytometry Standard) data files
+# From Roger Leigh <roger@whinlatter.uklinux.net>
+0 string FCS1.0 Flow Cytometry Standard (FCS) data, version 1.0
+0 string FCS2.0 Flow Cytometry Standard (FCS) data, version 2.0
+0 string FCS3.0 Flow Cytometry Standard (FCS) data, version 3.0
+
+#------------------------------------------------------------------------------
+# $File: filesystems,v 1.108 2015/01/01 17:43:47 christos Exp $
+# filesystems: file(1) magic for different filesystems
+#
+0 name partid
+>0 ubyte 0x00 Unused
+>0 ubyte 0x01 12-bit FAT
+>0 ubyte 0x02 XENIX /
+>0 ubyte 0x03 XENIX /usr
+>0 ubyte 0x04 16-bit FAT, less than 32M
+>0 ubyte 0x05 extended partition
+>0 ubyte 0x06 16-bit FAT, more than 32M
+>0 ubyte 0x07 OS/2 HPFS, NTFS, QNX2, Adv. UNIX
+>0 ubyte 0x08 AIX or os, or etc.
+>0 ubyte 0x09 AIX boot partition or Coherent
+>0 ubyte 0x0a O/2 boot manager or Coherent swap
+>0 ubyte 0x0b 32-bit FAT
+>0 ubyte 0x0c 32-bit FAT, LBA-mapped
+>0 ubyte 0x0d 7XXX, LBA-mapped
+>0 ubyte 0x0e 16-bit FAT, LBA-mapped
+>0 ubyte 0x0f extended partition, LBA-mapped
+>0 ubyte 0x10 OPUS
+>0 ubyte 0x11 OS/2 DOS 12-bit FAT
+>0 ubyte 0x12 Compaq diagnostics
+>0 ubyte 0x14 OS/2 DOS 16-bit FAT <32M
+>0 ubyte 0x16 OS/2 DOS 16-bit FAT >=32M
+>0 ubyte 0x17 OS/2 hidden IFS
+>0 ubyte 0x18 AST Windows swapfile
+>0 ubyte 0x19 Willowtech Photon coS
+>0 ubyte 0x1b hidden win95 fat 32
+>0 ubyte 0x1c hidden win95 fat 32 lba
+>0 ubyte 0x1d hidden win95 fat 16 lba
+>0 ubyte 0x20 Willowsoft OFS1
+>0 ubyte 0x21 reserved
+>0 ubyte 0x23 reserved
+>0 ubyte 0x24 NEC DOS
+>0 ubyte 0x26 reserved
+>0 ubyte 0x31 reserved
+>0 ubyte 0x32 Alien Internet Services NOS
+>0 ubyte 0x33 reserved
+>0 ubyte 0x34 reserved
+>0 ubyte 0x35 JFS on OS2
+>0 ubyte 0x36 reserved
+>0 ubyte 0x38 Theos
+>0 ubyte 0x39 Plan 9, or Theos spanned
+>0 ubyte 0x3a Theos ver 4 4gb partition
+>0 ubyte 0x3b Theos ve 4 extended partition
+>0 ubyte 0x3c PartitionMagic recovery
+>0 ubyte 0x3d Hidden Netware
+>0 ubyte 0x40 VENIX 286 or LynxOS
+>0 ubyte 0x41 PReP
+>0 ubyte 0x42 linux swap sharing DRDOS disk
+>0 ubyte 0x43 linux sharing DRDOS disk
+>0 ubyte 0x44 GoBack change utility
+>0 ubyte 0x45 Boot US Boot manager
+>0 ubyte 0x46 EUMEL/Elan or Ergos 3
+>0 ubyte 0x47 EUMEL/Elan or Ergos 3
+>0 ubyte 0x48 EUMEL/Elan or Ergos 3
+>0 ubyte 0x4a ALFX/THIN filesystem for DOS
+>0 ubyte 0x4c Oberon partition
+>0 ubyte 0x4d QNX4.x
+>0 ubyte 0x4e QNX4.x 2nd part
+>0 ubyte 0x4f QNX4.x 3rd part
+>0 ubyte 0x50 DM (disk manager)
+>0 ubyte 0x51 DM6 Aux1 (or Novell)
+>0 ubyte 0x52 CP/M or Microport SysV/AT
+>0 ubyte 0x53 DM6 Aux3
+>0 ubyte 0x54 DM6 DDO
+>0 ubyte 0x55 EZ-Drive (disk manager)
+>0 ubyte 0x56 Golden Bow (disk manager)
+>0 ubyte 0x57 Drive PRO
+>0 ubyte 0x5c Priam Edisk (disk manager)
+>0 ubyte 0x61 SpeedStor
+>0 ubyte 0x63 GNU HURD or Mach or Sys V/386
+>0 ubyte 0x64 Novell Netware 2.xx or Speedstore
+>0 ubyte 0x65 Novell Netware 3.xx
+>0 ubyte 0x66 Novell 386 Netware
+>0 ubyte 0x67 Novell
+>0 ubyte 0x68 Novell
+>0 ubyte 0x69 Novell
+>0 ubyte 0x70 DiskSecure Multi-Boot
+>0 ubyte 0x71 reserved
+>0 ubyte 0x73 reserved
+>0 ubyte 0x74 reserved
+>0 ubyte 0x75 PC/IX
+>0 ubyte 0x76 reserved
+>0 ubyte 0x77 M2FS/M2CS partition
+>0 ubyte 0x78 XOSL boot loader filesystem
+>0 ubyte 0x80 MINIX until 1.4a
+>0 ubyte 0x81 MINIX since 1.4b
+>0 ubyte 0x82 Linux swap or Solaris
+>0 ubyte 0x83 Linux native
+>0 ubyte 0x84 OS/2 hidden C: drive
+>0 ubyte 0x85 Linux extended partition
+>0 ubyte 0x86 NT FAT volume set
+>0 ubyte 0x87 NTFS volume set or HPFS mirrored
+>0 ubyte 0x8a Linux Kernel AiR-BOOT partition
+>0 ubyte 0x8b Legacy Fault tolerant FAT32
+>0 ubyte 0x8c Legacy Fault tolerant FAT32 ext
+>0 ubyte 0x8d Hidden free FDISK FAT12
+>0 ubyte 0x8e Linux Logical Volume Manager
+>0 ubyte 0x90 Hidden free FDISK FAT16
+>0 ubyte 0x91 Hidden free FDISK DOS EXT
+>0 ubyte 0x92 Hidden free FDISK FAT16 Big
+>0 ubyte 0x93 Amoeba filesystem
+>0 ubyte 0x94 Amoeba bad block table
+>0 ubyte 0x95 MIT EXOPC native partitions
+>0 ubyte 0x97 Hidden free FDISK FAT32
+>0 ubyte 0x98 Datalight ROM-DOS Super-Boot
+>0 ubyte 0x99 Mylex EISA SCSI
+>0 ubyte 0x9a Hidden free FDISK FAT16 LBA
+>0 ubyte 0x9b Hidden free FDISK EXT LBA
+>0 ubyte 0x9f BSDI?
+>0 ubyte 0xa0 IBM Thinkpad hibernation
+>0 ubyte 0xa1 HP Volume expansion (SpeedStor)
+>0 ubyte 0xa3 HP Volume expansion (SpeedStor)
+>0 ubyte 0xa4 HP Volume expansion (SpeedStor)
+>0 ubyte 0xa5 386BSD partition type
+>0 ubyte 0xa6 OpenBSD partition type
+>0 ubyte 0xa7 NeXTSTEP 486
+>0 ubyte 0xa8 Apple UFS
+>0 ubyte 0xa9 NetBSD partition type
+>0 ubyte 0xaa Olivetty Fat12 1.44MB Service part
+>0 ubyte 0xab Apple Boot
+>0 ubyte 0xae SHAG OS filesystem
+>0 ubyte 0xaf Apple HFS
+>0 ubyte 0xb0 BootStar Dummy
+>0 ubyte 0xb1 reserved
+>0 ubyte 0xb3 reserved
+>0 ubyte 0xb4 reserved
+>0 ubyte 0xb6 reserved
+>0 ubyte 0xb7 BSDI BSD/386 filesystem
+>0 ubyte 0xb8 BSDI BSD/386 swap
+>0 ubyte 0xbb Boot Wizard Hidden
+>0 ubyte 0xbe Solaris 8 partition type
+>0 ubyte 0xbf Solaris partition type
+>0 ubyte 0xc0 CTOS
+>0 ubyte 0xc1 DRDOS/sec (FAT-12)
+>0 ubyte 0xc2 Hidden Linux
+>0 ubyte 0xc3 Hidden Linux swap
+>0 ubyte 0xc4 DRDOS/sec (FAT-16, < 32M)
+>0 ubyte 0xc5 DRDOS/sec (EXT)
+>0 ubyte 0xc6 DRDOS/sec (FAT-16, >= 32M)
+>0 ubyte 0xc7 Syrinx (Cyrnix?) or HPFS disabled
+>0 ubyte 0xc8 Reserved for DR-DOS 8.0+
+>0 ubyte 0xc9 Reserved for DR-DOS 8.0+
+>0 ubyte 0xca Reserved for DR-DOS 8.0+
+>0 ubyte 0xcb DR-DOS 7.04+ Secured FAT32 CHS
+>0 ubyte 0xcc DR-DOS 7.04+ Secured FAT32 LBA
+>0 ubyte 0xcd CTOS Memdump
+>0 ubyte 0xce DR-DOS 7.04+ FAT16X LBA
+>0 ubyte 0xcf DR-DOS 7.04+ EXT LBA
+>0 ubyte 0xd0 REAL/32 secure big partition
+>0 ubyte 0xd1 Old Multiuser DOS FAT12
+>0 ubyte 0xd4 Old Multiuser DOS FAT16 Small
+>0 ubyte 0xd5 Old Multiuser DOS Extended
+>0 ubyte 0xd6 Old Multiuser DOS FAT16 Big
+>0 ubyte 0xd8 CP/M 86
+>0 ubyte 0xdb CP/M or Concurrent CP/M
+>0 ubyte 0xdd Hidden CTOS Memdump
+>0 ubyte 0xde Dell PowerEdge Server utilities
+>0 ubyte 0xdf DG/UX virtual disk manager
+>0 ubyte 0xe0 STMicroelectronics ST AVFS
+>0 ubyte 0xe1 DOS access or SpeedStor 12-bit
+>0 ubyte 0xe3 DOS R/O or Storage Dimensions
+>0 ubyte 0xe4 SpeedStor 16-bit FAT < 1024 cyl.
+>0 ubyte 0xe5 reserved
+>0 ubyte 0xe6 reserved
+>0 ubyte 0xeb BeOS
+>0 ubyte 0xee GPT Protective MBR
+>0 ubyte 0xef EFI system partition
+>0 ubyte 0xf0 Linux PA-RISC boot loader
+>0 ubyte 0xf1 SpeedStor or Storage Dimensions
+>0 ubyte 0xf2 DOS 3.3+ Secondary
+>0 ubyte 0xf3 reserved
+>0 ubyte 0xf4 SpeedStor large partition
+>0 ubyte 0xf5 Prologue multi-volumen partition
+>0 ubyte 0xf6 reserved
+>0 ubyte 0xf9 pCache: ext2/ext3 persistent cache
+>0 ubyte 0xfa Bochs x86 emulator
+>0 ubyte 0xfb VMware File System
+>0 ubyte 0xfc VMware Swap
+>0 ubyte 0xfd Linux RAID partition persistent sb
+>0 ubyte 0xfe LANstep or IBM PS/2 IML
+>0 ubyte 0xff Xenix Bad Block Table
+
+0 string \366\366\366\366 PC formatted floppy with no filesystem
+# Sun disk labels
+# From /usr/include/sun/dklabel.h:
+0774 beshort 0xdabe
+# modified by Joerg Jenderek, because original test
+# succeeds for Cabinet archive dao360.dl_ with negative blocks
+>0770 long >0 Sun disk label
+>>0 string x '%s
+>>>31 string >\0 \b%s
+>>>>63 string >\0 \b%s
+>>>>>95 string >\0 \b%s
+>>0 string x \b'
+>>0734 short >0 %d rpm,
+>>0736 short >0 %d phys cys,
+>>0740 short >0 %d alts/cyl,
+>>0746 short >0 %d interleave,
+>>0750 short >0 %d data cyls,
+>>0752 short >0 %d alt cyls,
+>>0754 short >0 %d heads/partition,
+>>0756 short >0 %d sectors/track,
+>>0764 long >0 start cyl %d,
+>>0770 long x %d blocks
+# Is there a boot block written 1 sector in?
+>512 belong&077777777 0600407 \b, boot block present
+
+# Joerg Jenderek: Smart Boot Manager backup file is 25 (MSDOS) or 41 (LINUX) byte header + first sectors of disk
+# (http://btmgr.sourceforge.net/docs/user-guide-3.html)
+0 string SBMBAKUP_ Smart Boot Manager backup file
+>9 string x \b, version %-5.5s
+>>14 string =_
+>>>15 string x %-.1s
+>>>>16 string =_ \b.
+>>>>>17 string x \b%-.1s
+>>>>>>18 string =_ \b.
+>>>>>>>19 string x \b%-.1s
+>>>22 ubyte 0
+>>>>21 ubyte x \b, from drive 0x%x
+>>>22 ubyte >0
+>>>>21 string x \b, from drive %s
+>>>535 search/17 \x55\xAA
+>>>>&-512 indirect x \b; contains
+
+# updated by Joerg Jenderek at Nov 2012
+# DOS Emulator image is 128 byte, null right padded header + harddisc image
+0 string DOSEMU\0
+>0x27E leshort 0xAA55
+#offset is 128
+>>19 ubyte 128
+>>>(19.b-1) ubyte 0x0 DOS Emulator image
+>>>>7 ulelong >0 \b, %u heads
+>>>>11 ulelong >0 \b, %d sectors/track
+>>>>15 ulelong >0 \b, %d cylinders
+>>>>128 indirect x \b; contains
+
+# added by Joerg Jenderek at Nov 2012
+# http://www.thenakedpc.com/articles/v04/08/0408-05.html
+# Symantec (Peter Norton) Image.dat file consists of variable header, bootrecord, part of FAT and root directory data
+0 string PNCIHISK\0 Norton Utilities disc image data
+# real x86 boot sector with jump instruction
+>509 search/1026 \x55\xAA\xeb
+>>&-1 indirect x \b; contains
+# http://file-extension.net/seeker/file_extension_dat
+0 string PNCIUNDO Norton Disk Doctor UnDo file
+#
+
+# DOS/MBR boot sector updated by Joerg Jenderek at Sep 2007,May 2011,2013
+# for any allowed sector sizes
+30 search/481 \x55\xAA
+# to display DOS/MBR boot sector (40) before old one (strength=50+21),Syslinux bootloader (71),SYSLINUX MBR (37+36),NetBSD mbr (110),AdvanceMAME mbr (111)
+# DOS BPB information (70) and after DOS floppy (120) like in previous file version
+!:strength +65
+# for sector sizes < 512 Bytes
+>11 uleshort <512
+>>(11.s-2) uleshort 0xAA55 DOS/MBR boot sector
+# for sector sizes with 512 or more Bytes
+>0x1FE leshort 0xAA55 DOS/MBR boot sector
+
+# keep old DOS/MBR boot sector as dummy for mbr and bootloader displaying
+# only for sector sizes with 512 or more Bytes
+0x1FE leshort 0xAA55 DOS/MBR boot sector
+#
+# to display information (50) before DOS BPB (strength=70) and after DOS floppy (120) like in old file version
+!:strength +65
+>2 string OSBS OS/BS MBR
+# added by Joerg Jenderek at Feb 2013 according to http://thestarman.pcministry.com/asm/mbr/
+# and http://en.wikipedia.org/wiki/Master_Boot_Record
+# test for nearly all MS-DOS Master Boot Record initial program loader (IPL) is now done by
+# characteristic assembler instructions: xor ax,ax;mov ss,ax;mov sp,7c00
+>0 search/2 \x33\xc0\x8e\xd0\xbc\x00\x7c MS-MBR
+# Microsoft Windows 95A and early ( http://thestarman.pcministry.com/asm/mbr/STDMBR.htm )
+# assembler instructions: mov si,sp;push ax;pop es;push ax;pop ds;sti;cld
+>>8 ubequad 0x8bf45007501ffbfc
+# http://thestarman.pcministry.com/asm/mbr/200MBR.htm
+>>>0x16 ubyte 0xF3 \b,DOS 2
+>>>>219 regex Author\ -\ Author:
+# found "David Litton" , "A Pehrsson "
+>>>>>&0 string x "%s"
+>>>0x16 ubyte 0xF2
+# NEC MS-DOS 3.30 Rev. 3 . See http://thestarman.pcministry.com/asm/mbr/DOS33MBR.htm
+# assembler instructions: mov di,077c;cmp word ptrl[di],a55a;jnz
+>>>>0x22 ubequad 0xbf7c07813d5aa575 \b,NEC 3.3
+# version MS-DOS 3.30 til MS-Windows 95A (WinVer=4.00.1111)
+>>>>0x22 default x \b,D0S version 3.3-7.0
+# error messages are printed by assembler instructions: mov si,06nn;...;int 10 (0xBEnn06;...)
+# where nn is string offset varying for different languages
+# "Invalid partition table" nn=0x8b for english version
+>>>>>(0x49.b) string Invalid\ partition\ table english
+>>>>>(0x49.b) string Ung\201ltige\ Partitionstabelle german
+>>>>>(0x49.b) string Table\ de\ partition\ invalide french
+>>>>>(0x49.b) string Tabela\ de\ parti\207ao\ inv\240lida portuguese
+>>>>>(0x49.b) string Tabla\ de\ partici\242n\ no\ v\240lida spanish
+>>>>>(0x49.b) string Tavola\ delle\ partizioni\ non\ valida italian
+>>>>>0x49 ubyte >0 at offset 0x%x
+>>>>>>(0x49.b) string >\0 "%s"
+# "Error loading operating system" nn=0xa3 for english version
+# "Fehler beim Laden des Betriebssystems" nn=0xa7 for german version
+# "Erreur en chargeant syst\212me d'exploitation" nn=0xa7 for french version
+# "Erro na inicializa\207ao do sistema operacional" nn=0xa7 for portuguese Brazilian version
+# "Error al cargar sistema operativo" nn=0xa8 for spanish version
+# "Errore durante il caricamento del sistema operativo" nn=0xae for italian version
+>>>>>0x74 ubyte >0 at offset 0x%x
+>>>>>>(0x74.b) string >\0 "%s"
+# "Missing operating system" nn=0xc2 for english version
+# "Betriebssystem fehlt" nn=0xcd for german version
+# "Syst\212me d'exploitation absent" nn=0xd2 for french version
+# "Sistema operacional nao encontrado" nn=0xd4 for portuguese Brazilian version
+# "Falta sistema operativo" nn=0xca for spanish version
+# "Sistema operativo mancante" nn=0xe2 for italian version
+>>>>>0x79 ubyte >0 at offset 0x%x
+>>>>>>(0x79.b) string >\0 "%s"
+# Microsoft Windows 95B to XP (http://thestarman.pcministry.com/asm/mbr/95BMEMBR.htm)
+# assembler instructions: push ax;pop es;push ax;pop ds;cld;mov si,7c1b
+>>8 ubequad 0x5007501ffcbe1b7c
+# assembler instructions: rep;movsb;retf;mov si,07be;mov cl,04
+>>>24 ubequad 0xf3a4cbbebe07b104 9M
+# "Invalid partition table" nn=0x10F for english version
+# "Ung\201ltige Partitionstabelle" nn=0x10F for german version
+# "Table de partition erron\202e" nn=0x10F for french version
+# "\216\257\245\340\240\346\250\256\255\255\240\357 \341\250\341\342\245\254\240 \255\245 \255\240\251\244\245\255\240" nn=0x10F for russian version
+>>>>(0x3C.b+0x0FF) string Invalid\ partition\ table english
+>>>>(0x3C.b+0x0FF) string Ung\201ltige\ Partitionstabelle german
+>>>>(0x3C.b+0x0FF) string Table\ de\ partition\ erron\202e french
+>>>>(0x3C.b+0x0FF) string \215\245\257\340\240\242\250\253\354\255\240\357\ \342\240\241\253\250\346\240 russian
+>>>>0x3C ubyte x at offset 0x%x+0xFF
+>>>>(0x3C.b+0x0FF) string >\0 "%s"
+# "Error loading operating system" nn=0x127 for english version
+# "Fehler beim Laden des Betriebssystems" nn=0x12b for german version
+# "Erreur lors du chargement du syst\212me d'exploitation" nn=0x12a for french version
+# "\216\350\250\241\252\240 \257\340\250 \247\240\243\340\343\247\252\245 \256\257\245\340\240\346\250\256\255\255\256\251 \341\250\341\342\245\254\353" nn=0x12d for russian version
+>>>>0xBD ubyte x at offset 0x1%x
+>>>>(0xBD.b+0x100) string >\0 "%s"
+# "Missing operating system" nn=0x146 for english version
+# "Betriebssystem fehlt" nn=0x151 for german version
+# "Syst\212me d'exploitation manquant" nn=0x15e for french version
+# "\216\257\245\340\240\346\250\256\255\255\240\357 \341\250\341\342\245\254\240 \255\245 \255\240\251\244\245\255\240" nn=0x156 for russian version
+>>>>0xA9 ubyte x at offset 0x1%x
+>>>>(0xA9.b+0x100) string >\0 "%s"
+# http://thestarman.pcministry.com/asm/mbr/Win2kmbr.htm
+# assembler instructions: rep;movsb;retf;mov BP,07be;mov cl,04
+>>>24 ubequad 0xf3a4cbbdbe07b104 XP
+# where xxyyzz are lower bits from offsets of error messages varying for different languages
+>>>>0x1B4 ubelong&0x00FFFFFF 0x002c4463 english
+>>>>0x1B4 ubelong&0x00FFFFFF 0x002c486e german
+# "Invalid partition table" xx=0x12C for english version
+# "Ung\201ltige Partitionstabelle" xx=0x12C for german version
+>>>>0x1b5 ubyte >0 at offset 0x1%x
+>>>>(0x1b5.b+0x100) string >\0 "%s"
+# "Error loading operating system" yy=0x144 for english version
+# "Fehler beim Laden des Betriebssystems" yy=0x148 for german version
+>>>>0x1b6 ubyte >0 at offset 0x1%x
+>>>>(0x1b6.b+0x100) string >\0 "%s"
+# "Missing operating system" zz=0x163 for english version
+# "Betriebssystem nicht vorhanden" zz=0x16e for german version
+>>>>0x1b7 ubyte >0 at offset 0x1%x
+>>>>(0x1b7.b+0x100) string >\0 "%s"
+# Microsoft Windows Vista or 7
+# assembler instructions: ..;mov ds,ax;mov si,7c00;mov di,..00
+>>8 ubequad 0xc08ed8be007cbf00
+# Microsoft Windows Vista (http://thestarman.pcministry.com/asm/mbr/VistaMBR.htm)
+# assembler instructions: jnz 0729;cmp ebx,"TCPA"
+>>>0xEC ubequad 0x753b6681fb544350 Vista
+# where xxyyzz are lower bits from offsets of error messages varying for different languages
+>>>>0x1B4 ubelong&0x00FFFFFF 0x00627a99 english
+#>>>>0x1B4 ubelong&0x00FFFFFF ? german
+# "Invalid partition table" xx=0x162 for english version
+# "Ung\201ltige Partitionstabelle" xx=0x1?? for german version
+>>>>0x1b5 ubyte >0 at offset 0x1%x
+>>>>(0x1b5.b+0x100) string >\0 "%s"
+# "Error loading operating system" yy=0x17a for english version
+# "Fehler beim Laden des Betriebssystems" yy= 0x1?? for german version
+>>>>0x1b6 ubyte >0 at offset 0x1%x
+>>>>(0x1b6.b+0x100) string >\0 "%s"
+# "Missing operating system" zz=0x199 for english version
+# "Betriebssystem nicht vorhanden" zz=0x1?? for german version
+>>>>0x1b7 ubyte >0 at offset 0x1%x
+>>>>(0x1b7.b+0x100) string >\0 "%s"
+# Microsoft Windows 7 (http://thestarman.pcministry.com/asm/mbr/W7MBR.htm)
+# assembler instructions: cmp ebx,"TCPA";cmp
+>>>0xEC ubequad 0x6681fb5443504175 Windows 7
+# where xxyyzz are lower bits from offsets of error messages varying for different languages
+>>>>0x1B4 ubelong&0x00FFFFFF 0x00637b9a english
+#>>>>0x1B4 ubelong&0x00FFFFFF ? german
+# "Invalid partition table" xx=0x163 for english version
+# "Ung\201ltige Partitionstabelle" xx=0x1?? for german version
+>>>>0x1b5 ubyte >0 at offset 0x1%x
+>>>>(0x1b5.b+0x100) string >\0 "%s"
+# "Error loading operating system" yy=0x17b for english version
+# "Fehler beim Laden des Betriebssystems" yy=0x1?? for german version
+>>>>0x1b6 ubyte >0 at offset 0x1%x
+>>>>(0x1b6.b+0x100) string >\0 "%s"
+# "Missing operating system" zz=0x19a for english version
+# "Betriebssystem nicht vorhanden" zz=0x1?? for german version
+>>>>0x1b7 ubyte >0 at offset 0x1%x
+>>>>(0x1b7.b+0x100) string >\0 "%s"
+# http://thestarman.pcministry.com/asm/mbr/Win2kmbr.htm#DiskSigs
+# http://en.wikipedia.org/wiki/MBR_disk_signature#ID
+>>0x1b8 ulelong >0 \b, disk signature 0x%-.4x
+# driveID/timestamp for Win 95B,98,98SE and ME. See http://thestarman.pcministry.com/asm/mbr/mystery.htm
+>>0xDA uleshort 0
+>>>0xDC ulelong >0 \b, created
+# physical drive number (0x80-0xFF) when the Windows wrote that byte to the drive
+>>>>0xDC ubyte x with driveID 0x%x
+# hours, minutes and seconds
+>>>>0xDf ubyte x at %x
+>>>>0xDe ubyte x \b:%x
+>>>>0xDd ubyte x \b:%x
+# special case for Microsoft MS-DOS 3.21 spanish
+# assembler instructions: cli;mov $0x30,%ax;mov %ax,%ss;mov
+>0 ubequad 0xfab830008ed0bc00
+# assembler instructions: $0x1f00,%sp;mov $0x80cb,%di;add %cl,(%bx,%si);in (%dx),%ax;mov
+>>8 ubequad 0x1fbfcb800008ed8 MS-MBR,D0S version 3.21 spanish
+# Microsoft MBR IPL end
+
+# dr-dos with some upper-, lowercase variants
+>0x9D string Invalid\ partition\ table$
+>>181 string No\ Operating\ System$
+>>>201 string Operating\ System\ load\ error$ \b, DR-DOS MBR, Version 7.01 to 7.03
+>0x9D string Invalid\ partition\ table$
+>>181 string No\ operating\ system$
+>>>201 string Operating\ system\ load\ error$ \b, DR-DOS MBR, Version 7.01 to 7.03
+>342 string Invalid\ partition\ table$
+>>366 string No\ operating\ system$
+>>>386 string Operating\ system\ load\ error$ \b, DR-DOS MBR, version 7.01 to 7.03
+>295 string NEWLDR\0
+>>302 string Bad\ PT\ $
+>>>310 string No\ OS\ $
+>>>>317 string OS\ load\ err$
+>>>>>329 string Moved\ or\ missing\ IBMBIO.LDR\n\r
+>>>>>>358 string Press\ any\ key\ to\ continue.\n\r$
+>>>>>>>387 string Copyright\ (c)\ 1984,1998
+>>>>>>>>411 string Caldera\ Inc.\0 \b, DR-DOS MBR (IBMBIO.LDR)
+#
+# tests for different MS-DOS Master Boot Records (MBR) moved and merged
+#
+#>0x145 string Default:\ F \b, FREE-DOS MBR
+#>0x14B string Default:\ F \b, FREE-DOS 1.0 MBR
+>0x145 search/7 Default:\ F \b, FREE-DOS MBR
+#>>313 string F0\ .\ .\ .
+#>>>322 string disk\ 1
+#>>>>382 string FAT3
+>64 string no\ active\ partition\ found
+>>96 string read\ error\ while\ reading\ drive \b, FREE-DOS Beta 0.9 MBR
+# Ranish Partition Manager http://www.ranish.com/part/
+>387 search/4 \0\ Error!\r
+>>378 search/7 Virus!
+>>>397 search/4 Booting\
+>>>>408 search/4 HD1/\0 \b, Ranish MBR (
+>>>>>416 string Writing\ changes... \b2.37
+>>>>>>438 ubyte x \b,0x%x dots
+>>>>>>440 ubyte >0 \b,virus check
+>>>>>>441 ubyte >0 \b,partition %c
+#2.38,2.42,2.44
+>>>>>416 string !Writing\ changes... \b
+>>>>>>418 ubyte 1 \bvirus check,
+>>>>>>419 ubyte x \b0x%x seconds
+>>>>>>420 ubyte&0x0F >0 \b,partition
+>>>>>>>420 ubyte&0x0F <5 \b %x
+>>>>>>>420 ubyte&0x0F 0Xf \b ask
+>>>>>420 ubyte x \b)
+#
+# SYSLINUX MBR moved
+# http://www.acronis.de/
+>362 string MBR\ Error\ \0\r
+>>376 string ress\ any\ key\ to\
+>>>392 string boot\ from\ floppy...\0 \b, Acronis MBR
+# added by Joerg Jenderek
+# http://www.visopsys.org/
+# http://partitionlogic.org.uk/
+>309 string No\ bootable\ partition\ found\r
+>>339 string I/O\ Error\ reading\ boot\ sector\r \b, Visopsys MBR
+>349 string No\ bootable\ partition\ found\r
+>>379 string I/O\ Error\ reading\ boot\ sector\r \b, simple Visopsys MBR
+# bootloader, bootmanager
+>0x40 string SBML
+# label with 11 characters of FAT 12 bit filesystem
+>>43 string SMART\ BTMGR
+>>>430 string SBMK\ Bad!\r \b, Smart Boot Manager
+# OEM-ID not always "SBM"
+#>>>>3 strings SBM
+>>>>6 string >\0 \b, version %s
+>382 string XOSLLOADXCF \b, eXtended Operating System Loader
+>6 string LILO \b, LInux i386 boot LOader
+>>120 string LILO \b, version 22.3.4 SuSe
+>>172 string LILO \b, version 22.5.8 Debian
+# updated by Joerg Jenderek at Oct 2008
+# variables according to grub-0.97/stage1/stage1.S or
+# http://www.gnu.org/software/grub/manual/grub.html#Embedded-data
+# usual values are marked with comments to get only informations of strange GRUB loaders
+>342 search/60 \0Geom\0
+#>0 ulelong x %x=0x009048EB , 0x2a9048EB 0
+>>0x41 ubyte <2
+>>>0x3E ubyte >2 \b; GRand Unified Bootloader
+# 0x3 for 0.5.95,0.93,0.94,0.96 0x4 for 1.90
+>>>>0x3E ubyte x \b, stage1 version 0x%x
+#If it is 0xFF, use a drive passed by BIOS
+>>>>0x40 ubyte <0xFF \b, boot drive 0x%x
+# in most case 0,1,0x2e for GRUB 0.5.95
+>>>>0x41 ubyte >0 \b, LBA flag 0x%x
+>>>>0x42 uleshort <0x8000 \b, stage2 address 0x%x
+#>>>>0x42 uleshort =0x8000 \b, stage2 address 0x%x (usual)
+>>>>0x42 uleshort >0x8000 \b, stage2 address 0x%x
+#>>>>0x44 ulelong =1 \b, 1st sector stage2 0x%x (default)
+>>>>0x44 ulelong >1 \b, 1st sector stage2 0x%x
+>>>>0x48 uleshort <0x800 \b, stage2 segment 0x%x
+#>>>>0x48 uleshort =0x800 \b, stage2 segment 0x%x (usual)
+>>>>0x48 uleshort >0x800 \b, stage2 segment 0x%x
+>>>>402 string Geom\0Hard\ Disk\0Read\0\ Error\0
+>>>>>394 string stage1 \b, GRUB version 0.5.95
+>>>>382 string Geom\0Hard\ Disk\0Read\0\ Error\0
+>>>>>376 string GRUB\ \0 \b, GRUB version 0.93 or 1.94
+>>>>383 string Geom\0Hard\ Disk\0Read\0\ Error\0
+>>>>>377 string GRUB\ \0 \b, GRUB version 0.94
+>>>>385 string Geom\0Hard\ Disk\0Read\0\ Error\0
+>>>>>379 string GRUB\ \0 \b, GRUB version 0.95 or 0.96
+>>>>391 string Geom\0Hard\ Disk\0Read\0\ Error\0
+>>>>>385 string GRUB\ \0 \b, GRUB version 0.97
+# unknown version
+>>>343 string Geom\0Read\0\ Error\0
+>>>>321 string Loading\ stage1.5 \b, GRUB version x.y
+>>>380 string Geom\0Hard\ Disk\0Read\0\ Error\0
+>>>>374 string GRUB\ \0 \b, GRUB version n.m
+# SYSLINUX bootloader moved
+>395 string chksum\0\ ERROR!\0 \b, Gujin bootloader
+# http://www.bcdwb.de/bcdw/index_e.htm
+>3 string BCDL
+>>498 string BCDL\ \ \ \ BIN \b, Bootable CD Loader (1.50Z)
+# mbr partition table entries updated by Joerg Jenderek at Sep 2013
+# skip Norton Utilities disc image data
+>3 string !IHISK
+# skip Linux style boot sector starting with assember instructions mov 0x7c0,ax;
+>>0 belong !0xb8c0078e
+# not Linux kernel
+>>>514 string !HdrS
+# not BeOS
+>>>>422 string !Be\ Boot\ Loader
+>>>>>32769 string CD001
+>>>>>>0 use cdrom
+# jump over BPB instruction implies DOS bootsector or AdvanceMAME mbr
+>>>>>0 ubelong&0xFD000000 =0xE9000000
+# AdvanceMAME mbr
+>>>>>>(1.b+2) ubequad 0xfa31c08ed88ec08e
+>>>>>>>446 use partition-table
+# mbr, Norton Utilities disc image data, or 2nd,etc. sector of x86 bootloader
+>>>>>0 ubelong&0xFD000000 !0xE9000000
+# skip FSInfosector
+>>>>>>0 string !RRaA
+# skip 3rd sector of MS x86 bootloader with assember instructions cli;MOVZX EAX,BYTE PTR [BP+10];MOV ECX,
+# http://thestarman.pcministry.com/asm/mbr/MSWIN41.htm
+>>>>>>>0 ubequad !0xfa660fb64610668b
+# skip 13rd sector of MS x86 bootloader
+>>>>>>>>0 ubequad !0x660fb64610668b4e
+# skip sector starting with DOS new line
+>>>>>>>>>0 string !\r\n
+# allowed active flag 0,80h-FFh
+>>>>>>>>>>446 ubyte 0
+>>>>>>>>>>>446 use partition-table
+>>>>>>>>>>446 ubyte >0x7F
+>>>>>>>>>>>446 use partition-table
+# TODO: test for extended bootrecord (ebr) moved and merged with mbr partition table entries
+# mbr partition table entries end
+# http://www.acronis.de/
+#FAT label=ACRONIS\ SZ
+#OEM-ID=BOOTWIZ0
+>442 string Non-system\ disk,\
+>>459 string press\ any\ key...\x7\0 \b, Acronis Startup Recovery Loader
+# updated by Joerg Jenderek at Nov 2012, Sep 2013
+# DOS names like F11.SYS or BOOTWIZ.SYS are 8 right space padded bytes+3 bytes
+# display 1 space
+>>>447 ubyte x \b
+>>>477 use DOS-filename
+#
+>185 string FDBOOT\ Version\
+>>204 string \rNo\ Systemdisk.\
+>>>220 string Booting\ from\ harddisk.\n\r
+>>>245 string Cannot\ load\ from\ harddisk.\n\r
+>>>>273 string Insert\ Systemdisk\
+>>>>>291 string and\ press\ any\ key.\n\r \b, FDBOOT harddisk Bootloader
+>>>>>>200 string >\0 \b, version %-3s
+>242 string Bootsector\ from\ C.H.\ Hochst\204
+# http://freecode.com/projects/dosfstools dosfstools-n.m/src/mkdosfs.c
+# updated by Joerg Jenderek at Nov 2012. Use search directive with offset instead of string
+# skip name "C.H. Hochstaetter" partly because it is sometimes written without umlaut
+>242 search/127 Bootsector\ from\ C.H.\ Hochst
+>>278 search/127 No\ Systemdisk.\ Booting\ from\ harddisk
+# followed by variants with point,CR-NL or NL-CR
+>>>208 search/261 Cannot\ load\ from\ harddisk.
+# followed by variants CR-NL or NL-CR
+>>>>236 search/235 Insert\ Systemdisk\ and\ press\ any\ key.
+# followed by variants with point,CR-NL or NL-CR
+>>>>>180 search/96 Disk\ formatted\ with\ WinImage\ \b, WinImage harddisk Bootloader
+# followed by string like "6.50 (c) 1993-2004 Gilles Vollant"
+>>>>>>&0 string x \b, version %-4.4s
+>(1.b+2) ubyte 0xe
+>>(1.b+3) ubyte 0x1f
+>>>(1.b+4) ubyte 0xbe
+# message offset found at (1.b+5) is 0x77 for FAT32 or 0x5b for others
+>>>>(1.b+5) ubyte&0xd3 0x53
+>>>>>(1.b+6) ubyte 0x7c
+# assembler instructions: lodsb;and al,al;jz 0xb;push si;mov ah,
+>>>>>>(1.b+7) ubyte 0xac
+>>>>>>>(1.b+8) ubyte 0x22
+>>>>>>>>(1.b+9) ubyte 0xc0
+>>>>>>>>>(1.b+10) ubyte 0x74
+>>>>>>>>>>(1.b+11) ubyte 0x0b
+>>>>>>>>>>>(1.b+12) ubyte 0x56
+>>>>>>>>>>>>(1.b+13) ubyte 0xb4 \b, mkdosfs boot message display
+# FAT1X version
+>>>>>>>>>>>>>(1.b+5) ubyte 0x5b
+>>>>>>>>>>>>>>0x5b string >\0 "%-s"
+# FAT32 version
+>>>>>>>>>>>>>(1.b+5) ubyte 0x77
+>>>>>>>>>>>>>>0x77 string >\0 "%-s"
+>214 string Please\ try\ to\ install\ FreeDOS\ \b, DOS Emulator boot message display
+#>>244 string from\ dosemu-freedos-*-bin.tgz\r
+#>>>170 string Sorry,\ could\ not\ load\ an\
+#>>>>195 string operating\ system.\r\n
+#
+>103 string This\ is\ not\ a\ bootable\ disk.\
+>>132 string Please\ insert\ a\ bootable\
+>>>157 string floppy\ and\r\n
+>>>>169 string press\ any\ key\ to\ try\ again...\r \b, FREE-DOS message display
+#
+>66 string Solaris\ Boot\ Sector
+>>99 string Incomplete\ MDBoot\ load.
+>>>89 string Version \b, Sun Solaris Bootloader
+>>>>97 byte x version %c
+#
+>408 string OS/2\ !!\ SYS01475\r\0
+>>429 string OS/2\ !!\ SYS02025\r\0
+>>>450 string OS/2\ !!\ SYS02027\r\0
+>>>469 string OS2BOOT\ \ \ \ \b, IBM OS/2 Warp bootloader
+#
+>409 string OS/2\ !!\ SYS01475\r\0
+>>430 string OS/2\ !!\ SYS02025\r\0
+>>>451 string OS/2\ !!\ SYS02027\r\0
+>>>470 string OS2BOOT\ \ \ \ \b, IBM OS/2 Warp Bootloader
+>112 string This\ disk\ is\ not\ bootable\r
+>>142 string If\ you\ wish\ to\ make\ it\ bootable
+>>>176 string run\ the\ DOS\ program\ SYS\
+>>>200 string after\ the\r
+>>>>216 string system\ has\ been\ loaded\r\n
+>>>>>242 string Please\ insert\ a\ DOS\ diskette\
+>>>>>271 string into\r\n\ the\ drive\ and\
+>>>>>>292 string strike\ any\ key...\0 \b, IBM OS/2 Warp message display
+# XP
+>430 string NTLDR\ is\ missing\xFF\r\n
+>>449 string Disk\ error\xFF\r\n
+>>>462 string Press\ any\ key\ to\ restart\r \b, Microsoft Windows XP Bootloader
+# DOS names like NTLDR,CMLDR,$LDR$ are 8 right space padded bytes+3 bytes
+>>>>417 ubyte&0xDF >0
+>>>>>417 string x %-.5s
+>>>>>>422 ubyte&0xDF >0
+>>>>>>>422 string x \b%-.3s
+>>>>>425 ubyte&0xDF >0
+>>>>>>425 string >\ \b.%-.3s
+#
+>>>>371 ubyte >0x20
+>>>>>368 ubyte&0xDF >0
+>>>>>>368 string x %-.5s
+>>>>>>>373 ubyte&0xDF >0
+>>>>>>>>373 string x \b%-.3s
+>>>>>>376 ubyte&0xDF >0
+>>>>>>>376 string x \b.%-.3s
+#
+>430 string NTLDR\ nicht\ gefunden\xFF\r\n
+>>453 string Datentr\204gerfehler\xFF\r\n
+>>>473 string Neustart\ mit\ beliebiger\ Taste\r \b, Microsoft Windows XP Bootloader (german)
+>>>>417 ubyte&0xDF >0
+>>>>>417 string x %-.5s
+>>>>>>422 ubyte&0xDF >0
+>>>>>>>422 string x \b%-.3s
+>>>>>425 ubyte&0xDF >0
+>>>>>>425 string >\ \b.%-.3s
+# offset variant
+>>>>379 string \0
+>>>>>368 ubyte&0xDF >0
+>>>>>>368 string x %-.5s
+>>>>>>>373 ubyte&0xDF >0
+>>>>>>>>373 string x \b%-.3s
+#
+>430 string NTLDR\ fehlt\xFF\r\n
+>>444 string Datentr\204gerfehler\xFF\r\n
+>>>464 string Neustart\ mit\ beliebiger\ Taste\r \b, Microsoft Windows XP Bootloader (2.german)
+>>>>417 ubyte&0xDF >0
+>>>>>417 string x %-.5s
+>>>>>>422 ubyte&0xDF >0
+>>>>>>>422 string x \b%-.3s
+>>>>>425 ubyte&0xDF >0
+>>>>>>425 string >\ \b.%-.3s
+# variant
+>>>>371 ubyte >0x20
+>>>>>368 ubyte&0xDF >0
+>>>>>>368 string x %-.5s
+>>>>>>>373 ubyte&0xDF >0
+>>>>>>>>373 string x \b%-.3s
+>>>>>>376 ubyte&0xDF >0
+>>>>>>>376 string x \b.%-.3s
+#
+>430 string NTLDR\ fehlt\xFF\r\n
+>>444 string Medienfehler\xFF\r\n
+>>>459 string Neustart:\ Taste\ dr\201cken\r \b, Microsoft Windows XP Bootloader (3.german)
+>>>>371 ubyte >0x20
+>>>>>368 ubyte&0xDF >0
+>>>>>>368 string x %-.5s
+>>>>>>>373 ubyte&0xDF >0
+>>>>>>>>373 string x \b%-.3s
+>>>>>>376 ubyte&0xDF >0
+>>>>>>>376 string x \b.%-.3s
+# variant
+>>>>417 ubyte&0xDF >0
+>>>>>417 string x %-.5s
+>>>>>>422 ubyte&0xDF >0
+>>>>>>>422 string x \b%-.3s
+>>>>>425 ubyte&0xDF >0
+>>>>>>425 string >\ \b.%-.3s
+#
+>430 string Datentr\204ger\ entfernen\xFF\r\n
+>>454 string Medienfehler\xFF\r\n
+>>>469 string Neustart:\ Taste\ dr\201cken\r \b, Microsoft Windows XP Bootloader (4.german)
+>>>>379 string \0
+>>>>>368 ubyte&0xDF >0
+>>>>>>368 string x %-.5s
+>>>>>>>373 ubyte&0xDF >0
+>>>>>>>>373 string x \b%-.3s
+>>>>>>376 ubyte&0xDF >0
+>>>>>>>376 string x \b.%-.3s
+# variant
+>>>>417 ubyte&0xDF >0
+>>>>>417 string x %-.5s
+>>>>>>422 ubyte&0xDF >0
+>>>>>>>422 string x \b%-.3s
+>>>>>425 ubyte&0xDF >0
+>>>>>>425 string >\ \b.%-.3s
+#
+
+#>3 string NTFS\ \ \ \
+>389 string Fehler\ beim\ Lesen\
+>>407 string des\ Datentr\204gers
+>>>426 string NTLDR\ fehlt
+>>>>440 string NTLDR\ ist\ komprimiert
+>>>>>464 string Neustart\ mit\ Strg+Alt+Entf\r \b, Microsoft Windows XP Bootloader NTFS (german)
+#>3 string NTFS\ \ \ \
+>313 string A\ disk\ read\ error\ occurred.\r
+>>345 string A\ kernel\ file\ is\ missing\
+>>>370 string from\ the\ disk.\r
+>>>>484 string NTLDR\ is\ compressed
+>>>>>429 string Insert\ a\ system\ diskette\
+>>>>>>454 string and\ restart\r\nthe\ system.\r \b, Microsoft Windows XP Bootloader NTFS
+# DOS loader variants different languages,offsets
+>472 ubyte&0xDF >0
+>>389 string Invalid\ system\ disk\xFF\r\n
+>>>411 string Disk\ I/O\ error
+>>>>428 string Replace\ the\ disk,\ and\
+>>>>>455 string press\ any\ key \b, Microsoft Windows 98 Bootloader
+#IO.SYS
+>>>>>>472 ubyte&0xDF >0
+>>>>>>>472 string x \b %-.2s
+>>>>>>>>474 ubyte&0xDF >0
+>>>>>>>>>474 string x \b%-.5s
+>>>>>>>>>>479 ubyte&0xDF >0
+>>>>>>>>>>>479 string x \b%-.1s
+>>>>>>>480 ubyte&0xDF >0
+>>>>>>>>480 string x \b.%-.3s
+#MSDOS.SYS
+>>>>>>>483 ubyte&0xDF >0 \b+
+>>>>>>>>483 string x \b%-.5s
+>>>>>>>>>488 ubyte&0xDF >0
+>>>>>>>>>>488 string x \b%-.3s
+>>>>>>>>491 ubyte&0xDF >0
+>>>>>>>>>491 string x \b.%-.3s
+#
+>>390 string Invalid\ system\ disk\xFF\r\n
+>>>412 string Disk\ I/O\ error\xFF\r\n
+>>>>429 string Replace\ the\ disk,\ and\
+>>>>>451 string then\ press\ any\ key\r \b, Microsoft Windows 98 Bootloader
+>>388 string Ungueltiges\ System\ \xFF\r\n
+>>>410 string E/A-Fehler\ \ \ \ \xFF\r\n
+>>>>427 string Datentraeger\ wechseln\ und\
+>>>>>453 string Taste\ druecken\r \b, Microsoft Windows 95/98/ME Bootloader (german)
+#WINBOOT.SYS only not spaces (0xDF)
+>>>>>>497 ubyte&0xDF >0
+>>>>>>>497 string x %-.5s
+>>>>>>>>502 ubyte&0xDF >0
+>>>>>>>>>502 string x \b%-.1s
+>>>>>>>>>>503 ubyte&0xDF >0
+>>>>>>>>>>>503 string x \b%-.1s
+>>>>>>>>>>>>504 ubyte&0xDF >0
+>>>>>>>>>>>>>504 string x \b%-.1s
+>>>>>>505 ubyte&0xDF >0
+>>>>>>>505 string x \b.%-.3s
+#IO.SYS
+>>>>>>472 ubyte&0xDF >0 or
+>>>>>>>472 string x \b %-.2s
+>>>>>>>>474 ubyte&0xDF >0
+>>>>>>>>>474 string x \b%-.5s
+>>>>>>>>>>479 ubyte&0xDF >0
+>>>>>>>>>>>479 string x \b%-.1s
+>>>>>>>480 ubyte&0xDF >0
+>>>>>>>>480 string x \b.%-.3s
+#MSDOS.SYS
+>>>>>>>483 ubyte&0xDF >0 \b+
+>>>>>>>>483 string x \b%-.5s
+>>>>>>>>>488 ubyte&0xDF >0
+>>>>>>>>>>488 string x \b%-.3s
+>>>>>>>>491 ubyte&0xDF >0
+>>>>>>>>>491 string x \b.%-.3s
+#
+>>390 string Ungueltiges\ System\ \xFF\r\n
+>>>412 string E/A-Fehler\ \ \ \ \xFF\r\n
+>>>>429 string Datentraeger\ wechseln\ und\
+>>>>>455 string Taste\ druecken\r \b, Microsoft Windows 95/98/ME Bootloader (German)
+#WINBOOT.SYS only not spaces (0xDF)
+>>>>>>497 ubyte&0xDF >0
+>>>>>>>497 string x %-.7s
+>>>>>>>>504 ubyte&0xDF >0
+>>>>>>>>>504 string x \b%-.1s
+>>>>>>505 ubyte&0xDF >0
+>>>>>>>505 string x \b.%-.3s
+#IO.SYS
+>>>>>>472 ubyte&0xDF >0 or
+>>>>>>>472 string x \b %-.2s
+>>>>>>>>474 ubyte&0xDF >0
+>>>>>>>>>474 string x \b%-.6s
+>>>>>>>480 ubyte&0xDF >0
+>>>>>>>>480 string x \b.%-.3s
+#MSDOS.SYS
+>>>>>>>483 ubyte&0xDF >0 \b+
+>>>>>>>>483 string x \b%-.5s
+>>>>>>>>>488 ubyte&0xDF >0
+>>>>>>>>>>488 string x \b%-.3s
+>>>>>>>>491 ubyte&0xDF >0
+>>>>>>>>>491 string x \b.%-.3s
+#
+>>389 string Ungueltiges\ System\ \xFF\r\n
+>>>411 string E/A-Fehler\ \ \ \ \xFF\r\n
+>>>>428 string Datentraeger\ wechseln\ und\
+>>>>>454 string Taste\ druecken\r \b, Microsoft Windows 95/98/ME Bootloader (GERMAN)
+# DOS names like IO.SYS,WINBOOT.SYS,MSDOS.SYS,WINBOOT.INI are 8 right space padded bytes+3 bytes
+>>>>>>472 string x %-.2s
+>>>>>>>474 ubyte&0xDF >0
+>>>>>>>>474 string x \b%-.5s
+>>>>>>>>479 ubyte&0xDF >0
+>>>>>>>>>479 string x \b%-.1s
+>>>>>>480 ubyte&0xDF >0
+>>>>>>>480 string x \b.%-.3s
+>>>>>>483 ubyte&0xDF >0 \b+
+>>>>>>>483 string x \b%-.5s
+>>>>>>>488 ubyte&0xDF >0
+>>>>>>>>488 string x \b%-.2s
+>>>>>>>>490 ubyte&0xDF >0
+>>>>>>>>>490 string x \b%-.1s
+>>>>>>>491 ubyte&0xDF >0
+>>>>>>>>491 string x \b.%-.3s
+>479 ubyte&0xDF >0
+>>416 string Kein\ System\ oder\
+>>>433 string Laufwerksfehler
+>>>>450 string Wechseln\ und\ Taste\ dr\201cken \b, Microsoft DOS Bootloader (german)
+#IO.SYS
+>>>>>479 string x \b %-.2s
+>>>>>>481 ubyte&0xDF >0
+>>>>>>>481 string x \b%-.6s
+>>>>>487 ubyte&0xDF >0
+>>>>>>487 string x \b.%-.3s
+#MSDOS.SYS
+>>>>>>490 ubyte&0xDF >0 \b+
+>>>>>>>490 string x \b%-.5s
+>>>>>>>>495 ubyte&0xDF >0
+>>>>>>>>>495 string x \b%-.3s
+>>>>>>>498 ubyte&0xDF >0
+>>>>>>>>498 string x \b.%-.3s
+#
+>376 search/41 Non-System\ disk\ or\
+>>395 search/41 disk\ error\r
+>>>407 search/41 Replace\ and\
+>>>>419 search/41 press\ \b,
+>>>>419 search/41 strike\ \b, old
+>>>>426 search/41 any\ key\ when\ ready\r MS or PC-DOS bootloader
+#449 Disk\ Boot\ failure\r MS 3.21
+#466 Boot\ Failure\r MS 3.30
+>>>>>468 search/18 \0
+#IO.SYS,IBMBIO.COM
+>>>>>>&0 string x \b %-.2s
+>>>>>>>&-20 ubyte&0xDF >0
+>>>>>>>>&-1 string x \b%-.4s
+>>>>>>>>>&-16 ubyte&0xDF >0
+>>>>>>>>>>&-1 string x \b%-.2s
+>>>>>>&8 ubyte&0xDF >0 \b.
+>>>>>>>&-1 string x \b%-.3s
+#MSDOS.SYS,IBMDOS.COM
+>>>>>>&11 ubyte&0xDF >0 \b+
+>>>>>>>&-1 string x \b%-.5s
+>>>>>>>>&-6 ubyte&0xDF >0
+>>>>>>>>>&-1 string x \b%-.1s
+>>>>>>>>>>&-5 ubyte&0xDF >0
+>>>>>>>>>>>&-1 string x \b%-.2s
+>>>>>>>&7 ubyte&0xDF >0 \b.
+>>>>>>>>&-1 string x \b%-.3s
+>441 string Cannot\ load\ from\ harddisk.\n\r
+>>469 string Insert\ Systemdisk\
+>>>487 string and\ press\ any\ key.\n\r \b, MS (2.11) DOS bootloader
+#>43 string \224R-LOADER\ \ SYS =label
+>54 string SYS
+>>324 string VASKK
+>>>495 string NEWLDR\0 \b, DR-DOS Bootloader (LOADER.SYS)
+#
+>98 string Press\ a\ key\ to\ retry\0\r
+>>120 string Cannot\ find\ file\ \0\r
+>>>139 string Disk\ read\ error\0\r
+>>>>156 string Loading\ ...\0 \b, DR-DOS (3.41) Bootloader
+#DRBIOS.SYS
+>>>>>44 ubyte&0xDF >0
+>>>>>>44 string x \b %-.6s
+>>>>>>>50 ubyte&0xDF >0
+>>>>>>>>50 string x \b%-.2s
+>>>>>>52 ubyte&0xDF >0
+>>>>>>>52 string x \b.%-.3s
+#
+>70 string IBMBIO\ \ COM
+>>472 string Cannot\ load\ DOS!\
+>>>489 string Any\ key\ to\ retry \b, DR-DOS Bootloader
+>>471 string Cannot\ load\ DOS\
+>>487 string press\ key\ to\ retry \b, Open-DOS Bootloader
+#??
+>444 string KERNEL\ \ SYS
+>>314 string BOOT\ error! \b, FREE-DOS Bootloader
+>499 string KERNEL\ \ SYS
+>>305 string BOOT\ err!\0 \b, Free-DOS Bootloader
+>449 string KERNEL\ \ SYS
+>>319 string BOOT\ error! \b, FREE-DOS 0.5 Bootloader
+#
+>449 string Loading\ FreeDOS
+>>0x1AF ulelong >0 \b, FREE-DOS 0.95,1.0 Bootloader
+>>>497 ubyte&0xDF >0
+>>>>497 string x \b %-.6s
+>>>>>503 ubyte&0xDF >0
+>>>>>>503 string x \b%-.1s
+>>>>>>>504 ubyte&0xDF >0
+>>>>>>>>504 string x \b%-.1s
+>>>>505 ubyte&0xDF >0
+>>>>>505 string x \b.%-.3s
+#
+>331 string Error!.0 \b, FREE-DOS 1.0 bootloader
+#
+>125 string Loading\ FreeDOS...\r
+>>311 string BOOT\ error!\r \b, FREE-DOS bootloader
+>>>441 ubyte&0xDF >0
+>>>>441 string x \b %-.6s
+>>>>>447 ubyte&0xDF >0
+>>>>>>447 string x \b%-.1s
+>>>>>>>448 ubyte&0xDF >0
+>>>>>>>>448 string x \b%-.1s
+>>>>449 ubyte&0xDF >0
+>>>>>449 string x \b.%-.3s
+>124 string FreeDOS\0
+>>331 string \ err\0 \b, FREE-DOS BETa 0.9 Bootloader
+# DOS names like KERNEL.SYS,KERNEL16.SYS,KERNEL32.SYS,METAKERN.SYS are 8 right space padded bytes+3 bytes
+>>>497 ubyte&0xDF >0
+>>>>497 string x \b %-.6s
+>>>>>503 ubyte&0xDF >0
+>>>>>>503 string x \b%-.1s
+>>>>>>>504 ubyte&0xDF >0
+>>>>>>>>504 string x \b%-.1s
+>>>>505 ubyte&0xDF >0
+>>>>>505 string x \b.%-.3s
+>>333 string \ err\0 \b, FREE-DOS BEta 0.9 Bootloader
+>>>497 ubyte&0xDF >0
+>>>>497 string x \b %-.6s
+>>>>>503 ubyte&0xDF >0
+>>>>>>503 string x \b%-.1s
+>>>>>>>504 ubyte&0xDF >0
+>>>>>>>>504 string x \b%-.1s
+>>>>505 ubyte&0xDF >0
+>>>>>505 string x \b.%-.3s
+>>334 string \ err\0 \b, FREE-DOS Beta 0.9 Bootloader
+>>>497 ubyte&0xDF >0
+>>>>497 string x \b %-.6s
+>>>>>503 ubyte&0xDF >0
+>>>>>>503 string x \b%-.1s
+>>>>>>>504 ubyte&0xDF >0
+>>>>>>>>504 string x \b%-.1s
+>>>>505 ubyte&0xDF >0
+>>>>>505 string x \b.%-.3s
+>336 string Error!\
+>>343 string Hit\ a\ key\ to\ reboot. \b, FREE-DOS Beta 0.9sr1 Bootloader
+>>>497 ubyte&0xDF >0
+>>>>497 string x \b %-.6s
+>>>>>503 ubyte&0xDF >0
+>>>>>>503 string x \b%-.1s
+>>>>>>>504 ubyte&0xDF >0
+>>>>>>>>504 string x \b%-.1s
+>>>>505 ubyte&0xDF >0
+>>>>>505 string x \b.%-.3s
+# added by Joerg Jenderek
+# http://www.visopsys.org/
+# http://partitionlogic.org.uk/
+# OEM-ID=Visopsys
+>478 ulelong 0
+>>(1.b+326) string I/O\ Error\ reading\
+>>>(1.b+344) string Visopsys\ loader\r
+>>>>(1.b+361) string Press\ any\ key\ to\ continue.\r \b, Visopsys loader
+# http://alexfru.chat.ru/epm.html#bootprog
+>494 ubyte >0x4D
+>>495 string >E
+>>>495 string <S
+#OEM-ID is not reliable
+>>>>3 string BootProg
+# It just looks for a program file name at the root directory
+# and loads corresponding file with following execution.
+# DOS names like STARTUP.BIN,STARTUPC.COM,STARTUPE.EXE are 8 right space padded bytes+3 bytes
+>>>>499 ubyte&0xDF >0 \b, COM/EXE Bootloader
+>>>>>499 use DOS-filename
+#If the boot sector fails to read any other sector,
+#it prints a very short message ("RE") to the screen and hangs the computer.
+#If the boot sector fails to find needed program in the root directory,
+#it also hangs with another message ("NF").
+>>>>>492 string RENF \b, FAT (12 bit)
+>>>>>495 string RENF \b, FAT (16 bit)
+#If the boot sector fails to read any other sector,
+#it prints a very short message ("RE") to the screen and hangs the computer.
+# x86 bootloader end
+
+# added by Joerg Jenderek at Feb 2013 according to http://thestarman.pcministry.com/asm/mbr/MSWIN41.htm#FSINFO
+# and http://en.wikipedia.org/wiki/File_Allocation_Table#FS_Information_Sector
+>0 string RRaA
+>>0x1E4 string rrAa \b, FSInfosector
+#>>0x1FC uleshort =0 SHOULD BE ZERO
+>>>0x1E8 ulelong <0xffffffff \b, %u free clusters
+>>>0x1EC ulelong <0xffffffff \b, last allocated cluster %u
+
+# updated by Joerg Jenderek at Sep 2007
+>3 ubyte 0
+#no active flag
+>>446 ubyte 0
+# partition 1 not empty
+>>>450 ubyte >0
+# partitions 3,4 empty
+>>>>482 ubyte 0
+>>>>>498 ubyte 0
+# partition 2 ID=0,5,15
+>>>>>>466 ubyte <0x10
+>>>>>>>466 ubyte 0x05 \b, extended partition table
+>>>>>>>466 ubyte 0x0F \b, extended partition table (LBA)
+>>>>>>>466 ubyte 0x0 \b, extended partition table (last)
+
+# DOS x86 sector separated and moved from "DOS/MBR boot sector" by Joerg Jenderek at May 2011
+
+>0x200 lelong 0x82564557 \b, BSD disklabel
+
+# by Joerg Jenderek at Apr 2013
+# Print the DOS filenames from directory entry form with 8 right space padded bytes + 3 bytes for extension
+# like IO.SYS. MSDOS.SYS , KERNEL.SYS , DRBIO.SYS
+0 name DOS-filename
+# space=0x20 (00100000b) means empty
+>0 ubyte&0xDF >0
+>>0 ubyte x \b%c
+>>>1 ubyte&0xDF >0
+>>>>1 ubyte x \b%c
+>>>>>2 ubyte&0xDF >0
+>>>>>>2 ubyte x \b%c
+>>>>>>>3 ubyte&0xDF >0
+>>>>>>>>3 ubyte x \b%c
+>>>>>>>>>4 ubyte&0xDF >0
+>>>>>>>>>>4 ubyte x \b%c
+>>>>>>>>>>>5 ubyte&0xDF >0
+>>>>>>>>>>>>5 ubyte x \b%c
+>>>>>>>>>>>>>6 ubyte&0xDF >0
+>>>>>>>>>>>>>>6 ubyte x \b%c
+>>>>>>>>>>>>>>>7 ubyte&0xDF >0
+>>>>>>>>>>>>>>>>7 ubyte x \b%c
+# DOS filename extension
+>>8 ubyte&0xDF >0 \b.
+>>>8 ubyte x \b%c
+>>>>9 ubyte&0xDF >0
+>>>>>9 ubyte x \b%c
+>>>>>>10 ubyte&0xDF >0
+>>>>>>>10 ubyte x \b%c
+# Print 2 following DOS filenames from directory entry form
+# like IO.SYS+MSDOS.SYS or ibmbio.com+ibmdos.com
+0 name 2xDOS-filename
+# display 1 space
+>0 ubyte x \b
+>0 use DOS-filename
+>11 ubyte x \b+
+>11 use DOS-filename
+
+# http://en.wikipedia.org/wiki/Master_boot_record#PTE
+# display standard partition table
+0 name partition-table
+#>0 ubyte x PARTITION-TABLE
+# test and display 1st til 4th partition table entry
+>0 use partition-entry-test
+>16 use partition-entry-test
+>32 use partition-entry-test
+>48 use partition-entry-test
+# test for entry of partition table
+0 name partition-entry-test
+# partition type ID > 0
+>4 ubyte >0
+# active flag 0
+>>0 ubyte 0
+>>>0 use partition-entry
+# active flag 0x80, 0x81, ...
+>>0 ubyte >0x7F
+>>>0 use partition-entry
+# Print entry of partition table
+0 name partition-entry
+# partition type ID > 0
+>4 ubyte >0 \b; partition
+>>64 leshort 0xAA55 1
+>>48 leshort 0xAA55 2
+>>32 leshort 0xAA55 3
+>>16 leshort 0xAA55 4
+>>4 ubyte x : ID=0x%x
+>>0 ubyte&0x80 0x80 \b, active
+>>0 ubyte >0x80 0x%x
+>>1 ubyte x \b, start-CHS (
+>>1 use partition-chs
+>>5 ubyte x \b), end-CHS (
+>>5 use partition-chs
+>>8 ulelong x \b), startsector %u
+>>12 ulelong x \b, %u sectors
+# Print cylinder,head,sector (CHS) of partition entry
+0 name partition-chs
+# cylinder
+>1 ubyte x \b0x
+>1 ubyte&0xC0 0x40 \b1
+>1 ubyte&0xC0 0x80 \b2
+>1 ubyte&0xC0 0xC0 \b3
+>2 ubyte x \b%x
+# head
+>0 ubyte x \b,%u
+# sector
+>1 ubyte&0x3F x \b,%u
+
+# FATX
+0 string FATX FATX filesystem data
+
+# romfs filesystems - Juan Cespedes <cespedes@debian.org>
+0 string -rom1fs- romfs filesystem, version 1
+>8 belong x %d bytes,
+>16 string x named %s.
+
+# netboot image - Juan Cespedes <cespedes@debian.org>
+0 lelong 0x1b031336L Netboot image,
+>4 lelong&0xFFFFFF00 0
+>>4 lelong&0x100 0x000 mode 2
+>>4 lelong&0x100 0x100 mode 3
+>4 lelong&0xFFFFFF00 !0 unknown mode
+
+0x18b string OS/2 OS/2 Boot Manager
+
+# updated by Joerg Jenderek at Oct 2008 and Sep 2012
+# http://syslinux.zytor.com/iso.php
+# tested with versions 1.47,1.48,1.49,1.50,1.62,1.76,2.00,2.10;3.00,3.11,3.31,;3.70,3.71,3.73,3.75,3.80,3.82,3.84,3.86,4.01,4.03 and 4.05
+# assembler instructions: cli;jmp 0:7Cyy (yy=0x40,0x5e,0x6c,0x6e,0x77);nop;nop
+0 ulequad&0x909000007cc0eafa 0x909000007c40eafa
+>631 search/689 ISOLINUX\ isolinux Loader
+>>&0 string x (version %-4.4s)
+# http://syslinux.zytor.com/pxe.php
+# assembler instructions: jmp 7C05
+0 ulelong 0x007c05ea pxelinux loader (version 2.13 or older)
+# assembler instructions: pushfd;pushad
+0 ulelong 0x60669c66 pxelinux loader
+# assembler instructions: jmp 05
+0 ulelong 0xc00005ea pxelinux loader (version 3.70 or newer)
+# http://syslinux.zytor.com/wiki/index.php/SYSLINUX
+0 string LDLINUX\ SYS\ SYSLINUX loader
+>12 string x (older version %-4.4s)
+0 string \r\nSYSLINUX\ SYSLINUX loader
+>11 string x (version %-4.4s)
+# syslinux updated and separated from "DOS/MBR boot sector" by Joerg Jenderek at Sep 2012
+# assembler instructions: jmp yy (yy=0x3c,0x58);nop;"SYSLINUX"
+0 ulelong&0x80909bEB 0x009018EB
+# OEM-ID not always "SYSLINUX"
+>434 search/47 Boot\ failed
+# followed by \r\n\0 or :\
+>>482 search/132 \0LDLINUX\ SYS Syslinux bootloader (version 2.13 or older)
+>>1 ubyte 0x58 Syslinux bootloader (version 3.0-3.9)
+>459 search/30 Boot\ error\r\n\0
+>>1 ubyte 0x58 Syslinux bootloader (version 3.10 or newer)
+# SYSLINUX MBR updated and separated from "DOS/MBR boot sector" by Joerg Jenderek at Sep 2012
+# assembler instructions: mov di,0600h;mov cx,0100h
+16 search/4 \xbf\x00\x06\xb9\x00\x01
+# to display SYSLINUX MBR (36) before old DOS/MBR boot sector one with partition table (strength=50+21)
+!:strength +36
+>94 search/249 Missing\ operating\ system
+# followed by \r for versions older 3.35 , .\r for versions newer 3.52 and point for other
+# skip Ranish MBR
+>>408 search/4 HD1/\0
+>>408 default x
+>>>250 search/118 \0Operating\ system\ load SYSLINUX MBR
+# followed by "ing " or space
+>>>>292 search/98 error
+>>>>>&0 string \r (version 3.35 or older)
+>>>>>&0 string .\r (version 3.52 or newer)
+>>>>>&0 default x (version 3.36-3.51 )
+>368 search/106 \0Disk\ error\ on\ boot\r\n SYSLINUX GPT-MBR
+>>156 search/10 \0Boot\ partition\ not\ found\r\n
+>>>270 search/10 \0OS\ not\ bootable\r\n (version 3.86 or older)
+>>174 search/10 \0Missing\ OS\r\n
+>>>189 search/10 \0Multiple\ active\ partitions\r\n (version 4.00 or newer)
+# SYSLINUX END
+
+# NetBSD mbr variants (master-boot-code version 1.22) added by Joerg Jenderek at Nov 2012
+# assembler instructions: xor ax,ax;mov ax,ss;mov sp,0x7c00;mov ax,
+0 ubequad 0x31c08ed0bc007c8e
+# mbr_bootsel magic before partition table not reliable with small ipl fragments
+#>444 uleshort 0xb5e1
+>0004 uleshort x
+# ERRorTeXT
+>>181 search/166 Error\ \0\r\n NetBSD mbr
+# NT Drive Serial Number http://thestarman.pcministry.com/asm/mbr/Win2kmbr.htm#DS
+>>>0x1B8 ubelong >0 \b,Serial 0x%-.8x
+# BOOTSEL definitions contains assembler instructions: int 0x13;pop dx;push dx;push dx
+>>>0xbb search/71 \xcd\x13\x5a\x52\x52 \b,bootselector
+# BOOT_EXTENDED definitions contains assembler instructions:
+# xchg ecx,edx;addl ecx,edx;movw lba_info,si;movb 0x42,ah;pop dx;push dx;int 0x13
+>>>0x96 search/1 \x66\x87\xca\x66\x01\xca\x66\x89\x16\x3a\x07\xbe\x32\x07\xb4\x42\x5a\x52\xcd\x13 \b,boot extended
+# COM_PORT_VAL definitions contains assembler instructions: outb al,dx;add 5,dl;inb %dx;test 0x40,al
+>>>0x130 search/55 \xee\x80\xc2\x05\xec\xa8\x40 \b,serial IO
+# not TERSE_ERROR
+>>>196 search/106 No\ active\ partition\0
+>>>>&0 string Disk\ read\ error\0
+>>>>>&0 string No\ operating\ system\0 \b,verbose
+# not NO_CHS definitions contains assembler instructions: pop dx;push dx;movb $8,ah;int0x13
+>>>0x7d search/7 \x5a\x52\xb4\x08\xcd\x13 \b,CHS
+# not NO_LBA_CHECK definitions contains assembler instructions: movw 0x55aa,bx;movb 0x41,ah;pop dx;push dx;int 0x13
+>>>0xa4 search/84 \xbb\xaa\x55\xb4\x41\x5a\x52\xcd\x13 \b,LBA-check
+# assembler instructions: movw nametab,bx
+>>>0x26 search/21 \xBB\x94\x07
+# not NO_BANNER definitions contains assembler instructions: mov banner,si;call message_crlf
+>>>>&-9 ubequad&0xBE00f0E800febb94 0xBE0000E80000bb94
+>>>>>181 search/166 Error\ \0
+# "a: disk" , "Fn: diskn" or "NetBSD MBR boot"
+>>>>>>&3 string x \b,"%s"
+>>>446 use partition-table
+# Andrea Mazzoleni AdvanceCD mbr loader of http://advancemame.sourceforge.net/boot-readme.html
+# added by Joerg Jenderek at Nov 2012 for versions 1.3 - 1.4
+# assembler instructions: jmp short 0x58;nop;ASCII
+0 ubequad&0xeb58908000000000 0xeb58900000000000
+# assembler instructions: cli;xor ax,ax;mov ds,ax;mov es,ax;mov ss,
+>(1.b+2) ubequad 0xfa31c08ed88ec08e
+# Error messages at end of code
+>>376 string No\ operating\ system\r\n\0
+>>>398 string Disk\ error\r\n\0FDD\0HDD\0
+>>>>419 string \ EBIOS\r\n\0 AdvanceMAME mbr
+
+# Neil Turton mbr loader variant of http://www.chiark.greenend.org.uk/~neilt/mbr/
+# added by Joerg Jenderek at Mar 2011 for versions 1.0.0 - 1.1.11
+# for 1st version assembler instructions: cld;xor ax,ax;mov DS,ax;MOV ES,AX;mov SI,
+# or cld;xor ax,ax;mov SS,ax;XOR SP,SP;mov DS,
+0 ulequad&0xcE1b40D48EC031FC 0x8E0000D08EC031FC
+# pointer to the data starting with Neil Turton signature string
+>(0x1BC.s) string NDTmbr
+>>&-14 string 1234F\0 Turton mbr (
+# parameters also viewed by install-mbr --list
+>>>(0x1BC.s+7) ubyte x \b%u<=
+>>>(0x1BC.s+9) ubyte x \bVersion<=%u
+#>>>(0x1BC.s+8) ubyte x asm_flag_%x
+>>>(0x1BC.s+8) ubyte&1 1 \b,Y2K-Fix
+# variant used by testdisk of http://www.cgsecurity.org/wiki/Menu_MBRCode
+>>>(0x1BC.s+8) ubyte&2 2 \b,TestDisk
+#0x1~1,..,0x8~4,0x10~F,0x80~A enabled
+#>>>(0x1BC.s+10) ubyte x \b,flags 0x%x
+#0x0~1,0x1~2,...,0x3~4,0x4~F,0x7~D default boot
+#>>>(0x1BC.s+11) ubyte x \b,cfg_def 0x%x
+# for older versions
+>>>(0x1BC.s+9) ubyte <2
+#>>>>(0x1BC.s+12) ubyte 18 \b,%hhu/18 seconds
+>>>>(0x1BC.s+12) ubyte !18 \b,%u/18 seconds
+# floppy A: or B:
+>>>>(0x1BC.s+13) ubyte <2 \b,floppy 0x%x
+>>>>(0x1BC.s+13) ubyte >1
+# 1st hard disc
+#>>>>>(0x1BC.s+13) ubyte 0x80 \b,drive 0x%x
+# not 1st hard disc
+>>>>>(0x1BC.s+13) ubyte !0x80 \b,drive 0x%x
+# for version >= 2 maximal timeout can be 65534
+>>>(0x1BC.s+9) ubyte >1
+#>>>>(0x1BC.s+12) uleshort 18 \b,%u/18 seconds
+>>>>(0x1BC.s+12) uleshort !18 \b,%u/18 seconds
+# floppy A: or B:
+>>>>(0x1BC.s+14) ubyte <2 \b,floppy 0x%x
+>>>>(0x1BC.s+14) ubyte >1
+# 1st hard disc
+#>>>>>(0x1BC.s+14) ubyte 0x80 \b,drive 0x%x
+# not 1st hard disc
+>>>>>(0x1BC.s+14) ubyte !0x80 \b,drive 0x%x
+>>>0 ubyte x \b)
+
+# added by Joerg Jenderek
+# In the second sector (+0x200) are variables according to grub-0.97/stage2/asm.S or
+# grub-1.94/kern/i386/pc/startup.S
+# http://www.gnu.org/software/grub/manual/grub.html#Embedded-data
+# usual values are marked with comments to get only informations of strange GRUB loaders
+0x200 uleshort 0x70EA
+# found only version 3.{1,2}
+>0x206 ubeshort >0x0300
+# GRUB version (0.5.)95,0.93,0.94,0.96,0.97 > "00"
+>>0x212 ubyte >0x29
+>>>0x213 ubyte >0x29
+# not iso9660_stage1_5
+#>>>0 ulelong&0x00BE5652 0x00BE5652
+>>>>0x213 ubyte >0x29 GRand Unified Bootloader
+# config_file for stage1_5 is 0xffffffff + default "/boot/grub/stage2"
+>>>>0x217 ubyte 0xFF stage1_5
+>>>>0x217 ubyte <0xFF stage2
+>>>>0x206 ubyte x \b version %u
+>>>>0x207 ubyte x \b.%u
+# module_size for 1.94
+>>>>0x208 ulelong <0xffffff \b, installed partition %u
+#>>>>0x208 ulelong =0xffffff \b, %lu (default)
+>>>>0x208 ulelong >0xffffff \b, installed partition %u
+# GRUB 0.5.95 unofficial
+>>>>0x20C ulelong&0x2E300000 0x2E300000
+# 0=stage2 1=ffs 2=e2fs 3=fat 4=minix 5=reiserfs
+>>>>>0x20C ubyte x \b, identifier 0x%x
+#>>>>>0x20D ubyte =0 \b, LBA flag 0x%x (default)
+>>>>>0x20D ubyte >0 \b, LBA flag 0x%x
+# GRUB version as string
+>>>>>0x20E string >\0 \b, GRUB version %-s
+# for stage1_5 is 0xffffffff + config_file "/boot/grub/stage2" default
+>>>>>>0x215 ulong 0xffffffff
+>>>>>>>0x219 string >\0 \b, configuration file %-s
+>>>>>>0x215 ulong !0xffffffff
+>>>>>>>0x215 string >\0 \b, configuration file %-s
+# newer GRUB versions
+>>>>0x20C ulelong&0x2E300000 !0x2E300000
+##>>>>>0x20C ulelong =0 \b, saved entry %d (usual)
+>>>>>0x20C ulelong >0 \b, saved entry %d
+# for 1.94 contains kernel image size
+# for 0.93,0.94,0.96,0.97
+# 0=stage2 1=ffs 2=e2fs 3=fat 4=minix 5=reiserfs 6=vstafs 7=jfs 8=xfs 9=iso9660 a=ufs2
+>>>>>0x210 ubyte x \b, identifier 0x%x
+# The flag for LBA forcing is in most cases 0
+#>>>>>0x211 ubyte =0 \b, LBA flag 0x%x (default)
+>>>>>0x211 ubyte >0 \b, LBA flag 0x%x
+# GRUB version as string
+>>>>>0x212 string >\0 \b, GRUB version %-s
+# for stage1_5 is 0xffffffff + config_file "/boot/grub/stage2" default
+>>>>>0x217 ulong 0xffffffff
+>>>>>>0x21b string >\0 \b, configuration file %-s
+>>>>>0x217 ulong !0xffffffff
+>>>>>>0x217 string >\0 \b, configuration file %-s
+
+# DOS x86 sector updated and separated from "DOS/MBR boot sector" by Joerg Jenderek at May 2011
+# JuMP short bootcodeoffset NOP assembler instructions will usually be EB xx 90
+# over BIOS parameter block (BPB)
+# http://thestarman.pcministry.com/asm/2bytejumps.htm#FWD
+# older drives may use Near JuMP instruction E9 xx xx
+# minimal short forward jump found 0x29 for bootloaders or 0x0
+# maximal short forward jump is 0x7f
+# OEM-ID is empty or contain readable bytes
+0 ulelong&0x804000E9 0x000000E9
+!:strength +60
+# mtools-3.9.8/msdos.h
+# usual values are marked with comments to get only informations of strange FAT systems
+# valid sectorsize must be a power of 2 from 32 to 32768
+>11 uleshort&0x001f 0
+>>11 uleshort <32769
+>>>11 uleshort >31
+>>>>21 ubyte&0xf0 0xF0
+>>>>>0 ubyte 0xEB DOS/MBR boot sector
+>>>>>>1 ubyte x \b, code offset 0x%x+2
+>>>>>0 ubyte 0xE9
+>>>>>>1 uleshort x \b, code offset 0x%x+3
+>>>>>3 string >\0 \b, OEM-ID "%-.8s"
+#http://mirror.href.com/thestarman/asm/debug/debug2.htm#IHC
+>>>>>>8 string IHC \b cached by Windows 9M
+>>>>>11 uleshort >512 \b, Bytes/sector %u
+#>>>>>11 uleshort =512 \b, Bytes/sector %u=512 (usual)
+>>>>>11 uleshort <512 \b, Bytes/sector %u
+>>>>>13 ubyte >1 \b, sectors/cluster %u
+#>>>>>13 ubyte =1 \b, sectors/cluster %u (usual on Floppies)
+# for lazy FAT32 implementation like Transcend digital photo frame PF830
+>>>>>82 string/c fat32
+>>>>>>14 uleshort !32 \b, reserved sectors %u
+#>>>>>>14 uleshort =32 \b, reserved sectors %u (usual Fat32)
+>>>>>82 string/c !fat32
+>>>>>>14 uleshort >1 \b, reserved sectors %u
+#>>>>>>14 uleshort =1 \b, reserved sectors %u (usual FAT12,FAT16)
+#>>>>>>14 uleshort 0 \b, reserved sectors %u (usual NTFS)
+>>>>>16 ubyte >2 \b, FATs %u
+#>>>>>16 ubyte =2 \b, FATs %u (usual)
+>>>>>16 ubyte =1 \b, FAT %u
+>>>>>16 ubyte >0
+>>>>>17 uleshort >0 \b, root entries %u
+#>>>>>17 uleshort =0 \b, root entries %hu=0 (usual Fat32)
+>>>>>19 uleshort >0 \b, sectors %u (volumes <=32 MB)
+#>>>>>19 uleshort =0 \b, sectors %hu=0 (usual Fat32)
+>>>>>21 ubyte >0xF0 \b, Media descriptor 0x%x
+#>>>>>21 ubyte =0xF0 \b, Media descriptor 0x%x (usual floppy)
+>>>>>21 ubyte <0xF0 \b, Media descriptor 0x%x
+>>>>>22 uleshort >0 \b, sectors/FAT %u
+#>>>>>22 uleshort =0 \b, sectors/FAT %hu=0 (usual Fat32)
+>>>>>24 uleshort x \b, sectors/track %u
+>>>>>26 ubyte >2 \b, heads %u
+#>>>>>26 ubyte =2 \b, heads %u (usual floppy)
+>>>>>26 ubyte =1 \b, heads %u
+# valid only for sector sizes with more then 32 Bytes
+>>>>>11 uleshort >32
+# http://en.wikipedia.org/wiki/Design_of_the_FAT_file_system#Extended_BIOS_Parameter_Block
+# skip for values 2,2Ah,70h,73h,DFh
+# and continue for extended boot signature values 0,28h,29h,80h
+>>>>>>38 ubyte&0x56 =0
+>>>>>>>28 ulelong >0 \b, hidden sectors %u
+#>>>>>>>28 ulelong =0 \b, hidden sectors %u (usual floppy)
+>>>>>>>32 ulelong >0 \b, sectors %u (volumes > 32 MB)
+#>>>>>>>32 ulelong =0 \b, sectors %u (volumes > 32 MB)
+# FAT<32 bit specific
+>>>>>>>82 string/c !fat32
+#>>>>>>>>36 ubyte 0x80 \b, physical drive 0x%x=0x80 (usual harddisk)
+#>>>>>>>>36 ubyte 0 \b, physical drive 0x%x=0 (usual floppy)
+>>>>>>>>36 ubyte !0x80
+>>>>>>>>>36 ubyte !0 \b, physical drive 0x%x
+# VGA-copy CRC or
+# in Windows NT bit 0 is a dirty flag to request chkdsk at boot time. bit 1 requests surface scan too
+>>>>>>>>37 ubyte >0 \b, reserved 0x%x
+#>>>>>>>>37 ubyte =0 \b, reserved 0x%x
+# extended boot signatur value is 0x80 for NTFS, 0x28 or 0x29 for others
+>>>>>>>>38 ubyte !0x29 \b, dos < 4.0 BootSector (0x%x)
+>>>>>>>>38 ubyte&0xFE =0x28
+>>>>>>>>>39 ulelong x \b, serial number 0x%x
+>>>>>>>>38 ubyte =0x29
+>>>>>>>>>43 string <NO\ NAME \b, label: "%11.11s"
+>>>>>>>>>43 string >NO\ NAME \b, label: "%11.11s"
+>>>>>>>>>43 string =NO\ NAME \b, unlabeled
+# there exist some old floppies without word FAT at offset 54
+# a word like "FATnm " is only a hint for a FAT size on nm-bits
+# Normally the number of clusters is calculated by the values of BPP.
+# if it is small enough FAT is 12 bit, if it is too big enough FAT is 32 bit,
+# otherwise FAT is 16 bit.
+# http://homepage.ntlworld.com/jonathan.deboynepollard/FGA/determining-fat-widths.html
+>>>>>82 string/c !fat32
+>>>>>>54 string FAT12 \b, FAT (12 bit)
+>>>>>>54 string FAT16 \b, FAT (16 bit)
+>>>>>>54 default x
+# determinate FAT bit size by media descriptor
+# small floppies implies FAT12
+>>>>>>>21 ubyte <0xF0 \b, FAT (12 bit by descriptor)
+# with media descriptor F0h floppy or maybe superfloppy with FAT16
+>>>>>>>21 ubyte =0xF0
+# superfloppy (many sectors) implies FAT16
+>>>>>>>>32 ulelong >0xFFFF \b, FAT (16 bit by descriptor+sectors)
+# no superfloppy with media descriptor F0h implies FAT12
+>>>>>>>>32 default x \b, FAT (12 bit by descriptor+sectors)
+# with media descriptor F8h floppy or hard disc with FAT12 or FAT16
+>>>>>>>21 ubyte =0xF8
+# 360 KiB with media descriptor F8h, 9 sectors per track ,single sided floppy implies FAT12
+>>>>>>>>19 ubequad 0xd002f80300090001 \b, FAT (12 bit by descriptor+geometry)
+# hard disc with FAT12 or FAT16
+>>>>>>>>19 default x \b, FAT (1Y bit by descriptor)
+# with media descriptor FAh floppy, RAM disc with FAT12 or FAT16 or Tandy hard disc
+>>>>>>>21 ubyte =0xFA
+# 320 KiB with media descriptor FAh, 8 sectors per track ,single sided floppy implies FAT12
+>>>>>>>>19 ubequad 0x8002fa0200080001 \b, FAT (12 bit by descriptor+geometry)
+# RAM disc with FAT12 or FAT16 or Tandy hard disc
+>>>>>>>>19 default x \b, FAT (1Y bit by descriptor)
+# others are floppy
+>>>>>>>21 default x \b, FAT (12 bit by descriptor)
+# FAT32 bit specific
+>>>>>82 string/c fat32 \b, FAT (32 bit)
+>>>>>>36 ulelong x \b, sectors/FAT %u
+# http://technet.microsoft.com/en-us/library/cc977221.aspx
+>>>>>>40 uleshort >0 \b, extension flags 0x%x
+#>>>>>>40 uleshort =0 \b, extension flags %hu
+>>>>>>42 uleshort >0 \b, fsVersion %u
+#>>>>>>42 uleshort =0 \b, fsVersion %u (usual)
+>>>>>>44 ulelong >2 \b, rootdir cluster %u
+#>>>>>>44 ulelong =2 \b, rootdir cluster %u
+#>>>>>>44 ulelong =1 \b, rootdir cluster %u
+>>>>>>48 uleshort >1 \b, infoSector %u
+#>>>>>>48 uleshort =1 \b, infoSector %u (usual)
+>>>>>>48 uleshort <1 \b, infoSector %u
+# 0 or 0xFFFF instead of usual 6 means no backup sector
+>>>>>>50 uleshort =0xFFFF \b, no Backup boot sector
+>>>>>>50 uleshort =0 \b, no Backup boot sector
+#>>>>>>50 uleshort =6 \b, Backup boot sector %u (usual)
+>>>>>>50 default x
+>>>>>>>50 uleshort x \b, Backup boot sector %u
+# corrected by Joerg Jenderek at Feb 2011 according to http://thestarman.pcministry.com/asm/mbr/MSWIN41.htm#FSINFO
+>>>>>>52 ulelong >0 \b, reserved1 0x%x
+>>>>>>56 ulelong >0 \b, reserved2 0x%x
+>>>>>>60 ulelong >0 \b, reserved3 0x%x
+# same structure as FAT1X
+#>>>>>>64 ubyte =0x80 \b, physical drive 0x%x=80 (usual harddisk)
+#>>>>>>64 ubyte =0 \b, physical drive 0x%x=0 (usual floppy)
+>>>>>>64 ubyte !0x80
+>>>>>>>64 ubyte >0 \b, physical drive 0x%x
+# in Windows NT bit 0 is a dirty flag to request chkdsk at boot time. bit 1 requests surface scan too
+>>>>>>65 ubyte >0 \b, reserved 0x%x
+>>>>>>66 ubyte !0x29 \b, dos < 4.0 BootSector (0x%x)
+>>>>>>66 ubyte =0x29
+>>>>>>>67 ulelong x \b, serial number 0x%x
+>>>>>>>71 string <NO\ NAME \b, label: "%11.11s"
+>>>>>>>71 string >NO\ NAME \b, label: "%11.11s"
+>>>>>>>71 string =NO\ NAME \b, unlabeled
+# additional tests for floppy image added by Joerg Jenderek
+# no fixed disk
+>>>>>21 ubyte !0xF8
+# floppy media with 12 bit FAT
+>>>>>>54 string !FAT16
+# test for FAT after bootsector
+>>>>>>>(11.s) ulelong&0x00ffffF0 0x00ffffF0 \b, followed by FAT
+# floppy image
+!:mime application/x-ima
+# NTFS specific added by Joerg Jenderek at Mar 2011 according to http://thestarman.pcministry.com/asm/mbr/NTFSBR.htm
+# and http://homepage.ntlworld.com/jonathan.deboynepollard/FGA/bios-parameter-block.html
+# 0 FATs
+>>>>>16 ubyte =0
+# 0 root entries
+>>>>>>17 uleshort =0
+# 0 DOS sectors
+>>>>>>>19 uleshort =0
+# 0 sectors/FAT
+# dos < 4.0 BootSector value found is 0x80
+#38 ubyte =0x80 \b, dos < 4.0 BootSector (0x%x)
+>>>>>>>>22 uleshort =0 \b; NTFS
+>>>>>>>>>24 uleshort >0 \b, sectors/track %u
+>>>>>>>>>36 ulelong !0x800080 \b, physical drive 0x%x
+>>>>>>>>>40 ulequad >0 \b, sectors %lld
+>>>>>>>>>48 ulequad >0 \b, $MFT start cluster %lld
+>>>>>>>>>56 ulequad >0 \b, $MFTMirror start cluster %lld
+# Values 0 to 127 represent MFT record sizes of 0 to 127 clusters.
+# Values 128 to 255 represent MFT record sizes of 2^(256-N) bytes.
+>>>>>>>>>64 lelong <256
+>>>>>>>>>>64 lelong <128 \b, clusters/RecordSegment %d
+>>>>>>>>>>64 ubyte >127 \b, bytes/RecordSegment 2^(-1*%i)
+# Values 0 to 127 represent index block sizes of 0 to 127 clusters.
+# Values 128 to 255 represent index block sizes of 2^(256-N) byte
+>>>>>>>>>68 ulelong <256
+>>>>>>>>>>68 ulelong <128 \b, clusters/index block %d
+#>>>>>>>>>>68 ulelong >127 \b, bytes/index block 2^(256-%d)
+>>>>>>>>>>68 ubyte >127 \b, bytes/index block 2^(-1*%i)
+>>>>>>>>>72 ulequad x \b, serial number 0%llx
+>>>>>>>>>80 ulelong >0 \b, checksum 0x%x
+#>>>>>>>>>80 ulelong =0 \b, checksum 0x%x=0 (usual)
+>>>>>>>>>0x258 ulelong&0x00009090 =0x00009090
+>>>>>>>>>>&-92 indirect x \b; contains
+# For 2nd NTFS sector added by Joerg Jenderek at Jan 2013
+# http://thestarman.pcministry.com/asm/mbr/NTFSbrHexEd.htm
+# unused assembler instructions JMP y2;NOP;NOP
+0x056 ulelong&0xFFFF0FFF 0x909002EB
+# unicode loadername terminated by CTRL-D
+>(0.s*2) ulelong&0xFFFFFF00 0x00040000
+# loadernames are NTLDR,CMLDR,PELDR,$LDR$ or BOOTMGR
+>>0x002 lestring16 x Microsoft Windows XP/VISTA bootloader %-5.5s
+>>0x12 string $
+>>>0x0c lestring16 x \b%-2.2s
+### DOS,NTFS boot sectors end
+
+9564 lelong 0x00011954 Unix Fast File system [v1] (little-endian),
+>8404 string x last mounted on %s,
+#>9504 ledate x last checked at %s,
+>8224 ledate x last written at %s,
+>8401 byte x clean flag %d,
+>8228 lelong x number of blocks %d,
+>8232 lelong x number of data blocks %d,
+>8236 lelong x number of cylinder groups %d,
+>8240 lelong x block size %d,
+>8244 lelong x fragment size %d,
+>8252 lelong x minimum percentage of free blocks %d,
+>8256 lelong x rotational delay %dms,
+>8260 lelong x disk rotational speed %drps,
+>8320 lelong 0 TIME optimization
+>8320 lelong 1 SPACE optimization
+
+42332 lelong 0x19540119 Unix Fast File system [v2] (little-endian)
+>&-1164 string x last mounted on %s,
+>&-696 string >\0 volume name %s,
+>&-304 leqldate x last written at %s,
+>&-1167 byte x clean flag %d,
+>&-1168 byte x readonly flag %d,
+>&-296 lequad x number of blocks %lld,
+>&-288 lequad x number of data blocks %lld,
+>&-1332 lelong x number of cylinder groups %d,
+>&-1328 lelong x block size %d,
+>&-1324 lelong x fragment size %d,
+>&-180 lelong x average file size %d,
+>&-176 lelong x average number of files in dir %d,
+>&-272 lequad x pending blocks to free %lld,
+>&-264 lelong x pending inodes to free %d,
+>&-664 lequad x system-wide uuid %0llx,
+>&-1316 lelong x minimum percentage of free blocks %d,
+>&-1248 lelong 0 TIME optimization
+>&-1248 lelong 1 SPACE optimization
+
+66908 lelong 0x19540119 Unix Fast File system [v2] (little-endian)
+>&-1164 string x last mounted on %s,
+>&-696 string >\0 volume name %s,
+>&-304 leqldate x last written at %s,
+>&-1167 byte x clean flag %d,
+>&-1168 byte x readonly flag %d,
+>&-296 lequad x number of blocks %lld,
+>&-288 lequad x number of data blocks %lld,
+>&-1332 lelong x number of cylinder groups %d,
+>&-1328 lelong x block size %d,
+>&-1324 lelong x fragment size %d,
+>&-180 lelong x average file size %d,
+>&-176 lelong x average number of files in dir %d,
+>&-272 lequad x pending blocks to free %lld,
+>&-264 lelong x pending inodes to free %d,
+>&-664 lequad x system-wide uuid %0llx,
+>&-1316 lelong x minimum percentage of free blocks %d,
+>&-1248 lelong 0 TIME optimization
+>&-1248 lelong 1 SPACE optimization
+
+9564 belong 0x00011954 Unix Fast File system [v1] (big-endian),
+>7168 belong 0x4c41424c Apple UFS Volume
+>>7186 string x named %s,
+>>7176 belong x volume label version %d,
+>>7180 bedate x created on %s,
+>8404 string x last mounted on %s,
+#>9504 bedate x last checked at %s,
+>8224 bedate x last written at %s,
+>8401 byte x clean flag %d,
+>8228 belong x number of blocks %d,
+>8232 belong x number of data blocks %d,
+>8236 belong x number of cylinder groups %d,
+>8240 belong x block size %d,
+>8244 belong x fragment size %d,
+>8252 belong x minimum percentage of free blocks %d,
+>8256 belong x rotational delay %dms,
+>8260 belong x disk rotational speed %drps,
+>8320 belong 0 TIME optimization
+>8320 belong 1 SPACE optimization
+
+42332 belong 0x19540119 Unix Fast File system [v2] (big-endian)
+>&-1164 string x last mounted on %s,
+>&-696 string >\0 volume name %s,
+>&-304 beqldate x last written at %s,
+>&-1167 byte x clean flag %d,
+>&-1168 byte x readonly flag %d,
+>&-296 bequad x number of blocks %lld,
+>&-288 bequad x number of data blocks %lld,
+>&-1332 belong x number of cylinder groups %d,
+>&-1328 belong x block size %d,
+>&-1324 belong x fragment size %d,
+>&-180 belong x average file size %d,
+>&-176 belong x average number of files in dir %d,
+>&-272 bequad x pending blocks to free %lld,
+>&-264 belong x pending inodes to free %d,
+>&-664 bequad x system-wide uuid %0llx,
+>&-1316 belong x minimum percentage of free blocks %d,
+>&-1248 belong 0 TIME optimization
+>&-1248 belong 1 SPACE optimization
+
+66908 belong 0x19540119 Unix Fast File system [v2] (big-endian)
+>&-1164 string x last mounted on %s,
+>&-696 string >\0 volume name %s,
+>&-304 beqldate x last written at %s,
+>&-1167 byte x clean flag %d,
+>&-1168 byte x readonly flag %d,
+>&-296 bequad x number of blocks %lld,
+>&-288 bequad x number of data blocks %lld,
+>&-1332 belong x number of cylinder groups %d,
+>&-1328 belong x block size %d,
+>&-1324 belong x fragment size %d,
+>&-180 belong x average file size %d,
+>&-176 belong x average number of files in dir %d,
+>&-272 bequad x pending blocks to free %lld,
+>&-264 belong x pending inodes to free %d,
+>&-664 bequad x system-wide uuid %0llx,
+>&-1316 belong x minimum percentage of free blocks %d,
+>&-1248 belong 0 TIME optimization
+>&-1248 belong 1 SPACE optimization
+
+# ext2/ext3 filesystems - Andreas Dilger <adilger@dilger.ca>
+# ext4 filesystem - Eric Sandeen <sandeen@sandeen.net>
+# volume label and UUID Russell Coker
+# http://etbe.coker.com.au/2008/07/08/label-vs-uuid-vs-device/
+0x438 leshort 0xEF53 Linux
+>0x44c lelong x rev %d
+>0x43e leshort x \b.%d
+# No journal? ext2
+>0x45c lelong ^0x0000004 ext2 filesystem data
+>>0x43a leshort ^0x0000001 (mounted or unclean)
+# Has a journal? ext3 or ext4
+>0x45c lelong &0x0000004
+# and small INCOMPAT?
+>>0x460 lelong <0x0000040
+# and small RO_COMPAT?
+>>>0x464 lelong <0x0000008 ext3 filesystem data
+# else large RO_COMPAT?
+>>>0x464 lelong >0x0000007 ext4 filesystem data
+# else large INCOMPAT?
+>>0x460 lelong >0x000003f ext4 filesystem data
+>0x468 belong x \b, UUID=%08x
+>0x46c beshort x \b-%04x
+>0x46e beshort x \b-%04x
+>0x470 beshort x \b-%04x
+>0x472 belong x \b-%08x
+>0x476 beshort x \b%04x
+>0x478 string >0 \b, volume name "%s"
+# General flags for any ext* fs
+>0x460 lelong &0x0000004 (needs journal recovery)
+>0x43a leshort &0x0000002 (errors)
+# INCOMPAT flags
+>0x460 lelong &0x0000001 (compressed)
+#>0x460 lelong &0x0000002 (filetype)
+#>0x460 lelong &0x0000010 (meta bg)
+>0x460 lelong &0x0000040 (extents)
+>0x460 lelong &0x0000080 (64bit)
+#>0x460 lelong &0x0000100 (mmp)
+#>0x460 lelong &0x0000200 (flex bg)
+# RO_INCOMPAT flags
+#>0x464 lelong &0x0000001 (sparse super)
+>0x464 lelong &0x0000002 (large files)
+>0x464 lelong &0x0000008 (huge files)
+#>0x464 lelong &0x0000010 (gdt checksum)
+#>0x464 lelong &0x0000020 (many subdirs)
+#>0x463 lelong &0x0000040 (extra isize)
+
+# Minix filesystems - Juan Cespedes <cespedes@debian.org>
+0x410 leshort 0x137f
+!:strength / 2
+>0x402 beshort < 100
+>0x402 beshort > -1 Minix filesystem, V1, %d zones
+>0x1e string minix \b, bootable
+0x410 beshort 0x137f
+!:strength / 2
+>0x402 beshort < 100
+>0x402 beshort > -1 Minix filesystem, V1 (big endian), %d zones
+>0x1e string minix \b, bootable
+0x410 leshort 0x138f
+!:strength / 2
+>0x402 beshort < 100
+>0x402 beshort > -1 Minix filesystem, V1, 30 char names, %d zones
+>0x1e string minix \b, bootable
+0x410 beshort 0x138f
+!:strength / 2
+>0x402 beshort < 100
+>0x402 beshort > -1 Minix filesystem, V1, 30 char names (big endian), %d zones
+>0x1e string minix \b, bootable
+0x410 leshort 0x2468
+>0x402 beshort < 100
+>>0x402 beshort > -1 Minix filesystem, V2, %d zones
+>0x1e string minix \b, bootable
+0x410 beshort 0x2468
+>0x402 beshort < 100
+>0x402 beshort > -1 Minix filesystem, V2 (big endian), %d zones
+>0x1e string minix \b, bootable
+
+0x410 leshort 0x2478
+>0x402 beshort < 100
+>0x402 beshort > -1 Minix filesystem, V2, 30 char names, %d zones
+>0x1e string minix \b, bootable
+0x410 leshort 0x2478
+>0x402 beshort < 100
+>0x402 beshort > -1 Minix filesystem, V2, 30 char names, %d zones
+>0x1e string minix \b, bootable
+0x410 beshort 0x2478
+>0x402 beshort !0 Minix filesystem, V2, 30 char names (big endian), %d zones
+>0x1e string minix \b, bootable
+0x410 leshort 0x4d5a
+>0x402 beshort !0 Minix filesystem, V3, %d zones
+>0x1e string minix \b, bootable
+
+# SGI disk labels - Nathan Scott <nathans@debian.org>
+0 belong 0x0BE5A941 SGI disk label (volume header)
+
+# SGI XFS filesystem - Nathan Scott <nathans@debian.org>
+0 belong 0x58465342 SGI XFS filesystem data
+>0x4 belong x (blksz %d,
+>0x68 beshort x inosz %d,
+>0x64 beshort ^0x2004 v1 dirs)
+>0x64 beshort &0x2004 v2 dirs)
+
+############################################################################
+# Minix-ST kernel floppy
+0x800 belong 0x46fc2700 Atari-ST Minix kernel image
+# http://en.wikipedia.org/wiki/BIOS_parameter_block
+# floppies with valid BPB and any instruction at beginning
+>19 string \240\005\371\005\0\011\0\2\0 \b, 720k floppy
+>19 string \320\002\370\005\0\011\0\1\0 \b, 360k floppy
+
+############################################################################
+# Hmmm, is this a better way of detecting _standard_ floppy images ?
+19 string \320\002\360\003\0\011\0\1\0 DOS floppy 360k
+>0x1FE leshort 0xAA55 \b, DOS/MBR hard disk boot sector
+19 string \240\005\371\003\0\011\0\2\0 DOS floppy 720k
+>0x1FE leshort 0xAA55 \b, DOS/MBR hard disk boot sector
+19 string \100\013\360\011\0\022\0\2\0 DOS floppy 1440k
+>0x1FE leshort 0xAA55 \b, DOS/MBR hard disk boot sector
+
+19 string \240\005\371\005\0\011\0\2\0 DOS floppy 720k, IBM
+>0x1FE leshort 0xAA55 \b, DOS/MBR hard disk boot sector
+19 string \100\013\371\005\0\011\0\2\0 DOS floppy 1440k, mkdosfs
+>0x1FE leshort 0xAA55 \b, DOS/MBR hard disk boot sector
+
+19 string \320\002\370\005\0\011\0\1\0 Atari-ST floppy 360k
+19 string \240\005\371\005\0\011\0\2\0 Atari-ST floppy 720k
+# | | | | |
+# | | | | heads
+# | | | sectors/track
+# | | sectors/FAT
+# | media descriptor
+# BPB: sectors
+
+# Valid media descriptor bytes for MS-DOS:
+#
+# Byte Capacity Media Size and Type
+# -------------------------------------------------
+#
+# F0 2.88 MB 3.5-inch, 2-sided, 36-sector
+# F0 1.44 MB 3.5-inch, 2-sided, 18-sector
+# F9 720K 3.5-inch, 2-sided, 9-sector
+# F9 1.2 MB 5.25-inch, 2-sided, 15-sector
+# FD 360K 5.25-inch, 2-sided, 9-sector
+# FF 320K 5.25-inch, 2-sided, 8-sector
+# FC 180K 5.25-inch, 1-sided, 9-sector
+# FE 160K 5.25-inch, 1-sided, 8-sector
+# FE 250K 8-inch, 1-sided, single-density
+# FD 500K 8-inch, 2-sided, single-density
+# FE 1.2 MB 8-inch, 2-sided, double-density
+# F8 ----- Fixed disk
+#
+# FC xxxK Apricot 70x1x9 boot disk.
+#
+# Originally a bitmap:
+# xxxxxxx0 Not two sided
+# xxxxxxx1 Double sided
+# xxxxxx0x Not 8 SPT
+# xxxxxx1x 8 SPT
+# xxxxx0xx Not Removable drive
+# xxxxx1xx Removable drive
+# 11111xxx Must be one.
+#
+# But now it's rather random:
+# 111111xx Low density disk
+# 00 SS, Not 8 SPT
+# 01 DS, Not 8 SPT
+# 10 SS, 8 SPT
+# 11 DS, 8 SPT
+#
+# 11111001 Double density 3 1/2 floppy disk, high density 5 1/4
+# 11110000 High density 3 1/2 floppy disk
+# 11111000 Hard disk any format
+#
+
+# all FAT12 (strength=70) floppies with sectorsize 512 added by Joerg Jenderek at Jun 2013
+# http://en.wikipedia.org/wiki/File_Allocation_Table#Exceptions
+# Too Weak.
+#512 ubelong&0xE0ffff00 0xE0ffff00
+# without valid Media descriptor in place of BPB, cases with are done at other places
+#>21 ubyte <0xE5 floppy with old FAT filesystem
+# but valid Media descriptor at begin of FAT
+#>>512 ubyte =0xed 720k
+#>>512 ubyte =0xf0 1440k
+#>>512 ubyte =0xf8 720k
+#>>512 ubyte =0xf9 1220k
+#>>512 ubyte =0xfa 320k
+#>>512 ubyte =0xfb 640k
+#>>512 ubyte =0xfc 180k
+# look like an an old DOS directory entry
+#>>>0xA0E ubequad 0
+#>>>>0xA00 ubequad !0
+#!:mime application/x-ima
+#>>512 ubyte =0xfd
+# look for 2nd FAT at different location to distinguish between 360k and 500k
+#>>>0x600 ubelong&0xE0ffff00 0xE0ffff00 360k
+#>>>0x500 ubelong&0xE0ffff00 0xE0ffff00 500k
+#>>>0xA0E ubequad 0
+#!:mime application/x-ima
+#>>512 ubyte =0xfe
+#>>>0x400 ubelong&0xE0ffff00 0xE0ffff00 160k
+#>>>>0x60E ubequad 0
+#>>>>>0x600 ubequad !0
+#!:mime application/x-ima
+#>>>0xC00 ubelong&0xE0ffff00 0xE0ffff00 1200k
+#>>512 ubyte =0xff 320k
+#>>>0x60E ubequad 0
+#>>>>0x600 ubequad !0
+#!:mime application/x-ima
+#>>512 ubyte x \b, Media descriptor 0x%x
+# without x86 jump instruction
+#>>0 ulelong&0x804000E9 !0x000000E9
+# assembler instructions: CLI;MOV SP,1E7;MOV AX;07c0;MOV
+#>>>0 ubequad 0xfabce701b8c0078e \b, MS-DOS 1.12 bootloader
+# IOSYS.COM+MSDOS.COM
+#>>>>0xc4 use 2xDOS-filename
+#>>0 ulelong&0x804000E9 =0x000000E9
+# only x86 short jump instruction found
+#>>>0 ubyte =0xEB
+#>>>>1 ubyte x \b, code offset 0x%x+2
+# http://thestarman.pcministry.com/DOS/ibm100/Boot.htm
+# assembler instructions: CLI;MOV AX,CS;MOV DS,AX;MOV DX,0
+#>>>>(1.b+2) ubequad 0xfa8cc88ed8ba0000 \b, PC-DOS 1.0 bootloader
+# ibmbio.com+ibmdos.com
+#>>>>>0x176 use DOS-filename
+#>>>>>0x181 ubyte x \b+
+#>>>>>0x182 use DOS-filename
+# http://thestarman.pcministry.com/DOS/ibm110/Boot.htm
+# assembler instructions: CLI;MOV AX,CS;MOV DS,AX;XOR DX,DX;MOV
+#>>>>(1.b+2) ubequad 0xfa8cc88ed833d28e \b, PC-DOS 1.1 bootloader
+# ibmbio.com+ibmdos.com
+#>>>>>0x18b use DOS-filename
+#>>>>>0x196 ubyte x \b+
+#>>>>>0x197 use DOS-filename
+# http://en.wikipedia.org/wiki/Zenith_Data_Systems
+# assembler instructions: MOV BX,07c0;MOV SS,BX;MOV SP,01c6
+#>>>>(1.b+2) ubequad 0xbbc0078ed3bcc601 \b, Zenith Data Systems MS-DOS 1.25 bootloader
+# IO.SYS+MSDOS.SYS
+#>>>>>0x20 use 2xDOS-filename
+# http://en.wikipedia.org/wiki/Corona_Data_Systems
+# assembler instructions: MOV AX,CS;MOV DS,AX;CLI;MOV SS,AX;
+#>>>>(1.b+2) ubequad 0x8cc88ed8fa8ed0bc \b, MS-DOS 1.25 bootloader
+# IO.SYS+MSDOS.SYS
+#>>>>>0x69 use 2xDOS-filename
+# assembler instructions: CLI;PUSH CS;POP SS;MOV SP,7c00;
+#>>>>(1.b+2) ubequad 0xfa0e17bc007cb860 \b, MS-DOS 2.11 bootloader
+# defect IO.SYS+MSDOS.SYS ?
+#>>>>>0x162 use 2xDOS-filename
+
+0 name cdrom
+>38913 string !NSR0 ISO 9660 CD-ROM filesystem data
+!:mime application/x-iso9660-image
+>38913 string NSR0 UDF filesystem data
+!:mime application/x-iso9660-image
+>>38917 string 1 (version 1.0)
+>>38917 string 2 (version 1.5)
+>>38917 string 3 (version 2.0)
+>>38917 byte >0x33 (unknown version, ID 0x%X)
+>>38917 byte <0x31 (unknown version, ID 0x%X)
+>0x1FE leshort 0xAA55 (DOS/MBR boot sector)
+# "application id" which appears to be used as a volume label
+>32808 string/T >\0 '%s'
+>34816 string \000CD001\001EL\ TORITO\ SPECIFICATION (bootable)
+37633 string CD001 ISO 9660 CD-ROM filesystem data (raw 2352 byte sectors)
+!:mime application/x-iso9660-image
+32777 string CDROM High Sierra CD-ROM filesystem data
+
+# CDROM Filesystems
+# https://en.wikipedia.org/wiki/ISO_9660
+# Modified for UDF by gerardo.cacciari@gmail.com
+32769 string CD001
+# mime line at that position does not work
+# to display CD-ROM (70=81-11) after MBR (113=40+72+1), partition-table (71=50+21) and before Apple Driver Map (51)
+!:strength -11
+# to display CD-ROM (114=81+33) before MBR (113=40+72+1), partition-table (71=50+21) and Apple Driver Map (51)
+# does not work
+#!:strength +33
+>0 use cdrom
+
+# .cso files
+0 string CISO Compressed ISO CD image
+
+# cramfs filesystem - russell@coker.com.au
+0 lelong 0x28cd3d45 Linux Compressed ROM File System data, little endian
+>4 lelong x size %u
+>8 lelong &1 version #2
+>8 lelong &2 sorted_dirs
+>8 lelong &4 hole_support
+>32 lelong x CRC 0x%x,
+>36 lelong x edition %u,
+>40 lelong x %u blocks,
+>44 lelong x %u files
+
+0 belong 0x28cd3d45 Linux Compressed ROM File System data, big endian
+>4 belong x size %u
+>8 belong &1 version #2
+>8 belong &2 sorted_dirs
+>8 belong &4 hole_support
+>32 belong x CRC 0x%x,
+>36 belong x edition %u,
+>40 belong x %u blocks,
+>44 belong x %u files
+
+# reiserfs - russell@coker.com.au
+0x10034 string ReIsErFs ReiserFS V3.5
+0x10034 string ReIsEr2Fs ReiserFS V3.6
+0x10034 string ReIsEr3Fs ReiserFS V3.6.19
+>0x1002c leshort x block size %d
+>0x10032 leshort &2 (mounted or unclean)
+>0x10000 lelong x num blocks %d
+>0x10040 lelong 1 tea hash
+>0x10040 lelong 2 yura hash
+>0x10040 lelong 3 r5 hash
+
+# JFFS - russell@coker.com.au
+0 lelong 0x34383931 Linux Journalled Flash File system, little endian
+0 belong 0x34383931 Linux Journalled Flash File system, big endian
+
+# EST flat binary format (which isn't, but anyway)
+# From: Mark Brown <broonie@sirena.org.uk>
+0 string ESTFBINR EST flat binary
+
+# Aculab VoIP firmware
+# From: Mark Brown <broonie@sirena.org.uk>
+0 string VoIP\ Startup\ and Aculab VoIP firmware
+>35 string x format %s
+
+# From: Mark Brown <broonie@sirena.org.uk> [old]
+# From: Behan Webster <behanw@websterwood.com>
+0 belong 0x27051956 u-boot legacy uImage,
+>32 string x %s,
+>28 byte 0 Invalid os/
+>28 byte 1 OpenBSD/
+>28 byte 2 NetBSD/
+>28 byte 3 FreeBSD/
+>28 byte 4 4.4BSD/
+>28 byte 5 Linux/
+>28 byte 6 SVR4/
+>28 byte 7 Esix/
+>28 byte 8 Solaris/
+>28 byte 9 Irix/
+>28 byte 10 SCO/
+>28 byte 11 Dell/
+>28 byte 12 NCR/
+>28 byte 13 LynxOS/
+>28 byte 14 VxWorks/
+>28 byte 15 pSOS/
+>28 byte 16 QNX/
+>28 byte 17 Firmware/
+>28 byte 18 RTEMS/
+>28 byte 19 ARTOS/
+>28 byte 20 Unity OS/
+>28 byte 21 INTEGRITY/
+>29 byte 0 \bInvalid CPU,
+>29 byte 1 \bAlpha,
+>29 byte 2 \bARM,
+>29 byte 3 \bIntel x86,
+>29 byte 4 \bIA64,
+>29 byte 5 \bMIPS,
+>29 byte 6 \bMIPS 64-bit,
+>29 byte 7 \bPowerPC,
+>29 byte 8 \bIBM S390,
+>29 byte 9 \bSuperH,
+>29 byte 10 \bSparc,
+>29 byte 11 \bSparc 64-bit,
+>29 byte 12 \bM68K,
+>29 byte 13 \bNios-32,
+>29 byte 14 \bMicroBlaze,
+>29 byte 15 \bNios-II,
+>29 byte 16 \bBlackfin,
+>29 byte 17 \bAVR32,
+>29 byte 18 \bSTMicroelectronics ST200,
+>30 byte 0 Invalid Image
+>30 byte 1 Standalone Program
+>30 byte 2 OS Kernel Image
+>30 byte 3 RAMDisk Image
+>30 byte 4 Multi-File Image
+>30 byte 5 Firmware Image
+>30 byte 6 Script File
+>30 byte 7 Filesystem Image (any type)
+>30 byte 8 Binary Flat Device Tree BLOB
+>31 byte 0 (Not compressed),
+>31 byte 1 (gzip),
+>31 byte 2 (bzip2),
+>31 byte 3 (lzma),
+>12 belong x %d bytes,
+>8 bedate x %s,
+>16 belong x Load Address: 0x%08X,
+>20 belong x Entry Point: 0x%08X,
+>4 belong x Header CRC: 0x%08X,
+>24 belong x Data CRC: 0x%08X
+
+# JFFS2 file system
+0 leshort 0x1984 Linux old jffs2 filesystem data little endian
+0 leshort 0x1985 Linux jffs2 filesystem data little endian
+
+# Squashfs
+0 string sqsh Squashfs filesystem, big endian,
+>28 beshort x version %d.
+>30 beshort x \b%d,
+>28 beshort <3
+>>8 belong x %d bytes,
+>28 beshort >2
+>>28 beshort <4
+>>>63 bequad x %lld bytes,
+>>28 beshort >3
+>>>40 bequad x %lld bytes,
+#>>67 belong x %d bytes,
+>4 belong x %d inodes,
+>28 beshort <2
+>>32 beshort x blocksize: %d bytes,
+>28 beshort >1
+>>28 beshort <4
+>>>51 belong x blocksize: %d bytes,
+>>28 beshort >3
+>>>12 belong x blocksize: %d bytes,
+>28 beshort <4
+>>39 bedate x created: %s
+>28 beshort >3
+>>8 bedate x created: %s
+0 string hsqs Squashfs filesystem, little endian,
+>28 leshort x version %d.
+>30 leshort x \b%d,
+>28 leshort <3
+>>8 lelong x %d bytes,
+>28 leshort >2
+>>28 leshort <4
+>>>63 lequad x %lld bytes,
+>>28 leshort >3
+>>>40 lequad x %lld bytes,
+#>>63 lelong x %d bytes,
+>4 lelong x %d inodes,
+>28 leshort <2
+>>32 leshort x blocksize: %d bytes,
+>28 leshort >1
+>>28 leshort <4
+>>>51 lelong x blocksize: %d bytes,
+>>28 leshort >3
+>>>12 lelong x blocksize: %d bytes,
+>28 leshort <4
+>>39 ledate x created: %s
+>28 leshort >3
+>>8 ledate x created: %s
+
+# AFS Dump Magic
+# From: Ty Sarna <tsarna@sarna.org>
+0 string \x01\xb3\xa1\x13\x22 AFS Dump
+>&0 belong x (v%d)
+>>&0 byte 0x76
+>>>&0 belong x Vol %d,
+>>>>&0 byte 0x6e
+>>>>>&0 string x %s
+>>>>>>&1 byte 0x74
+>>>>>>>&0 beshort 2
+>>>>>>>>&4 bedate x on: %s
+>>>>>>>>&0 bedate =0 full dump
+>>>>>>>>&0 bedate !0 incremental since: %s
+
+#----------------------------------------------------------
+#delta ISO Daniel Novotny (dnovotny@redhat.com)
+0 string DISO Delta ISO data
+!:strength +50
+>4 belong x version %d
+
+# VMS backup savesets - gerardo.cacciari@gmail.com
+#
+4 string \x01\x00\x01\x00\x01\x00
+>(0.s+16) string \x01\x01
+>>&(&0.b+8) byte 0x42 OpenVMS backup saveset data
+>>>40 lelong x (block size %d,
+>>>49 string >\0 original name '%s',
+>>>2 short 1024 VAX generated)
+>>>2 short 2048 AXP generated)
+>>>2 short 4096 I64 generated)
+
+# Summary: Oracle Clustered Filesystem
+# Created by: Aaron Botsis <redhat@digitalmafia.org>
+8 string OracleCFS Oracle Clustered Filesystem,
+>4 long x rev %d
+>0 long x \b.%d,
+>560 string x label: %.64s,
+>136 string x mountpoint: %.128s
+
+# Summary: Oracle ASM tagged volume
+# Created by: Aaron Botsis <redhat@digitalmafia.org>
+32 string ORCLDISK Oracle ASM Volume,
+>40 string x Disk Name: %0.12s
+32 string ORCLCLRD Oracle ASM Volume (cleared),
+>40 string x Disk Name: %0.12s
+
+# Oracle Clustered Filesystem - Aaron Botsis <redhat@digitalmafia.org>
+8 string OracleCFS Oracle Clustered Filesystem,
+>4 long x rev %d
+>0 long x \b.%d,
+>560 string x label: %.64s,
+>136 string x mountpoint: %.128s
+
+# Oracle ASM tagged volume - Aaron Botsis <redhat@digitalmafia.org>
+32 string ORCLDISK Oracle ASM Volume,
+>40 string x Disk Name: %0.12s
+32 string ORCLCLRD Oracle ASM Volume (cleared),
+>40 string x Disk Name: %0.12s
+
+# Compaq/HP RILOE floppy image
+# From: Dirk Jagdmann <doj@cubic.org>
+0 string CPQRFBLO Compaq/HP RILOE floppy image
+
+#------------------------------------------------------------------------------
+# Files-11 On-Disk Structure (File system for various RSX-11 and VMS flavours).
+# These bits come from LBN 1 (home block) of ODS-1, ODS-2 and ODS-5 volumes,
+# which is mapped to VBN 2 of [000000]INDEXF.SYS;1 - gerardo.cacciari@gmail.com
+#
+1008 string DECFILE11 Files-11 On-Disk Structure
+>525 byte x (ODS-%d);
+>1017 string A RSX-11, VAX/VMS or OpenVMS VAX file system;
+>1017 string B
+>>525 byte 2 VAX/VMS or OpenVMS file system;
+>>525 byte 5 OpenVMS Alpha or Itanium file system;
+>984 string x volume label is '%-12.12s'
+
+# From: Thomas Klausner <wiz@NetBSD.org>
+# http://filext.com/file-extension/DAA
+# describes the daa file format. The magic would be:
+0 string DAA\x0\x0\x0\x0\x0 PowerISO Direct-Access-Archive
+
+# From Albert Cahalan <acahalan@gmail.com>
+# really le32 operation,destination,payloadsize (but quite predictable)
+# 01 00 00 00 00 00 00 c0 00 02 00 00
+0 string \1\0\0\0\0\0\0\300\0\2\0\0 Marvell Libertas firmware
+
+# From Eric Sandeen
+# GFS2
+0x10000 belong 0x01161970
+>0x10018 belong 0x0000051d GFS1 Filesystem
+>>0x10024 belong x (blocksize %d,
+>>0x10060 string >\0 lockproto %s)
+>0x10018 belong 0x00000709 GFS2 Filesystem
+>>0x10024 belong x (blocksize %d,
+>>0x10060 string >\0 lockproto %s)
+
+# BTRFS
+0x10040 string _BHRfS_M BTRFS Filesystem
+>0x1012b string >\0 (label "%s",
+>0x10090 lelong x sectorsize %d,
+>0x10094 lelong x nodesize %d,
+>0x10098 lelong x leafsize %d)
+
+
+# dvdisaster's .ecc
+# From: "Nelson A. de Oliveira" <naoliv@gmail.com>
+0 string *dvdisaster* dvdisaster error correction file
+
+# xfs metadump image
+# mb_magic XFSM at 0; superblock magic XFSB at 1 << mb_blocklog
+# but can we do the << ? For now it's always 512 (0x200) anyway.
+0 string XFSM
+>0x200 string XFSB XFS filesystem metadump image
+
+# Type: CROM filesystem
+# From: Werner Fink <werner@suse.de>
+0 string CROMFS CROMFS
+>6 string >\0 \b version %2.2s,
+>8 ulequad >0 \b block data at %lld,
+>16 ulequad >0 \b fblock table at %lld,
+>24 ulequad >0 \b inode table at %lld,
+>32 ulequad >0 \b root at %lld,
+>40 ulelong >0 \b fblock size = %d,
+>44 ulelong >0 \b block size = %d,
+>48 ulequad >0 \b bytes = %lld
+
+# Type: xfs metadump image
+# From: Daniel Novotny <dnovotny@redhat.com>
+# mb_magic XFSM at 0; superblock magic XFSB at 1 << mb_blocklog
+# but can we do the << ? For now it's always 512 (0x200) anyway.
+0 string XFSM
+>0x200 string XFSB XFS filesystem metadump image
+
+# Type: delta ISO
+# From: Daniel Novotny <dnovotny@redhat.com>
+0 string DISO Delta ISO data,
+>4 belong x version %d
+
+# JFS2 (Journaling File System) image. (Old JFS1 has superblock at 0x1000.)
+# See linux/fs/jfs/jfs_superblock.h for layout; see jfs_filsys.h for flags.
+# From: Adam Buchbinder <adam.buchbinder@gmail.com>
+0x8000 string JFS1
+# Because it's text-only magic, check a binary value (version) to be sure.
+# Should always be 2, but mkfs.jfs writes it as 1. Needs to be 2 or 1 to be
+# mountable.
+>&0 lelong <3 JFS2 filesystem image
+# Label is followed by a UUID; we have to limit string length to avoid
+# appending the UUID in the case of a 16-byte label.
+>>&144 regex [\x20-\x7E]{1,16} (label "%s")
+>>&0 lequad x \b, %lld blocks
+>>&8 lelong x \b, blocksize %d
+>>&32 lelong&0x00000006 >0 (dirty)
+>>&36 lelong >0 (compressed)
+
+# LFS
+0 lelong 0x070162 LFS filesystem image
+>4 lelong 1 version 1,
+>>8 lelong x \b blocks %u,
+>>12 lelong x \b blocks per segment %u,
+>4 lelong 2 version 2,
+>>8 lelong x \b fragments %u,
+>>12 lelong x \b bytes per segment %u,
+>16 lelong x \b disk blocks %u,
+>20 lelong x \b block size %u,
+>24 lelong x \b fragment size %u,
+>28 lelong x \b fragments per block %u,
+>32 lelong x \b start for free list %u,
+>36 lelong x \b number of free blocks %d,
+>40 lelong x \b number of files %u,
+>44 lelong x \b blocks available for writing %d,
+>48 lelong x \b inodes in cache %d,
+>52 lelong x \b inode file disk address 0x%x,
+>56 lelong x \b inode file inode number %u,
+>60 lelong x \b address of last segment written 0x%x,
+>64 lelong x \b address of next segment to write 0x%x,
+>68 lelong x \b address of current segment written 0x%x
+
+0 string td\000 floppy image data (TeleDisk, compressed)
+0 string TD\000 floppy image data (TeleDisk)
+
+0 string CQ\024 floppy image data (CopyQM,
+>16 leshort x %d sectors,
+>18 leshort x %d heads.)
+
+0 string ACT\020Apricot\020disk\020image\032\004 floppy image data (ApriDisk)
+
+0 beshort 0xAA58 floppy image data (IBM SaveDskF, old)
+0 beshort 0xAA59 floppy image data (IBM SaveDskF)
+0 beshort 0xAA5A floppy image data (IBM SaveDskF, compressed)
+
+0 string \074CPM_Disk\076 disk image data (YAZE)
+
+# ReFS
+# Richard W.M. Jones <rjones@redhat.com>
+0 string \0\0\0ReFS\0 ReFS filesystem image
+
+# EFW encase image file format:
+# Gregoire Passault
+# http://www.forensicswiki.org/wiki/Encase_image_file_format
+0 string EVF\x09\x0d\x0a\xff\x00 EWF/Expert Witness/EnCase image file format
+
+# UBIfs
+# Linux kernel sources: fs/ubifs/ubifs-media.h
+0 lelong 0x06101831
+>0x16 leshort 0 UBIfs image
+>0x08 lequad x \b, sequence number %llu
+>0x10 leshort x \b, length %u
+>0x04 lelong x \b, CRC 0x%08x
+
+0 lelong 0x23494255
+>0x04 leshort <2
+>0x05 string \0\0\0
+>0x1c string \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0
+>0x04 leshort x UBI image, version %u
+
+# NEC PC-88 2D disk image
+# From Fabio R. Schmidlin <sd-snatcher@users.sourceforge.net>
+0x20 ulelong&0xFFFFFEFF 0x2A0
+>0x10 string \0\0\0\0\0\0\0\0\0\0
+>>0x280 string \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0
+>>>0x1A ubyte&0xEF 0
+>>>>0x1B ubyte&0x8F 0
+>>>>>0x1B ubyte&70 <0x40
+>>>>>>0x1C ulelong >0x21
+>>>>>>>0 regex [[:print:]]* NEC PC-88 disk image, name=%s
+>>>>>>>>0x1B ubyte 0 \b, media=2D
+>>>>>>>>0x1B ubyte 0x10 \b, media=2DD
+>>>>>>>>0x1B ubyte 0x20 \b, media=2HD
+>>>>>>>>0x1B ubyte 0x30 \b, media=1D
+>>>>>>>>0x1B ubyte 0x40 \b, media=1DD
+>>>>>>>>0x1A ubyte 0x10 \b, write-protected
+
+#------------------------------------------------------------------------------
+# $File: flash,v 1.10 2014/03/06 16:07:24 christos Exp $
+# flash: file(1) magic for Macromedia Flash file format
+#
+# See
+#
+# http://www.macromedia.com/software/flash/open/
+# http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/\
+# en/devnet/swf/pdf/swf-file-format-spec.pdf page 27
+#
+
+0 name swf-details
+>0 string F Macromedia Flash data
+!:mime application/x-shockwave-flash
+>0 string C Macromedia Flash data (compressed)
+!:mime application/x-shockwave-flash
+>0 string Z Macromedia Flash data (lzma compressed)
+!:mime application/x-shockwave-flash
+>3 byte x \b, version %d
+
+1 string WS
+>4 lelong !0
+>>3 byte 255 Suspicious
+>>>0 use swf-details
+
+>>3 ubyte <32
+>>>3 ubyte !0
+>>>>0 use swf-details
+
+# From: Cal Peake <cp@absolutedigital.net>
+0 string FLV\x01 Macromedia Flash Video
+!:mime video/x-flv
+
+#
+# Yosu Gomez
+0 string AGD2\xbe\xb8\xbb\xcd\x00 Macromedia Freehand 7 Document
+0 string AGD3\xbe\xb8\xbb\xcc\x00 Macromedia Freehand 8 Document
+# From Dave Wilson
+0 string AGD4\xbe\xb8\xbb\xcb\x00 Macromedia Freehand 9 Document
+
+#------------------------------------------------------------------------------
+# $File: fonts,v 1.26 2013/03/09 22:36:00 christos Exp $
+# fonts: file(1) magic for font data
+#
+0 search/1 FONT ASCII vfont text
+0 short 0436 Berkeley vfont data
+0 short 017001 byte-swapped Berkeley vfont data
+
+# PostScript fonts (must precede "printer" entries), quinlan@yggdrasil.com
+0 string %!PS-AdobeFont-1. PostScript Type 1 font text
+>20 string >\0 (%s)
+6 string %!PS-AdobeFont-1. PostScript Type 1 font program data
+0 string %!FontType1 PostScript Type 1 font program data
+6 string %!FontType1 PostScript Type 1 font program data
+0 string %!PS-Adobe-3.0\ Resource-Font PostScript Type 1 font text
+
+# X11 font files in SNF (Server Natural Format) format
+# updated by Joerg Jenderek at Feb 2013
+# http://computer-programming-forum.com/51-perl/8f22fb96d2e34bab.htm
+0 belong 00000004 X11 SNF font data, MSB first
+#>104 belong 00000004 X11 SNF font data, MSB first
+!:mime application/x-font-sfn
+# GRR: line below too general as it catches also Xbase index file t3-CHAR.NDX
+0 lelong 00000004
+>104 lelong 00000004 X11 SNF font data, LSB first
+!:mime application/x-font-sfn
+
+# X11 Bitmap Distribution Format, from Daniel Quinlan (quinlan@yggdrasil.com)
+0 search/1 STARTFONT\ X11 BDF font text
+
+# X11 fonts, from Daniel Quinlan (quinlan@yggdrasil.com)
+# PCF must come before SGI additions ("MIPSEL MIPS-II COFF" collides)
+0 string \001fcp X11 Portable Compiled Font data
+>12 byte 0x02 \b, LSB first
+>12 byte 0x0a \b, MSB first
+0 string D1.0\015 X11 Speedo font data
+
+#------------------------------------------------------------------------------
+# FIGlet fonts and controlfiles
+# From figmagic supplied with Figlet version 2.2
+# "David E. O'Brien" <obrien@FreeBSD.ORG>
+0 string flf FIGlet font
+>3 string >2a version %-2.2s
+0 string flc FIGlet controlfile
+>3 string >2a version %-2.2s
+
+# libGrx graphics lib fonts, from Albert Cahalan (acahalan@cs.uml.edu)
+# Used with djgpp (DOS Gnu C++), sometimes Linux or Turbo C++
+0 belong 0x14025919 libGrx font data,
+>8 leshort x %dx
+>10 leshort x \b%d
+>40 string x %s
+# Misc. DOS VGA fonts, from Albert Cahalan (acahalan@cs.uml.edu)
+0 belong 0xff464f4e DOS code page font data collection
+7 belong 0x00454741 DOS code page font data
+7 belong 0x00564944 DOS code page font data (from Linux?)
+4098 string DOSFONT DOSFONT2 encrypted font data
+
+# downloadable fonts for browser (prints type) anthon@mnt.org
+0 string PFR1 PFR1 font
+>102 string >0 \b: %s
+
+# True Type fonts
+0 string \000\001\000\000\000 TrueType font data
+!:mime application/x-font-ttf
+
+0 string \007\001\001\000Copyright\ (c)\ 199 Adobe Multiple Master font
+0 string \012\001\001\000Copyright\ (c)\ 199 Adobe Multiple Master font
+
+# TrueType/OpenType font collections (.ttc)
+# http://www.microsoft.com/typography/otspec/otff.htm
+0 string ttcf TrueType font collection data
+>4 belong 0x00010000 \b, 1.0
+>>8 belong >0 \b, %d fonts
+>4 belong 0x00020000 \b, 2.0
+>>8 belong >0 \b, %d fonts
+# 0x44454947 = 'DSIG'
+>>>16 belong 0x44534947 \b, digitally signed
+
+# Opentype font data from Avi Bercovich
+0 string OTTO OpenType font data
+!:mime application/vnd.ms-opentype
+
+# Gurkan Sengun <gurkan@linuks.mine.nu>, www.linuks.mine.nu
+0 string SplineFontDB: Spline Font Database
+!:mime application/vnd.font-fontforge-sfd
+>14 string x version %s
+
+# EOT
+34 string LP Embedded OpenType (EOT)
+!:mime application/vnd.ms-fontobject
+
+# Web Open Font Format (.woff)
+# http://www.w3.org/TR/WOFF/
+0 string wOFF Web Open Font Format
+>4 belong x \b, flavor %d
+>8 belong x \b, length %d
+>20 beshort x \b, version %d
+>22 beshort x \b.%d
+
+#------------------------------------------------------------------------------
+# $File: fortran,v 1.7 2012/06/21 01:55:02 christos Exp $
+# FORTRAN source
+0 regex/100l \^[Cc][\ \t] FORTRAN program
+!:mime text/x-fortran
+!:strength - 5
+
+#------------------------------------------------------------------------------
+# $File$
+# frame: file(1) magic for FrameMaker files
+#
+# This stuff came on a FrameMaker demo tape, most of which is
+# copyright, but this file is "published" as witness the following:
+#
+# Note that this is the Framemaker Maker Interchange Format, not the
+# Normal format which would be application/vnd.framemaker.
+#
+0 string \<MakerFile FrameMaker document
+!:mime application/x-mif
+>11 string 5.5 (5.5
+>11 string 5.0 (5.0
+>11 string 4.0 (4.0
+>11 string 3.0 (3.0
+>11 string 2.0 (2.0
+>11 string 1.0 (1.0
+>14 byte x %c)
+0 string \<MIFFile FrameMaker MIF (ASCII) file
+!:mime application/x-mif
+>9 string 4.0 (4.0)
+>9 string 3.0 (3.0)
+>9 string 2.0 (2.0)
+>9 string 1.0 (1.x)
+0 search/1 \<MakerDictionary FrameMaker Dictionary text
+!:mime application/x-mif
+>17 string 3.0 (3.0)
+>17 string 2.0 (2.0)
+>17 string 1.0 (1.x)
+0 string \<MakerScreenFont FrameMaker Font file
+!:mime application/x-mif
+>17 string 1.01 (%s)
+0 string \<MML FrameMaker MML file
+!:mime application/x-mif
+0 string \<BookFile FrameMaker Book file
+!:mime application/x-mif
+>10 string 3.0 (3.0
+>10 string 2.0 (2.0
+>10 string 1.0 (1.0
+>13 byte x %c)
+# XXX - this book entry should be verified, if you find one, uncomment this
+#0 string \<Book\ FrameMaker Book (ASCII) file
+#!:mime application/x-mif
+#>6 string 3.0 (3.0)
+#>6 string 2.0 (2.0)
+#>6 string 1.0 (1.0)
+0 string \<Maker Intermediate Print File FrameMaker IPL file
+!:mime application/x-mif
+
+#------------------------------------------------------------------------------
+# $File$
+# freebsd: file(1) magic for FreeBSD objects
+#
+# All new-style FreeBSD magic numbers are in host byte order (i.e.,
+# little-endian on x86).
+#
+# XXX - this comes from the file "freebsd" in a recent FreeBSD version of
+# "file"; it, and the NetBSD stuff in "netbsd", appear to use different
+# schemes for distinguishing between executable images, shared libraries,
+# and object files.
+#
+# FreeBSD says:
+#
+# Regardless of whether it's pure, demand-paged, or none of the
+# above:
+#
+# if the entry point is < 4096, then it's a shared library if
+# the "has run-time loader information" bit is set, and is
+# position-independent if the "is position-independent" bit
+# is set;
+#
+# if the entry point is >= 4096 (or >4095, same thing), then it's
+# an executable, and is dynamically-linked if the "has run-time
+# loader information" bit is set.
+#
+# On x86, NetBSD says:
+#
+# If it's neither pure nor demand-paged:
+#
+# if it has the "has run-time loader information" bit set, it's
+# a dynamically-linked executable;
+#
+# if it doesn't have that bit set, then:
+#
+# if it has the "is position-independent" bit set, it's
+# position-independent;
+#
+# if the entry point is non-zero, it's an executable, otherwise
+# it's an object file.
+#
+# If it's pure:
+#
+# if it has the "has run-time loader information" bit set, it's
+# a dynamically-linked executable, otherwise it's just an
+# executable.
+#
+# If it's demand-paged:
+#
+# if it has the "has run-time loader information" bit set,
+# then:
+#
+# if the entry point is < 4096, it's a shared library;
+#
+# if the entry point is = 4096 or > 4096 (i.e., >= 4096),
+# it's a dynamically-linked executable);
+#
+# if it doesn't have the "has run-time loader information" bit
+# set, then it's just an executable.
+#
+# (On non-x86, NetBSD does much the same thing, except that it uses
+# 8192 on 68K - except for "68k4k", which is presumably "68K with 4K
+# pages - SPARC, and MIPS, presumably because Sun-3's and Sun-4's
+# had 8K pages; dunno about MIPS.)
+#
+# I suspect the two will differ only in perverse and uninteresting cases
+# ("shared" libraries that aren't demand-paged and whose pages probably
+# won't actually be shared, executables with entry points <4096).
+#
+# I leave it to those more familiar with FreeBSD and NetBSD to figure out
+# what the right answer is (although using ">4095", FreeBSD-style, is
+# probably better than separately checking for "=4096" and ">4096",
+# NetBSD-style). (The old "netbsd" file analyzed FreeBSD demand paged
+# executables using the NetBSD technique.)
+#
+0 lelong&0377777777 041400407 FreeBSD/i386
+>20 lelong <4096
+>>3 byte&0xC0 &0x80 shared library
+>>3 byte&0xC0 0x40 PIC object
+>>3 byte&0xC0 0x00 object
+>20 lelong >4095
+>>3 byte&0x80 0x80 dynamically linked executable
+>>3 byte&0x80 0x00 executable
+>16 lelong >0 not stripped
+
+0 lelong&0377777777 041400410 FreeBSD/i386 pure
+>20 lelong <4096
+>>3 byte&0xC0 &0x80 shared library
+>>3 byte&0xC0 0x40 PIC object
+>>3 byte&0xC0 0x00 object
+>20 lelong >4095
+>>3 byte&0x80 0x80 dynamically linked executable
+>>3 byte&0x80 0x00 executable
+>16 lelong >0 not stripped
+
+0 lelong&0377777777 041400413 FreeBSD/i386 demand paged
+>20 lelong <4096
+>>3 byte&0xC0 &0x80 shared library
+>>3 byte&0xC0 0x40 PIC object
+>>3 byte&0xC0 0x00 object
+>20 lelong >4095
+>>3 byte&0x80 0x80 dynamically linked executable
+>>3 byte&0x80 0x00 executable
+>16 lelong >0 not stripped
+
+0 lelong&0377777777 041400314 FreeBSD/i386 compact demand paged
+>20 lelong <4096
+>>3 byte&0xC0 &0x80 shared library
+>>3 byte&0xC0 0x40 PIC object
+>>3 byte&0xC0 0x00 object
+>20 lelong >4095
+>>3 byte&0x80 0x80 dynamically linked executable
+>>3 byte&0x80 0x00 executable
+>16 lelong >0 not stripped
+
+# XXX gross hack to identify core files
+# cores start with a struct tss; we take advantage of the following:
+# byte 7: highest byte of the kernel stack pointer, always 0xfe
+# 8/9: kernel (ring 0) ss value, always 0x0010
+# 10 - 27: ring 1 and 2 ss/esp, unused, thus always 0
+# 28: low order byte of the current PTD entry, always 0 since the
+# PTD is page-aligned
+#
+7 string \357\020\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 FreeBSD/i386 a.out core file
+>1039 string >\0 from '%s'
+
+# /var/run/ld.so.hints
+# What are you laughing about?
+0 lelong 011421044151 ld.so hints file (Little Endian
+>4 lelong >0 \b, version %d)
+>4 belong <1 \b)
+0 belong 011421044151 ld.so hints file (Big Endian
+>4 belong >0 \b, version %d)
+>4 belong <1 \b)
+
+#
+# Files generated by FreeBSD scrshot(1)/vidcontrol(1) utilities
+#
+0 string SCRSHOT_ scrshot(1) screenshot,
+>8 byte x version %d,
+>9 byte 2 %d bytes in header,
+>>10 byte x %d chars wide by
+>>11 byte x %d chars high
+
+#------------------------------------------------------------------------------
+# $File: fsav,v 1.12 2013/03/23 14:15:30 christos Exp $
+# fsav: file(1) magic for datafellows fsav virus definition files
+# Anthon van der Neut (anthon@mnt.org)
+
+# ftp://ftp.f-prot.com/pub/{macrdef2.zip,nomacro.def}
+0 beshort 0x1575 fsav macro virus signatures
+>8 leshort >0 (%d-
+>11 byte >0 \b%02d-
+>10 byte >0 \b%02d)
+# ftp://ftp.f-prot.com/pub/sign.zip
+#10 ubyte <12
+#>9 ubyte <32
+#>>8 ubyte 0x0a
+#>>>12 ubyte 0x07
+#>>>>11 uleshort >0 fsav DOS/Windows virus signatures (%d-
+#>>>>10 byte 0 \b01-
+#>>>>10 byte 1 \b02-
+#>>>>10 byte 2 \b03-
+#>>>>10 byte 3 \b04-
+#>>>>10 byte 4 \b05-
+#>>>>10 byte 5 \b06-
+#>>>>10 byte 6 \b07-
+#>>>>10 byte 7 \b08-
+#>>>>10 byte 8 \b09-
+#>>>>10 byte 9 \b10-
+#>>>>10 byte 10 \b11-
+#>>>>10 byte 11 \b12-
+#>>>>9 ubyte >0 \b%02d)
+# ftp://ftp.f-prot.com/pub/sign2.zip
+#0 ubyte 0x62
+#>1 ubyte 0xF5
+#>>2 ubyte 0x1
+#>>>3 ubyte 0x1
+#>>>>4 ubyte 0x0e
+#>>>>>13 ubyte >0 fsav virus signatures
+#>>>>>>11 ubyte x size 0x%02x
+#>>>>>>12 ubyte x \b%02x
+#>>>>>>13 ubyte x \b%02x bytes
+
+# Joerg Jenderek: joerg dot jenderek at web dot de
+# http://www.clamav.net/doc/latest/html/node45.html
+# .cvd files start with a 512 bytes colon separated header
+# ClamAV-VDB:buildDate:version:signaturesNumbers:functionalityLevelRequired:MD5:Signature:builder:buildTime
+# + gzipped tarball files
+0 string ClamAV-VDB:
+>11 string >\0 Clam AntiVirus database %-.23s
+>>34 string :
+>>>35 string !: \b, version
+>>>>35 string x \b%-.1s
+>>>>>36 string !:
+>>>>>>36 string x \b%-.1s
+>>>>>>>37 string !:
+>>>>>>>>37 string x \b%-.1s
+>>>>>>>>>38 string !:
+>>>>>>>>>>38 string x \b%-.1s
+>512 string \037\213 \b, gzipped
+>769 string ustar\0 \b, tarred
+
+# Type: Grisoft AVG AntiVirus
+# From: David Newgas <david@newgas.net>
+0 string AVG7_ANTIVIRUS_VAULT_FILE AVG 7 Antivirus vault file data
+
+0 string X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR
+>33 string -STANDARD-ANTIVIRUS-TEST-FILE!$H+H* EICAR virus test files
+
+#------------------------------------------------------------------------------
+# $File: mcrypt,v 1.5 2009/09/19 16:28:10 christos Exp $
+# fusecompress: file(1) magic for fusecompress
+0 string \037\135\211 FuseCompress(ed) data
+>3 byte 0x00 (none format)
+>3 byte 0x01 (bz2 format)
+>3 byte 0x02 (gz format)
+>3 byte 0x03 (lzo format)
+>3 byte 0x04 (xor format)
+>3 byte >0x04 (unknown format)
+>4 long x uncompressed size: %d
+
+#------------------------------------------------------------------------------
+# $File: games,v 1.13 2012/02/13 22:50:50 christos Exp $
+# games: file(1) for games
+
+# Fabio Bonelli <fabiobonelli@libero.it>
+# Quake II - III data files
+0 string IDP2 Quake II 3D Model file,
+>20 long x %u skin(s),
+>8 long x (%u x
+>12 long x %u),
+>40 long x %u frame(s),
+>16 long x Frame size %u bytes,
+>24 long x %u vertices/frame,
+>28 long x %u texture coordinates,
+>32 long x %u triangles/frame
+
+0 string IBSP Quake
+>4 long 0x26 II Map file (BSP)
+>4 long 0x2E III Map file (BSP)
+
+0 string IDS2 Quake II SP2 sprite file
+
+#---------------------------------------------------------------------------
+# Doom and Quake
+# submitted by Nicolas Patrois
+
+0 string \xcb\x1dBoom\xe6\xff\x03\x01 Boom or linuxdoom demo
+# some doom lmp files don't match, I've got one beginning with \x6d\x02\x01\x01
+
+24 string LxD\ 203 Linuxdoom save
+>0 string x , name=%s
+>44 string x , world=%s
+
+# Quake
+
+0 string PACK Quake I or II world or extension
+>8 lelong >0 \b, %d entries
+
+#0 string -1\x0a Quake I demo
+#>30 string x version %.4s
+#>61 string x level %s
+
+#0 string 5\x0a Quake I save
+
+# The levels
+
+# Quake 1
+
+0 string 5\x0aIntroduction Quake I save: start Introduction
+0 string 5\x0athe_Slipgate_Complex Quake I save: e1m1 The slipgate complex
+0 string 5\x0aCastle_of_the_Damned Quake I save: e1m2 Castle of the damned
+0 string 5\x0athe_Necropolis Quake I save: e1m3 The necropolis
+0 string 5\x0athe_Grisly_Grotto Quake I save: e1m4 The grisly grotto
+0 string 5\x0aZiggurat_Vertigo Quake I save: e1m8 Ziggurat vertigo (secret)
+0 string 5\x0aGloom_Keep Quake I save: e1m5 Gloom keep
+0 string 5\x0aThe_Door_To_Chthon Quake I save: e1m6 The door to Chthon
+0 string 5\x0aThe_House_of_Chthon Quake I save: e1m7 The house of Chthon
+0 string 5\x0athe_Installation Quake I save: e2m1 The installation
+0 string 5\x0athe_Ogre_Citadel Quake I save: e2m2 The ogre citadel
+0 string 5\x0athe_Crypt_of_Decay Quake I save: e2m3 The crypt of decay (dopefish lives!)
+0 string 5\x0aUnderearth Quake I save: e2m7 Underearth (secret)
+0 string 5\x0athe_Ebon_Fortress Quake I save: e2m4 The ebon fortress
+0 string 5\x0athe_Wizard's_Manse Quake I save: e2m5 The wizard's manse
+0 string 5\x0athe_Dismal_Oubliette Quake I save: e2m6 The dismal oubliette
+0 string 5\x0aTermination_Central Quake I save: e3m1 Termination central
+0 string 5\x0aVaults_of_Zin Quake I save: e3m2 Vaults of Zin
+0 string 5\x0athe_Tomb_of_Terror Quake I save: e3m3 The tomb of terror
+0 string 5\x0aSatan's_Dark_Delight Quake I save: e3m4 Satan's dark delight
+0 string 5\x0athe_Haunted_Halls Quake I save: e3m7 The haunted halls (secret)
+0 string 5\x0aWind_Tunnels Quake I save: e3m5 Wind tunnels
+0 string 5\x0aChambers_of_Torment Quake I save: e3m6 Chambers of torment
+0 string 5\x0athe_Sewage_System Quake I save: e4m1 The sewage system
+0 string 5\x0aThe_Tower_of_Despair Quake I save: e4m2 The tower of despair
+0 string 5\x0aThe_Elder_God_Shrine Quake I save: e4m3 The elder god shrine
+0 string 5\x0athe_Palace_of_Hate Quake I save: e4m4 The palace of hate
+0 string 5\x0aHell's_Atrium Quake I save: e4m5 Hell's atrium
+0 string 5\x0athe_Nameless_City Quake I save: e4m8 The nameless city (secret)
+0 string 5\x0aThe_Pain_Maze Quake I save: e4m6 The pain maze
+0 string 5\x0aAzure_Agony Quake I save: e4m7 Azure agony
+0 string 5\x0aShub-Niggurath's_Pit Quake I save: end Shub-Niggurath's pit
+
+# Quake DeathMatch levels
+
+0 string 5\x0aPlace_of_Two_Deaths Quake I save: dm1 Place of two deaths
+0 string 5\x0aClaustrophobopolis Quake I save: dm2 Claustrophobopolis
+0 string 5\x0aThe_Abandoned_Base Quake I save: dm3 The abandoned base
+0 string 5\x0aThe_Bad_Place Quake I save: dm4 The bad place
+0 string 5\x0aThe_Cistern Quake I save: dm5 The cistern
+0 string 5\x0aThe_Dark_Zone Quake I save: dm6 The dark zone
+
+# Scourge of Armagon
+
+0 string 5\x0aCommand_HQ Quake I save: start Command HQ
+0 string 5\x0aThe_Pumping_Station Quake I save: hip1m1 The pumping station
+0 string 5\x0aStorage_Facility Quake I save: hip1m2 Storage facility
+0 string 5\x0aMilitary_Complex Quake I save: hip1m5 Military complex (secret)
+0 string 5\x0athe_Lost_Mine Quake I save: hip1m3 The lost mine
+0 string 5\x0aResearch_Facility Quake I save: hip1m4 Research facility
+0 string 5\x0aAncient_Realms Quake I save: hip2m1 Ancient realms
+0 string 5\x0aThe_Gremlin's_Domain Quake I save: hip2m6 The gremlin's domain (secret)
+0 string 5\x0aThe_Black_Cathedral Quake I save: hip2m2 The black cathedral
+0 string 5\x0aThe_Catacombs Quake I save: hip2m3 The catacombs
+0 string 5\x0athe_Crypt__ Quake I save: hip2m4 The crypt
+0 string 5\x0aMortum's_Keep Quake I save: hip2m5 Mortum's keep
+0 string 5\x0aTur_Torment Quake I save: hip3m1 Tur torment
+0 string 5\x0aPandemonium Quake I save: hip3m2 Pandemonium
+0 string 5\x0aLimbo Quake I save: hip3m3 Limbo
+0 string 5\x0athe_Edge_of_Oblivion Quake I save: hipdm1 The edge of oblivion (secret)
+0 string 5\x0aThe_Gauntlet Quake I save: hip3m4 The gauntlet
+0 string 5\x0aArmagon's_Lair Quake I save: hipend Armagon's lair
+
+# Malice
+
+0 string 5\x0aThe_Academy Quake I save: start The academy
+0 string 5\x0aThe_Lab Quake I save: d1 The lab
+0 string 5\x0aArea_33 Quake I save: d1b Area 33
+0 string 5\x0aSECRET_MISSIONS Quake I save: d3b Secret missions
+0 string 5\x0aThe_Hospital Quake I save: d10 The hospital (secret)
+0 string 5\x0aThe_Genetics_Lab Quake I save: d11 The genetics lab (secret)
+0 string 5\x0aBACK_2_MALICE Quake I save: d4b Back to Malice
+0 string 5\x0aArea44 Quake I save: d1c Area 44
+0 string 5\x0aTakahiro_Towers Quake I save: d2 Takahiro towers
+0 string 5\x0aA_Rat's_Life Quake I save: d3 A rat's life
+0 string 5\x0aInto_The_Flood Quake I save: d4 Into the flood
+0 string 5\x0aThe_Flood Quake I save: d5 The flood
+0 string 5\x0aNuclear_Plant Quake I save: d6 Nuclear plant
+0 string 5\x0aThe_Incinerator_Plant Quake I save: d7 The incinerator plant
+0 string 5\x0aThe_Foundry Quake I save: d7b The foundry
+0 string 5\x0aThe_Underwater_Base Quake I save: d8 The underwater base
+0 string 5\x0aTakahiro_Base Quake I save: d9 Takahiro base
+0 string 5\x0aTakahiro_Laboratories Quake I save: d12 Takahiro laboratories
+0 string 5\x0aStayin'_Alive Quake I save: d13 Stayin' alive
+0 string 5\x0aB.O.S.S._HQ Quake I save: d14 B.O.S.S. HQ
+0 string 5\x0aSHOWDOWN! Quake I save: d15 Showdown!
+
+# Malice DeathMatch levels
+
+0 string 5\x0aThe_Seventh_Precinct Quake I save: ddm1 The seventh precinct
+0 string 5\x0aSub_Station Quake I save: ddm2 Sub station
+0 string 5\x0aCrazy_Eights! Quake I save: ddm3 Crazy eights!
+0 string 5\x0aEast_Side_Invertationa Quake I save: ddm4 East side invertationa
+0 string 5\x0aSlaughterhouse Quake I save: ddm5 Slaughterhouse
+0 string 5\x0aDOMINO Quake I save: ddm6 Domino
+0 string 5\x0aSANDRA'S_LADDER Quake I save: ddm7 Sandra's ladder
+
+
+0 string MComprHD MAME CHD compressed hard disk image,
+>12 belong x version %u
+
+# doom - submitted by Jon Dowland
+
+0 string =IWAD doom main IWAD data
+>4 lelong x containing %d lumps
+0 string =PWAD doom patch PWAD data
+>4 lelong x containing %d lumps
+
+# Build engine group files (Duke Nukem, Shadow Warrior, ...)
+# Extension: .grp
+# Created by: "Ganael Laplanche" <ganael.laplanche@martymac.org>
+0 string KenSilverman Build engine group file
+>12 lelong x containing %d files
+
+# Summary: Warcraft 3 save
+# Extension: .w3g
+# Created by: "Nelson A. de Oliveira" <naoliv@gmail.com>
+0 string Warcraft\ III\ recorded\ game %s
+
+
+# Summary: Warcraft 3 map
+# Extension: .w3m
+# Created by: "Nelson A. de Oliveira" <naoliv@gmail.com>
+0 string HM3W Warcraft III map file
+
+
+# Summary: SGF Smart Game Format
+# Extension: .sgf
+# Reference: http://www.red-bean.com/sgf/
+# Created by: Eduardo Sabbatella <eduardo_sabbatella@yahoo.com.ar>
+# Modified by (1): Abel Cheung (regex, more game format)
+# FIXME: Some games don't have GM (game type)
+0 regex \\(;.*GM\\[[0-9]{1,2}\\] Smart Game Format
+>2 search/0x200/b GM[
+>>&0 string 1] (Go)
+>>&0 string 2] (Othello)
+>>&0 string 3] (chess)
+>>&0 string 4] (Gomoku+Renju)
+>>&0 string 5] (Nine Men's Morris)
+>>&0 string 6] (Backgammon)
+>>&0 string 7] (Chinese chess)
+>>&0 string 8] (Shogi)
+>>&0 string 9] (Lines of Action)
+>>&0 string 10] (Ataxx)
+>>&0 string 11] (Hex)
+>>&0 string 12] (Jungle)
+>>&0 string 13] (Neutron)
+>>&0 string 14] (Philosopher's Football)
+>>&0 string 15] (Quadrature)
+>>&0 string 16] (Trax)
+>>&0 string 17] (Tantrix)
+>>&0 string 18] (Amazons)
+>>&0 string 19] (Octi)
+>>&0 string 20] (Gess)
+>>&0 string 21] (Twixt)
+>>&0 string 22] (Zertz)
+>>&0 string 23] (Plateau)
+>>&0 string 24] (Yinsh)
+>>&0 string 25] (Punct)
+>>&0 string 26] (Gobblet)
+>>&0 string 27] (hive)
+>>&0 string 28] (Exxit)
+>>&0 string 29] (Hnefatal)
+>>&0 string 30] (Kuba)
+>>&0 string 31] (Tripples)
+>>&0 string 32] (Chase)
+>>&0 string 33] (Tumbling Down)
+>>&0 string 34] (Sahara)
+>>&0 string 35] (Byte)
+>>&0 string 36] (Focus)
+>>&0 string 37] (Dvonn)
+>>&0 string 38] (Tamsk)
+>>&0 string 39] (Gipf)
+>>&0 string 40] (Kropki)
+
+##############################################
+# NetImmerse/Gamebryo game engine entries
+
+# Summary: Gamebryo game engine file
+# Extension: .nif, .kf
+# Created by: Abel Cheung <abelcheung@gmail.com>
+0 string Gamebryo\ File\ Format,\ Version\ Gamebryo game engine file
+>&0 regex [0-9a-z.]+ \b, version %s
+
+# Summary: Gamebryo game engine file
+# Extension: .kfm
+# Created by: Abel Cheung <abelcheung@gmail.com>
+0 string ;Gamebryo\ KFM\ File\ Version\ Gamebryo game engine animation File
+>&0 regex [0-9a-z.]+ \b, version %s
+
+# Summary: NetImmerse game engine file
+# Extension .nif
+# Created by: Abel Cheung <abelcheung@gmail.com>
+0 string NetImmerse\ File\ Format,\ Versio
+>&0 string n\ NetImmerse game engine file
+>>&0 regex [0-9a-z.]+ \b, version %s
+
+# Type: SGF Smart Game Format
+# URL: http://www.red-bean.com/sgf/
+# From: Eduardo Sabbatella <eduardo_sabbatella@yahoo.com.ar>
+2 regex/c \\(;.*GM\\[[0-9]{1,2}\\] Smart Game Format
+>2 regex/c GM\\[1\\] - Go Game
+>2 regex/c GM\\[6\\] - BackGammon Game
+>2 regex/c GM\\[11\\] - Hex Game
+>2 regex/c GM\\[18\\] - Amazons Game
+>2 regex/c GM\\[19\\] - Octi Game
+>2 regex/c GM\\[20\\] - Gess Game
+>2 regex/c GM\\[21\\] - twix Game
+
+# Epic Games/Unreal Engine Package
+#
+0 lelong 0x9E2A83C1 Unreal Engine Package,
+>4 leshort x version: %i
+>12 lelong !0 \b, names: %i
+>28 lelong !0 \b, imports: %i
+>20 lelong !0 \b, exports: %i
+
+#------------------------------------------------------------------------------
+# $File$
+# gcc: file(1) magic for GCC special files
+#
+0 string gpch GCC precompiled header
+
+# The version field is annoying. It's 3 characters, not zero-terminated.
+>5 byte x (version %c
+>6 byte x \b%c
+>7 byte x \b%c)
+
+# 67 = 'C', 111 = 'o', 43 = '+', 79 = 'O'
+>4 byte 67 for C
+>4 byte 111 for Objective C
+>4 byte 43 for C++
+>4 byte 79 for Objective C++
+
+#------------------------------------------------------------------------------
+# $File: geo,v 1.2 2013/01/02 15:27:53 christos Exp $
+# Geo- files from Kurt Schwehr <schwehr@ccom.unh.edu>
+
+######################################################################
+#
+# Acoustic Doppler Current Profilers (ADCP)
+#
+######################################################################
+
+0 beshort 0x7f7f RDI Acoustic Doppler Current Profiler (ADCP)
+
+######################################################################
+#
+# Metadata
+#
+######################################################################
+
+0 string Identification_Information FGDC ASCII metadata
+
+######################################################################
+#
+# Seimsic / Subbottom
+#
+######################################################################
+
+# Knudsen subbottom chirp profiler - Binary File Format: B9
+# KEB D409-03167 V1.75 Huffman
+0 string KEB\ Knudsen seismic KEL binary (KEB) -
+>4 regex [-A-Z0-9]* Software: %s
+>>&1 regex V[0-9]*\.[0-9]* version %s
+
+######################################################################
+#
+# LIDAR - Laser altimetry or bathy
+#
+######################################################################
+
+
+# Caris LIDAR format for LADS comes as two parts... ascii location file and binary waveform data
+0 string HCA LADS Caris Ascii Format (CAF) bathymetric lidar
+>4 regex [0-9]*\.[0-9]* version %s
+
+0 string HCB LADS Caris Binary Format (CBF) bathymetric lidar waveform data
+>3 byte x version %d .
+>4 byte x %d
+
+
+######################################################################
+#
+# MULTIBEAM SONARS http://www.ldeo.columbia.edu/res/pi/MB-System/formatdoc/
+#
+######################################################################
+
+# GeoAcoustics - GeoSwath Plus
+4 beshort 0x2002 GeoSwath RDF
+0 string Start:- GeoSwatch auf text file
+
+# Seabeam 2100
+# mbsystem code mb41
+0 string SB2100 SeaBeam 2100 multibeam sonar
+0 string SB2100DR SeaBeam 2100 DR multibeam sonar
+0 string SB2100PR SeaBeam 2100 PR multibeam sonar
+
+# This corresponds to MB-System format 94, L-3/ELAC/SeaBeam XSE vendor
+# format. It is the format of our upgraded SeaBeam 2112 on R/V KNORR.
+0 string $HSF XSE multibeam
+
+# mb121 http://www.saic.com/maritime/gsf/
+8 string GSF-v SAIC generic sensor format (GSF) sonar data,
+>&0 regex [0-9]*\.[0-9]* version %s
+
+# MGD77 - http://www.ngdc.noaa.gov/mgg/dat/geodas/docs/mgd77.htm
+# mb161
+9 string MGD77 MGD77 Header, Marine Geophysical Data Exchange Format
+
+# MBSystem processing caches the mbinfo output
+1 string Swath\ Data\ File: mbsystem info cache
+
+# Caris John Hughes Clark format
+0 string HDCS Caris multibeam sonar related data
+1 string Start/Stop\ parameter\ header: Caris ASCII project summary
+
+######################################################################
+#
+# Visualization and 3D modeling
+#
+######################################################################
+
+# IVS - IVS3d.com Tagged Data Represetation
+0 string %%\ TDR\ 2.0 IVS Fledermaus TDR file
+
+# http://www.ecma-international.org/publications/standards/Ecma-363.htm
+# 3D in PDFs
+0 string U3D ECMA-363, Universal 3D
+
+######################################################################
+#
+# Support files
+#
+######################################################################
+
+# https://midas.psi.ch/elog/
+0 string $@MID@$ elog journal entry
+
+# Geospatial Designs http://www.geospatialdesigns.com/surfer6_format.htm
+0 string DSBB Surfer 6 binary grid file
+>4 leshort x \b, %d
+>6 leshort x \bx%d
+>8 ledouble x \b, minx=%g
+>16 ledouble x \b, maxx=%g
+>24 ledouble x \b, miny=%g
+>32 ledouble x \b, maxy=%g
+>40 ledouble x \b, minz=%g
+>48 ledouble x \b, maxz=%g
+
+
+#------------------------------------------------------------------------------
+# $File$
+# GEOS files (Vidar Madsen, vidar@gimp.org)
+# semi-commonly used in embedded and handheld systems.
+0 belong 0xc745c153 GEOS
+>40 byte 1 executable
+>40 byte 2 VMFile
+>40 byte 3 binary
+>40 byte 4 directory label
+>40 byte <1 unknown
+>40 byte >4 unknown
+>4 string >\0 \b, name "%s"
+#>44 short x \b, version %d
+#>46 short x \b.%d
+#>48 short x \b, rev %d
+#>50 short x \b.%d
+#>52 short x \b, proto %d
+#>54 short x \br%d
+#>168 string >\0 \b, copyright "%s"
+
+#------------------------------------------------------------------------------
+# $File: gimp,v 1.8 2013/12/21 14:29:45 christos Exp $
+# GIMP Gradient: file(1) magic for the GIMP's gradient data files (.ggr)
+# by Federico Mena <federico@nuclecu.unam.mx>
+
+0 string/t GIMP\ Gradient GIMP gradient data
+
+# GIMP palette (.gpl)
+# From: Markus Heidelberg <markus.heidelberg@web.de>
+0 string/t GIMP\ Palette GIMP palette data
+
+#------------------------------------------------------------------------------
+# XCF: file(1) magic for the XCF image format used in the GIMP (.xcf) developed
+# by Spencer Kimball and Peter Mattis
+# ('Bucky' LaDieu, nega@vt.edu)
+
+0 string gimp\ xcf GIMP XCF image data,
+!:mime image/x-xcf
+>9 string file version 0,
+>9 string v version
+>>10 string >\0 %s,
+>14 belong x %u x
+>18 belong x %u,
+>22 belong 0 RGB Color
+>22 belong 1 Greyscale
+>22 belong 2 Indexed Color
+>22 belong >2 Unknown Image Type.
+
+#------------------------------------------------------------------------------
+# XCF: file(1) magic for the patterns used in the GIMP (.pat), developed
+# by Spencer Kimball and Peter Mattis
+# ('Bucky' LaDieu, nega@vt.edu)
+
+20 string GPAT GIMP pattern data,
+>24 string x %s
+
+#------------------------------------------------------------------------------
+# XCF: file(1) magic for the brushes used in the GIMP (.gbr), developed
+# by Spencer Kimball and Peter Mattis
+# ('Bucky' LaDieu, nega@vt.edu)
+
+20 string GIMP GIMP brush data
+
+# GIMP Curves File
+# From: "Nelson A. de Oliveira" <naoliv@gmail.com>
+0 string #\040GIMP\040Curves\040File GIMP curve file
+
+#------------------------------------------------------------------------------
+# $File: gnome,v 1.4 2014/04/28 12:04:50 christos Exp $
+# GNOME related files
+
+# Contributed by Josh Triplett
+# FIXME: Could be simplified if pstring supported two-byte counts
+0 string GnomeKeyring\n\r\0\n GNOME keyring
+>&0 ubyte 0 \b, major version 0
+>>&0 ubyte 0 \b, minor version 0
+>>>&0 ubyte 0 \b, crypto type 0 (AES)
+>>>&0 ubyte >0 \b, crypto type %u (unknown)
+>>>&1 ubyte 0 \b, hash type 0 (MD5)
+>>>&1 ubyte >0 \b, hash type %u (unknown)
+>>>&2 ubelong 0xFFFFFFFF \b, name NULL
+>>>&2 ubelong !0xFFFFFFFF
+>>>>&-4 ubelong >255 \b, name too long for file's pstring type
+>>>>&-4 ubelong <256
+>>>>>&-1 pstring x \b, name "%s"
+>>>>>>&0 ubeqdate x \b, last modified %s
+>>>>>>&8 ubeqdate x \b, created %s
+>>>>>>&16 ubelong &1
+>>>>>>>&0 ubelong x \b, locked if idle for %u seconds
+>>>>>>&16 ubelong ^1 \b, not locked if idle
+>>>>>>&24 ubelong x \b, hash iterations %u
+>>>>>>&28 ubequad x \b, salt %llu
+>>>>>>&52 ubelong x \b, %u item(s)
+
+# From: Alex Beregszaszi <alex@fsn.hu>
+4 string gtktalog GNOME Catalogue (gtktalog)
+>13 string >\0 version %s
+
+# Summary: GStreamer binary registry
+# Extension: .bin
+# Submitted by: Josh Triplett <josh@joshtriplett.org>
+0 belong 0xc0def00d GStreamer binary registry
+>4 string x \b, version %s
+
+# GVariant Database file
+# By Elan Ruusamae <glen@delfi.ee>
+# https://github.com/GNOME/gvdb/blob/master/gvdb-format.h
+# It's always "GVariant", it's byte swapped on incompatible archs
+# See https://github.com/GNOME/gvdb/blob/master/gvdb-builder.c
+# file_builder_serialise()
+# http://developer.gnome.org/glib/2.34/glib-GVariant.html#GVariant
+0 string GVariant GVariant Database file,
+# version is never filled. probably future extension
+>8 lelong x version %d
+# not sure are these usable, so commented out
+#>>16 lelong x start %d,
+#>>>20 lelong x end %d
+
+# G-IR database made by gobject-introspect toolset,
+# http://live.gnome.org/GObjectIntrospection
+0 string GOBJ\nMETADATA\r\n\032 G-IR binary database
+>16 byte x \b, v%d
+>17 byte x \b.%d
+>20 leshort x \b, %d entries
+>22 leshort x \b/%d local
+
+#------------------------------------------------------------------------------
+# $File: gnu,v 1.14 2012/10/03 23:38:12 christos Exp $
+# gnu: file(1) magic for various GNU tools
+#
+# GNU nlsutils message catalog file format
+#
+# GNU message catalog (.mo and .gmo files)
+
+0 string \336\22\4\225 GNU message catalog (little endian),
+>6 leshort x revision %d.
+>4 leshort >0 \b%d,
+>>8 lelong x %d messages,
+>>36 lelong x %d sysdep messages
+>4 leshort =0 \b%d,
+>>8 lelong x %d messages
+
+0 string \225\4\22\336 GNU message catalog (big endian),
+>4 beshort x revision %d.
+>6 beshort >0 \b%d,
+>>8 belong x %d messages,
+>>36 belong x %d sysdep messages
+>6 beshort =0 \b%d,
+>>8 belong x %d messages
+
+
+# GnuPG
+# The format is very similar to pgp
+0 string \001gpg GPG key trust database
+>4 byte x version %d
+# Note: magic.mime had 0x8501 for the next line instead of 0x8502
+0 beshort 0x8502 GPG encrypted data
+!:mime text/PGP # encoding: data
+
+# This magic is not particularly good, as the keyrings don't have true
+# magic. Nevertheless, it covers many keyrings.
+0 beshort 0x9901 GPG key public ring
+!:mime application/x-gnupg-keyring
+
+# Symmetric encryption
+0 leshort 0x0d8c
+>4 leshort 0x0203
+>>2 leshort 0x0204 GPG symmetrically encrypted data (3DES cipher)
+>>2 leshort 0x0304 GPG symmetrically encrypted data (CAST5 cipher)
+>>2 leshort 0x0404 GPG symmetrically encrypted data (BLOWFISH cipher)
+>>2 leshort 0x0704 GPG symmetrically encrypted data (AES cipher)
+>>2 leshort 0x0804 GPG symmetrically encrypted data (AES192 cipher)
+>>2 leshort 0x0904 GPG symmetrically encrypted data (AES256 cipher)
+>>2 leshort 0x0a04 GPG symmetrically encrypted data (TWOFISH cipher)
+>>2 leshort 0x0b04 GPG symmetrically encrypted data (CAMELLIA128 cipher)
+>>2 leshort 0x0c04 GPG symmetrically encrypted data (CAMELLIA192 cipher)
+>>2 leshort 0x0d04 GPG symmetrically encrypted data (CAMELLIA256 cipher)
+
+
+# GnuPG Keybox file
+# <http://git.gnupg.org/cgi-bin/gitweb.cgi?p=gnupg.git;a=blob;f=kbx/keybox-blob.c;hb=HEAD>
+# From: Philipp Hahn <hahn@univention.de>
+0 belong 32
+>4 byte 1
+>>8 string KBXf GPG keybox database
+>>>5 byte 1 version %d
+>>>16 bedate x \b, created-at %s
+>>>20 bedate x \b, last-maintained %s
+
+
+# Gnumeric spreadsheet
+# This entry is only semi-helpful, as Gnumeric compresses its files, so
+# they will ordinarily reported as "compressed", but at least -z helps
+39 string =<gmr:Workbook Gnumeric spreadsheet
+
+# From: James Youngman <jay@gnu.org>
+# gnu find magic
+0 string \0LOCATE GNU findutils locate database data
+>7 string >\0 \b, format %s
+>7 string 02 \b (frcode)
+
+# Files produced by GNU gettext
+0 long 0xDE120495 GNU-format message catalog data
+0 long 0x950412DE GNU-format message catalog data
+
+# gettext message catalogue
+0 regex \^msgid\ GNU gettext message catalogue text
+!:mime text/x-po
+
+#------------------------------------------------------------------------------
+# $File$
+# gnumeric: file(1) magic for Gnumeric spreadsheet
+# This entry is only semi-helpful, as Gnumeric compresses its files, so
+# they will ordinarily reported as "compressed", but at least -z helps
+39 string =<gmr:Workbook Gnumeric spreadsheet
+!:mime application/x-gnumeric
+
+#------------------------------------------------------------------------------
+# $File: gpt,v 1.2 2014/04/28 12:04:50 christos Exp $
+#
+# GPT Partition table patterns.
+# Author: Rogier Goossens (goossens.rogier@gmail.com)
+# Note that a GPT-formatted disk must contain an MBR as well.
+#
+
+# The initial segment (up to >>>>>>>>422) was copied from the X86
+# partition table code (aka MBR).
+# This is kept separate, so that MBR partitions are not reported as well.
+# (use -k if you do want them as well)
+
+# First, detect the MBR partiton table
+# If more than one GPT protective MBR partition exists, don't print anything
+# (the other MBR detection code will then just print the MBR partition table)
+0x1FE leshort 0xAA55
+>3 string !MS
+>>3 string !SYSLINUX
+>>>3 string !MTOOL
+>>>>3 string !NEWLDR
+>>>>>5 string !DOS
+# not FAT (32 bit)
+>>>>>>82 string !FAT32
+#not Linux kernel
+>>>>>>>514 string !HdrS
+#not BeOS
+>>>>>>>>422 string !Be\ Boot\ Loader
+# GPT with protective MBR entry in partition 1 (only)
+>>>>>>>>>450 ubyte 0xee
+>>>>>>>>>>466 ubyte !0xee
+>>>>>>>>>>>482 ubyte !0xee
+>>>>>>>>>>>>498 ubyte !0xee
+#>>>>>>>>>>>>>446 use gpt-mbr-partition
+>>>>>>>>>>>>>(454.l*8192) string EFI\ PART GPT partition table
+>>>>>>>>>>>>>>0 use gpt-mbr-type
+>>>>>>>>>>>>>>&-8 use gpt-table
+>>>>>>>>>>>>>>0 ubyte x of 8192 bytes
+>>>>>>>>>>>>>(454.l*8192) string !EFI\ PART
+>>>>>>>>>>>>>>(454.l*4096) string EFI\ PART GPT partition table
+>>>>>>>>>>>>>>>0 use gpt-mbr-type
+>>>>>>>>>>>>>>>&-8 use gpt-table
+>>>>>>>>>>>>>>>0 ubyte x of 4096 bytes
+>>>>>>>>>>>>>>(454.l*4096) string !EFI\ PART
+>>>>>>>>>>>>>>>(454.l*2048) string EFI\ PART GPT partition table
+>>>>>>>>>>>>>>>>0 use gpt-mbr-type
+>>>>>>>>>>>>>>>>&-8 use gpt-table
+>>>>>>>>>>>>>>>>0 ubyte x of 2048 bytes
+>>>>>>>>>>>>>>>(454.l*2048) string !EFI\ PART
+>>>>>>>>>>>>>>>>(454.l*1024) string EFI\ PART GPT partition table
+>>>>>>>>>>>>>>>>>0 use gpt-mbr-type
+>>>>>>>>>>>>>>>>>&-8 use gpt-table
+>>>>>>>>>>>>>>>>>0 ubyte x of 1024 bytes
+>>>>>>>>>>>>>>>>(454.l*1024) string !EFI\ PART
+>>>>>>>>>>>>>>>>>(454.l*512) string EFI\ PART GPT partition table
+>>>>>>>>>>>>>>>>>>0 use gpt-mbr-type
+>>>>>>>>>>>>>>>>>>&-8 use gpt-table
+>>>>>>>>>>>>>>>>>>0 ubyte x of 512 bytes
+# GPT with protective MBR entry in partition 2 (only)
+>>>>>>>>>450 ubyte !0xee
+>>>>>>>>>>466 ubyte 0xee
+>>>>>>>>>>>482 ubyte !0xee
+>>>>>>>>>>>>498 ubyte !0xee
+#>>>>>>>>>>>>>462 use gpt-mbr-partition
+>>>>>>>>>>>>>(470.l*8192) string EFI\ PART GPT partition table
+>>>>>>>>>>>>>>0 use gpt-mbr-type
+>>>>>>>>>>>>>>&-8 use gpt-table
+>>>>>>>>>>>>>>0 ubyte x of 8192 bytes
+>>>>>>>>>>>>>(470.l*8192) string !EFI\ PART
+>>>>>>>>>>>>>>(470.l*4096) string EFI\ PART GPT partition table
+>>>>>>>>>>>>>>>0 use gpt-mbr-type
+>>>>>>>>>>>>>>>&-8 use gpt-table
+>>>>>>>>>>>>>>>0 ubyte x of 4096 bytes
+>>>>>>>>>>>>>>(470.l*4096) string !EFI\ PART
+>>>>>>>>>>>>>>>(470.l*2048) string EFI\ PART GPT partition table
+>>>>>>>>>>>>>>>>0 use gpt-mbr-type
+>>>>>>>>>>>>>>>>&-8 use gpt-table
+>>>>>>>>>>>>>>>>0 ubyte x of 2048 bytes
+>>>>>>>>>>>>>>>(470.l*2048) string !EFI\ PART
+>>>>>>>>>>>>>>>>(470.l*1024) string EFI\ PART GPT partition table
+>>>>>>>>>>>>>>>>>0 use gpt-mbr-type
+>>>>>>>>>>>>>>>>>&-8 use gpt-table
+>>>>>>>>>>>>>>>>>0 ubyte x of 1024 bytes
+>>>>>>>>>>>>>>>>(470.l*1024) string !EFI\ PART
+>>>>>>>>>>>>>>>>>(470.l*512) string EFI\ PART GPT partition table
+>>>>>>>>>>>>>>>>>>0 use gpt-mbr-type
+>>>>>>>>>>>>>>>>>>&-8 use gpt-table
+>>>>>>>>>>>>>>>>>>0 ubyte x of 512 bytes
+# GPT with protective MBR entry in partition 3 (only)
+>>>>>>>>>450 ubyte !0xee
+>>>>>>>>>>466 ubyte !0xee
+>>>>>>>>>>>482 ubyte 0xee
+>>>>>>>>>>>>498 ubyte !0xee
+#>>>>>>>>>>>>>478 use gpt-mbr-partition
+>>>>>>>>>>>>>(486.l*8192) string EFI\ PART GPT partition table
+>>>>>>>>>>>>>>0 use gpt-mbr-type
+>>>>>>>>>>>>>>&-8 use gpt-table
+>>>>>>>>>>>>>>0 ubyte x of 8192 bytes
+>>>>>>>>>>>>>(486.l*8192) string !EFI\ PART
+>>>>>>>>>>>>>>(486.l*4096) string EFI\ PART GPT partition table
+>>>>>>>>>>>>>>>0 use gpt-mbr-type
+>>>>>>>>>>>>>>>&-8 use gpt-table
+>>>>>>>>>>>>>>>0 ubyte x of 4096 bytes
+>>>>>>>>>>>>>>(486.l*4096) string !EFI\ PART
+>>>>>>>>>>>>>>>(486.l*2048) string EFI\ PART GPT partition table
+>>>>>>>>>>>>>>>>0 use gpt-mbr-type
+>>>>>>>>>>>>>>>>&-8 use gpt-table
+>>>>>>>>>>>>>>>>0 ubyte x of 2048 bytes
+>>>>>>>>>>>>>>>(486.l*2048) string !EFI\ PART
+>>>>>>>>>>>>>>>>(486.l*1024) string EFI\ PART GPT partition table
+>>>>>>>>>>>>>>>>>0 use gpt-mbr-type
+>>>>>>>>>>>>>>>>>&-8 use gpt-table
+>>>>>>>>>>>>>>>>>0 ubyte x of 1024 bytes
+>>>>>>>>>>>>>>>>(486.l*1024) string !EFI\ PART
+>>>>>>>>>>>>>>>>>(486.l*512) string EFI\ PART GPT partition table
+>>>>>>>>>>>>>>>>>>0 use gpt-mbr-type
+>>>>>>>>>>>>>>>>>>&-8 use gpt-table
+>>>>>>>>>>>>>>>>>>0 ubyte x of 512 bytes
+# GPT with protective MBR entry in partition 4 (only)
+>>>>>>>>>450 ubyte !0xee
+>>>>>>>>>>466 ubyte !0xee
+>>>>>>>>>>>482 ubyte !0xee
+>>>>>>>>>>>>498 ubyte 0xee
+#>>>>>>>>>>>>>494 use gpt-mbr-partition
+>>>>>>>>>>>>>(502.l*8192) string EFI\ PART GPT partition table
+>>>>>>>>>>>>>>0 use gpt-mbr-type
+>>>>>>>>>>>>>>&-8 use gpt-table
+>>>>>>>>>>>>>>0 ubyte x of 8192 bytes
+>>>>>>>>>>>>>(502.l*8192) string !EFI\ PART
+>>>>>>>>>>>>>>(502.l*4096) string EFI\ PART GPT partition table
+>>>>>>>>>>>>>>>0 use gpt-mbr-type
+>>>>>>>>>>>>>>>&-8 use gpt-table
+>>>>>>>>>>>>>>>0 ubyte x of 4096 bytes
+>>>>>>>>>>>>>>(502.l*4096) string !EFI\ PART
+>>>>>>>>>>>>>>>(502.l*2048) string EFI\ PART GPT partition table
+>>>>>>>>>>>>>>>>0 use gpt-mbr-type
+>>>>>>>>>>>>>>>>&-8 use gpt-table
+>>>>>>>>>>>>>>>>0 ubyte x of 2048 bytes
+>>>>>>>>>>>>>>>(502.l*2048) string !EFI\ PART
+>>>>>>>>>>>>>>>>(502.l*1024) string EFI\ PART GPT partition table
+>>>>>>>>>>>>>>>>>0 use gpt-mbr-type
+>>>>>>>>>>>>>>>>>&-8 use gpt-table
+>>>>>>>>>>>>>>>>>0 ubyte x of 1024 bytes
+>>>>>>>>>>>>>>>>(502.l*1024) string !EFI\ PART
+>>>>>>>>>>>>>>>>>(502.l*512) string EFI\ PART GPT partition table
+>>>>>>>>>>>>>>>>>>0 use gpt-mbr-type
+>>>>>>>>>>>>>>>>>>&-8 use gpt-table
+>>>>>>>>>>>>>>>>>>0 ubyte x of 512 bytes
+
+# The following code does GPT detection and processing, including
+# sector size detection.
+# It has to be duplicated above because the top-level pattern
+# (i.e. not called using 'use') must print *something* for file
+# to count it as a match. Text only printed in named patterns is
+# not counted, and causes file to continue, and try and match
+# other patterns.
+#
+# Unfortunately, when assuming sector sizes >=16k, if the sector size
+# happens to be 512 instead, we may find confusing data after the GPT
+# table... If the GPT table has less than 128 entries, this may even
+# happen for assumed sector sizes as small as 4k
+# This could be solved by checking for the presence of the backup GPT
+# header as well, but that makes the logic extremely complex
+##0 name gpt-mbr-partition
+##>(8.l*8192) string EFI\ PART
+##>>(8.l*8192) use gpt-mbr-type
+##>>&-8 use gpt-table
+##>>0 ubyte x of 8192 bytes
+##>(8.l*8192) string !EFI\ PART
+##>>(8.l*4096) string EFI\ PART GPT partition table
+##>>>0 use gpt-mbr-type
+##>>>&-8 use gpt-table
+##>>>0 ubyte x of 4096 bytes
+##>>(8.l*4096) string !EFI\ PART
+##>>>(8.l*2048) string EFI\ PART GPT partition table
+##>>>>0 use gpt-mbr-type
+##>>>>&-8 use gpt-table
+##>>>>0 ubyte x of 2048 bytes
+##>>>(8.l*2048) string !EFI\ PART
+##>>>>(8.l*1024) string EFI\ PART GPT partition table
+##>>>>>0 use gpt-mbr-type
+##>>>>>&-8 use gpt-table
+##>>>>>0 ubyte x of 1024 bytes
+##>>>>(8.l*1024) string !EFI\ PART
+##>>>>>(8.l*512) string EFI\ PART GPT partition table
+##>>>>>>0 use gpt-mbr-type
+##>>>>>>&-8 use gpt-table
+##>>>>>>0 ubyte x of 512 bytes
+
+# Print details of MBR type for a GPT-disk
+# Calling code ensures that there is only one 0xee partition.
+0 name gpt-mbr-type
+# GPT with protective MBR entry in partition 1
+>450 ubyte 0xee
+>>454 ulelong 1
+>>>462 string !\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \b (with hybrid MBR)
+>>454 ulelong !1 \b (nonstandard: not at LBA 1)
+# GPT with protective MBR entry in partition 2
+>466 ubyte 0xee
+>>470 ulelong 1
+>>>478 string \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0
+>>>>446 string !\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \b (with hybrid MBR)
+>>>478 string !\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \b (with hybrid MBR)
+>>470 ulelong !1 \b (nonstandard: not at LBA 1)
+# GPT with protective MBR entry in partition 3
+>482 ubyte 0xee
+>>486 ulelong 1
+>>>494 string \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0
+>>>>446 string !\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \b (with hybrid MBR)
+>>>494 string !\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \b (with hybrid MBR)
+>>486 ulelong !1 \b (nonstandard: not at LBA 1)
+# GPT with protective MBR entry in partition 4
+>498 ubyte 0xee
+>>502 ulelong 1
+>>>446 string !\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \b (with hybrid MBR)
+>>502 ulelong !1 \b (nonstandard: not at LBA 1)
+
+# Print the information from a GPT partition table structure
+0 name gpt-table
+>10 uleshort x \b, version %u
+>8 uleshort x \b.%u
+>56 ulelong x \b, GUID: %08x
+>60 uleshort x \b-%04x
+>62 uleshort x \b-%04x
+>64 ubeshort x \b-%04x
+>66 ubeshort x \b-%04x
+>68 ubelong x \b%08x
+#>80 uleshort x \b, %d partition entries
+>32 ulequad+1 x \b, disk size: %lld sectors
+
+# In case a GPT data-structure is at LBA 0, report it as well
+# This covers systems which are not GPT-aware, and which show
+# and allow access to the protective partition. This code will
+# detect the contents of such a partition.
+0 string EFI\ PART GPT data structure (nonstandard: at LBA 0)
+>0 use gpt-table
+>0 ubyte x (sector size unknown)
+
+
+
+#------------------------------------------------------------------------------
+# $File$
+# ACE/gr and Grace type files - PLEASE DO NOT REMOVE THIS LINE
+#
+# ACE/gr binary
+0 string \000\000\0001\000\000\0000\000\000\0000\000\000\0002\000\000\0000\000\000\0000\000\000\0003 old ACE/gr binary file
+>39 byte >0 - version %c
+# ACE/gr ascii
+0 string #\ xvgr\ parameter\ file ACE/gr ascii file
+0 string #\ xmgr\ parameter\ file ACE/gr ascii file
+0 string #\ ACE/gr\ parameter\ file ACE/gr ascii file
+# Grace projects
+0 string #\ Grace\ project\ file Grace project file
+>23 string @version\ (version
+>>32 byte >0 %c
+>>33 string >\0 \b.%.2s
+>>35 string >\0 \b.%.2s)
+# ACE/gr fit description files
+0 string #\ ACE/gr\ fit\ description\ ACE/gr fit description file
+# end of ACE/gr and Grace type files - PLEASE DO NOT REMOVE THIS LINE
+
+#------------------------------------------------------------------------------
+# $File: graphviz,v 1.7 2009/09/19 16:28:09 christos Exp $
+# graphviz: file(1) magic for http://www.graphviz.org/
+
+# FIXME: These patterns match too generally. For example, the first
+# line matches a LaTeX file containing the word "graph" (with a {
+# following later) and the second line matches this file.
+#0 regex/100l [\r\n\t\ ]*graph[\r\n\t\ ]+.*\\{ graphviz graph text
+#!:mime text/vnd.graphviz
+#0 regex/100l [\r\n\t\ ]*digraph[\r\n\t\ ]+.*\\{ graphviz digraph text
+#!:mime text/vnd.graphviz
+
+#------------------------------------------------------------------------------
+# $File$
+# gringotts: file(1) magic for Gringotts
+# http://devel.pluto.linux.it/projects/Gringotts/
+# author: Germano Rizzo <mano@pluto.linux.it>
+#GRG3????Y
+0 string GRG Gringotts data file
+#file format 1
+>3 string 1 v.1, MCRYPT S2K, SERPENT crypt, SHA-256 hash, ZLib lvl.9
+#file format 2
+>3 string 2 v.2, MCRYPT S2K,
+>>8 byte&0x70 0x00 RIJNDAEL-128 crypt,
+>>8 byte&0x70 0x10 SERPENT crypt,
+>>8 byte&0x70 0x20 TWOFISH crypt,
+>>8 byte&0x70 0x30 CAST-256 crypt,
+>>8 byte&0x70 0x40 SAFER+ crypt,
+>>8 byte&0x70 0x50 LOKI97 crypt,
+>>8 byte&0x70 0x60 3DES crypt,
+>>8 byte&0x70 0x70 RIJNDAEL-256 crypt,
+>>8 byte&0x08 0x00 SHA1 hash,
+>>8 byte&0x08 0x08 RIPEMD-160 hash,
+>>8 byte&0x04 0x00 ZLib
+>>8 byte&0x04 0x04 BZip2
+>>8 byte&0x03 0x00 lvl.0
+>>8 byte&0x03 0x01 lvl.3
+>>8 byte&0x03 0x02 lvl.6
+>>8 byte&0x03 0x03 lvl.9
+#file format 3
+>3 string 3 v.3, OpenPGP S2K,
+>>8 byte&0x70 0x00 RIJNDAEL-128 crypt,
+>>8 byte&0x70 0x10 SERPENT crypt,
+>>8 byte&0x70 0x20 TWOFISH crypt,
+>>8 byte&0x70 0x30 CAST-256 crypt,
+>>8 byte&0x70 0x40 SAFER+ crypt,
+>>8 byte&0x70 0x50 LOKI97 crypt,
+>>8 byte&0x70 0x60 3DES crypt,
+>>8 byte&0x70 0x70 RIJNDAEL-256 crypt,
+>>8 byte&0x08 0x00 SHA1 hash,
+>>8 byte&0x08 0x08 RIPEMD-160 hash,
+>>8 byte&0x04 0x00 ZLib
+>>8 byte&0x04 0x04 BZip2
+>>8 byte&0x03 0x00 lvl.0
+>>8 byte&0x03 0x01 lvl.3
+>>8 byte&0x03 0x02 lvl.6
+>>8 byte&0x03 0x03 lvl.9
+#file format >3
+>3 string >3 v.%.1s (unknown details)
+
+#------------------------------------------------------------------------------
+# $File: grace,v 1.4 2009/09/19 16:28:09 christos Exp $
+# Guile file magic from <dalepsmith@gmail.com>
+# http://www.gnu.org/s/guile/
+# http://git.savannah.gnu.org/gitweb/?p=guile.git;f=libguile/_scm.h;hb=HEAD#l250
+
+0 string GOOF---- Guile Object
+>8 string LE \b, little endian
+>8 string BE \b, big endian
+>11 string 4 \b, 32bit
+>11 string 8 \b, 64bit
+>13 regex .\.. \b, bytecode v%s
+
+#------------------------------------------------------------------------------
+# $File: hitachi-sh,v 1.5 2009/09/19 16:28:09 christos Exp $
+# hitach-sh: file(1) magic for Hitachi Super-H
+#
+# Super-H COFF
+#
+# below test line conflicts with 2nd NTFS filesystem sector
+0 beshort 0x0500 Hitachi SH big-endian COFF
+# 2nd NTFS filesystem sector often starts with 0x05004e00 for unicode string 5 NTLDR
+#0 ubelong&0xFFFFNMPQ 0x0500NMPQ Hitachi SH big-endian COFF
+>18 beshort&0x0002 =0x0000 object
+>18 beshort&0x0002 =0x0002 executable
+>18 beshort&0x0008 =0x0008 \b, stripped
+>18 beshort&0x0008 =0x0000 \b, not stripped
+#
+0 leshort 0x0550 Hitachi SH little-endian COFF
+>18 leshort&0x0002 =0x0000 object
+>18 leshort&0x0002 =0x0002 executable
+>18 leshort&0x0008 =0x0008 \b, stripped
+>18 leshort&0x0008 =0x0000 \b, not stripped
+
+
+#------------------------------------------------------------------------------
+# $File: hp,v 1.23 2009/09/19 16:28:09 christos Exp $
+# hp: file(1) magic for Hewlett Packard machines (see also "printer")
+#
+# XXX - somebody should figure out whether any byte order needs to be
+# applied to the "TML" stuff; I'm assuming the Apollo stuff is
+# big-endian as it was mostly 68K-based.
+#
+# I think the 500 series was the old stack-based machines, running a
+# UNIX environment atop the "SUN kernel"; dunno whether it was
+# big-endian or little-endian.
+#
+# Daniel Quinlan (quinlan@yggdrasil.com): hp200 machines are 68010 based;
+# hp300 are 68020+68881 based; hp400 are also 68k. The following basic
+# HP magic is useful for reference, but using "long" magic is a better
+# practice in order to avoid collisions.
+#
+# Guy Harris (guy@netapp.com): some additions to this list came from
+# HP-UX 10.0's "/usr/include/sys/unistd.h" (68030, 68040, PA-RISC 1.1,
+# 1.2, and 2.0). The 1.2 and 2.0 stuff isn't in the HP-UX 10.0
+# "/etc/magic", though, except for the "archive file relocatable library"
+# stuff, and the 68030 and 68040 stuff isn't there at all - are they not
+# used in executables, or have they just not yet updated "/etc/magic"
+# completely?
+#
+# 0 beshort 200 hp200 (68010) BSD binary
+# 0 beshort 300 hp300 (68020+68881) BSD binary
+# 0 beshort 0x20c hp200/300 HP-UX binary
+# 0 beshort 0x20d hp400 (68030) HP-UX binary
+# 0 beshort 0x20e hp400 (68040?) HP-UX binary
+# 0 beshort 0x20b PA-RISC1.0 HP-UX binary
+# 0 beshort 0x210 PA-RISC1.1 HP-UX binary
+# 0 beshort 0x211 PA-RISC1.2 HP-UX binary
+# 0 beshort 0x214 PA-RISC2.0 HP-UX binary
+
+#
+# The "misc" stuff needs a byte order; the archives look suspiciously
+# like the old 177545 archives (0xff65 = 0177545).
+#
+#### Old Apollo stuff
+0 beshort 0627 Apollo m68k COFF executable
+>18 beshort ^040000 not stripped
+>22 beshort >0 - version %d
+0 beshort 0624 apollo a88k COFF executable
+>18 beshort ^040000 not stripped
+>22 beshort >0 - version %d
+0 long 01203604016 TML 0123 byte-order format
+0 long 01702407010 TML 1032 byte-order format
+0 long 01003405017 TML 2301 byte-order format
+0 long 01602007412 TML 3210 byte-order format
+#### PA-RISC 1.1
+0 belong 0x02100106 PA-RISC1.1 relocatable object
+0 belong 0x02100107 PA-RISC1.1 executable
+>168 belong &0x00000004 dynamically linked
+>(144) belong 0x054ef630 dynamically linked
+>96 belong >0 - not stripped
+
+0 belong 0x02100108 PA-RISC1.1 shared executable
+>168 belong&0x4 0x4 dynamically linked
+>(144) belong 0x054ef630 dynamically linked
+>96 belong >0 - not stripped
+
+0 belong 0x0210010b PA-RISC1.1 demand-load executable
+>168 belong&0x4 0x4 dynamically linked
+>(144) belong 0x054ef630 dynamically linked
+>96 belong >0 - not stripped
+
+0 belong 0x0210010e PA-RISC1.1 shared library
+>96 belong >0 - not stripped
+
+0 belong 0x0210010d PA-RISC1.1 dynamic load library
+>96 belong >0 - not stripped
+
+#### PA-RISC 2.0
+0 belong 0x02140106 PA-RISC2.0 relocatable object
+
+0 belong 0x02140107 PA-RISC2.0 executable
+>168 belong &0x00000004 dynamically linked
+>(144) belong 0x054ef630 dynamically linked
+>96 belong >0 - not stripped
+
+0 belong 0x02140108 PA-RISC2.0 shared executable
+>168 belong &0x00000004 dynamically linked
+>(144) belong 0x054ef630 dynamically linked
+>96 belong >0 - not stripped
+
+0 belong 0x0214010b PA-RISC2.0 demand-load executable
+>168 belong &0x00000004 dynamically linked
+>(144) belong 0x054ef630 dynamically linked
+>96 belong >0 - not stripped
+
+0 belong 0x0214010e PA-RISC2.0 shared library
+>96 belong >0 - not stripped
+
+0 belong 0x0214010d PA-RISC2.0 dynamic load library
+>96 belong >0 - not stripped
+
+#### 800
+0 belong 0x020b0106 PA-RISC1.0 relocatable object
+
+0 belong 0x020b0107 PA-RISC1.0 executable
+>168 belong&0x4 0x4 dynamically linked
+>(144) belong 0x054ef630 dynamically linked
+>96 belong >0 - not stripped
+
+0 belong 0x020b0108 PA-RISC1.0 shared executable
+>168 belong&0x4 0x4 dynamically linked
+>(144) belong 0x054ef630 dynamically linked
+>96 belong >0 - not stripped
+
+0 belong 0x020b010b PA-RISC1.0 demand-load executable
+>168 belong&0x4 0x4 dynamically linked
+>(144) belong 0x054ef630 dynamically linked
+>96 belong >0 - not stripped
+
+0 belong 0x020b010e PA-RISC1.0 shared library
+>96 belong >0 - not stripped
+
+0 belong 0x020b010d PA-RISC1.0 dynamic load library
+>96 belong >0 - not stripped
+
+0 belong 0x213c6172 archive file
+>68 belong 0x020b0619 - PA-RISC1.0 relocatable library
+>68 belong 0x02100619 - PA-RISC1.1 relocatable library
+>68 belong 0x02110619 - PA-RISC1.2 relocatable library
+>68 belong 0x02140619 - PA-RISC2.0 relocatable library
+
+#### 500
+0 long 0x02080106 HP s500 relocatable executable
+>16 long >0 - version %d
+
+0 long 0x02080107 HP s500 executable
+>16 long >0 - version %d
+
+0 long 0x02080108 HP s500 pure executable
+>16 long >0 - version %d
+
+#### 200
+0 belong 0x020c0108 HP s200 pure executable
+>4 beshort >0 - version %d
+>8 belong &0x80000000 save fp regs
+>8 belong &0x40000000 dynamically linked
+>8 belong &0x20000000 debuggable
+>36 belong >0 not stripped
+
+0 belong 0x020c0107 HP s200 executable
+>4 beshort >0 - version %d
+>8 belong &0x80000000 save fp regs
+>8 belong &0x40000000 dynamically linked
+>8 belong &0x20000000 debuggable
+>36 belong >0 not stripped
+
+0 belong 0x020c010b HP s200 demand-load executable
+>4 beshort >0 - version %d
+>8 belong &0x80000000 save fp regs
+>8 belong &0x40000000 dynamically linked
+>8 belong &0x20000000 debuggable
+>36 belong >0 not stripped
+
+0 belong 0x020c0106 HP s200 relocatable executable
+>4 beshort >0 - version %d
+>6 beshort >0 - highwater %d
+>8 belong &0x80000000 save fp regs
+>8 belong &0x20000000 debuggable
+>8 belong &0x10000000 PIC
+
+0 belong 0x020a0108 HP s200 (2.x release) pure executable
+>4 beshort >0 - version %d
+>36 belong >0 not stripped
+
+0 belong 0x020a0107 HP s200 (2.x release) executable
+>4 beshort >0 - version %d
+>36 belong >0 not stripped
+
+0 belong 0x020c010e HP s200 shared library
+>4 beshort >0 - version %d
+>6 beshort >0 - highwater %d
+>36 belong >0 not stripped
+
+0 belong 0x020c010d HP s200 dynamic load library
+>4 beshort >0 - version %d
+>6 beshort >0 - highwater %d
+>36 belong >0 not stripped
+
+#### MISC
+0 long 0x0000ff65 HP old archive
+0 long 0x020aff65 HP s200 old archive
+0 long 0x020cff65 HP s200 old archive
+0 long 0x0208ff65 HP s500 old archive
+
+0 long 0x015821a6 HP core file
+
+0 long 0x4da7eee8 HP-WINDOWS font
+>8 byte >0 - version %d
+0 string Bitmapfile HP Bitmapfile
+
+0 string IMGfile CIS compimg HP Bitmapfile
+# XXX - see "lif"
+#0 short 0x8000 lif file
+0 long 0x020c010c compiled Lisp
+
+0 string msgcat01 HP NLS message catalog,
+>8 long >0 %d messages
+
+# Summary: HP-48/49 calculator
+# Created by: phk@data.fls.dk
+# Modified by (1): AMAKAWA Shuhei <sa264@cam.ac.uk>
+# Modified by (2): Samuel Thibault <samuel.thibault@ens-lyon.org> (HP49 support)
+0 string HPHP HP
+>4 string 48 48 binary
+>4 string 49 49 binary
+>7 byte >64 - Rev %c
+>8 leshort 0x2911 (ADR)
+>8 leshort 0x2933 (REAL)
+>8 leshort 0x2955 (LREAL)
+>8 leshort 0x2977 (COMPLX)
+>8 leshort 0x299d (LCOMPLX)
+>8 leshort 0x29bf (CHAR)
+>8 leshort 0x29e8 (ARRAY)
+>8 leshort 0x2a0a (LNKARRAY)
+>8 leshort 0x2a2c (STRING)
+>8 leshort 0x2a4e (HXS)
+>8 leshort 0x2a74 (LIST)
+>8 leshort 0x2a96 (DIR)
+>8 leshort 0x2ab8 (ALG)
+>8 leshort 0x2ada (UNIT)
+>8 leshort 0x2afc (TAGGED)
+>8 leshort 0x2b1e (GROB)
+>8 leshort 0x2b40 (LIB)
+>8 leshort 0x2b62 (BACKUP)
+>8 leshort 0x2b88 (LIBDATA)
+>8 leshort 0x2d9d (PROG)
+>8 leshort 0x2dcc (CODE)
+>8 leshort 0x2e48 (GNAME)
+>8 leshort 0x2e6d (LNAME)
+>8 leshort 0x2e92 (XLIB)
+
+0 string %%HP: HP text
+>6 string T(0) - T(0)
+>6 string T(1) - T(1)
+>6 string T(2) - T(2)
+>6 string T(3) - T(3)
+>10 string A(D) A(D)
+>10 string A(R) A(R)
+>10 string A(G) A(G)
+>14 string F(.) F(.);
+>14 string F(,) F(,);
+
+
+# Summary: HP-38/39 calculator
+# Created by: Samuel Thibault <samuel.thibault@ens-lyon.org>
+0 string HP3
+>3 string 8 HP 38
+>3 string 9 HP 39
+>4 string Bin binary
+>4 string Asc ASCII
+>7 string A (Directory List)
+>7 string B (Zaplet)
+>7 string C (Note)
+>7 string D (Program)
+>7 string E (Variable)
+>7 string F (List)
+>7 string G (Matrix)
+>7 string H (Library)
+>7 string I (Target List)
+>7 string J (ASCII Vector specification)
+>7 string K (wildcard)
+
+# Summary: HP-38/39 calculator
+# Created by: Samuel Thibault <samuel.thibault@ens-lyon.org>
+0 string HP3
+>3 string 8 HP 38
+>3 string 9 HP 39
+>4 string Bin binary
+>4 string Asc ASCII
+>7 string A (Directory List)
+>7 string B (Zaplet)
+>7 string C (Note)
+>7 string D (Program)
+>7 string E (Variable)
+>7 string F (List)
+>7 string G (Matrix)
+>7 string H (Library)
+>7 string I (Target List)
+>7 string J (ASCII Vector specification)
+>7 string K (wildcard)
+
+# hpBSD magic numbers
+0 beshort 200 hp200 (68010) BSD
+>2 beshort 0407 impure binary
+>2 beshort 0410 read-only binary
+>2 beshort 0413 demand paged binary
+0 beshort 300 hp300 (68020+68881) BSD
+>2 beshort 0407 impure binary
+>2 beshort 0410 read-only binary
+>2 beshort 0413 demand paged binary
+#
+# From David Gero <dgero@nortelnetworks.com>
+# HP-UX 10.20 core file format from /usr/include/sys/core.h
+# Unfortunately, HP-UX uses corehead blocks without specifying the order
+# There are four we care about:
+# CORE_KERNEL, which starts with the string "HP-UX"
+# CORE_EXEC, which contains the name of the command
+# CORE_PROC, which contains the signal number that caused the core dump
+# CORE_FORMAT, which contains the version of the core file format (== 1)
+# The only observed order in real core files is KERNEL, EXEC, FORMAT, PROC
+# but we include all 6 variations of the order of the first 3, and
+# assume that PROC will always be last
+# Order 1: KERNEL, EXEC, FORMAT, PROC
+0x10 string HP-UX
+>0 belong 2
+>>0xC belong 0x3C
+>>>0x4C belong 0x100
+>>>>0x58 belong 0x44
+>>>>>0xA0 belong 1
+>>>>>>0xAC belong 4
+>>>>>>>0xB0 belong 1
+>>>>>>>>0xB4 belong 4 core file
+>>>>>>>>>0x90 string >\0 from '%s'
+>>>>>>>>>0xC4 belong 3 - received SIGQUIT
+>>>>>>>>>0xC4 belong 4 - received SIGILL
+>>>>>>>>>0xC4 belong 5 - received SIGTRAP
+>>>>>>>>>0xC4 belong 6 - received SIGABRT
+>>>>>>>>>0xC4 belong 7 - received SIGEMT
+>>>>>>>>>0xC4 belong 8 - received SIGFPE
+>>>>>>>>>0xC4 belong 10 - received SIGBUS
+>>>>>>>>>0xC4 belong 11 - received SIGSEGV
+>>>>>>>>>0xC4 belong 12 - received SIGSYS
+>>>>>>>>>0xC4 belong 33 - received SIGXCPU
+>>>>>>>>>0xC4 belong 34 - received SIGXFSZ
+# Order 2: KERNEL, FORMAT, EXEC, PROC
+>>>0x4C belong 1
+>>>>0x58 belong 4
+>>>>>0x5C belong 1
+>>>>>>0x60 belong 0x100
+>>>>>>>0x6C belong 0x44
+>>>>>>>>0xB4 belong 4 core file
+>>>>>>>>>0xA4 string >\0 from '%s'
+>>>>>>>>>0xC4 belong 3 - received SIGQUIT
+>>>>>>>>>0xC4 belong 4 - received SIGILL
+>>>>>>>>>0xC4 belong 5 - received SIGTRAP
+>>>>>>>>>0xC4 belong 6 - received SIGABRT
+>>>>>>>>>0xC4 belong 7 - received SIGEMT
+>>>>>>>>>0xC4 belong 8 - received SIGFPE
+>>>>>>>>>0xC4 belong 10 - received SIGBUS
+>>>>>>>>>0xC4 belong 11 - received SIGSEGV
+>>>>>>>>>0xC4 belong 12 - received SIGSYS
+>>>>>>>>>0xC4 belong 33 - received SIGXCPU
+>>>>>>>>>0xC4 belong 34 - received SIGXFSZ
+# Order 3: FORMAT, KERNEL, EXEC, PROC
+0x24 string HP-UX
+>0 belong 1
+>>0xC belong 4
+>>>0x10 belong 1
+>>>>0x14 belong 2
+>>>>>0x20 belong 0x3C
+>>>>>>0x60 belong 0x100
+>>>>>>>0x6C belong 0x44
+>>>>>>>>0xB4 belong 4 core file
+>>>>>>>>>0xA4 string >\0 from '%s'
+>>>>>>>>>0xC4 belong 3 - received SIGQUIT
+>>>>>>>>>0xC4 belong 4 - received SIGILL
+>>>>>>>>>0xC4 belong 5 - received SIGTRAP
+>>>>>>>>>0xC4 belong 6 - received SIGABRT
+>>>>>>>>>0xC4 belong 7 - received SIGEMT
+>>>>>>>>>0xC4 belong 8 - received SIGFPE
+>>>>>>>>>0xC4 belong 10 - received SIGBUS
+>>>>>>>>>0xC4 belong 11 - received SIGSEGV
+>>>>>>>>>0xC4 belong 12 - received SIGSYS
+>>>>>>>>>0xC4 belong 33 - received SIGXCPU
+>>>>>>>>>0xC4 belong 34 - received SIGXFSZ
+# Order 4: EXEC, KERNEL, FORMAT, PROC
+0x64 string HP-UX
+>0 belong 0x100
+>>0xC belong 0x44
+>>>0x54 belong 2
+>>>>0x60 belong 0x3C
+>>>>>0xA0 belong 1
+>>>>>>0xAC belong 4
+>>>>>>>0xB0 belong 1
+>>>>>>>>0xB4 belong 4 core file
+>>>>>>>>>0x44 string >\0 from '%s'
+>>>>>>>>>0xC4 belong 3 - received SIGQUIT
+>>>>>>>>>0xC4 belong 4 - received SIGILL
+>>>>>>>>>0xC4 belong 5 - received SIGTRAP
+>>>>>>>>>0xC4 belong 6 - received SIGABRT
+>>>>>>>>>0xC4 belong 7 - received SIGEMT
+>>>>>>>>>0xC4 belong 8 - received SIGFPE
+>>>>>>>>>0xC4 belong 10 - received SIGBUS
+>>>>>>>>>0xC4 belong 11 - received SIGSEGV
+>>>>>>>>>0xC4 belong 12 - received SIGSYS
+>>>>>>>>>0xC4 belong 33 - received SIGXCPU
+>>>>>>>>>0xC4 belong 34 - received SIGXFSZ
+# Order 5: FORMAT, EXEC, KERNEL, PROC
+0x78 string HP-UX
+>0 belong 1
+>>0xC belong 4
+>>>0x10 belong 1
+>>>>0x14 belong 0x100
+>>>>>0x20 belong 0x44
+>>>>>>0x68 belong 2
+>>>>>>>0x74 belong 0x3C
+>>>>>>>>0xB4 belong 4 core file
+>>>>>>>>>0x58 string >\0 from '%s'
+>>>>>>>>>0xC4 belong 3 - received SIGQUIT
+>>>>>>>>>0xC4 belong 4 - received SIGILL
+>>>>>>>>>0xC4 belong 5 - received SIGTRAP
+>>>>>>>>>0xC4 belong 6 - received SIGABRT
+>>>>>>>>>0xC4 belong 7 - received SIGEMT
+>>>>>>>>>0xC4 belong 8 - received SIGFPE
+>>>>>>>>>0xC4 belong 10 - received SIGBUS
+>>>>>>>>>0xC4 belong 11 - received SIGSEGV
+>>>>>>>>>0xC4 belong 12 - received SIGSYS
+>>>>>>>>>0xC4 belong 33 - received SIGXCPU
+>>>>>>>>>0xC4 belong 34 - received SIGXFSZ
+# Order 6: EXEC, FORMAT, KERNEL, PROC
+>0 belong 0x100
+>>0xC belong 0x44
+>>>0x54 belong 1
+>>>>0x60 belong 4
+>>>>>0x64 belong 1
+>>>>>>0x68 belong 2
+>>>>>>>0x74 belong 0x2C
+>>>>>>>>0xB4 belong 4 core file
+>>>>>>>>>0x44 string >\0 from '%s'
+>>>>>>>>>0xC4 belong 3 - received SIGQUIT
+>>>>>>>>>0xC4 belong 4 - received SIGILL
+>>>>>>>>>0xC4 belong 5 - received SIGTRAP
+>>>>>>>>>0xC4 belong 6 - received SIGABRT
+>>>>>>>>>0xC4 belong 7 - received SIGEMT
+>>>>>>>>>0xC4 belong 8 - received SIGFPE
+>>>>>>>>>0xC4 belong 10 - received SIGBUS
+>>>>>>>>>0xC4 belong 11 - received SIGSEGV
+>>>>>>>>>0xC4 belong 12 - received SIGSYS
+>>>>>>>>>0xC4 belong 33 - received SIGXCPU
+>>>>>>>>>0xC4 belong 34 - received SIGXFSZ
+
+
+
+#------------------------------------------------------------------------------
+# $File$
+# human68k: file(1) magic for Human68k (X680x0 DOS) binary formats
+# Magic too short!
+#0 string HU Human68k
+#>68 string LZX LZX compressed
+#>>72 string >\0 (version %s)
+#>(8.L+74) string LZX LZX compressed
+#>>(8.L+78) string >\0 (version %s)
+#>60 belong >0 binded
+#>(8.L+66) string #HUPAIR hupair
+#>0 string HU X executable
+#>(8.L+74) string #LIBCV1 - linked PD LIBC ver 1
+#>4 belong >0 - base address 0x%x
+#>28 belong >0 not stripped
+#>32 belong >0 with debug information
+#0 beshort 0x601a Human68k Z executable
+#0 beshort 0x6000 Human68k object file
+#0 belong 0xd1000000 Human68k ar binary archive
+#0 belong 0xd1010000 Human68k ar ascii archive
+#0 beshort 0x0068 Human68k lib archive
+#4 string LZX Human68k LZX compressed
+#>8 string >\0 (version %s)
+#>4 string LZX R executable
+#2 string #HUPAIR Human68k hupair R executable
+
+#------------------------------------------------------------------------------
+# $File: ibm370,v 1.8 2009/09/19 16:28:09 christos Exp $
+# ibm370: file(1) magic for IBM 370 and compatibles.
+#
+# "ibm370" said that 0x15d == 0535 was "ibm 370 pure executable".
+# What the heck *is* "USS/370"?
+# AIX 4.1's "/etc/magic" has
+#
+# 0 short 0535 370 sysV executable
+# >12 long >0 not stripped
+# >22 short >0 - version %d
+# >30 long >0 - 5.2 format
+# 0 short 0530 370 sysV pure executable
+# >12 long >0 not stripped
+# >22 short >0 - version %d
+# >30 long >0 - 5.2 format
+#
+# instead of the "USS/370" versions of the same magic numbers.
+#
+0 beshort 0537 370 XA sysV executable
+>12 belong >0 not stripped
+>22 beshort >0 - version %d
+>30 belong >0 - 5.2 format
+0 beshort 0532 370 XA sysV pure executable
+>12 belong >0 not stripped
+>22 beshort >0 - version %d
+>30 belong >0 - 5.2 format
+0 beshort 054001 370 sysV pure executable
+>12 belong >0 not stripped
+0 beshort 055001 370 XA sysV pure executable
+>12 belong >0 not stripped
+0 beshort 056401 370 sysV executable
+>12 belong >0 not stripped
+0 beshort 057401 370 XA sysV executable
+>12 belong >0 not stripped
+0 beshort 0531 SVR2 executable (Amdahl-UTS)
+>12 belong >0 not stripped
+>24 belong >0 - version %d
+0 beshort 0534 SVR2 pure executable (Amdahl-UTS)
+>12 belong >0 not stripped
+>24 belong >0 - version %d
+0 beshort 0530 SVR2 pure executable (USS/370)
+>12 belong >0 not stripped
+>24 belong >0 - version %d
+0 beshort 0535 SVR2 executable (USS/370)
+>12 belong >0 not stripped
+>24 belong >0 - version %d
+
+#------------------------------------------------------------------------------
+# $File: ibm6000,v 1.11 2013/01/08 20:13:01 christos Exp $
+# ibm6000: file(1) magic for RS/6000 and the RT PC.
+#
+0 beshort 0x01df executable (RISC System/6000 V3.1) or obj module
+>12 belong >0 not stripped
+# Breaks sun4 statically linked execs.
+#0 beshort 0x0103 executable (RT Version 2) or obj module
+#>2 byte 0x50 pure
+#>28 belong >0 not stripped
+#>6 beshort >0 - version %ld
+0 beshort 0x0104 shared library
+0 beshort 0x0105 ctab data
+0 beshort 0xfe04 structured file
+0 string 0xabcdef AIX message catalog
+0 belong 0x000001f9 AIX compiled message catalog
+0 string \<aiaff> archive
+0 string \<bigaf> archive (big format)
+
+0 beshort 0x01f7 64-bit XCOFF executable or object module
+>20 belong 0 not stripped
+# GRR: this test is still too general as it catches also many FATs of DOS filesystems
+4 belong &0x0feeddb0
+# real core dump could not be 32-bit and 64-bit together
+>7 byte&0x03 !3 AIX core file
+>>1 byte &0x01 fulldump
+>>7 byte &0x01 32-bit
+>>>0x6e0 string >\0 \b, %s
+>>7 byte &0x02 64-bit
+>>>0x524 string >\0 \b, %s
+
+#------------------------------------------------------------------------------
+# $File$
+# icc: file(1) magic for International Color Consortium file formats
+
+#
+# Color profiles as per the ICC's "Image technology colour management -
+# Architecture, profile format, and data structure" specification.
+# See
+#
+# http://www.color.org/specification/ICC1v43_2010-12.pdf
+#
+# for Specification ICC.1:2010 (Profile version 4.3.0.0).
+#
+# Bytes 36 to 39 contain a generic profile file signature of "acsp";
+# bytes 40 to 43 "may be used to identify the primary platform/operating
+# system framework for which the profile was created".
+#
+# There are other fields that might be worth dumping as well.
+#
+
+# This appears to be what's used for Apple ColorSync profiles.
+# Instead of adding that, Apple just changed the generic "acsp" entry
+# to be for "ColorSync ICC Color Profile" rather than "Kodak Color
+# Management System, ICC Profile".
+# Yes, it's "APPL", not "AAPL"; see the spec.
+36 string acspAPPL ColorSync ICC Profile
+!:mime application/vnd.iccprofile
+
+# Microsoft ICM color profile
+36 string acspMSFT Microsoft ICM Color Profile
+!:mime application/vnd.iccprofile
+
+# Yes, that's a blank after "SGI".
+36 string acspSGI\ SGI ICC Profile
+!:mime application/vnd.iccprofile
+
+# XXX - is this what's used for the Sun KCMS or not? The standard file
+# uses just "acsp" for that, but Apple's file uses it for "ColorSync",
+# and there *is* an identified "primary platform" value of SUNW.
+36 string acspSUNW Sun KCMS ICC Profile
+!:mime application/vnd.iccprofile
+
+# Any other profile.
+# XXX - should we use "acsp\0\0\0\0" for "no primary platform" profiles,
+# and use "acsp" for everything else and dump the "primary platform"
+# string in those cases?
+36 string acsp ICC Profile
+!:mime application/vnd.iccprofile
+
+
+
+#------------------------------------------------------------------------------
+# $File: iff,v 1.12 2009/09/19 16:28:09 christos Exp $
+# iff: file(1) magic for Interchange File Format (see also "audio" & "images")
+#
+# Daniel Quinlan (quinlan@yggdrasil.com) -- IFF was designed by Electronic
+# Arts for file interchange. It has also been used by Apple, SGI, and
+# especially Commodore-Amiga.
+#
+# IFF files begin with an 8 byte FORM header, followed by a 4 character
+# FORM type, which is followed by the first chunk in the FORM.
+
+0 string FORM IFF data
+#>4 belong x \b, FORM is %d bytes long
+# audio formats
+>8 string AIFF \b, AIFF audio
+!:mime audio/x-aiff
+>8 string AIFC \b, AIFF-C compressed audio
+!:mime audio/x-aiff
+>8 string 8SVX \b, 8SVX 8-bit sampled sound voice
+!:mime audio/x-aiff
+>8 string 16SV \b, 16SV 16-bit sampled sound voice
+>8 string SAMP \b, SAMP sampled audio
+>8 string MAUD \b, MAUD MacroSystem audio
+>8 string SMUS \b, SMUS simple music
+>8 string CMUS \b, CMUS complex music
+# image formats
+>8 string ILBMBMHD \b, ILBM interleaved image
+>>20 beshort x \b, %d x
+>>22 beshort x %d
+>8 string RGBN \b, RGBN 12-bit RGB image
+>8 string RGB8 \b, RGB8 24-bit RGB image
+>8 string DEEP \b, DEEP TVPaint/XiPaint image
+>8 string DR2D \b, DR2D 2-D object
+>8 string TDDD \b, TDDD 3-D rendering
+>8 string LWOB \b, LWOB 3-D object
+>8 string LWO2 \b, LWO2 3-D object, v2
+>8 string LWLO \b, LWLO 3-D layered object
+>8 string REAL \b, REAL Real3D rendering
+>8 string MC4D \b, MC4D MaxonCinema4D rendering
+>8 string ANIM \b, ANIM animation
+>8 string YAFA \b, YAFA animation
+>8 string SSA\ \b, SSA super smooth animation
+>8 string ACBM \b, ACBM continuous image
+>8 string FAXX \b, FAXX fax image
+# other formats
+>8 string FTXT \b, FTXT formatted text
+>8 string CTLG \b, CTLG message catalog
+>8 string PREF \b, PREF preferences
+>8 string DTYP \b, DTYP datatype description
+>8 string PTCH \b, PTCH binary patch
+>8 string AMFF \b, AMFF AmigaMetaFile format
+>8 string WZRD \b, WZRD StormWIZARD resource
+>8 string DOC\ \b, DOC desktop publishing document
+>8 string WVQA \b, Westwood Studios VQA Multimedia,
+>>24 leshort x %d video frames,
+>>26 leshort x %d x
+>>28 leshort x %d
+>8 string MOVE \b, Wing Commander III Video
+>>12 string _PC_ \b, PC version
+>>12 string 3DO_ \b, 3DO version
+
+# These go at the end of the iff rules
+#
+# I don't see why these might collide with anything else.
+#
+# Interactive Fiction related formats
+#
+>8 string IFRS \b, Blorb Interactive Fiction
+>>24 string Exec with executable chunk
+>8 string IFZS \b, Z-machine or Glulx saved game file (Quetzal)
+
+#------------------------------------------------------------------------------
+# $File: images,v 1.105 2015/02/14 17:30:03 christos Exp $
+# images: file(1) magic for image formats (see also "iff", and "c-lang" for
+# XPM bitmaps)
+#
+# originally from jef@helios.ee.lbl.gov (Jef Poskanzer),
+# additions by janl@ifi.uio.no as well as others. Jan also suggested
+# merging several one- and two-line files into here.
+#
+# little magic: PCX (first byte is 0x0a)
+
+# Targa - matches `povray', `ppmtotga' and `xv' outputs
+# by Philippe De Muyter <phdm@macqel.be>
+# at 2, byte ImgType must be 1, 2, 3, 9, 10 or 11
+# at 1, byte CoMapType must be 1 if ImgType is 1 or 9, 0 otherwise
+# at 3, leshort Index is 0 for povray, ppmtotga and xv outputs
+# `xv' recognizes only a subset of the following (RGB with pixelsize = 24)
+# `tgatoppm' recognizes a superset (Index may be anything)
+1 belong&0xfff7ffff 0x01010000 Targa image data - Map
+!:strength + 2
+>2 byte&8 8 - RLE
+>12 leshort >0 %d x
+>14 leshort >0 %d
+1 belong&0xfff7ffff 0x00020000 Targa image data - RGB
+!:strength + 2
+>2 byte&8 8 - RLE
+>12 leshort >0 %d x
+>14 leshort >0 %d
+1 belong&0xfff7ffff 0x00030000 Targa image data - Mono
+!:strength + 2
+>2 byte&8 8 - RLE
+>12 leshort >0 %d x
+>14 leshort >0 %d
+
+# PBMPLUS images
+# The next byte following the magic is always whitespace.
+# strength is changed to try these patterns before "x86 boot sector"
+0 name netpbm
+>3 regex/s =[0-9]{1,50}\ [0-9]{1,50} Netpbm PPM image data
+>>&0 regex =[0-9]{1,50} \b, size = %s x
+>>>&0 regex =[0-9]{1,50} \b %s
+
+0 search/1 P1
+>0 use netpbm
+>>0 string x \b, bitmap
+!:strength + 45
+!:mime image/x-portable-bitmap
+
+0 search/1 P2
+>0 use netpbm
+>>0 string x \b, greymap
+!:strength + 45
+!:mime image/x-portable-greymap
+
+0 search/1 P3
+>0 use netpbm
+>>0 string x \b, pixmap
+!:strength + 45
+!:mime image/x-portable-pixmap
+
+
+0 string P4
+>0 use netpbm
+>>0 string x \b, rawbits, bitmap
+!:strength + 45
+!:mime image/x-portable-bitmap
+
+0 string P5
+>0 use netpbm
+>>0 string x \b, rawbits, greymap
+!:strength + 45
+!:mime image/x-portable-greymap
+
+0 string P6
+>0 use netpbm
+>>0 string x \b, rawbits, pixmap
+!:strength + 45
+!:mime image/x-portable-pixmap
+
+0 string P7 Netpbm PAM image file
+!:mime image/x-portable-pixmap
+
+# From: bryanh@giraffe-data.com (Bryan Henderson)
+0 string \117\072 Solitaire Image Recorder format
+>4 string \013 MGI Type 11
+>4 string \021 MGI Type 17
+0 string .MDA MicroDesign data
+>21 byte 48 version 2
+>21 byte 51 version 3
+0 string .MDP MicroDesign page data
+>21 byte 48 version 2
+>21 byte 51 version 3
+
+# NIFF (Navy Interchange File Format, a modification of TIFF) images
+# [GRR: this *must* go before TIFF]
+0 string IIN1 NIFF image data
+!:mime image/x-niff
+
+# Canon RAW version 1 (CRW) files are a type of Canon Image File Format
+# (CIFF) file. These are apparently all little-endian.
+# From: Adam Buchbinder <adam.buchbinder@gmail.com>
+# URL: http://www.sno.phy.queensu.ca/~phil/exiftool/canon_raw.html
+0 string II\x1a\0\0\0HEAPCCDR Canon CIFF raw image data
+!:mime image/x-canon-crw
+>16 leshort x \b, version %d.
+>14 leshort x \b%d
+
+# Canon RAW version 2 (CR2) files are a kind of TIFF with an extra magic
+# number. Put this above the TIFF test to make sure we detect them.
+# These are apparently all little-endian.
+# From: Adam Buchbinder <adam.buchbinder@gmail.com>
+# URL: http://libopenraw.freedesktop.org/wiki/Canon_CR2
+0 string II\x2a\0\x10\0\0\0CR Canon CR2 raw image data
+!:mime image/x-canon-cr2
+>10 byte x \b, version %d.
+>11 byte x \b%d
+
+# Tag Image File Format, from Daniel Quinlan (quinlan@yggdrasil.com)
+# The second word of TIFF files is the TIFF version number, 42, which has
+# never changed. The TIFF specification recommends testing for it.
+0 string MM\x00\x2a TIFF image data, big-endian
+!:mime image/tiff
+>(4.L) use \^tiff_ifd
+0 string II\x2a\x00 TIFF image data, little-endian
+!:mime image/tiff
+>(4.l) use tiff_ifd
+
+0 name tiff_ifd
+>0 leshort x \b, direntries=%d
+>2 use tiff_entry
+
+0 name tiff_entry
+# NewSubFileType
+>0 leshort 0xfe
+>>12 use tiff_entry
+>0 leshort 0x100
+>>4 lelong 1
+>>>12 use tiff_entry
+>>>8 leshort x \b, width=%d
+>0 leshort 0x101
+>>4 lelong 1
+>>>8 leshort x \b, height=%d
+>>>12 use tiff_entry
+>0 leshort 0x102
+>>8 leshort x \b, bps=%d
+>>12 use tiff_entry
+>0 leshort 0x103
+>>4 lelong 1 \b, compression=
+>>>8 leshort 1 \bnone
+>>>8 leshort 2 \bhuffman
+>>>8 leshort 3 \bbi-level group 3
+>>>8 leshort 4 \bbi-level group 4
+>>>8 leshort 5 \bLZW
+>>>8 leshort 6 \bJPEG (old)
+>>>8 leshort 7 \bJPEG
+>>>8 leshort 8 \bdeflate
+>>>8 leshort 9 \bJBIG, ITU-T T.85
+>>>8 leshort 0xa \bJBIG, ITU-T T.43
+>>>8 leshort 0x7ffe \bNeXT RLE 2-bit
+>>>8 leshort 0x8005 \bPackBits (Macintosh RLE)
+>>>8 leshort 0x8029 \bThunderscan RLE
+>>>8 leshort 0x807f \bRasterPadding (CT or MP)
+>>>8 leshort 0x8080 \bRLE (Line Work)
+>>>8 leshort 0x8081 \bRLE (High-Res Cont-Tone)
+>>>8 leshort 0x8082 \bRLE (Binary Line Work)
+>>>8 leshort 0x80b2 \bDeflate (PKZIP)
+>>>8 leshort 0x80b3 \bKodak DCS
+>>>8 leshort 0x8765 \bJBIG
+>>>8 leshort 0x8798 \bJPEG2000
+>>>8 leshort 0x8799 \bNikon NEF Compressed
+>>>8 default x
+>>>>8 leshort x \b(unknown 0x%x)
+>>>12 use tiff_entry
+>0 leshort 0x106 \b, PhotometricIntepretation=
+>>8 clear x
+>>8 leshort 0 \bWhiteIsZero
+>>8 leshort 1 \bBlackIsZero
+>>8 leshort 2 \bRGB
+>>8 leshort 3 \bRGB Palette
+>>8 leshort 4 \bTransparency Mask
+>>8 leshort 5 \bCMYK
+>>8 leshort 6 \bYCbCr
+>>8 leshort 8 \bCIELab
+>>8 default x
+>>>8 leshort x \b(unknown=0x%x)
+>>12 use tiff_entry
+# FillOrder
+>0 leshort 0x10a
+>>4 lelong 1
+>>>12 use tiff_entry
+# DocumentName
+>0 leshort 0x10d
+>>(8.l) string x \b, name=%s
+>>>12 use tiff_entry
+# ImageDescription
+>0 leshort 0x10e
+>>(8.l) string x \b, description=%s
+>>>12 use tiff_entry
+# Make
+>0 leshort 0x10f
+>>(8.l) string x \b, manufacturer=%s
+>>>12 use tiff_entry
+# Model
+>0 leshort 0x110
+>>(8.l) string x \b, model=%s
+>>>12 use tiff_entry
+# StripOffsets
+>0 leshort 0x111
+>>12 use tiff_entry
+# Orientation
+>0 leshort 0x112 \b, orientation=
+>>8 leshort 1 \bupper-left
+>>8 leshort 3 \blower-right
+>>8 leshort 6 \bupper-right
+>>8 leshort 8 \blower-left
+>>8 leshort 9 \bundefined
+>>8 default x
+>>>8 leshort x \b[*%d*]
+>>12 use tiff_entry
+# XResolution
+>0 leshort 0x11a
+>>8 lelong x \b, xresolution=%d
+>>12 use tiff_entry
+# YResolution
+>0 leshort 0x11b
+>>8 lelong x \b, yresolution=%d
+>>12 use tiff_entry
+# ResolutionUnit
+>0 leshort 0x128
+>>8 leshort x \b, resolutionunit=%d
+>>12 use tiff_entry
+# Software
+>0 leshort 0x131
+>>(8.l) string x \b, software=%s
+>>12 use tiff_entry
+# Datetime
+>0 leshort 0x132
+>>(8.l) string x \b, datetime=%s
+>>12 use tiff_entry
+# HostComputer
+>0 leshort 0x13c
+>>(8.l) string x \b, hostcomputer=%s
+>>12 use tiff_entry
+# WhitePoint
+>0 leshort 0x13e
+>>12 use tiff_entry
+# PrimaryChromaticities
+>0 leshort 0x13f
+>>12 use tiff_entry
+# YCbCrCoefficients
+>0 leshort 0x211
+>>12 use tiff_entry
+# YCbCrPositioning
+>0 leshort 0x213
+>>12 use tiff_entry
+# ReferenceBlackWhite
+>0 leshort 0x214
+>>12 use tiff_entry
+# Copyright
+>0 leshort 0x8298
+>>(8.l) string x \b, copyright=%s
+>>12 use tiff_entry
+# ExifOffset
+>0 leshort 0x8769
+>>12 use tiff_entry
+# GPS IFD
+>0 leshort 0x8825 \b, GPS-Data
+>>12 use tiff_entry
+
+#>0 leshort x \b, unknown=0x%x
+#>>12 use tiff_entry
+
+0 string MM\x00\x2b Big TIFF image data, big-endian
+!:mime image/tiff
+0 string II\x2b\x00 Big TIFF image data, little-endian
+!:mime image/tiff
+
+# PNG [Portable Network Graphics, or "PNG's Not GIF"] images
+# (Greg Roelofs, newt@uchicago.edu)
+# (Albert Cahalan, acahalan@cs.uml.edu)
+#
+# 137 P N G \r \n ^Z \n [4-byte length] H E A D [HEAD data] [HEAD crc] ...
+#
+0 string \x89PNG\x0d\x0a\x1a\x0a PNG image data
+!:mime image/png
+>16 belong x \b, %d x
+>20 belong x %d,
+>24 byte x %d-bit
+>25 byte 0 grayscale,
+>25 byte 2 \b/color RGB,
+>25 byte 3 colormap,
+>25 byte 4 gray+alpha,
+>25 byte 6 \b/color RGBA,
+#>26 byte 0 deflate/32K,
+>28 byte 0 non-interlaced
+>28 byte 1 interlaced
+
+# possible GIF replacements; none yet released!
+# (Greg Roelofs, newt@uchicago.edu)
+#
+# GRR 950115: this was mine ("Zip GIF"):
+0 string GIF94z ZIF image (GIF+deflate alpha)
+!:mime image/x-unknown
+#
+# GRR 950115: this is Jeremy Wohl's Free Graphics Format (better):
+#
+0 string FGF95a FGF image (GIF+deflate beta)
+!:mime image/x-unknown
+#
+# GRR 950115: this is Thomas Boutell's Portable Bitmap Format proposal
+# (best; not yet implemented):
+#
+0 string PBF PBF image (deflate compression)
+!:mime image/x-unknown
+
+# GIF
+0 string GIF8 GIF image data
+!:mime image/gif
+!:apple 8BIMGIFf
+>4 string 7a \b, version 8%s,
+>4 string 9a \b, version 8%s,
+>6 leshort >0 %d x
+>8 leshort >0 %d
+#>10 byte &0x80 color mapped,
+#>10 byte&0x07 =0x00 2 colors
+#>10 byte&0x07 =0x01 4 colors
+#>10 byte&0x07 =0x02 8 colors
+#>10 byte&0x07 =0x03 16 colors
+#>10 byte&0x07 =0x04 32 colors
+#>10 byte&0x07 =0x05 64 colors
+#>10 byte&0x07 =0x06 128 colors
+#>10 byte&0x07 =0x07 256 colors
+
+# ITC (CMU WM) raster files. It is essentially a byte-reversed Sun raster,
+# 1 plane, no encoding.
+0 string \361\0\100\273 CMU window manager raster image data
+>4 lelong >0 %d x
+>8 lelong >0 %d,
+>12 lelong >0 %d-bit
+
+# Magick Image File Format
+0 string id=ImageMagick MIFF image data
+
+# Artisan
+0 long 1123028772 Artisan image data
+>4 long 1 \b, rectangular 24-bit
+>4 long 2 \b, rectangular 8-bit with colormap
+>4 long 3 \b, rectangular 32-bit (24-bit with matte)
+
+# FIG (Facility for Interactive Generation of figures), an object-based format
+0 search/1 #FIG FIG image text
+>5 string x \b, version %.3s
+
+# PHIGS
+0 string ARF_BEGARF PHIGS clear text archive
+0 string @(#)SunPHIGS SunPHIGS
+# version number follows, in the form m.n
+>40 string SunBin binary
+>32 string archive archive
+
+# GKS (Graphics Kernel System)
+0 string GKSM GKS Metafile
+>24 string SunGKS \b, SunGKS
+
+# CGM image files
+0 string BEGMF clear text Computer Graphics Metafile
+
+# MGR bitmaps (Michael Haardt, u31b3hs@pool.informatik.rwth-aachen.de)
+0 string yz MGR bitmap, modern format, 8-bit aligned
+0 string zz MGR bitmap, old format, 1-bit deep, 16-bit aligned
+0 string xz MGR bitmap, old format, 1-bit deep, 32-bit aligned
+0 string yx MGR bitmap, modern format, squeezed
+
+# Fuzzy Bitmap (FBM) images
+0 string %bitmap\0 FBM image data
+>30 long 0x31 \b, mono
+>30 long 0x33 \b, color
+
+# facsimile data
+1 string PC\ Research,\ Inc group 3 fax data
+>29 byte 0 \b, normal resolution (204x98 DPI)
+>29 byte 1 \b, fine resolution (204x196 DPI)
+# From: Herbert Rosmanith <herp@wildsau.idv.uni.linz.at>
+0 string Sfff structured fax file
+
+# From: Joerg Jenderek <joerg.jen.der.ek@gmx.net>
+# most files with the extension .EPA and some with .BMP
+0 string \x11\x06 Award BIOS Logo, 136 x 84
+!:mime image/x-award-bioslogo
+0 string \x11\x09 Award BIOS Logo, 136 x 126
+!:mime image/x-award-bioslogo
+#0 string \x07\x1f BIOS Logo corrupted?
+# http://www.blackfiveservices.co.uk/awbmtools.shtml
+# http://biosgfx.narod.ru/v3/
+# http://biosgfx.narod.ru/abr-2/
+0 string AWBM
+>4 leshort <1981 Award BIOS bitmap
+!:mime image/x-award-bmp
+# image width is a multiple of 4
+>>4 leshort&0x0003 0
+>>>4 leshort x \b, %d
+>>>6 leshort x x %d
+>>4 leshort&0x0003 >0 \b,
+>>>4 leshort&0x0003 =1
+>>>>4 leshort x %d+3
+>>>4 leshort&0x0003 =2
+>>>>4 leshort x %d+2
+>>>4 leshort&0x0003 =3
+>>>>4 leshort x %d+1
+>>>6 leshort x x %d
+# at offset 8 starts imagedata followed by "RGB " marker
+
+# PC bitmaps (OS/2, Windows BMP files) (Greg Roelofs, newt@uchicago.edu)
+# http://en.wikipedia.org/wiki/BMP_file_format#DIB_header_.\
+# 28bitmap_information_header.29
+0 string BM
+>14 leshort 12 PC bitmap, OS/2 1.x format
+!:mime image/x-ms-bmp
+>>18 leshort x \b, %d x
+>>20 leshort x %d
+>14 leshort 64 PC bitmap, OS/2 2.x format
+!:mime image/x-ms-bmp
+>>18 leshort x \b, %d x
+>>20 leshort x %d
+>14 leshort 40 PC bitmap, Windows 3.x format
+!:mime image/x-ms-bmp
+>>18 lelong x \b, %d x
+>>22 lelong x %d x
+>>28 leshort x %d
+>14 leshort 124 PC bitmap, Windows 98/2000 and newer format
+!:mime image/x-ms-bmp
+>>18 lelong x \b, %d x
+>>22 lelong x %d x
+>>28 leshort x %d
+>14 leshort 108 PC bitmap, Windows 95/NT4 and newer format
+!:mime image/x-ms-bmp
+>>18 lelong x \b, %d x
+>>22 lelong x %d x
+>>28 leshort x %d
+>14 leshort 128 PC bitmap, Windows NT/2000 format
+!:mime image/x-ms-bmp
+>>18 lelong x \b, %d x
+>>22 lelong x %d x
+>>28 leshort x %d
+# Too simple - MPi
+#0 string IC PC icon data
+#0 string PI PC pointer image data
+#0 string CI PC color icon data
+#0 string CP PC color pointer image data
+# Conflicts with other entries [BABYL]
+#0 string BA PC bitmap array data
+
+# XPM icons (Greg Roelofs, newt@uchicago.edu)
+0 search/1 /*\ XPM\ */ X pixmap image text
+!:mime image/x-xpmi
+
+# Utah Raster Toolkit RLE images (janl@ifi.uio.no)
+0 leshort 0xcc52 RLE image data,
+>6 leshort x %d x
+>8 leshort x %d
+>2 leshort >0 \b, lower left corner: %d
+>4 leshort >0 \b, lower right corner: %d
+>10 byte&0x1 =0x1 \b, clear first
+>10 byte&0x2 =0x2 \b, no background
+>10 byte&0x4 =0x4 \b, alpha channel
+>10 byte&0x8 =0x8 \b, comment
+>11 byte >0 \b, %d color channels
+>12 byte >0 \b, %d bits per pixel
+>13 byte >0 \b, %d color map channels
+
+# image file format (Robert Potter, potter@cs.rochester.edu)
+0 string Imagefile\ version- iff image data
+# this adds the whole header (inc. version number), informative but longish
+>10 string >\0 %s
+
+# Sun raster images, from Daniel Quinlan (quinlan@yggdrasil.com)
+0 belong 0x59a66a95 Sun raster image data
+>4 belong >0 \b, %d x
+>8 belong >0 %d,
+>12 belong >0 %d-bit,
+#>16 belong >0 %d bytes long,
+>20 belong 0 old format,
+#>20 belong 1 standard,
+>20 belong 2 compressed,
+>20 belong 3 RGB,
+>20 belong 4 TIFF,
+>20 belong 5 IFF,
+>20 belong 0xffff reserved for testing,
+>24 belong 0 no colormap
+>24 belong 1 RGB colormap
+>24 belong 2 raw colormap
+#>28 belong >0 colormap is %d bytes long
+
+# SGI image file format, from Daniel Quinlan (quinlan@yggdrasil.com)
+#
+# See
+# http://reality.sgi.com/grafica/sgiimage.html
+#
+0 beshort 474 SGI image data
+#>2 byte 0 \b, verbatim
+>2 byte 1 \b, RLE
+#>3 byte 1 \b, normal precision
+>3 byte 2 \b, high precision
+>4 beshort x \b, %d-D
+>6 beshort x \b, %d x
+>8 beshort x %d
+>10 beshort x \b, %d channel
+>10 beshort !1 \bs
+>80 string >0 \b, "%s"
+
+0 string IT01 FIT image data
+>4 belong x \b, %d x
+>8 belong x %d x
+>12 belong x %d
+#
+0 string IT02 FIT image data
+>4 belong x \b, %d x
+>8 belong x %d x
+>12 belong x %d
+#
+2048 string PCD_IPI Kodak Photo CD image pack file
+>0xe02 byte&0x03 0x00 , landscape mode
+>0xe02 byte&0x03 0x01 , portrait mode
+>0xe02 byte&0x03 0x02 , landscape mode
+>0xe02 byte&0x03 0x03 , portrait mode
+0 string PCD_OPA Kodak Photo CD overview pack file
+
+# FITS format. Jeff Uphoff <juphoff@tarsier.cv.nrao.edu>
+# FITS is the Flexible Image Transport System, the de facto standard for
+# data and image transfer, storage, etc., for the astronomical community.
+# (FITS floating point formats are big-endian.)
+0 string SIMPLE\ \ = FITS image data
+>109 string 8 \b, 8-bit, character or unsigned binary integer
+>108 string 16 \b, 16-bit, two's complement binary integer
+>107 string \ 32 \b, 32-bit, two's complement binary integer
+>107 string -32 \b, 32-bit, floating point, single precision
+>107 string -64 \b, 64-bit, floating point, double precision
+
+# other images
+0 string This\ is\ a\ BitMap\ file Lisp Machine bit-array-file
+
+# From SunOS 5.5.1 "/etc/magic" - appeared right before Sun raster image
+# stuff.
+#
+0 beshort 0x1010 PEX Binary Archive
+
+# DICOM medical imaging data
+128 string DICM DICOM medical imaging data
+!:mime application/dicom
+
+# XWD - X Window Dump file.
+# As described in /usr/X11R6/include/X11/XWDFile.h
+# used by the xwd program.
+# Bradford Castalia, idaeim, 1/01
+# updated by Adam Buchbinder, 2/09
+# The following assumes version 7 of the format; the first long is the length
+# of the header, which is at least 25 4-byte longs, and the one at offset 8
+# is a constant which is always either 1 or 2. Offset 12 is the pixmap depth,
+# which is a maximum of 32.
+0 belong >100
+>8 belong <3
+>>12 belong <33
+>>>4 belong 7 XWD X Window Dump image data
+!:mime image/x-xwindowdump
+>>>>100 string >\0 \b, "%s"
+>>>>16 belong x \b, %dx
+>>>>20 belong x \b%dx
+>>>>12 belong x \b%d
+
+# PDS - Planetary Data System
+# These files use Parameter Value Language in the header section.
+# Unfortunately, there is no certain magic, but the following
+# strings have been found to be most likely.
+0 string NJPL1I00 PDS (JPL) image data
+2 string NJPL1I PDS (JPL) image data
+0 string CCSD3ZF PDS (CCSD) image data
+2 string CCSD3Z PDS (CCSD) image data
+0 string PDS_ PDS image data
+0 string LBLSIZE= PDS (VICAR) image data
+
+# pM8x: ATARI STAD compressed bitmap format
+#
+# from Oskar Schirmer <schirmer@scara.com> Feb 2, 2001
+# p M 8 5/6 xx yy zz data...
+# Atari ST STAD bitmap is always 640x400, bytewise runlength compressed.
+# bytes either run horizontally (pM85) or vertically (pM86). yy is the
+# most frequent byte, xx and zz are runlength escape codes, where xx is
+# used for runs of yy.
+#
+0 string pM85 Atari ST STAD bitmap image data (hor)
+>5 byte 0x00 (white background)
+>5 byte 0xFF (black background)
+0 string pM86 Atari ST STAD bitmap image data (vert)
+>5 byte 0x00 (white background)
+>5 byte 0xFF (black background)
+
+# Gurkan Sengun <gurkan@linuks.mine.nu>, www.linuks.mine.nu
+# http://www.atarimax.com/jindroush.atari.org/afmtatr.html
+0 leshort 0x0296 Atari ATR image
+
+# XXX:
+# This is bad magic 0x5249 == 'RI' conflicts with RIFF and other
+# magic.
+# SGI RICE image file <mpruett@sgi.com>
+#0 beshort 0x5249 RICE image
+#>2 beshort x v%d
+#>4 beshort x (%d x
+#>6 beshort x %d)
+#>8 beshort 0 8 bit
+#>8 beshort 1 10 bit
+#>8 beshort 2 12 bit
+#>8 beshort 3 13 bit
+#>10 beshort 0 4:2:2
+#>10 beshort 1 4:2:2:4
+#>10 beshort 2 4:4:4
+#>10 beshort 3 4:4:4:4
+#>12 beshort 1 RGB
+#>12 beshort 2 CCIR601
+#>12 beshort 3 RP175
+#>12 beshort 4 YUV
+
+# PCX image files
+# From: Dan Fandrich <dan@coneharvesters.com>
+# updated by Joerg Jenderek at Feb 2013 by http://de.wikipedia.org/wiki/PCX
+# http://web.archive.org/web/20100206055706/http://www.qzx.com/pc-gpe/pcx.txt
+# GRR: original test was still too general as it catches xbase examples T5.DBT,T6.DBT with 0xa000000
+# test for bytes 0x0a,version byte (0,2,3,4,5),compression byte flag(0,1), bit depth (>0) of PCX or T5.DBT,T6.DBT
+0 ubelong&0xffF8fe00 0x0a000000
+# for PCX bit depth > 0
+>3 ubyte >0
+# test for valid versions
+>>1 ubyte <6
+>>>1 ubyte !1 PCX
+!:mime image/x-pcx
+#!:mime image/pcx
+>>>>1 ubyte 0 ver. 2.5 image data
+>>>>1 ubyte 2 ver. 2.8 image data, with palette
+>>>>1 ubyte 3 ver. 2.8 image data, without palette
+>>>>1 ubyte 4 for Windows image data
+>>>>1 ubyte 5 ver. 3.0 image data
+>>>>4 uleshort x bounding box [%d,
+>>>>6 uleshort x %d] -
+>>>>8 uleshort x [%d,
+>>>>10 uleshort x %d],
+>>>>65 ubyte >1 %d planes each of
+>>>>3 ubyte x %d-bit
+>>>>68 byte 1 colour,
+>>>>68 byte 2 grayscale,
+# this should not happen
+>>>>68 default x image,
+>>>>12 leshort >0 %d x
+>>>>>14 uleshort x %d dpi,
+>>>>2 byte 0 uncompressed
+>>>>2 byte 1 RLE compressed
+
+# Adobe Photoshop
+# From: Asbjoern Sloth Toennesen <asbjorn@lila.io>
+0 string 8BPS Adobe Photoshop Image
+!:mime image/vnd.adobe.photoshop
+>4 beshort 2 (PSB)
+>18 belong x \b, %d x
+>14 belong x %d,
+>24 beshort 0 bitmap
+>24 beshort 1 grayscale
+>>12 beshort 2 with alpha
+>24 beshort 2 indexed
+>24 beshort 3 RGB
+>>12 beshort 4 \bA
+>24 beshort 4 CMYK
+>>12 beshort 5 \bA
+>24 beshort 7 multichannel
+>24 beshort 8 duotone
+>24 beshort 9 lab
+>12 beshort > 1
+>>12 beshort x \b, %dx
+>12 beshort 1 \b,
+>22 beshort x %d-bit channel
+>12 beshort > 1 \bs
+
+# XV thumbnail indicator (ThMO)
+0 string P7\ 332 XV thumbnail image data
+
+# NITF is defined by United States MIL-STD-2500A
+0 string NITF National Imagery Transmission Format
+>25 string >\0 dated %.14s
+
+# GEM Image: Version 1, Headerlen 8 (Wolfram Kleff)
+# Format variations from: Bernd Nuernberger <bernd.nuernberger@web.de>
+# See http://fileformats.archiveteam.org/wiki/GEM_Raster
+# For variations, also see:
+# http://www.seasip.info/Gem/ff_img.html (Ventura)
+# http://www.atari-wiki.com/?title=IMG_file (XIMG, STTT)
+# http://www.fileformat.info/format/gemraster/spec/index.htm (XIMG, STTT)
+# http://sylvana.net/1stguide/1STGUIDE.ENG (TIMG)
+0 beshort 0x0001
+>2 beshort 0x0008 GEM Image data
+>>0 use gem_info
+>2 beshort 0x0009 GEM Image data (Ventura)
+>>0 use gem_info
+16 string XIMG\0 GEM XIMG Image data
+>0 use gem_info
+16 string STTT\0\x10 GEM STTT Image data
+>0 use gem_info
+16 string TIMG\0 GEM TIMG Image data
+>0 use gem_info
+
+0 name gem_info
+>12 beshort x %d x
+>14 beshort x %d,
+>4 beshort x %d planes,
+>8 beshort x %d x
+>10 beshort x %d pixelsize
+
+# GEM Metafile (Wolfram Kleff)
+0 lelong 0x0018FFFF GEM Metafile data
+>4 leshort x version %d
+
+#
+# SMJPEG. A custom Motion JPEG format used by Loki Entertainment
+# Software Torbjorn Andersson <d91tan@Update.UU.SE>.
+#
+0 string \0\nSMJPEG SMJPEG
+>8 belong x %d.x data
+# According to the specification you could find any number of _TXT
+# headers here, but I can't think of any way of handling that. None of
+# the SMJPEG files I tried it on used this feature. Even if such a
+# file is encountered the output should still be reasonable.
+>16 string _SND \b,
+>>24 beshort >0 %d Hz
+>>26 byte 8 8-bit
+>>26 byte 16 16-bit
+>>28 string NONE uncompressed
+# >>28 string APCM ADPCM compressed
+>>27 byte 1 mono
+>>28 byte 2 stereo
+# Help! Isn't there any way to avoid writing this part twice?
+>>32 string _VID \b,
+# >>>48 string JFIF JPEG
+>>>40 belong >0 %d frames
+>>>44 beshort >0 (%d x
+>>>46 beshort >0 %d)
+>16 string _VID \b,
+# >>32 string JFIF JPEG
+>>24 belong >0 %d frames
+>>28 beshort >0 (%d x
+>>30 beshort >0 %d)
+
+0 string Paint\ Shop\ Pro\ Image\ File Paint Shop Pro Image File
+
+# "thumbnail file" (icon)
+# descended from "xv", but in use by other applications as well (Wolfram Kleff)
+0 string P7\ 332 XV "thumbnail file" (icon) data
+
+# taken from fkiss: (<yav@mte.biglobe.ne.jp> ?)
+0 string KiSS KISS/GS
+>4 byte 16 color
+>>5 byte x %d bit
+>>8 leshort x %d colors
+>>10 leshort x %d groups
+>4 byte 32 cell
+>>5 byte x %d bit
+>>8 leshort x %d x
+>>10 leshort x %d
+>>12 leshort x +%d
+>>14 leshort x +%d
+
+# Webshots (www.webshots.com), by John Harrison
+0 string C\253\221g\230\0\0\0 Webshots Desktop .wbz file
+
+# Hercules DASD image files
+# From Jan Jaeger <jj@septa.nl>
+0 string CKD_P370 Hercules CKD DASD image file
+>8 long x \b, %d heads per cylinder
+>12 long x \b, track size %d bytes
+>16 byte x \b, device type 33%2.2X
+
+0 string CKD_C370 Hercules compressed CKD DASD image file
+>8 long x \b, %d heads per cylinder
+>12 long x \b, track size %d bytes
+>16 byte x \b, device type 33%2.2X
+
+0 string CKD_S370 Hercules CKD DASD shadow file
+>8 long x \b, %d heads per cylinder
+>12 long x \b, track size %d bytes
+>16 byte x \b, device type 33%2.2X
+
+# Squeak images and programs - etoffi@softhome.net
+0 string \146\031\0\0 Squeak image data
+0 search/1 'From\040Squeak Squeak program text
+
+# partimage: file(1) magic for PartImage files (experimental, incomplete)
+# Author: Hans-Joachim Baader <hjb@pro-linux.de>
+0 string PaRtImAgE-VoLuMe PartImage
+>0x0020 string 0.6.1 file version %s
+>>0x0060 lelong >-1 volume %d
+#>>0x0064 8 byte identifier
+#>>0x007c reserved
+>>0x0200 string >\0 type %s
+>>0x1400 string >\0 device %s,
+>>0x1600 string >\0 original filename %s,
+# Some fields omitted
+>>0x2744 lelong 0 not compressed
+>>0x2744 lelong 1 gzip compressed
+>>0x2744 lelong 2 bzip2 compressed
+>>0x2744 lelong >2 compressed with unknown algorithm
+>0x0020 string >0.6.1 file version %s
+>0x0020 string <0.6.1 file version %s
+
+# DCX is multi-page PCX, using a simple header of up to 1024
+# offsets for the respective PCX components.
+# From: Joerg Wunsch <joerg_wunsch@uriah.heep.sax.de>
+0 lelong 987654321 DCX multi-page PCX image data
+
+# Simon Walton <simonw@matteworld.com>
+# Kodak Cineon format for scanned negatives
+# http://www.kodak.com/US/en/motion/support/dlad/
+0 lelong 0xd75f2a80 Cineon image data
+>200 belong >0 \b, %d x
+>204 belong >0 %d
+
+
+# Bio-Rad .PIC is an image format used by microscope control systems
+# and related image processing software used by biologists.
+# From: Vebjorn Ljosa <vebjorn@ljosa.com>
+# BOOL values are two-byte integers; use them to rule out false positives.
+# http://web.archive.org/web/20050317223257/www.cs.ubc.ca/spider/ladic/text/biorad.txt
+# Samples: http://www.loci.wisc.edu/software/sample-data
+14 leshort <2
+>62 leshort <2
+>>54 leshort 12345 Bio-Rad .PIC Image File
+>>>0 leshort >0 %d x
+>>>2 leshort >0 %d,
+>>>4 leshort =1 1 image in file
+>>>4 leshort >1 %d images in file
+
+# From Jan "Yenya" Kasprzak <kas@fi.muni.cz>
+# The description of *.mrw format can be found at
+# http://www.dalibor.cz/minolta/raw_file_format.htm
+0 string \000MRM Minolta Dimage camera raw image data
+
+# Summary: DjVu image / document
+# Extension: .djvu
+# Reference: http://djvu.org/docs/DjVu3Spec.djvu
+# Submitted by: Stephane Loeuillet <stephane.loeuillet@tiscali.fr>
+# Modified by (1): Abel Cheung <abelcheung@gmail.com>
+0 string AT&TFORM
+>12 string DJVM DjVu multiple page document
+!:mime image/vnd.djvu
+>12 string DJVU DjVu image or single page document
+!:mime image/vnd.djvu
+>12 string DJVI DjVu shared document
+!:mime image/vnd.djvu
+>12 string THUM DjVu page thumbnails
+!:mime image/vnd.djvu
+
+# Originally by Marc Espie
+# Modified by Robert Minsk <robertminsk at yahoo.com>
+# http://www.openexr.com/openexrfilelayout.pdf
+0 lelong 20000630 OpenEXR image data,
+!:mime image/x-exr
+>4 lelong&0x000000ff x version %d,
+>4 lelong ^0x00000200 storage: scanline
+>4 lelong &0x00000200 storage: tiled
+>8 search/0x1000 compression\0 \b, compression:
+>>&16 byte 0 none
+>>&16 byte 1 rle
+>>&16 byte 2 zips
+>>&16 byte 3 zip
+>>&16 byte 4 piz
+>>&16 byte 5 pxr24
+>>&16 byte 6 b44
+>>&16 byte 7 b44a
+>>&16 byte >7 unknown
+>8 search/0x1000 dataWindow\0 \b, dataWindow:
+>>&10 lelong x (%d
+>>&14 lelong x %d)-
+>>&18 lelong x \b(%d
+>>&22 lelong x %d)
+>8 search/0x1000 displayWindow\0 \b, displayWindow:
+>>&10 lelong x (%d
+>>&14 lelong x %d)-
+>>&18 lelong x \b(%d
+>>&22 lelong x %d)
+>8 search/0x1000 lineOrder\0 \b, lineOrder:
+>>&14 byte 0 increasing y
+>>&14 byte 1 decreasing y
+>>&14 byte 2 random y
+>>&14 byte >2 unknown
+
+# SMPTE Digital Picture Exchange Format, SMPTE DPX
+#
+# ANSI/SMPTE 268M-1994, SMPTE Standard for File Format for Digital
+# Moving-Picture Exchange (DPX), v1.0, 18 February 1994
+# Robert Minsk <robertminsk at yahoo.com>
+0 string SDPX DPX image data, big-endian,
+!:mime image/x-dpx
+>768 beshort <4
+>>772 belong x %dx
+>>776 belong x \b%d,
+>768 beshort >3
+>>776 belong x %dx
+>>772 belong x \b%d,
+>768 beshort 0 left to right/top to bottom
+>768 beshort 1 right to left/top to bottom
+>768 beshort 2 left to right/bottom to top
+>768 beshort 3 right to left/bottom to top
+>768 beshort 4 top to bottom/left to right
+>768 beshort 5 top to bottom/right to left
+>768 leshort 6 bottom to top/left to right
+>768 leshort 7 bottom to top/right to left
+
+# From: Tom Hilinski <tom.hilinski@comcast.net>
+# http://www.unidata.ucar.edu/packages/netcdf/
+0 string CDF\001 NetCDF Data Format data
+
+#-----------------------------------------------------------------------
+# Hierarchical Data Format, used to facilitate scientific data exchange
+# specifications at http://hdf.ncsa.uiuc.edu/
+0 belong 0x0e031301 Hierarchical Data Format (version 4) data
+!:mime application/x-hdf
+0 string \211HDF\r\n\032\n Hierarchical Data Format (version 5) data
+!:mime application/x-hdf
+512 string \211HDF\r\n\032\n Hierarchical Data Format (version 5) with 512 bytes user block
+!:mime application/x-hdf
+1024 string \211HDF\r\n\032\n Hierarchical Data Format (version 5) with 1k user block
+!:mime application/x-hdf
+2048 string \211HDF\r\n\032\n Hierarchical Data Format (version 5) with 2k user block
+!:mime application/x-hdf
+4096 string \211HDF\r\n\032\n Hierarchical Data Format (version 5) with 4k user block
+!:mime application/x-hdf
+
+
+# From: Tobias Burnus <burnus@net-b.de>
+# Xara (for a while: Corel Xara) is a graphic package, see
+# http://www.xara.com/ for Windows and as GPL application for Linux
+0 string XARA\243\243 Xara graphics file
+
+# http://www.cartesianinc.com/Tech/
+0 string CPC\262 Cartesian Perceptual Compression image
+!:mime image/x-cpi
+
+# From Albert Cahalan <acahalan@gmail.com>
+# puredigital used it for the CVS disposable camcorder
+#8 lelong 4 ZBM bitmap image data
+#>4 leshort x %u x
+#>6 leshort x %u
+
+# From Albert Cahalan <acahalan@gmail.com>
+# uncompressed 5:6:5 HighColor image for OLPC XO firmware icons
+0 string C565 OLPC firmware icon image data
+>4 leshort x %u x
+>6 leshort x %u
+
+# Applied Images - Image files from Cytovision
+# Gustavo Junior Alves <gjalves@gjalves.com.br>
+0 string \xce\xda\xde\xfa Cytovision Metaphases file
+0 string \xed\xad\xef\xac Cytovision Karyotype file
+0 string \x0b\x00\x03\x00 Cytovision FISH Probe file
+0 string \xed\xfe\xda\xbe Cytovision FLEX file
+0 string \xed\xab\xed\xfe Cytovision FLEX file
+0 string \xad\xfd\xea\xad Cytovision RATS file
+
+# Wavelet Scalar Quantization format used in gray-scale fingerprint images
+# From Tano M Fotang <mfotang@quanteq.com>
+0 string \xff\xa0\xff\xa8\x00 Wavelet Scalar Quantization image data
+
+# Type: PCO B16 image files
+# URL: http://www.pco.de/fileadmin/user_upload/db/download/MA_CWDCOPIE_0412b.pdf
+# From: Florian Philipp <florian.philipp@binarywings.net>
+# Extension: .b16
+# Description: Pixel image format produced by PCO Camware, typically used
+# together with PCO cameras.
+# Note: Different versions exist for e.g. 8 bit and 16 bit images.
+# Documentation is incomplete.
+0 string/b PCO- PCO B16 image data
+>12 lelong x \b, %dx
+>16 lelong x \b%d
+>20 lelong 0 \b, short header
+>20 lelong -1 \b, extended header
+>>24 lelong 0 \b, grayscale
+>>>36 lelong 0 linear LUT
+>>>36 lelong 1 logarithmic LUT
+>>>28 lelong x [%d
+>>>32 lelong x \b,%d]
+>>24 lelong 1 \b, color
+>>>64 lelong 0 linear LUT
+>>>64 lelong 1 logarithmic LUT
+>>>40 lelong x r[%d
+>>>44 lelong x \b,%d]
+>>>48 lelong x g[%d
+>>>52 lelong x \b,%d]
+>>>56 lelong x b[%d
+>>>60 lelong x \b,%d]
+
+# Polar Monitor Bitmap (.pmb) used as logo for Polar Electro watches
+# From: Markus Heidelberg <markus.heidelberg at web.de>
+0 string/t [BitmapInfo2] Polar Monitor Bitmap text
+!:mime image/x-polar-monitor-bitmap
+
+# From: Rick Richardson <rickrich@gmail.com>
+0 string GARMIN\ BITMAP\ 01 Garmin Bitmap file
+
+# Type: Ulead Photo Explorer5 (.pe5)
+# URL: http://www.jisyo.com/cgibin/view.cgi?EXT=pe5 (Japanese)
+# From: Simon Horman <horms@debian.org>
+0 string IIO2H Ulead Photo Explorer5
+
+# Type: X11 cursor
+# URL: http://webcvs.freedesktop.org/mime/shared-mime-info/freedesktop.org.xml.in?view=markup
+# From: Mathias Brodala <info@noctus.net>
+0 string Xcur X11 cursor
+
+# Type: Olympus ORF raw images.
+# URL: http://libopenraw.freedesktop.org/wiki/Olympus_ORF
+# From: Adam Buchbinder <adam.buchbinder@gmail.com>
+0 string MMOR Olympus ORF raw image data, big-endian
+!:mime image/x-olympus-orf
+0 string IIRO Olympus ORF raw image data, little-endian
+!:mime image/x-olympus-orf
+0 string IIRS Olympus ORF raw image data, little-endian
+!:mime image/x-olympus-orf
+
+# Type: files used in modern AVCHD camcoders to store clip information
+# Extension: .cpi
+# From: Alexander Danilov <alexander.a.danilov@gmail.com>
+0 string HDMV0100 AVCHD Clip Information
+
+# From: Adam Buchbinder <adam.buchbinder@gmail.com>
+# URL: http://local.wasp.uwa.edu.au/~pbourke/dataformats/pic/
+# Radiance HDR; usually has .pic or .hdr extension.
+0 string #?RADIANCE\n Radiance HDR image data
+#!mime image/vnd.radiance
+
+# From: Adam Buchbinder <adam.buchbinder@gmail.com>
+# URL: http://www.mpi-inf.mpg.de/resources/pfstools/pfs_format_spec.pdf
+# Used by the pfstools packages. The regex matches for the image size could
+# probably use some work. The MIME type is made up; if there's one in
+# actual common use, it should replace the one below.
+0 string PFS1\x0a PFS HDR image data
+#!mime image/x-pfs
+>1 regex [0-9]*\ \b, %s
+>>1 regex \ [0-9]{4} \bx%s
+
+# Type: Foveon X3F
+# URL: http://www.photofo.com/downloads/x3f-raw-format.pdf
+# From: Adam Buchbinder <adam.buchbinder@gmail.com>
+# Note that the MIME type isn't defined anywhere that I can find; if
+# there's a canonical type for this format, it should replace this one.
+0 string FOVb Foveon X3F raw image data
+!:mime image/x-x3f
+>6 leshort x \b, version %d.
+>4 leshort x \b%d
+>28 lelong x \b, %dx
+>32 lelong x \b%d
+
+# Paint.NET file
+# From Adam Buchbinder <adam.buchbinder@gmail.com>
+0 string PDN3 Paint.NET image data
+!:mime image/x-paintnet
+
+# Not really an image.
+# From: "Tano M. Fotang" <mfotang@quanteq.com>
+0 string \x46\x4d\x52\x00 ISO/IEC 19794-2 Format Minutiae Record (FMR)
+
+# doc: http://www.shikino.co.jp/eng/products/images/FLOWER.jpg.zip
+# example: http://www.shikino.co.jp/eng/products/images/FLOWER.wdp.zip
+90 bequad 0x574D50484F544F00 JPEG-XR Image
+>98 byte&0x08 =0x08 \b, hard tiling
+>99 byte&0x80 =0x80 \b, tiling present
+>99 byte&0x40 =0x40 \b, codestream present
+>99 byte&0x38 x \b, spatial xform=
+>99 byte&0x38 0x00 \bTL
+>99 byte&0x38 0x08 \bBL
+>99 byte&0x38 0x10 \bTR
+>99 byte&0x38 0x18 \bBR
+>99 byte&0x38 0x20 \bBT
+>99 byte&0x38 0x28 \bRB
+>99 byte&0x38 0x30 \bLT
+>99 byte&0x38 0x38 \bLB
+>100 byte&0x80 =0x80 \b, short header
+>>102 beshort+1 x \b, %d
+>>104 beshort+1 x \bx%d
+>100 byte&0x80 =0x00 \b, long header
+>>102 belong+1 x \b, %x
+>>106 belong+1 x \bx%x
+>101 beshort&0xf x \b, bitdepth=
+>>101 beshort&0xf 0x0 \b1-WHITE=1
+>>101 beshort&0xf 0x1 \b8
+>>101 beshort&0xf 0x2 \b16
+>>101 beshort&0xf 0x3 \b16-SIGNED
+>>101 beshort&0xf 0x4 \b16-FLOAT
+>>101 beshort&0xf 0x5 \b(reserved 5)
+>>101 beshort&0xf 0x6 \b32-SIGNED
+>>101 beshort&0xf 0x7 \b32-FLOAT
+>>101 beshort&0xf 0x8 \b5
+>>101 beshort&0xf 0x9 \b10
+>>101 beshort&0xf 0xa \b5-6-5
+>>101 beshort&0xf 0xb \b(reserved %d)
+>>101 beshort&0xf 0xc \b(reserved %d)
+>>101 beshort&0xf 0xd \b(reserved %d)
+>>101 beshort&0xf 0xe \b(reserved %d)
+>>101 beshort&0xf 0xf \b1-BLACK=1
+>101 beshort&0xf0 x \b, colorfmt=
+>>101 beshort&0xf0 0x00 \bYONLY
+>>101 beshort&0xf0 0x10 \bYUV240
+>>101 beshort&0xf0 0x20 \bYWV422
+>>101 beshort&0xf0 0x30 \bYWV444
+>>101 beshort&0xf0 0x40 \bCMYK
+>>101 beshort&0xf0 0x50 \bCMYKDIRECT
+>>101 beshort&0xf0 0x60 \bNCOMPONENT
+>>101 beshort&0xf0 0x70 \bRGB
+>>101 beshort&0xf0 0x80 \bRGBE
+>>101 beshort&0xf0 >0x80 \b(reserved 0x%x)
+
+# From: Johan van der Knijff <johan.vanderknijff@kb.nl>
+#
+# BPG (Better Portable Graphics) format
+# http://bellard.org/bpg/
+# http://fileformats.archiveteam.org/wiki/BPG
+#
+0 string \x42\x50\x47\xFB BPG (Better Portable Graphics)
+!:mime image/bpg
+
+#------------------------------------------------------------------------------
+# $File$
+# inform: file(1) magic for Inform interactive fiction language
+
+# URL: http://www.inform-fiction.org/
+# From: Reuben Thomas <rrt@sc3d.org>
+
+0 search/100/cW constant\ story Inform source text
+
+#------------------------------------------------------------------------------
+# $File: intel,v 1.11 2013/02/06 14:18:52 christos Exp $
+# intel: file(1) magic for x86 Unix
+#
+# Various flavors of x86 UNIX executable/object (other than Xenix, which
+# is in "microsoft"). DOS is in "msdos"; the ambitious soul can do
+# Windows as well.
+#
+# Windows NT belongs elsewhere, as you need x86 and MIPS and Alpha and
+# whatever comes next (HP-PA Hummingbird?). OS/2 may also go elsewhere
+# as well, if, as, and when IBM makes it portable.
+#
+# The `versions' should be un-commented if they work for you.
+# (Was the problem just one of endianness?)
+#
+0 leshort 0502 basic-16 executable
+>12 lelong >0 not stripped
+#>22 leshort >0 - version %d
+0 leshort 0503 basic-16 executable (TV)
+>12 lelong >0 not stripped
+#>22 leshort >0 - version %d
+0 leshort 0510 x86 executable
+>12 lelong >0 not stripped
+0 leshort 0511 x86 executable (TV)
+>12 lelong >0 not stripped
+0 leshort =0512 iAPX 286 executable small model (COFF)
+>12 lelong >0 not stripped
+#>22 leshort >0 - version %d
+0 leshort =0522 iAPX 286 executable large model (COFF)
+>12 lelong >0 not stripped
+#>22 leshort >0 - version %d
+# SGI labeled the next entry as "iAPX 386 executable" --Dan Quinlan
+0 leshort =0514 80386 COFF executable
+>12 lelong >0 not stripped
+>22 leshort >0 - version %d
+
+# rom: file(1) magic for BIOS ROM Extensions found in intel machines
+# mapped into memory between 0xC0000 and 0xFFFFF
+# From Gurkan Sengun <gurkan@linuks.mine.nu>, www.linuks.mine.nu
+0 beshort 0x55AA BIOS (ia32) ROM Ext.
+>5 string USB USB
+>7 string LDR UNDI image
+>30 string IBM IBM comp. Video
+>26 string Adaptec Adaptec
+>28 string Adaptec Adaptec
+>42 string PROMISE Promise
+>2 byte x (%d*512)
+
+# Flash descriptors for Intel SPI flash roms.
+# From Dr. Jesus <j@hug.gs>
+0 lelong 0x0ff0a55a Intel serial flash for ICH/PCH ROM <= 5 or 3400 series A-step
+16 lelong 0x0ff0a55a Intel serial flash for PCH ROM
+
+#------------------------------------------------------------------------------
+# $File$
+# interleaf: file(1) magic for InterLeaf TPS:
+#
+0 string =\210OPS Interleaf saved data
+0 string =<!OPS Interleaf document text
+>5 string ,\ Version\ = \b, version
+>>17 string >\0 %.3s
+
+#------------------------------------------------------------------------------
+# $File$
+# island: file(1) magic for IslandWite/IslandDraw, from SunOS 5.5.1
+# "/etc/magic":
+# From: guy@netapp.com (Guy Harris)
+#
+4 string pgscriptver IslandWrite document
+13 string DrawFile IslandDraw document
+
+
+#------------------------------------------------------------------------------
+# $File$
+# ispell: file(1) magic for ispell
+#
+# Ispell 3.0 has a magic of 0x9601 and ispell 3.1 has 0x9602. This magic
+# will match 0x9600 through 0x9603 in *both* little endian and big endian.
+# (No other current magic entries collide.)
+#
+# Updated by Daniel Quinlan (quinlan@yggdrasil.com)
+#
+0 leshort&0xFFFC 0x9600 little endian ispell
+>0 byte 0 hash file (?),
+>0 byte 1 3.0 hash file,
+>0 byte 2 3.1 hash file,
+>0 byte 3 hash file (?),
+>2 leshort 0x00 8-bit, no capitalization, 26 flags
+>2 leshort 0x01 7-bit, no capitalization, 26 flags
+>2 leshort 0x02 8-bit, capitalization, 26 flags
+>2 leshort 0x03 7-bit, capitalization, 26 flags
+>2 leshort 0x04 8-bit, no capitalization, 52 flags
+>2 leshort 0x05 7-bit, no capitalization, 52 flags
+>2 leshort 0x06 8-bit, capitalization, 52 flags
+>2 leshort 0x07 7-bit, capitalization, 52 flags
+>2 leshort 0x08 8-bit, no capitalization, 128 flags
+>2 leshort 0x09 7-bit, no capitalization, 128 flags
+>2 leshort 0x0A 8-bit, capitalization, 128 flags
+>2 leshort 0x0B 7-bit, capitalization, 128 flags
+>2 leshort 0x0C 8-bit, no capitalization, 256 flags
+>2 leshort 0x0D 7-bit, no capitalization, 256 flags
+>2 leshort 0x0E 8-bit, capitalization, 256 flags
+>2 leshort 0x0F 7-bit, capitalization, 256 flags
+>4 leshort >0 and %d string characters
+0 beshort&0xFFFC 0x9600 big endian ispell
+>1 byte 0 hash file (?),
+>1 byte 1 3.0 hash file,
+>1 byte 2 3.1 hash file,
+>1 byte 3 hash file (?),
+>2 beshort 0x00 8-bit, no capitalization, 26 flags
+>2 beshort 0x01 7-bit, no capitalization, 26 flags
+>2 beshort 0x02 8-bit, capitalization, 26 flags
+>2 beshort 0x03 7-bit, capitalization, 26 flags
+>2 beshort 0x04 8-bit, no capitalization, 52 flags
+>2 beshort 0x05 7-bit, no capitalization, 52 flags
+>2 beshort 0x06 8-bit, capitalization, 52 flags
+>2 beshort 0x07 7-bit, capitalization, 52 flags
+>2 beshort 0x08 8-bit, no capitalization, 128 flags
+>2 beshort 0x09 7-bit, no capitalization, 128 flags
+>2 beshort 0x0A 8-bit, capitalization, 128 flags
+>2 beshort 0x0B 7-bit, capitalization, 128 flags
+>2 beshort 0x0C 8-bit, no capitalization, 256 flags
+>2 beshort 0x0D 7-bit, no capitalization, 256 flags
+>2 beshort 0x0E 8-bit, capitalization, 256 flags
+>2 beshort 0x0F 7-bit, capitalization, 256 flags
+>4 beshort >0 and %d string characters
+# ispell 4.0 hash files kromJx <kromJx@crosswinds.net>
+# Ispell 4.0
+0 string ISPL ispell
+>4 long x hash file version %d,
+>8 long x lexletters %d,
+>12 long x lexsize %d,
+>16 long x hashsize %d,
+>20 long x stblsize %d
+
+#------------------------------------------------------------------------------
+# $File: isz,v 1.2 2014/04/28 12:04:50 christos Exp $
+# ISO Zipped file format
+# http://www.ezbsystems.com/isz/iszspec.txt
+0 string IsZ! ISO Zipped file
+>4 byte x \b, header size %u
+>5 byte x \b, version %u
+>8 lelong x \b, serial %u
+#12 leshort x \b, sector size %u
+#>16 lelong x \b, total sectors %u
+>17 byte >0 \b, password protected
+#>24 lequad x \b, segment size %llu
+#>32 lelong x \b, blocks %u
+#>36 lelong x \b, block size %u
+
+#------------------------------------------------------------
+# $File: java,v 1.15 2013/08/14 09:10:36 christos Exp $
+# Java ByteCode and Mach-O binaries (e.g., Mac OS X) use the
+# same magic number, 0xcafebabe, so they are both handled
+# in the entry called "cafebabe".
+#------------------------------------------------------------
+# Java serialization
+# From Martin Pool (m.pool@pharos.com.au)
+0 beshort 0xaced Java serialization data
+>2 beshort >0x0004 \b, version %d
+
+0 belong 0xfeedfeed Java KeyStore
+!:mime application/x-java-keystore
+0 belong 0xcececece Java JCE KeyStore
+!:mime application/x-java-jce-keystore
+
+# Java source
+0 regex ^import.*;$ Java source
+!:mime text/x-java
+
+#------------------------------------------------------------------------------
+# $File: $
+# javascript: magic for javascript and node.js scripts.
+#
+0 search/1/w #!/bin/node Node.js script text executable
+!:mime application/javascript
+0 search/1/w #!/usr/bin/node Node.js script text executable
+!:mime application/javascript
+0 search/1/w #!/bin/nodejs Node.js script text executable
+!:mime application/javascript
+0 search/1/w #!/usr/bin/nodejs Node.js script text executable
+!:mime application/javascript
+0 search/1 #!/usr/bin/env\ node Node.js script text executable
+!:mime application/javascript
+0 search/1 #!/usr/bin/env\ nodejs Node.js script text executable
+!:mime application/javascript
+
+#------------------------------------------------------------------------------
+# $File: jpeg,v 1.26 2015/01/02 22:40:27 christos Exp $
+# JPEG images
+# SunOS 5.5.1 had
+#
+# 0 string \377\330\377\340 JPEG file
+# 0 string \377\330\377\356 JPG file
+#
+# both of which turn into "JPEG image data" here.
+#
+0 beshort 0xffd8 JPEG image data
+!:mime image/jpeg
+!:apple 8BIMJPEG
+!:strength *3
+>6 string JFIF \b, JFIF standard
+# The following added by Erik Rossen <rossen@freesurf.ch> 1999-09-06
+# in a vain attempt to add image size reporting for JFIF. Note that these
+# tests are not fool-proof since some perfectly valid JPEGs are currently
+# impossible to specify in magic(4) format.
+# First, a little JFIF version info:
+>>11 byte x \b %d.
+>>12 byte x \b%02d
+# Next, the resolution or aspect ratio of the image:
+>>13 byte 0 \b, aspect ratio
+>>13 byte 1 \b, resolution (DPI)
+>>13 byte 2 \b, resolution (DPCM)
+>>14 beshort x \b, density %dx
+>>16 beshort x \b%d
+>>4 beshort x \b, segment length %d
+# Next, show thumbnail info, if it exists:
+>>18 byte !0 \b, thumbnail %dx
+>>>19 byte x \b%d
+>6 string Exif \b, Exif standard: [
+>>12 indirect/r x
+>>12 string x \b]
+
+# Jump to the first segment
+>(4.S+4) use jpeg_segment
+
+# This uses recursion...
+0 name jpeg_segment
+>0 beshort 0xFFFE
+# Recursion handled by FFE0
+#>>(2.S+2) use jpeg_segment
+>>2 pstring/HJ x \b, comment: "%s"
+
+>0 beshort 0xFFC0
+>>(2.S+2) use jpeg_segment
+>>4 byte x \b, baseline, precision %d
+>>7 beshort x \b, %dx
+>>5 beshort x \b%d
+>>9 byte x \b, frames %d
+
+>0 beshort 0xFFC1
+>>(2.S+2) use jpeg_segment
+>>4 byte x \b, extended sequential, precision %d
+>>7 beshort x \b, %dx
+>>5 beshort x \b%d
+>>9 byte x \b, frames %d
+
+>0 beshort 0xFFC2
+>>(2.S+2) use jpeg_segment
+>>4 byte x \b, progressive, precision %d
+>>7 beshort x \b, %dx
+>>5 beshort x \b%d
+>>9 byte x \b, frames %d
+
+# Define Huffman Tables
+>0 beshort 0xFFC4
+>>(2.S+2) use jpeg_segment
+
+>0 beshort 0xFFE1
+# Recursion handled by FFE0
+#>>(2.S+2) use jpeg_segment
+>>4 string Exif \b, Exif Standard: [
+>>>10 indirect/r x
+>>>10 string x \b]
+
+# Application specific markers
+>0 beshort&0xFFE0 =0xFFE0
+>>(2.S+2) use jpeg_segment
+
+# DB: Define Quantization tables
+# DD: Define Restart interval [XXX: wrong here, it is 4 bytes]
+# D8: Start of image
+# D9: End of image
+# Dn: Restart
+>0 beshort&0xFFD0 =0xFFD0
+>>0 beshort&0xFFE0 !0xFFE0
+>>>(2.S+2) use jpeg_segment
+
+#>0 beshort x unknown 0x%x
+#>>(2.S+2) use jpeg_segment
+
+# HSI is Handmade Software's proprietary JPEG encoding scheme
+0 string hsi1 JPEG image data, HSI proprietary
+
+# From: David Santinoli <david@santinoli.com>
+0 string \x00\x00\x00\x0C\x6A\x50\x20\x20\x0D\x0A\x87\x0A JPEG 2000
+# From: Johan van der Knijff <johan.vanderknijff@kb.nl>
+# Added sub-entries for JP2, JPX, JPM and MJ2 formats; added mimetypes
+# https://github.com/bitsgalore/jp2kMagic
+#
+# Now read value of 'Brand' field, which yields a few possibilities:
+>20 string \x6a\x70\x32\x20 Part 1 (JP2)
+!:mime image/jp2
+>20 string \x6a\x70\x78\x20 Part 2 (JPX)
+!:mime image/jpx
+>20 string \x6a\x70\x6d\x20 Part 6 (JPM)
+!:mime image/jpm
+>20 string \x6d\x6a\x70\x32 Part 3 (MJ2)
+!:mime video/mj2
+
+# Type: JPEG 2000 codesream
+# From: Mathieu Malaterre <mathieu.malaterre@gmail.com>
+0 belong 0xff4fff51 JPEG 2000 codestream
+45 beshort 0xff52
+
+#------------------------------------------------------------------------------
+# $File: karma,v 1.6 2009/09/19 16:28:10 christos Exp $
+# karma: file(1) magic for Karma data files
+#
+# From <rgooch@atnf.csiro.au>
+
+0 string KarmaRHD Version Karma Data Structure Version
+>16 belong x %u
+
+#------------------------------------------------------------------------------
+# $File: kde,v 1.4 2009/09/19 16:28:10 christos Exp $
+# kde: file(1) magic for KDE
+
+0 string/t [KDE\ Desktop\ Entry] KDE desktop entry
+!:mime application/x-kdelnk
+0 string/t #\ KDE\ Config\ File KDE config file
+!:mime application/x-kdelnk
+0 string/t #\ xmcd xmcd database file for kscd
+!:mime text/x-xmcd
+
+#------------------------------------------------------------------------------
+# $File: kml,v 1.3 2010/11/25 15:00:12 christos Exp $
+# keepass: file(1) magic for KeePass file
+#
+# Keepass Password Safe:
+# * original one: http://keepass.info/
+# * *nix port: http://www.keepassx.org/
+# * android port: http://code.google.com/p/keepassdroid/
+
+0 lelong 0x9AA2D903 Keepass password database
+>4 lelong 0xB54BFB65 1.x KDB
+>>48 lelong >0 \b, %d groups
+>>52 lelong >0 \b, %d entries
+>>8 lelong&0x0f 1 \b, SHA-256
+>>8 lelong&0x0f 2 \b, AES
+>>8 lelong&0x0f 4 \b, RC4
+>>8 lelong&0x0f 8 \b, Twofish
+>>120 lelong >0 \b, %d key transformation rounds
+>4 lelong 0xB54BFB67 2.x KDBX
+
+#------------------------------------------------------------------------------
+# $File: map,v 1.1 2014/06/03 18:22:25 christos Exp $
+# kerberos: MIT kerberos file binary formats
+#
+
+# This magic entry is for demonstration purposes and could be improved
+# if the following features were implemented in file:
+#
+# Strings inside [[ .. ]] in the descriptions have special meanings and
+# are not printed.
+#
+# - Provide some form of iteration in number of components
+# [[${counter}=%d]] in the description
+# then append
+# [${counter}--] in the offset of the entries
+# - Provide a way to round the next offset
+# Add [R:4] after the offset?
+# - Provide a way to have optional entries
+# XXX: Syntax:
+# - Provide a way to "save" entries to print them later.
+# if the description is [[${name}=%s]], then nothing is
+# printed and a subsequent entry in the same magic file
+# can refer to ${name}
+# - Provide a way to format strings as hex values
+#
+# http://www.gnu.org/software/shishi/manual/html_node/\
+# The-Keytab-Binary-File-Format.html
+#
+
+0 name keytab_entry
+#>0 beshort x \b, size=%d
+#>2 beshort x \b, components=%d
+>4 pstring/H x \b, realm=%s
+>>&0 pstring/H x \b, principal=%s/
+>>>&0 pstring/H x \b%s
+>>>>&0 belong x \b, type=%d
+>>>>>&0 bedate x \b, date=%s
+>>>>>>&0 byte x \b, kvno=%u
+#>>>>>>>&0 pstring/H x
+#>>>>>>>>&0 belong x
+#>>>>>>>>>>&0 use keytab_entry
+
+0 belong 0x05020000 Kerberos Keytab file
+>4 use keytab_entry
+
+#------------------------------------------------------------------------------
+# $File: kml,v 1.2 2009/09/19 16:28:10 christos Exp $
+# Type: Google KML, formerly Keyhole Markup Language
+# Future development of this format has been handed
+# over to the Open Geospatial Consortium.
+# http://www.opengeospatial.org/standards/kml/
+# From: Asbjoern Sloth Toennesen <asbjorn@lila.io>
+0 string/t \<?xml
+>20 search/400 \ xmlns=
+>>&0 regex ['"]http://earth.google.com/kml Google KML document
+!:mime application/vnd.google-earth.kml+xml
+>>>&1 string 2.0' \b, version 2.0
+>>>&1 string 2.1' \b, version 2.1
+>>>&1 string 2.2' \b, version 2.2
+
+#------------------------------------------------------------------------------
+# Type: OpenGIS KML, formerly Keyhole Markup Language
+# This standard is maintained by the
+# Open Geospatial Consortium.
+# http://www.opengeospatial.org/standards/kml/
+# From: Asbjoern Sloth Toennesen <asbjorn@lila.io>
+>>&0 regex ['"]http://www.opengis.net/kml OpenGIS KML document
+!:mime application/vnd.google-earth.kml+xml
+>>>&1 string/t 2.2 \b, version 2.2
+
+#------------------------------------------------------------------------------
+# Type: Google KML Archive (ZIP based)
+# http://code.google.com/apis/kml/documentation/kml_tut.html
+# From: Asbjoern Sloth Toennesen <asbjorn@lila.io>
+0 string PK\003\004
+>4 byte 0x14
+>>30 string doc.kml Compressed Google KML Document, including resources.
+!:mime application/vnd.google-earth.kmz
+
+#------------------------------------------------------------------------------
+# $File$
+# DEC SRC Virtual Paper: Lectern files
+# Karl M. Hegbloom <karlheg@inetarena.com>
+0 string lect DEC SRC Virtual Paper Lectern file
+
+#------------------------------------------------------------------------------
+# $File$
+# lex: file(1) magic for lex
+#
+# derived empirically, your offsets may vary!
+0 search/100 yyprevious C program text (from lex)
+>3 search/1 >\0 for %s
+# C program text from GNU flex, from Daniel Quinlan <quinlan@yggdrasil.com>
+0 search/100 generated\ by\ flex C program text (from flex)
+# lex description file, from Daniel Quinlan <quinlan@yggdrasil.com>
+0 search/1 %{ lex description text
+
+#------------------------------------------------------------------------------
+# $File$
+# lif: file(1) magic for lif
+#
+# (Daniel Quinlan <quinlan@yggdrasil.com>)
+#
+0 beshort 0x8000 lif file
+
+#------------------------------------------------------------------------------
+# $File: linux,v 1.58 2014/08/04 06:21:30 christos Exp $
+# linux: file(1) magic for Linux files
+#
+# Values for Linux/i386 binaries, from Daniel Quinlan <quinlan@yggdrasil.com>
+# The following basic Linux magic is useful for reference, but using
+# "long" magic is a better practice in order to avoid collisions.
+#
+# 2 leshort 100 Linux/i386
+# >0 leshort 0407 impure executable (OMAGIC)
+# >0 leshort 0410 pure executable (NMAGIC)
+# >0 leshort 0413 demand-paged executable (ZMAGIC)
+# >0 leshort 0314 demand-paged executable (QMAGIC)
+#
+0 lelong 0x00640107 Linux/i386 impure executable (OMAGIC)
+>16 lelong 0 \b, stripped
+0 lelong 0x00640108 Linux/i386 pure executable (NMAGIC)
+>16 lelong 0 \b, stripped
+0 lelong 0x0064010b Linux/i386 demand-paged executable (ZMAGIC)
+>16 lelong 0 \b, stripped
+0 lelong 0x006400cc Linux/i386 demand-paged executable (QMAGIC)
+>16 lelong 0 \b, stripped
+#
+0 string \007\001\000 Linux/i386 object file
+>20 lelong >0x1020 \b, DLL library
+# Linux-8086 stuff:
+0 string \01\03\020\04 Linux-8086 impure executable
+>28 long !0 not stripped
+0 string \01\03\040\04 Linux-8086 executable
+>28 long !0 not stripped
+#
+0 string \243\206\001\0 Linux-8086 object file
+#
+0 string \01\03\020\20 Minix-386 impure executable
+>28 long !0 not stripped
+0 string \01\03\040\20 Minix-386 executable
+>28 long !0 not stripped
+0 string \01\03\04\20 Minix-386 NSYM/GNU executable
+>28 long !0 not stripped
+# core dump file, from Bill Reynolds <bill@goshawk.lanl.gov>
+216 lelong 0421 Linux/i386 core file
+!:strength / 2
+>220 string >\0 of '%s'
+>200 lelong >0 (signal %d)
+#
+# LILO boot/chain loaders, from Daniel Quinlan <quinlan@yggdrasil.com>
+# this can be overridden by the DOS executable (COM) entry
+2 string LILO Linux/i386 LILO boot/chain loader
+#
+# Linux make config build file, from Ole Aamot <oka@oka.no>
+# Updated by Ken Sharp
+28 string make\ config Linux make config build file (old)
+49 search/70 Kernel\ Configuration Linux make config build file
+
+#
+# PSF fonts, from H. Peter Anvin <hpa@yggdrasil.com>
+# Updated by Adam Buchbinder <adam.buchbinder@gmail.com>
+# See: http://www.win.tue.nl/~aeb/linux/kbd/font-formats-1.html
+0 leshort 0x0436 Linux/i386 PC Screen Font v1 data,
+>2 byte&0x01 0 256 characters,
+>2 byte&0x01 !0 512 characters,
+>2 byte&0x02 0 no directory,
+>2 byte&0x02 !0 Unicode directory,
+>3 byte >0 8x%d
+0 string \x72\xb5\x4a\x86\x00\x00 Linux/i386 PC Screen Font v2 data,
+>16 lelong x %d characters,
+>12 lelong&0x01 0 no directory,
+>12 lelong&0x01 !0 Unicode directory,
+>24 lelong x %d
+>28 lelong x \bx%d
+
+# Linux swap file, from Daniel Quinlan <quinlan@yggdrasil.com>
+4086 string SWAP-SPACE Linux/i386 swap file
+# From: Jeff Bailey <jbailey@ubuntu.com>
+# Linux swap file with swsusp1 image, from Jeff Bailey <jbailey@ubuntu.com>
+4076 string SWAPSPACE2S1SUSPEND Linux/i386 swap file (new style) with SWSUSP1 image
+# From: James Hunt <james.hunt@ubuntu.com>
+4076 string SWAPSPACE2LINHIB0001 Linux/i386 swap file (new style) (compressed hibernate)
+# according to man page of mkswap (8) March 1999
+# volume label and UUID Russell Coker
+# http://etbe.coker.com.au/2008/07/08/label-vs-uuid-vs-device/
+4086 string SWAPSPACE2 Linux/i386 swap file (new style),
+>0x400 long x version %d (4K pages),
+>0x404 long x size %d pages,
+>1052 string \0 no label,
+>1052 string >\0 LABEL=%s,
+>0x40c belong x UUID=%08x
+>0x410 beshort x \b-%04x
+>0x412 beshort x \b-%04x
+>0x414 beshort x \b-%04x
+>0x416 belong x \b-%08x
+>0x41a beshort x \b%04x
+# From Daniel Novotny <dnovotny@redhat.com>
+# swap file for PowerPC
+65526 string SWAPSPACE2 Linux/ppc swap file
+16374 string SWAPSPACE2 Linux/ia64 swap file
+#
+# Linux kernel boot images, from Albert Cahalan <acahalan@cs.uml.edu>
+# and others such as Axel Kohlmeyer <akohlmey@rincewind.chemie.uni-ulm.de>
+# and Nicolas Lichtmaier <nick@debian.org>
+# All known start with: b8 c0 07 8e d8 b8 00 90 8e c0 b9 00 01 29 f6 29
+# Linux kernel boot images (i386 arch) (Wolfram Kleff)
+514 string HdrS Linux kernel
+!:strength + 55
+>510 leshort 0xAA55 x86 boot executable
+>>518 leshort >0x1ff
+>>>529 byte 0 zImage,
+>>>529 byte 1 bzImage,
+>>>526 lelong >0
+>>>>(526.s+0x200) string >\0 version %s,
+>>498 leshort 1 RO-rootFS,
+>>498 leshort 0 RW-rootFS,
+>>508 leshort >0 root_dev 0x%X,
+>>502 leshort >0 swap_dev 0x%X,
+>>504 leshort >0 RAMdisksize %u KB,
+>>506 leshort 0xFFFF Normal VGA
+>>506 leshort 0xFFFE Extended VGA
+>>506 leshort 0xFFFD Prompt for Videomode
+>>506 leshort >0 Video mode %d
+# This also matches new kernels, which were caught above by "HdrS".
+0 belong 0xb8c0078e Linux kernel
+>0x1e3 string Loading version 1.3.79 or older
+>0x1e9 string Loading from prehistoric times
+
+# System.map files - Nicolas Lichtmaier <nick@debian.org>
+8 search/1 \ A\ _text Linux kernel symbol map text
+
+# LSM entries - Nicolas Lichtmaier <nick@debian.org>
+0 search/1 Begin3 Linux Software Map entry text
+0 search/1 Begin4 Linux Software Map entry text (new format)
+
+# From Matt Zimmerman, enhanced for v3 by Matthew Palmer
+0 belong 0x4f4f4f4d User-mode Linux COW file
+>4 belong <3 \b, version %d
+>>8 string >\0 \b, backing file %s
+>4 belong >2 \b, version %d
+>>32 string >\0 \b, backing file %s
+
+############################################################################
+# Linux kernel versions
+
+0 string \xb8\xc0\x07\x8e\xd8\xb8\x00\x90 Linux
+>497 leshort 0 x86 boot sector
+>>514 belong 0x8e of a kernel from the dawn of time!
+>>514 belong 0x908ed8b4 version 0.99-1.1.42
+>>514 belong 0x908ed8b8 for memtest86
+
+>497 leshort !0 x86 kernel
+>>504 leshort >0 RAMdisksize=%u KB
+>>502 leshort >0 swap=0x%X
+>>508 leshort >0 root=0x%X
+>>>498 leshort 1 \b-ro
+>>>498 leshort 0 \b-rw
+>>506 leshort 0xFFFF vga=normal
+>>506 leshort 0xFFFE vga=extended
+>>506 leshort 0xFFFD vga=ask
+>>506 leshort >0 vga=%d
+>>514 belong 0x908ed881 version 1.1.43-1.1.45
+>>514 belong 0x15b281cd
+>>>0xa8e belong 0x55AA5a5a version 1.1.46-1.2.13,1.3.0
+>>>0xa99 belong 0x55AA5a5a version 1.3.1,2
+>>>0xaa3 belong 0x55AA5a5a version 1.3.3-1.3.30
+>>>0xaa6 belong 0x55AA5a5a version 1.3.31-1.3.41
+>>>0xb2b belong 0x55AA5a5a version 1.3.42-1.3.45
+>>>0xaf7 belong 0x55AA5a5a version 1.3.46-1.3.72
+>>514 string HdrS
+>>>518 leshort >0x1FF
+>>>>529 byte 0 \b, zImage
+>>>>529 byte 1 \b, bzImage
+>>>>(526.s+0x200) string >\0 \b, version %s
+
+# Linux boot sector thefts.
+0 belong 0xb8c0078e Linux
+>0x1e6 belong 0x454c4b53 ELKS Kernel
+>0x1e6 belong !0x454c4b53 style boot sector
+
+############################################################################
+# Linux S390 kernel image
+# Created by: Jan Kaluza <jkaluza@redhat.com>
+8 string \x02\x00\x00\x18\x60\x00\x00\x50\x02\x00\x00\x68\x60\x00\x00\x50\x40\x40\x40\x40\x40\x40\x40\x40 Linux S390
+>0x00010000 search/b/4096 \x00\x0a\x00\x00\x8b\xad\xcc\xcc
+# 64bit
+>>&0 string \xc1\x00\xef\xe3\xf0\x68\x00\x00 Z10 64bit kernel
+>>&0 string \xc1\x00\xef\xc3\x00\x00\x00\x00 Z9-109 64bit kernel
+>>&0 string \xc0\x00\x20\x00\x00\x00\x00\x00 Z990 64bit kernel
+>>&0 string \x00\x00\x00\x00\x00\x00\x00\x00 Z900 64bit kernel
+# 32bit
+>>&0 string \x81\x00\xc8\x80\x00\x00\x00\x00 Z10 32bit kernel
+>>&0 string \x81\x00\xc8\x80\x00\x00\x00\x00 Z9-109 32bit kernel
+>>&0 string \x80\x00\x20\x00\x00\x00\x00\x00 Z990 32bit kernel
+>>&0 string \x80\x00\x00\x00\x00\x00\x00\x00 Z900 32bit kernel
+
+# Linux ARM compressed kernel image
+# From: Kevin Cernekee <cernekee@gmail.com>
+36 lelong 0x016f2818 Linux kernel ARM boot executable zImage (little-endian)
+36 belong 0x016f2818 Linux kernel ARM boot executable zImage (big-endian)
+
+############################################################################
+# Linux 8086 executable
+0 lelong&0xFF0000FF 0xC30000E9 Linux-Dev86 executable, headerless
+>5 string .
+>>4 string >\0 \b, libc version %s
+
+0 lelong&0xFF00FFFF 0x4000301 Linux-8086 executable
+>2 byte&0x01 !0 \b, unmapped zero page
+>2 byte&0x20 0 \b, impure
+>2 byte&0x20 !0
+>>2 byte&0x10 !0 \b, A_EXEC
+>2 byte&0x02 !0 \b, A_PAL
+>2 byte&0x04 !0 \b, A_NSYM
+>2 byte&0x08 !0 \b, A_STAND
+>2 byte&0x40 !0 \b, A_PURE
+>2 byte&0x80 !0 \b, A_TOVLY
+>28 long !0 \b, not stripped
+>37 string .
+>>36 string >\0 \b, libc version %s
+
+# 0 lelong&0xFF00FFFF 0x10000301 ld86 I80386 executable
+# 0 lelong&0xFF00FFFF 0xB000301 ld86 M68K executable
+# 0 lelong&0xFF00FFFF 0xC000301 ld86 NS16K executable
+# 0 lelong&0xFF00FFFF 0x17000301 ld86 SPARC executable
+
+# SYSLINUX boot logo files (from 'ppmtolss16' sources)
+# http://www.syslinux.org/wiki/index.php/SYSLINUX#Display_graphic_from_filename:
+# file extension .lss .16
+0 lelong =0x1413f33d SYSLINUX' LSS16 image data
+# syslinux-4.05/mime/image/x-lss16.xml
+!:mime image/x-lss16
+>4 leshort x \b, width %d
+>6 leshort x \b, height %d
+
+0 string OOOM User-Mode-Linux's Copy-On-Write disk image
+>4 belong x version %d
+
+# SE Linux policy database
+# From: Mike Frysinger <vapier@gentoo.org>
+0 lelong 0xf97cff8c SE Linux policy
+>16 lelong x v%d
+>20 lelong 1 MLS
+>24 lelong x %d symbols
+>28 lelong x %d ocons
+
+# Linux Logical Volume Manager (LVM)
+# Emmanuel VARAGNAT <emmanuel.varagnat@guzu.net>
+#
+# System ID, UUID and volume group name are 128 bytes long
+# but they should never be full and initialized with zeros...
+#
+# LVM1
+#
+0x0 string HM\001 LVM1 (Linux Logical Volume Manager), version 1
+>0x12c string >\0 , System ID: %s
+
+0x0 string HM\002 LVM1 (Linux Logical Volume Manager), version 2
+>0x12c string >\0 , System ID: %s
+
+# LVM2
+#
+# It seems that the label header can be in one the four first sector
+# of the disk... (from _find_labeller in lib/label/label.c of LVM2)
+#
+# 0x200 seems to be the common case
+
+0x218 string LVM2\ 001 LVM2 PV (Linux Logical Volume Manager)
+# read the offset to add to the start of the header, and the header
+# start in 0x200
+>&(&-12.l-0x21) byte x
+# display UUID in LVM format + display all 32 bytes (instead of max string length: 31)
+>>&0x0 string >\x2f \b, UUID: %.6s
+>>&0x6 string >\x2f \b-%.4s
+>>&0xa string >\x2f \b-%.4s
+>>&0xe string >\x2f \b-%.4s
+>>&0x12 string >\x2f \b-%.4s
+>>&0x16 string >\x2f \b-%.4s
+>>&0x1a string >\x2f \b-%.6s
+>>&0x20 lequad x \b, size: %lld
+
+0x018 string LVM2\ 001 LVM2 PV (Linux Logical Volume Manager)
+>&(&-12.l-0x21) byte x
+# display UUID in LVM format + display all 32 bytes (instead of max string length: 31)
+>>&0x0 string >\x2f \b, UUID: %.6s
+>>&0x6 string >\x2f \b-%.4s
+>>&0xa string >\x2f \b-%.4s
+>>&0xe string >\x2f \b-%.4s
+>>&0x12 string >\x2f \b-%.4s
+>>&0x16 string >\x2f \b-%.4s
+>>&0x1a string >\x2f \b-%.6s
+>>&0x20 lequad x \b, size: %lld
+
+0x418 string LVM2\ 001 LVM2 PV (Linux Logical Volume Manager)
+>&(&-12.l-0x21) byte x
+# display UUID in LVM format + display all 32 bytes (instead of max string length: 31)
+>>&0x0 string >\x2f \b, UUID: %.6s
+>>&0x6 string >\x2f \b-%.4s
+>>&0xa string >\x2f \b-%.4s
+>>&0xe string >\x2f \b-%.4s
+>>&0x12 string >\x2f \b-%.4s
+>>&0x16 string >\x2f \b-%.4s
+>>&0x1a string >\x2f \b-%.6s
+>>&0x20 lequad x \b, size: %lld
+
+0x618 string LVM2\ 001 LVM2 PV (Linux Logical Volume Manager)
+>&(&-12.l-0x21) byte x
+# display UUID in LVM format + display all 32 bytes (instead of max string length: 31)
+>>&0x0 string >\x2f \b, UUID: %.6s
+>>&0x6 string >\x2f \b-%.4s
+>>&0xa string >\x2f \b-%.4s
+>>&0xe string >\x2f \b-%.4s
+>>&0x12 string >\x2f \b-%.4s
+>>&0x16 string >\x2f \b-%.4s
+>>&0x1a string >\x2f \b-%.6s
+>>&0x20 lequad x \b, size: %lld
+
+# LVM snapshot
+# from Jason Farrel
+0 string SnAp LVM Snapshot (CopyOnWrite store)
+>4 lelong !0 - valid,
+>4 lelong 0 - invalid,
+>8 lelong x version %d,
+>12 lelong x chunk_size %d
+
+# SE Linux policy database
+0 lelong 0xf97cff8c SE Linux policy
+>16 lelong x v%d
+>20 lelong 1 MLS
+>24 lelong x %d symbols
+>28 lelong x %d ocons
+
+# LUKS: Linux Unified Key Setup, On-Disk Format, http://luks.endorphin.org/spec
+# Anthon van der Neut (anthon@mnt.org)
+0 string LUKS\xba\xbe LUKS encrypted file,
+>6 beshort x ver %d
+>8 string x [%s,
+>40 string x %s,
+>72 string x %s]
+>168 string x UUID: %s
+
+
+# Summary: Xen saved domain file
+# Created by: Radek Vokal <rvokal@redhat.com>
+0 string LinuxGuestRecord Xen saved domain
+>20 search/256 (name
+>>&1 string x (name %s)
+
+# Type: Xen, the virtual machine monitor
+# From: Radek Vokal <rvokal@redhat.com>
+0 string LinuxGuestRecord Xen saved domain
+#>2 regex \(name\ [^)]*\) %s
+>20 search/256 (name (name
+>>&1 string x %s...)
+
+# Systemd journald files
+# See http://www.freedesktop.org/wiki/Software/systemd/journal-files/.
+# From: Zbigniew Jedrzejewski-Szmek <zbyszek@in.waw.pl>
+
+# check magic
+0 string LPKSHHRH
+# check that state is one of known values
+>16 ubyte&252 0
+# check that each half of three unique id128s is non-zero
+>>24 ubequad >0
+>>>32 ubequad >0
+>>>>40 ubequad >0
+>>>>>48 ubequad >0
+>>>>>>56 ubequad >0
+>>>>>>>64 ubequad >0 Journal file
+!:mime application/octet-stream
+# provide more info
+>>>>>>>>184 leqdate 0 empty
+>>>>>>>>16 ubyte 0 \b, offline
+>>>>>>>>16 ubyte 1 \b, online
+>>>>>>>>16 ubyte 2 \b, archived
+>>>>>>>>8 ulelong&1 1 \b, sealed
+>>>>>>>>12 ulelong&1 1 \b, compressed
+
+# BCache backing and cache devices
+# From: Gabriel de Perthuis <g2p.code@gmail.com>
+0x1008 lequad 8
+>0x1018 string \xc6\x85\x73\xf6\x4e\x1a\x45\xca\x82\x65\xf5\x7f\x48\xba\x6d\x81 BCache
+>>0x1010 ulequad 0 cache device
+>>0x1010 ulequad 1 backing device
+>>0x1010 ulequad 3 cache device
+>>0x1010 ulequad 4 backing device
+>>0x1048 string >0 \b, label "%.32s"
+>>0x1028 ubelong x \b, uuid %08x
+>>0x102c ubeshort x \b-%04x
+>>0x102e ubeshort x \b-%04x
+>>0x1030 ubeshort x \b-%04x
+>>0x1032 ubelong x \b-%08x
+>>0x1036 ubeshort x \b%04x
+>>0x1038 ubelong x \b, set uuid %08x
+>>0x103c ubeshort x \b-%04x
+>>0x103e ubeshort x \b-%04x
+>>0x1040 ubeshort x \b-%04x
+>>0x1042 ubelong x \b-%08x
+>>0x1046 ubeshort x \b%04x
+
+# Linux device tree:
+# File format description can be found in the Linux kernel sources at
+# Documentation/devicetree/booting-without-of.txt
+# From Christoph Biedl
+0 belong 0xd00dfeed
+# structure and strings must be within blob
+>&(8.L) byte x
+>>&(12.L) byte x
+>>>20 belong >1 Device Tree Blob version %d
+>>>>4 belong x \b, size=%d
+>>>>20 belong >1
+>>>>>28 belong x \b, boot CPU=%d
+>>>>20 belong >2
+>>>>>32 belong x \b, string block size=%d
+>>>>20 belong >16
+>>>>>36 belong x \b, DT structure block size=%d
+
+# glibc locale archive as defined in glibc locale/locarchive.h
+0 lelong 0xde020109 locale archive
+>24 lelong x %d strings
+
+# Summary: Database file for mlocate
+# Description: A database file as used by mlocate, a fast implementation
+# of locate/updatedb. It uses merging to reuse the existing
+# database and avoid rereading most of the filesystem. It's
+# the default version of locate on Arch Linux (and others).
+# File path: /var/lib/mlocate/mlocate.db by default (but configurable)
+# Site: https://fedorahosted.org/mlocate/
+# Format docs: http://linux.die.net/man/5/mlocate.db
+# Type: mlocate database file
+# URL: https://fedorahosted.org/mlocate/
+# From: Wander Nauta <info@wandernauta.nl>
+0 string \0mlocate mlocate database
+>12 byte x \b, version %d
+>13 byte 1 \b, require visibility
+>16 string x \b, root %s
+
+#------------------------------------------------------------------------------
+# $File$
+# lisp: file(1) magic for lisp programs
+#
+# various lisp types, from Daniel Quinlan (quinlan@yggdrasil.com)
+
+# updated by Joerg Jenderek
+# GRR: This lot is too weak
+#0 string ;;
+# windows INF files often begin with semicolon and use CRLF as line end
+# lisp files are mainly created on unix system with LF as line end
+#>2 search/4096 !\r Lisp/Scheme program text
+#>2 search/4096 \r Windows INF file
+
+0 search/4096 (setq\ Lisp/Scheme program text
+!:mime text/x-lisp
+0 search/4096 (defvar\ Lisp/Scheme program text
+!:mime text/x-lisp
+0 search/4096 (defparam\ Lisp/Scheme program text
+!:mime text/x-lisp
+0 search/4096 (defun\ Lisp/Scheme program text
+!:mime text/x-lisp
+0 search/4096 (autoload\ Lisp/Scheme program text
+!:mime text/x-lisp
+0 search/4096 (custom-set-variables\ Lisp/Scheme program text
+!:mime text/x-lisp
+
+# Emacs 18 - this is always correct, but not very magical.
+0 string \012( Emacs v18 byte-compiled Lisp data
+!:mime application/x-elc
+# Emacs 19+ - ver. recognition added by Ian Springer
+# Also applies to XEmacs 19+ .elc files; could tell them apart with regexs
+# - Chris Chittleborough <cchittleborough@yahoo.com.au>
+0 string ;ELC
+>4 byte >18
+>4 byte <32 Emacs/XEmacs v%d byte-compiled Lisp data
+!:mime application/x-elc
+
+# Files produced by CLISP Common Lisp From: Bruno Haible <haible@ilog.fr>
+0 string (SYSTEM::VERSION\040' CLISP byte-compiled Lisp program (pre 2004-03-27)
+0 string (|SYSTEM|::|VERSION|\040' CLISP byte-compiled Lisp program text
+
+0 long 0x70768BD2 CLISP memory image data
+0 long 0xD28B7670 CLISP memory image data, other endian
+
+#.com and .bin for MIT scheme
+0 string \372\372\372\372 MIT scheme (library?)
+
+# From: David Allouche <david@allouche.net>
+0 search/1 \<TeXmacs| TeXmacs document text
+!:mime text/texmacs
+
+#------------------------------------------------------------------------------
+# $File: llvm,v 1.7 2013/01/08 01:34:38 christos Exp $
+# llvm: file(1) magic for LLVM byte-codes
+# URL: http://llvm.org/docs/BitCodeFormat.html
+# From: Al Stone <ahs3@fc.hp.com>
+
+0 string llvm LLVM byte-codes, uncompressed
+0 string llvc0 LLVM byte-codes, null compression
+0 string llvc1 LLVM byte-codes, gzip compression
+0 string llvc2 LLVM byte-codes, bzip2 compression
+
+0 lelong 0x0b17c0de LLVM bitcode, wrapper
+# Are these Mach-O ABI values? They appear to be.
+>16 lelong 0x01000007 x86_64
+>16 lelong 0x00000007 i386
+>16 lelong 0x00000012 ppc
+>16 lelong 0x01000012 ppc64
+>16 lelong 0x0000000c arm
+
+0 string BC\xc0\xde LLVM IR bitcode
+
+#------------------------------------------------------------------------------
+# $File: lua,v 1.5 2009/09/19 16:28:10 christos Exp $
+# lua: file(1) magic for Lua scripting language
+# URL: http://www.lua.org/
+# From: Reuben Thomas <rrt@sc3d.org>, Seo Sanghyeon <tinuviel@sparcs.kaist.ac.kr>
+
+# Lua scripts
+0 search/1/w #!\ /usr/bin/lua Lua script text executable
+!:mime text/x-lua
+0 search/1/w #!\ /usr/local/bin/lua Lua script text executable
+!:mime text/x-lua
+0 search/1 #!/usr/bin/env\ lua Lua script text executable
+!:mime text/x-lua
+0 search/1 #!\ /usr/bin/env\ lua Lua script text executable
+!:mime text/x-lua
+
+# Lua bytecode
+0 string \033Lua Lua bytecode,
+>4 byte 0x50 version 5.0
+>4 byte 0x51 version 5.1
+>4 byte 0x52 version 5.2
+
+#------------------------------------------------------------------------------
+# $File$
+# luks: file(1) magic for Linux Unified Key Setup
+# URL: http://luks.endorphin.org/spec
+# From: Anthon van der Neut <anthon@mnt.org>
+
+0 string LUKS\xba\xbe LUKS encrypted file,
+>6 beshort x ver %d
+>8 string x [%s,
+>40 string x %s,
+>72 string x %s]
+>168 string x UUID: %s
+#------------------------------------------------------------------------------
+# $File$
+# make: file(1) magic for M4 scripts
+#
+0 regex \^dnl\ M4 macro processor script text
+!:mime text/x-m4
+
+#------------------------------------------------------------
+# $File: mach,v 1.18 2014/03/29 15:40:34 christos Exp $
+# Mach has two magic numbers, 0xcafebabe and 0xfeedface.
+# Unfortunately the first, cafebabe, is shared with
+# Java ByteCode, so they are both handled in the file "cafebabe".
+# The "feedface" ones are handled herein.
+#------------------------------------------------------------
+# if set, it's for the 64-bit version of the architecture
+# yes, this is separate from the low-order magic number bit
+# it's also separate from the "64-bit libraries" bit in the
+# upper 8 bits of the CPU subtype
+
+0 name mach-o-cpu
+>0 belong&0x01000000 0
+#
+# 32-bit ABIs.
+#
+# 1 vax
+>>0 belong&0x00ffffff 1
+>>>4 belong&0x00ffffff 0 vax
+>>>4 belong&0x00ffffff 1 vax11/780
+>>>4 belong&0x00ffffff 2 vax11/785
+>>>4 belong&0x00ffffff 3 vax11/750
+>>>4 belong&0x00ffffff 4 vax11/730
+>>>4 belong&0x00ffffff 5 uvaxI
+>>>4 belong&0x00ffffff 6 uvaxII
+>>>4 belong&0x00ffffff 7 vax8200
+>>>4 belong&0x00ffffff 8 vax8500
+>>>4 belong&0x00ffffff 9 vax8600
+>>>4 belong&0x00ffffff 10 vax8650
+>>>4 belong&0x00ffffff 11 vax8800
+>>>4 belong&0x00ffffff 12 uvaxIII
+>>>4 belong&0x00ffffff >12 vax subarchitecture=%d
+>>0 belong&0x00ffffff 2 romp
+>>0 belong&0x00ffffff 3 architecture=3
+>>0 belong&0x00ffffff 4 ns32032
+>>0 belong&0x00ffffff 5 ns32332
+>>0 belong&0x00ffffff 6 m68k
+# 7 x86
+>>0 belong&0x00ffffff 7
+>>>4 belong&0x0000000f 3 i386
+>>>4 belong&0x0000000f 4 i486
+>>>>4 belong&0x00fffff0 0
+>>>>4 belong&0x00fffff0 0x80 \bsx
+>>>4 belong&0x0000000f 5 i586
+>>>4 belong&0x0000000f 6
+>>>>4 belong&0x00fffff0 0 p6
+>>>>4 belong&0x00fffff0 0x10 pentium_pro
+>>>>4 belong&0x00fffff0 0x20 pentium_2_m0x20
+>>>>4 belong&0x00fffff0 0x30 pentium_2_m3
+>>>>4 belong&0x00fffff0 0x40 pentium_2_m0x40
+>>>>4 belong&0x00fffff0 0x50 pentium_2_m5
+>>>>4 belong&0x00fffff0 >0x50 pentium_2_m0x%x
+>>>4 belong&0x0000000f 7 celeron
+>>>>4 belong&0x00fffff0 0x00 \b_m0x%x
+>>>>4 belong&0x00fffff0 0x10 \b_m0x%x
+>>>>4 belong&0x00fffff0 0x20 \b_m0x%x
+>>>>4 belong&0x00fffff0 0x30 \b_m0x%x
+>>>>4 belong&0x00fffff0 0x40 \b_m0x%x
+>>>>4 belong&0x00fffff0 0x50 \b_m0x%x
+>>>>4 belong&0x00fffff0 0x60
+>>>>4 belong&0x00fffff0 0x70 \b_mobile
+>>>>4 belong&0x00fffff0 >0x70 \b_m0x%x
+>>>4 belong&0x0000000f 8 pentium_3
+>>>>4 belong&0x00fffff0 0x00
+>>>>4 belong&0x00fffff0 0x10 \b_m
+>>>>4 belong&0x00fffff0 0x20 \b_xeon
+>>>>4 belong&0x00fffff0 >0x20 \b_m0x%x
+>>>4 belong&0x0000000f 9 pentiumM
+>>>>4 belong&0x00fffff0 0x00
+>>>>4 belong&0x00fffff0 >0x00 \b_m0x%x
+>>>4 belong&0x0000000f 10 pentium_4
+>>>>4 belong&0x00fffff0 0x00
+>>>>4 belong&0x00fffff0 0x10 \b_m
+>>>>4 belong&0x00fffff0 >0x10 \b_m0x%x
+>>>4 belong&0x0000000f 11 itanium
+>>>>4 belong&0x00fffff0 0x00
+>>>>4 belong&0x00fffff0 0x10 \b_2
+>>>>4 belong&0x00fffff0 >0x10 \b_m0x%x
+>>>4 belong&0x0000000f 12 xeon
+>>>>4 belong&0x00fffff0 0x00
+>>>>4 belong&0x00fffff0 0x10 \b_mp
+>>>>4 belong&0x00fffff0 >0x10 \b_m0x%x
+>>>4 belong&0x0000000f >12 ia32 family=%d
+>>>>4 belong&0x00fffff0 0x00
+>>>>4 belong&0x00fffff0 >0x00 model=%x
+>>0 belong&0x00ffffff 8 mips
+>>>4 belong&0x00ffffff 1 R2300
+>>>4 belong&0x00ffffff 2 R2600
+>>>4 belong&0x00ffffff 3 R2800
+>>>4 belong&0x00ffffff 4 R2000a
+>>>4 belong&0x00ffffff 5 R2000
+>>>4 belong&0x00ffffff 6 R3000a
+>>>4 belong&0x00ffffff 7 R3000
+>>>4 belong&0x00ffffff >7 subarchitecture=%d
+>>0 belong&0x00ffffff 9 ns32532
+>>0 belong&0x00ffffff 10 mc98000
+>>0 belong&0x00ffffff 11 hppa
+>>>4 belong&0x00ffffff 0 7100
+>>>4 belong&0x00ffffff 1 7100LC
+>>>4 belong&0x00ffffff >1 subarchitecture=%d
+>>0 belong&0x00ffffff 12 arm
+>>>4 belong&0x00ffffff 0
+>>>4 belong&0x00ffffff 1 subarchitecture=%d
+>>>4 belong&0x00ffffff 2 subarchitecture=%d
+>>>4 belong&0x00ffffff 3 subarchitecture=%d
+>>>4 belong&0x00ffffff 4 subarchitecture=%d
+>>>4 belong&0x00ffffff 5 \b_v4t
+>>>4 belong&0x00ffffff 6 \b_v6
+>>>4 belong&0x00ffffff 7 \b_v5tej
+>>>4 belong&0x00ffffff 8 \b_xscale
+>>>4 belong&0x00ffffff 9 \b_v7
+>>>4 belong&0x00ffffff 10 \b_v7f
+>>>4 belong&0x00ffffff 11 subarchitecture=%d
+>>>4 belong&0x00ffffff 12 \b_v7k
+>>>4 belong&0x00ffffff >12 subarchitecture=%d
+# 13 m88k
+>>0 belong&0x00ffffff 13
+>>>4 belong&0x00ffffff 0 mc88000
+>>>4 belong&0x00ffffff 1 mc88100
+>>>4 belong&0x00ffffff 2 mc88110
+>>>4 belong&0x00ffffff >2 mc88000 subarchitecture=%d
+>>0 belong&0x00ffffff 14 SPARC
+>>0 belong&0x00ffffff 15 i860g
+>>0 belong&0x00ffffff 16 alpha
+>>0 belong&0x00ffffff 17 rs6000
+>>0 belong&0x00ffffff 18 ppc
+>>>4 belong&0x00ffffff 0
+>>>4 belong&0x00ffffff 1 \b_601
+>>>4 belong&0x00ffffff 2 \b_602
+>>>4 belong&0x00ffffff 3 \b_603
+>>>4 belong&0x00ffffff 4 \b_603e
+>>>4 belong&0x00ffffff 5 \b_603ev
+>>>4 belong&0x00ffffff 6 \b_604
+>>>4 belong&0x00ffffff 7 \b_604e
+>>>4 belong&0x00ffffff 8 \b_620
+>>>4 belong&0x00ffffff 9 \b_650
+>>>4 belong&0x00ffffff 10 \b_7400
+>>>4 belong&0x00ffffff 11 \b_7450
+>>>4 belong&0x00ffffff 100 \b_970
+>>>4 belong&0x00ffffff >100 subarchitecture=%d
+>>0 belong&0x00ffffff >18 architecture=%d
+>0 belong&0x01000000 0x01000000
+#
+# 64-bit ABIs.
+#
+>>0 belong&0x00ffffff 0 64-bit architecture=%d
+>>0 belong&0x00ffffff 1 64-bit architecture=%d
+>>0 belong&0x00ffffff 2 64-bit architecture=%d
+>>0 belong&0x00ffffff 3 64-bit architecture=%d
+>>0 belong&0x00ffffff 4 64-bit architecture=%d
+>>0 belong&0x00ffffff 5 64-bit architecture=%d
+>>0 belong&0x00ffffff 6 64-bit architecture=%d
+>>0 belong&0x00ffffff 7 x86_64
+>>>4 belong&0x00ffffff 0 subarchitecture=%d
+>>>4 belong&0x00ffffff 1 subarchitecture=%d
+>>>4 belong&0x00ffffff 2 subarchitecture=%d
+>>>4 belong&0x00ffffff 3
+>>>4 belong&0x00ffffff 4 \b_arch1
+>>>4 belong&0x00ffffff >4 subarchitecture=%d
+>>0 belong&0x00ffffff 8 64-bit architecture=%d
+>>0 belong&0x00ffffff 9 64-bit architecture=%d
+>>0 belong&0x00ffffff 10 64-bit architecture=%d
+>>0 belong&0x00ffffff 11 64-bit architecture=%d
+>>0 belong&0x00ffffff 12 64-bit architecture=%d
+>>0 belong&0x00ffffff 13 64-bit architecture=%d
+>>0 belong&0x00ffffff 14 64-bit architecture=%d
+>>0 belong&0x00ffffff 15 64-bit architecture=%d
+>>0 belong&0x00ffffff 16 64-bit architecture=%d
+>>0 belong&0x00ffffff 17 64-bit architecture=%d
+>>0 belong&0x00ffffff 18 ppc64
+>>>4 belong&0x00ffffff 0
+>>>4 belong&0x00ffffff 1 \b_601
+>>>4 belong&0x00ffffff 2 \b_602
+>>>4 belong&0x00ffffff 3 \b_603
+>>>4 belong&0x00ffffff 4 \b_603e
+>>>4 belong&0x00ffffff 5 \b_603ev
+>>>4 belong&0x00ffffff 6 \b_604
+>>>4 belong&0x00ffffff 7 \b_604e
+>>>4 belong&0x00ffffff 8 \b_620
+>>>4 belong&0x00ffffff 9 \b_650
+>>>4 belong&0x00ffffff 10 \b_7400
+>>>4 belong&0x00ffffff 11 \b_7450
+>>>4 belong&0x00ffffff 100 \b_970
+>>>4 belong&0x00ffffff >100 subarchitecture=%d
+>>0 belong&0x00ffffff >18 64-bit architecture=%d
+
+
+0 name mach-o-be
+>0 byte 0xcf 64-bit
+>4 use mach-o-cpu
+>12 belong 1 object
+>12 belong 2 executable
+>12 belong 3 fixed virtual memory shared library
+>12 belong 4 core
+>12 belong 5 preload executable
+>12 belong 6 dynamically linked shared library
+>12 belong 7 dynamic linker
+>12 belong 8 bundle
+>12 belong 9 dynamically linked shared library stub
+>12 belong 10 dSYM companion file
+>12 belong 11 kext bundle
+>12 belong >11
+>>12 belong x filetype=%d
+
+#
+0 lelong&0xfffffffe 0xfeedface Mach-O
+!:strength +1
+>0 use \^mach-o-be
+
+0 belong&0xfffffffe 0xfeedface Mach-O
+!:strength +1
+>0 use mach-o-be
+
+#------------------------------------------------------------------------------
+# $File: macintosh,v 1.24 2014/08/30 08:34:17 christos Exp $
+# macintosh description
+#
+# BinHex is the Macintosh ASCII-encoded file format (see also "apple")
+# Daniel Quinlan, quinlan@yggdrasil.com
+11 string must\ be\ converted\ with\ BinHex BinHex binary text
+!:mime application/mac-binhex40
+>41 string x \b, version %.3s
+
+# Stuffit archives are the de facto standard of compression for Macintosh
+# files obtained from most archives. (franklsm@tuns.ca)
+0 string SIT! StuffIt Archive (data)
+!:mime application/x-stuffit
+!:apple SIT!SIT!
+>2 string x : %s
+0 string SITD StuffIt Deluxe (data)
+>2 string x : %s
+0 string Seg StuffIt Deluxe Segment (data)
+>2 string x : %s
+
+# Newer StuffIt archives (grant@netbsd.org)
+0 string StuffIt StuffIt Archive
+!:mime application/x-stuffit
+!:apple SIT!SIT!
+#>162 string >0 : %s
+
+# Macintosh Applications and Installation binaries (franklsm@tuns.ca)
+# GRR: Too weak
+#0 string APPL Macintosh Application (data)
+#>2 string x \b: %s
+
+# Macintosh System files (franklsm@tuns.ca)
+# GRR: Too weak
+#0 string zsys Macintosh System File (data)
+#0 string FNDR Macintosh Finder (data)
+#0 string libr Macintosh Library (data)
+#>2 string x : %s
+#0 string shlb Macintosh Shared Library (data)
+#>2 string x : %s
+#0 string cdev Macintosh Control Panel (data)
+#>2 string x : %s
+#0 string INIT Macintosh Extension (data)
+#>2 string x : %s
+#0 string FFIL Macintosh Truetype Font (data)
+#>2 string x : %s
+#0 string LWFN Macintosh Postscript Font (data)
+#>2 string x : %s
+
+# Additional Macintosh Files (franklsm@tuns.ca)
+# GRR: Too weak
+#0 string PACT Macintosh Compact Pro Archive (data)
+#>2 string x : %s
+#0 string ttro Macintosh TeachText File (data)
+#>2 string x : %s
+#0 string TEXT Macintosh TeachText File (data)
+#>2 string x : %s
+#0 string PDF Macintosh PDF File (data)
+#>2 string x : %s
+
+# MacBinary format (Eric Fischer, enf@pobox.com)
+#
+# Unfortunately MacBinary doesn't really have a magic number prior
+# to the MacBinary III format. The checksum is really the way to
+# do it, but the magic file format isn't up to the challenge.
+#
+# 0 byte 0
+# 1 byte # filename length
+# 2 string # filename
+# 65 string # file type
+# 69 string # file creator
+# 73 byte # Finder flags
+# 74 byte 0
+# 75 beshort # vertical posn in window
+# 77 beshort # horiz posn in window
+# 79 beshort # window or folder ID
+# 81 byte # protected?
+# 82 byte 0
+# 83 belong # length of data segment
+# 87 belong # length of resource segment
+# 91 belong # file creation date
+# 95 belong # file modification date
+# 99 beshort # length of comment after resource
+# 101 byte # new Finder flags
+# 102 string mBIN # (only in MacBinary III)
+# 106 byte # char. code of file name
+# 107 byte # still more Finder flags
+# 116 belong # total file length
+# 120 beshort # length of add'l header
+# 122 byte 129 # for MacBinary II
+# 122 byte 130 # for MacBinary III
+# 123 byte 129 # minimum version that can read fmt
+# 124 beshort # checksum
+#
+# This attempts to use the version numbers as a magic number, requiring
+# that the first one be 0x80, 0x81, 0x82, or 0x83, and that the second
+# be 0x81. This works for the files I have, but maybe not for everyone's.
+
+# Unfortunately, this magic is quite weak - MPi
+#122 beshort&0xFCFF 0x8081 Macintosh MacBinary data
+
+# MacBinary I doesn't have the version number field at all, but MacBinary II
+# has been in use since 1987 so I hope there aren't many really old files
+# floating around that this will miss. The original spec calls for using
+# the nulls in 0, 74, and 82 as the magic number.
+#
+# Another possibility, that would also work for MacBinary I, is to use
+# the assumption that 65-72 will all be ASCII (0x20-0x7F), that 73 will
+# have bits 1 (changed), 2 (busy), 3 (bozo), and 6 (invisible) unset,
+# and that 74 will be 0. So something like
+#
+# 71 belong&0x80804EFF 0x00000000 Macintosh MacBinary data
+#
+# >73 byte&0x01 0x01 \b, inited
+# >73 byte&0x02 0x02 \b, changed
+# >73 byte&0x04 0x04 \b, busy
+# >73 byte&0x08 0x08 \b, bozo
+# >73 byte&0x10 0x10 \b, system
+# >73 byte&0x10 0x20 \b, bundle
+# >73 byte&0x10 0x40 \b, invisible
+# >73 byte&0x10 0x80 \b, locked
+
+#>65 string x \b, type "%4.4s"
+
+#>65 string 8BIM (PhotoShop)
+#>65 string ALB3 (PageMaker 3)
+#>65 string ALB4 (PageMaker 4)
+#>65 string ALT3 (PageMaker 3)
+#>65 string APPL (application)
+#>65 string AWWP (AppleWorks word processor)
+#>65 string CIRC (simulated circuit)
+#>65 string DRWG (MacDraw)
+#>65 string EPSF (Encapsulated PostScript)
+#>65 string FFIL (font suitcase)
+#>65 string FKEY (function key)
+#>65 string FNDR (Macintosh Finder)
+#>65 string GIFf (GIF image)
+#>65 string Gzip (GNU gzip)
+#>65 string INIT (system extension)
+#>65 string LIB\ (library)
+#>65 string LWFN (PostScript font)
+#>65 string MSBC (Microsoft BASIC)
+#>65 string PACT (Compact Pro archive)
+#>65 string PDF\ (Portable Document Format)
+#>65 string PICT (picture)
+#>65 string PNTG (MacPaint picture)
+#>65 string PREF (preferences)
+#>65 string PROJ (Think C project)
+#>65 string QPRJ (Think Pascal project)
+#>65 string SCFL (Defender scores)
+#>65 string SCRN (startup screen)
+#>65 string SITD (StuffIt Deluxe)
+#>65 string SPn3 (SuperPaint)
+#>65 string STAK (HyperCard stack)
+#>65 string Seg\ (StuffIt segment)
+#>65 string TARF (Unix tar archive)
+#>65 string TEXT (ASCII)
+#>65 string TIFF (TIFF image)
+#>65 string TOVF (Eudora table of contents)
+#>65 string WDBN (Microsoft Word word processor)
+#>65 string WORD (MacWrite word processor)
+#>65 string XLS\ (Microsoft Excel)
+#>65 string ZIVM (compress (.Z))
+#>65 string ZSYS (Pre-System 7 system file)
+#>65 string acf3 (Aldus FreeHand)
+#>65 string cdev (control panel)
+#>65 string dfil (Desk Accessory suitcase)
+#>65 string libr (library)
+#>65 string nX^d (WriteNow word processor)
+#>65 string nX^w (WriteNow dictionary)
+#>65 string rsrc (resource)
+#>65 string scbk (Scrapbook)
+#>65 string shlb (shared library)
+#>65 string ttro (SimpleText read-only)
+#>65 string zsys (system file)
+
+#>69 string x \b, creator "%4.4s"
+
+# Somewhere, Apple has a repository of registered Creator IDs. These are
+# just the ones that I happened to have files from and was able to identify.
+
+#>69 string 8BIM (Adobe Photoshop)
+#>69 string ALD3 (PageMaker 3)
+#>69 string ALD4 (PageMaker 4)
+#>69 string ALFA (Alpha editor)
+#>69 string APLS (Apple Scanner)
+#>69 string APSC (Apple Scanner)
+#>69 string BRKL (Brickles)
+#>69 string BTFT (BitFont)
+#>69 string CCL2 (Common Lisp 2)
+#>69 string CCL\ (Common Lisp)
+#>69 string CDmo (The Talking Moose)
+#>69 string CPCT (Compact Pro)
+#>69 string CSOm (Eudora)
+#>69 string DMOV (Font/DA Mover)
+#>69 string DSIM (DigSim)
+#>69 string EDIT (Macintosh Edit)
+#>69 string ERIK (Macintosh Finder)
+#>69 string EXTR (self-extracting archive)
+#>69 string Gzip (GNU gzip)
+#>69 string KAHL (Think C)
+#>69 string LWFU (LaserWriter Utility)
+#>69 string LZIV (compress)
+#>69 string MACA (MacWrite)
+#>69 string MACS (Macintosh operating system)
+#>69 string MAcK (MacKnowledge terminal emulator)
+#>69 string MLND (Defender)
+#>69 string MPNT (MacPaint)
+#>69 string MSBB (Microsoft BASIC (binary))
+#>69 string MSWD (Microsoft Word)
+#>69 string NCSA (NCSA Telnet)
+#>69 string PJMM (Think Pascal)
+#>69 string PSAL (Hunt the Wumpus)
+#>69 string PSI2 (Apple File Exchange)
+#>69 string R*ch (BBEdit)
+#>69 string RMKR (Resource Maker)
+#>69 string RSED (Resource Editor)
+#>69 string Rich (BBEdit)
+#>69 string SIT! (StuffIt)
+#>69 string SPNT (SuperPaint)
+#>69 string Unix (NeXT Mac filesystem)
+#>69 string VIM! (Vim editor)
+#>69 string WILD (HyperCard)
+#>69 string XCEL (Microsoft Excel)
+#>69 string aCa2 (Fontographer)
+#>69 string aca3 (Aldus FreeHand)
+#>69 string dosa (Macintosh MS-DOS file system)
+#>69 string movr (Font/DA Mover)
+#>69 string nX^n (WriteNow)
+#>69 string pdos (Apple ProDOS file system)
+#>69 string scbk (Scrapbook)
+#>69 string ttxt (SimpleText)
+#>69 string ufox (Foreign File Access)
+
+# Just in case...
+
+102 string mBIN MacBinary III data with surprising version number
+
+# sas magic from Bruce Foster (bef@nwu.edu)
+#
+#0 string SAS SAS
+#>8 string x %s
+0 string SAS SAS
+>24 string DATA data file
+>24 string CATALOG catalog
+>24 string INDEX data file index
+>24 string VIEW data view
+# sas 7+ magic from Reinhold Koch (reinhold.koch@roche.com)
+#
+0x54 string SAS SAS 7+
+>0x9C string DATA data file
+>0x9C string CATALOG catalog
+>0x9C string INDEX data file index
+>0x9C string VIEW data view
+
+# spss magic for SPSS system and portable files,
+# from Bruce Foster (bef@nwu.edu).
+
+0 long 0xc1e2c3c9 SPSS Portable File
+>40 string x %s
+
+0 string $FL2 SPSS System File
+>24 string x %s
+
+0 string $FL3 SPSS System File
+>24 string x %s
+
+# Macintosh filesystem data
+# From "Tom N Harris" <telliamed@mac.com>
+# Fixed HFS+ and Partition map magic: Ethan Benson <erbenson@alaska.net>
+# The MacOS epoch begins on 1 Jan 1904 instead of 1 Jan 1970, so these
+# entries depend on the data arithmetic added after v.35
+# There's also some Pascal strings in here, ditto...
+
+# The boot block signature, according to IM:Files, is
+# "for HFS volumes, this field always contains the value 0x4C4B."
+# But if this is true for MFS or HFS+ volumes, I don't know.
+# Alternatively, the boot block is supposed to be zeroed if it's
+# unused, so a simply >0 should suffice.
+
+0x400 beshort 0xD2D7 Macintosh MFS data
+>0 beshort 0x4C4B (bootable)
+>0x40a beshort &0x8000 (locked)
+>0x402 beldate-0x7C25B080 x created: %s,
+>0x406 beldate-0x7C25B080 >0 last backup: %s,
+>0x414 belong x block size: %d,
+>0x412 beshort x number of blocks: %d,
+>0x424 pstring x volume name: %s
+
+# *.hfs updated by Joerg Jenderek
+# http://en.wikipedia.org/wiki/Hierarchical_File_System
+# "BD" gives many false positives
+0x400 beshort 0x4244
+# ftp://ftp.mars.org/pub/hfs/hfsutils-3.2.6.tar.gz/hfsutils-3.2.6/libhfs/apple.h
+# first block of volume bit map (always 3)
+>0x40e ubeshort 0x0003
+# maximal length of volume name is 27
+>>0x424 ubyte <28 Macintosh HFS data
+#!:mime application/octet-stream
+# these mime and apple types are not sure
+!:mime application/x-apple-diskimage
+#!:apple hfsdINIT
+#!:apple MACSdisk
+>>>0 beshort 0x4C4B (bootable)
+#>>>0 beshort 0x0000 (not bootable)
+>>>0x40a beshort &0x8000 (locked)
+>>>0x40a beshort ^0x0100 (mounted)
+>>>0x40a beshort &0x0200 (spared blocks)
+>>>0x40a beshort &0x0800 (unclean)
+>>>0x47C beshort 0x482B (Embedded HFS+ Volume)
+# http://www.epochconverter.com/
+# 0x7C245F00 seconds ~ 2082758400 ~ 01 Jan 2036 00:00:00 ~ 66 years to 1970
+# 0x7C25B080 seconds ~ 2082844800 ~ 02 Jan 2036 00:00:00
+# construct not working
+#>>>0x402 beldate-0x7C25B080 x created: %s,
+#>>>0x406 beldate-0x7C25B080 x last modified: %s,
+#>>>0x440 beldate-0x7C25B080 >0 last backup: %s,
+# found block sizes 200h,1200h,2800h
+>>>0x414 belong x block size: %d,
+>>>0x412 beshort x number of blocks: %d,
+>>>0x424 pstring x volume name: %s
+
+0x400 beshort 0x482B Macintosh HFS Extended
+>&0 beshort x version %d data
+>0 beshort 0x4C4B (bootable)
+>0x404 belong ^0x00000100 (mounted)
+>&2 belong &0x00000200 (spared blocks)
+>&2 belong &0x00000800 (unclean)
+>&2 belong &0x00008000 (locked)
+>&6 string x last mounted by: '%.4s',
+# really, that should be treated as a belong and we print a string
+# based on the value. TN1150 only mentions '8.10' for "MacOS 8.1"
+>&14 beldate-0x7C25B080 x created: %s,
+# only the creation date is local time, all other timestamps in HFS+ are UTC.
+>&18 bedate-0x7C25B080 x last modified: %s,
+>&22 bedate-0x7C25B080 >0 last backup: %s,
+>&26 bedate-0x7C25B080 >0 last checked: %s,
+>&38 belong x block size: %d,
+>&42 belong x number of blocks: %d,
+>&46 belong x free blocks: %d
+
+## AFAIK, only the signature is different
+# same as Apple Partition Map
+# GRR: This magic is too weak, it is just "TS"
+#0x200 beshort 0x5453 Apple Old Partition data
+#>0x2 beshort x block size: %d,
+#>0x230 string x first type: %s,
+#>0x210 string x name: %s,
+#>0x254 belong x number of blocks: %d,
+#>0x400 beshort 0x504D
+#>>0x430 string x second type: %s,
+#>>0x410 string x name: %s,
+#>>0x454 belong x number of blocks: %d,
+#>>0x800 beshort 0x504D
+#>>>0x830 string x third type: %s,
+#>>>0x810 string x name: %s,
+#>>>0x854 belong x number of blocks: %d,
+#>>>0xa00 beshort 0x504D
+#>>>>0xa30 string x fourth type: %s,
+#>>>>0xa10 string x name: %s,
+#>>>>0xa54 belong x number of blocks: %d
+
+# From: Remi Mommsen <mommsen@slac.stanford.edu>
+0 string BOMStore Mac OS X bill of materials (BOM) file
+
+# From: Adam Buchbinder <adam.buchbinder@gmail.com>
+# URL: http://en.wikipedia.org/wiki/Datafork_TrueType
+# Derived from the 'fondu' and 'ufond' source code (fondu.sf.net). 'sfnt' is
+# TrueType; 'POST' is PostScript. 'FONT' and 'NFNT' sometimes appear, but I
+# don't know what they mean.
+0 belong 0x100
+>(0x4.L+24) beshort x
+>>&4 belong 0x73666e74 Mac OSX datafork font, TrueType
+>>&4 belong 0x464f4e54 Mac OSX datafork font, 'FONT'
+>>&4 belong 0x4e464e54 Mac OSX datafork font, 'NFNT'
+>>&4 belong 0x504f5354 Mac OSX datafork font, PostScript
+
+#------------------------------------------------------------------------------
+# $File: cups,v 1.2 2012/11/02 21:50:29 christos Exp $
+# MacOS files
+#
+
+0 string book\0\0\0\0mark\0\0\0\0 MacOS Alias file
+
+#------------------------------------------------------------------------------
+# $File: magic,v 1.9 2009/09/19 16:28:10 christos Exp $
+# magic: file(1) magic for magic files
+#
+0 string/t #\ Magic magic text file for file(1) cmd
+0 lelong 0xF11E041C magic binary file for file(1) cmd
+>4 lelong x (version %d) (little endian)
+0 belong 0xF11E041C magic binary file for file(1) cmd
+>4 belong x (version %d) (big endian)
+#------------------------------------------------------------------------------
+# $File: mail.news,v 1.21 2012/06/21 01:44:52 christos Exp $
+# mail.news: file(1) magic for mail and news
+#
+# Unfortunately, saved netnews also has From line added in some news software.
+#0 string From mail text
+0 string/t Relay-Version: old news text
+!:mime message/rfc822
+0 string/t #!\ rnews batched news text
+!:mime message/rfc822
+0 string/t N#!\ rnews mailed, batched news text
+!:mime message/rfc822
+0 string/t Forward\ to mail forwarding text
+!:mime message/rfc822
+0 string/t Pipe\ to mail piping text
+!:mime message/rfc822
+0 string/tc delivered-to: SMTP mail text
+!:mime message/rfc822
+0 string/tc return-path: SMTP mail text
+!:mime message/rfc822
+0 string/t Path: news text
+!:mime message/news
+0 string/t Xref: news text
+!:mime message/news
+0 string/t From: news or mail text
+!:mime message/rfc822
+0 string/t Article saved news text
+!:mime message/news
+0 string/t BABYL Emacs RMAIL text
+0 string/t Received: RFC 822 mail text
+!:mime message/rfc822
+0 string/t MIME-Version: MIME entity text
+#0 string/t Content- MIME entity text
+
+# TNEF files...
+0 lelong 0x223E9F78 Transport Neutral Encapsulation Format
+!:mime application/vnd.ms-tnef
+
+# From: Kevin Sullivan <ksulliva@psc.edu>
+0 string *mbx* MBX mail folder
+
+# From: Simon Matter <simon.matter@invoca.ch>
+0 string \241\002\213\015skiplist\ file\0\0\0 Cyrus skiplist DB
+
+# JAM(mbp) Fidonet message area databases
+# JHR file
+0 string JAM\0 JAM message area header file
+>12 leshort >0 (%d messages)
+
+# Squish Fidonet message area databases
+# SQD file (requires at least one message in the area)
+# XXX: Weak magic
+#256 leshort 0xAFAE4453 Squish message area data file
+#>4 leshort >0 (%d messages)
+
+#0 string \<!--\ MHonArc text/html; x-type=mhonarc
+
+# Cyrus: file(1) magic for compiled Cyrus sieve scripts
+# URL: http://www.cyrusimap.org/docs/cyrus-imapd/2.4.6/internal/bytecode.php
+# URL: http://git.cyrusimap.org/cyrus-imapd/tree/sieve/bytecode.h?h=master
+# From: Philipp Hahn <hahn@univention.de>
+
+# Compiled Cyrus sieve script
+0 string CyrSBytecode Cyrus sieve bytecode data,
+>12 belong =1 version 1, big-endian
+>12 lelong =1 version 1, little-endian
+>12 belong x version %d, network-endian
+#------------------------------------------------------------------------------
+# $File$
+# make: file(1) magic for makefiles
+#
+0 regex \^CFLAGS makefile script text
+!:mime text/x-makefile
+0 regex \^LDFLAGS makefile script text
+!:mime text/x-makefile
+0 regex \^all: makefile script text
+!:mime text/x-makefile
+0 regex \^.PRECIOUS makefile script text
+!:mime text/x-makefile
+
+0 regex \^SUBDIRS automake makefile script text
+!:mime text/x-makefile
+
+
+#------------------------------------------------------------------------------
+# $File: msdos,v 1.99 2014/06/03 01:40:24 christos Exp $
+# map: file(1) magic for Map data
+#
+
+# Garmin .FIT files http://pub.ks-and-ks.ne.jp/cycling/edge500_fit.shtml
+8 string .FIT FIT Map data
+>15 byte 0
+>>35 belong x \b, unit id %d
+# 20 years after unix epoch
+>>39 lelong x \b, serial %u
+>>43 ledate/631152000 x \b, %s
+
+>>47 leshort x \b, manufacturer %d
+>>47 leshort 1 \b (garmin)
+>>49 leshort x \b, product %d
+>>53 byte x \b, type %d
+>>53 byte 1 \b (Device)
+>>53 byte 2 \b (Settings)
+>>53 byte 3 \b (Sports/Cycling)
+>>53 byte 4 \b (Activity)
+>>53 byte 8 \b (Elevations)
+>>53 byte 10 \b (Totals)
+
+#------------------------------------------------------------------------------
+# $File: maple,v 1.6 2009/09/19 16:28:10 christos Exp $
+# maple: file(1) magic for maple files
+# "H. Nanosecond" <aldomel@ix.netcom.com>
+# Maple V release 4, a multi-purpose math program
+#
+
+# maple library .lib
+0 string \000MVR4\nI MapleVr4 library
+
+# .ind
+# no magic for these :-(
+# they are compiled indexes for maple files
+
+# .hdb
+0 string \000\004\000\000 Maple help database
+
+# .mhp
+# this has the form <PACKAGE=name>
+0 string \<PACKAGE= Maple help file
+0 string \<HELP\ NAME= Maple help file
+0 string \n\<HELP\ NAME= Maple help file with extra carriage return at start (yuck)
+#0 string #\ Newton Maple help file, old style
+0 string #\ daub Maple help file, old style
+#0 string #=========== Maple help file, old style
+
+# .mws
+0 string \000\000\001\044\000\221 Maple worksheet
+#this is anomalous
+0 string WriteNow\000\002\000\001\000\000\000\000\100\000\000\000\000\000 Maple worksheet, but weird
+# this has the form {VERSION 2 3 "IBM INTEL NT" "2.3" }\n
+# that is {VERSION major_version miunor_version computer_type version_string}
+0 string {VERSION\ Maple worksheet
+>9 string >\0 version %.1s.
+>>11 string >\0 %.1s
+
+# .mps
+0 string \0\0\001$ Maple something
+# from byte 4 it is either 'nul E' or 'soh R'
+# I think 'nul E' means a file that was saved as a different name
+# a sort of revision marking
+# 'soh R' means new
+>4 string \000\105 An old revision
+>4 string \001\122 The latest save
+
+# .mpl
+# some of these are the same as .mps above
+#0000000 000 000 001 044 000 105 same as .mps
+#0000000 000 000 001 044 001 122 same as .mps
+
+0 string #\n##\ <SHAREFILE= Maple something
+0 string \n#\n##\ <SHAREFILE= Maple something
+0 string ##\ <SHAREFILE= Maple something
+0 string #\r##\ <SHAREFILE= Maple something
+0 string \r#\r##\ <SHAREFILE= Maple something
+0 string #\ \r##\ <DESCRIBE> Maple something anomalous.
+#--------------------------------------------
+# marc21: file(1) magic for MARC 21 Format
+#
+# Kevin Ford (kefo@loc.gov)
+#
+# MARC21 formats are for the representation and communication
+# of bibliographic and related information in machine-readable
+# form. For more info, see http://www.loc.gov/marc/
+
+
+# leader position 20-21 must be 45
+20 string 45
+
+# leader starts with 5 digits, followed by codes specific to MARC format
+>0 regex/1l (^[0-9]{5})[acdnp][^bhlnqsu-z] MARC21 Bibliographic
+!:mime application/marc
+>0 regex/1l (^[0-9]{5})[acdnosx][z] MARC21 Authority
+!:mime application/marc
+>0 regex/1l (^[0-9]{5})[cdn][uvxy] MARC21 Holdings
+!:mime application/marc
+0 regex/1l (^[0-9]{5})[acdn][w] MARC21 Classification
+!:mime application/marc
+>0 regex/1l (^[0-9]{5})[cdn][q] MARC21 Community
+!:mime application/marc
+
+# leader position 22-23, should be "00" but is it?
+>0 regex/1l (^.{21})([^0]{2}) (non-conforming)
+!:mime application/marc
+
+#------------------------------------------------------------------------------
+# $File$
+# mathcad: file(1) magic for Mathcad documents
+# URL: http://www.mathsoft.com/
+# From: Josh Triplett <josh@freedesktop.org>
+
+0 string .MCAD\t Mathcad document
+
+#------------------------------------------------------------------------------
+# $File$
+# mathematica: file(1) magic for mathematica files
+# "H. Nanosecond" <aldomel@ix.netcom.com>
+# Mathematica a multi-purpose math program
+# versions 2.2 and 3.0
+
+#mathematica .mb
+0 string \064\024\012\000\035\000\000\000 Mathematica version 2 notebook
+0 string \064\024\011\000\035\000\000\000 Mathematica version 2 notebook
+
+# .ma
+# multiple possibilites:
+
+0 string (*^\n\n::[\011frontEndVersion\ =\ Mathematica notebook
+#>41 string >\0 %s
+
+#0 string (*^\n\n::[\011palette Mathematica notebook version 2.x
+
+#0 string (*^\n\n::[\011Information Mathematica notebook version 2.x
+#>675 string >\0 %s #doesn't work well
+
+# there may be 'cr' instread of 'nl' in some does this matter?
+
+# generic:
+0 string (*^\r\r::[\011 Mathematica notebook version 2.x
+0 string (*^\r\n\r\n::[\011 Mathematica notebook version 2.x
+0 string (*^\015 Mathematica notebook version 2.x
+0 string (*^\n\r\n\r::[\011 Mathematica notebook version 2.x
+0 string (*^\r::[\011 Mathematica notebook version 2.x
+0 string (*^\r\n::[\011 Mathematica notebook version 2.x
+0 string (*^\n\n::[\011 Mathematica notebook version 2.x
+0 string (*^\n::[\011 Mathematica notebook version 2.x
+
+
+# Mathematica .mx files
+
+#0 string (*This\ is\ a\ Mathematica\ binary\ dump\ file.\ It\ can\ be\ loaded\ with\ Get.*) Mathematica binary file
+0 string (*This\ is\ a\ Mathematica\ binary\ Mathematica binary file
+#>71 string \000\010\010\010\010\000\000\000\000\000\000\010\100\010\000\000\000
+# >71... is optional
+>88 string >\0 from %s
+
+
+# Mathematica files PBF:
+# 115 115 101 120 102 106 000 001 000 000 000 203 000 001 000
+0 string MMAPBF\000\001\000\000\000\203\000\001\000 Mathematica PBF (fonts I think)
+
+# .ml files These are menu resources I think
+# these start with "[0-9][0-9][0-9]\ A~[0-9][0-9][0-9]\
+# how to put that into a magic rule?
+4 string \ A~ MAthematica .ml file
+
+# .nb files
+#too long 0 string (***********************************************************************\n\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ Mathematica-Compatible Notebook Mathematica 3.0 notebook
+0 string (*********************** Mathematica 3.0 notebook
+
+# other (* matches it is a comment start in these langs
+# GRR: Too weak; also matches other languages e.g. ML
+#0 string (* Mathematica, or Pascal, Modula-2 or 3 code text
+
+#########################
+# MatLab v5
+0 string MATLAB Matlab v5 mat-file
+>126 short 0x494d (big endian)
+>>124 beshort x version 0x%04x
+>126 short 0x4d49 (little endian)
+>>124 leshort x version 0x%04x
+
+
+#------------------------------------------------------------------------------
+# $File: matroska,v 1.7 2012/08/26 10:06:15 christos Exp $
+# matroska: file(1) magic for Matroska files
+#
+# See http://www.matroska.org/
+#
+
+# EBML id:
+0 belong 0x1a45dfa3
+# DocType id:
+>4 search/4096 \x42\x82
+# DocType contents:
+>>&1 string webm WebM
+!:mime video/webm
+>>&1 string matroska Matroska data
+!:mime video/x-matroska
+
+#------------------------------------------------------------------------------
+# $File$
+# Mavroyanopoulos Nikos <nmav@hellug.gr>
+# mcrypt: file(1) magic for mcrypt 2.2.x;
+0 string \0m\3 mcrypt 2.5 encrypted data,
+>4 string >\0 algorithm: %s,
+>>&1 leshort >0 keysize: %d bytes,
+>>>&0 string >\0 mode: %s,
+
+0 string \0m\2 mcrypt 2.2 encrypted data,
+>3 byte 0 algorithm: blowfish-448,
+>3 byte 1 algorithm: DES,
+>3 byte 2 algorithm: 3DES,
+>3 byte 3 algorithm: 3-WAY,
+>3 byte 4 algorithm: GOST,
+>3 byte 6 algorithm: SAFER-SK64,
+>3 byte 7 algorithm: SAFER-SK128,
+>3 byte 8 algorithm: CAST-128,
+>3 byte 9 algorithm: xTEA,
+>3 byte 10 algorithm: TWOFISH-128,
+>3 byte 11 algorithm: RC2,
+>3 byte 12 algorithm: TWOFISH-192,
+>3 byte 13 algorithm: TWOFISH-256,
+>3 byte 14 algorithm: blowfish-128,
+>3 byte 15 algorithm: blowfish-192,
+>3 byte 16 algorithm: blowfish-256,
+>3 byte 100 algorithm: RC6,
+>3 byte 101 algorithm: IDEA,
+>4 byte 0 mode: CBC,
+>4 byte 1 mode: ECB,
+>4 byte 2 mode: CFB,
+>4 byte 3 mode: OFB,
+>4 byte 4 mode: nOFB,
+>5 byte 0 keymode: 8bit
+>5 byte 1 keymode: 4bit
+>5 byte 2 keymode: SHA-1 hash
+>5 byte 3 keymode: MD5 hash
+
+#------------------------------------------------------------------------------
+# $File$
+# mercurial: file(1) magic for Mercurial changeset bundles
+# http://www.selenic.com/mercurial/wiki/
+#
+# Jesse Glick (jesse.glick@sun.com)
+#
+
+0 string HG10 Mercurial changeset bundle
+>4 string UN (uncompressed)
+>4 string GZ (gzip compressed)
+>4 string BZ (bzip2 compressed)
+
+#------------------------------------------------------------------------------
+# $File: mathematica,v 1.7 2009/09/19 16:28:10 christos Exp $
+# metastore: file(1) magic for metastore files
+# From: Thomas Wissen
+# see http://david.hardeman.nu/software.php#metastore
+0 string MeTaSt00r3 Metastore data file,
+>10 bequad x version %0llx
+
+#------------------------------------------------------------------------------
+# $File: rinex,v 1.4 2011/05/03 01:44:17 christos Exp $
+# rinex: file(1) magic for RINEX files
+# http://igscb.jpl.nasa.gov/igscb/data/format/rinex210.txt
+# ftp://cddis.gsfc.nasa.gov/pub/reports/formats/rinex300.pdf
+# data for testing: ftp://cddis.gsfc.nasa.gov/pub/gps/data
+60 string RINEX
+>80 search/256 XXRINEXB RINEX Data, GEO SBAS Broadcast
+>>&32 string x \b, date %15.15s
+>>5 string x \b, version %6.6s
+!:mime rinex/broadcast
+>80 search/256 XXRINEXD RINEX Data, Observation (Hatanaka comp)
+>>&32 string x \b, date %15.15s
+>>5 string x \b, version %6.6s
+!:mime rinex/observation
+>80 search/256 XXRINEXC RINEX Data, Clock
+>>&32 string x \b, date %15.15s
+>>5 string x \b, version %6.6s
+!:mime rinex/clock
+>80 search/256 XXRINEXH RINEX Data, GEO SBAS Navigation
+>>&32 string x \b, date %15.15s
+>>5 string x \b, version %6.6s
+!:mime rinex/navigation
+>80 search/256 XXRINEXG RINEX Data, GLONASS Navigation
+>>&32 string x \b, date %15.15s
+>>5 string x \b, version %6.6s
+!:mime rinex/navigation
+>80 search/256 XXRINEXL RINEX Data, Galileo Navigation
+>>&32 string x \b, date %15.15s
+>>5 string x \b, version %6.6s
+!:mime rinex/navigation
+>80 search/256 XXRINEXM RINEX Data, Meteorological
+>>&32 string x \b, date %15.15s
+>>5 string x \b, version %6.6s
+!:mime rinex/meteorological
+>80 search/256 XXRINEXN RINEX Data, Navigation
+>>&32 string x \b, date %15.15s
+>>5 string x \b, version %6.6s
+!:mime rinex/navigation
+>80 search/256 XXRINEXO RINEX Data, Observation
+>>&32 string x \b, date %15.15s
+>>5 string x \b, version %6.6s
+!:mime rinex/observation
+
+# https://en.wikipedia.org/wiki/GRIB
+0 string GRIB
+>7 byte =1 Gridded binary (GRIB) version 1
+>7 byte =2 Gridded binary (GRIB) version 2
+
+#------------------------------------------------------------------------------
+# $File: mime,v 1.5 2009/09/19 16:28:10 christos Exp $
+# mime: file(1) magic for MIME encoded files
+#
+0 string/t Content-Type:\
+>14 string >\0 %s
+0 string/t Content-Type:
+>13 string >\0 %s
+
+#------------------------------------------------------------------------------
+# $File: mips,v 1.9 2013/01/12 03:09:51 christos Exp $
+# mips: file(1) magic for MIPS ECOFF and Ucode, as used in SGI IRIX
+# and DEC Ultrix
+#
+0 beshort 0x0160 MIPSEB ECOFF executable
+>20 beshort 0407 (impure)
+>20 beshort 0410 (swapped)
+>20 beshort 0413 (paged)
+>8 belong >0 not stripped
+>8 belong 0 stripped
+>22 byte x - version %d
+>23 byte x \b.%d
+#
+0 beshort 0x0162 MIPSEL-BE ECOFF executable
+>20 beshort 0407 (impure)
+>20 beshort 0410 (swapped)
+>20 beshort 0413 (paged)
+>8 belong >0 not stripped
+>8 belong 0 stripped
+>23 byte x - version %d
+>22 byte x \b.%d
+#
+0 beshort 0x6001 MIPSEB-LE ECOFF executable
+>20 beshort 03401 (impure)
+>20 beshort 04001 (swapped)
+>20 beshort 05401 (paged)
+>8 belong >0 not stripped
+>8 belong 0 stripped
+>23 byte x - version %d
+>22 byte x \b.%d
+#
+0 beshort 0x6201 MIPSEL ECOFF executable
+>20 beshort 03401 (impure)
+>20 beshort 04001 (swapped)
+>20 beshort 05401 (paged)
+>8 belong >0 not stripped
+>8 belong 0 stripped
+>23 byte x - version %d
+>22 byte x \b.%d
+#
+# MIPS 2 additions
+#
+0 beshort 0x0163 MIPSEB MIPS-II ECOFF executable
+>20 beshort 0407 (impure)
+>20 beshort 0410 (swapped)
+>20 beshort 0413 (paged)
+>8 belong >0 not stripped
+>8 belong 0 stripped
+>22 byte x - version %d
+>23 byte x \b.%d
+#
+0 beshort 0x0166 MIPSEL-BE MIPS-II ECOFF executable
+>20 beshort 0407 (impure)
+>20 beshort 0410 (swapped)
+>20 beshort 0413 (paged)
+>8 belong >0 not stripped
+>8 belong 0 stripped
+>22 byte x - version %d
+>23 byte x \b.%d
+#
+0 beshort 0x6301 MIPSEB-LE MIPS-II ECOFF executable
+>20 beshort 03401 (impure)
+>20 beshort 04001 (swapped)
+>20 beshort 05401 (paged)
+>8 belong >0 not stripped
+>8 belong 0 stripped
+>23 byte x - version %d
+>22 byte x \b.%d
+#
+0 beshort 0x6601 MIPSEL MIPS-II ECOFF executable
+>20 beshort 03401 (impure)
+>20 beshort 04001 (swapped)
+>20 beshort 05401 (paged)
+>8 belong >0 not stripped
+>8 belong 0 stripped
+>23 byte x - version %d
+>22 byte x \b.%d
+#
+# MIPS 3 additions
+#
+0 beshort 0x0140 MIPSEB MIPS-III ECOFF executable
+>20 beshort 0407 (impure)
+>20 beshort 0410 (swapped)
+>20 beshort 0413 (paged)
+>8 belong >0 not stripped
+>8 belong 0 stripped
+>22 byte x - version %d
+>23 byte x \b.%d
+#
+0 beshort 0x0142 MIPSEL-BE MIPS-III ECOFF executable
+>20 beshort 0407 (impure)
+>20 beshort 0410 (swapped)
+>20 beshort 0413 (paged)
+>8 belong >0 not stripped
+>8 belong 0 stripped
+>22 byte x - version %d
+>23 byte x \b.%d
+#
+0 beshort 0x4001 MIPSEB-LE MIPS-III ECOFF executable
+>20 beshort 03401 (impure)
+>20 beshort 04001 (swapped)
+>20 beshort 05401 (paged)
+>8 belong >0 not stripped
+>8 belong 0 stripped
+>23 byte x - version %d
+>22 byte x \b.%d
+#
+0 beshort 0x4201 MIPSEL MIPS-III ECOFF executable
+>20 beshort 03401 (impure)
+>20 beshort 04001 (swapped)
+>20 beshort 05401 (paged)
+>8 belong >0 not stripped
+>8 belong 0 stripped
+>23 byte x - version %d
+>22 byte x \b.%d
+#
+0 beshort 0x180 MIPSEB Ucode
+0 beshort 0x182 MIPSEL-BE Ucode
+
+#------------------------------------------------------------------------------
+# $File$
+# mirage: file(1) magic for Mirage executables
+#
+# XXX - byte order?
+#
+0 long 31415 Mirage Assembler m.out executable
+
+#-----------------------------------------------------------------------------
+# $File: misctools,v 1.13 2013/01/16 13:53:10 christos Exp $
+# misctools: file(1) magic for miscellaneous UNIX tools.
+#
+0 search/1 %%!! X-Post-It-Note text
+0 string/c BEGIN:VCALENDAR vCalendar calendar file
+!:mime text/calendar
+0 string/c BEGIN:VCARD vCard visiting card
+!:mime text/x-vcard
+
+# Summary: Libtool library file
+# Extension: .la
+# Submitted by: Tomasz Trojanowski <tomek@uninet.com.pl>
+0 search/80 .la\ -\ a\ libtool\ library\ file libtool library file
+
+# Summary: Libtool object file
+# Extension: .lo
+# Submitted by: Abel Cheung <abelcheung@gmail.com>
+0 search/80 .lo\ -\ a\ libtool\ object\ file libtool object file
+
+# From: Daniel Novotny <dnovotny@redhat.com>
+0 string MDMP\x93\xA7 MDMP crash report data
+
+# Summary: abook addressbook file
+# Submitted by: Mark Schreiber <mark7@alumni.cmu.edu>
+0 string #\x20abook\x20addressbook\x20file abook address book
+!:mime application/x-abook-addressbook
+
+#------------------------------------------------------------------------------
+# $File$
+# mkid: file(1) magic for mkid(1) databases
+#
+# ID is the binary tags database produced by mkid(1).
+#
+# XXX - byte order?
+#
+0 string \311\304 ID tags data
+>2 short >0 version %d
+
+#------------------------------------------------------------------------------
+# $File$
+# mlssa: file(1) magic for MLSSA datafiles
+#
+0 lelong 0xffffabcd MLSSA datafile,
+>4 leshort x algorithm %d,
+>10 lelong x %d samples
+
+#------------------------------------------------------------------------------
+# $File$
+# mmdf: file(1) magic for MMDF mail files
+#
+0 string \001\001\001\001 MMDF mailbox
+
+#------------------------------------------------------------------------------
+# $File: modem,v 1.5 2010/09/20 18:55:20 rrt Exp $
+# modem: file(1) magic for modem programs
+#
+# From: Florian La Roche <florian@knorke.saar.de>
+1 string PC\ Research,\ Inc Digifax-G3-File
+>29 byte 1 \b, fine resolution
+>29 byte 0 \b, normal resolution
+
+0 short 0x0100 raw G3 data, byte-padded
+0 short 0x1400 raw G3 data
+#
+# Magic data for vgetty voice formats
+# (Martin Seine & Marc Eberhard)
+
+#
+# raw modem data version 1
+#
+0 string RMD1 raw modem data
+>4 string >\0 (%s /
+>20 short >0 compression type 0x%04x)
+
+#
+# portable voice format 1
+#
+0 string PVF1\n portable voice format
+>5 string >\0 (binary %s)
+
+#
+# portable voice format 2
+#
+0 string PVF2\n portable voice format
+>5 string >\0 (ascii %s)
+
+# From: Bernd Nuernberger <bernd.nuernberger@web.de>
+# Brooktrout G3 fax data incl. 128 byte header
+# Common suffixes: 3??, BRK, BRT, BTR
+0 leshort 0x01bb
+>2 leshort 0x0100 Brooktrout 301 fax image,
+>>9 leshort x %d x
+>>0x2d leshort x %d
+>>6 leshort 200 \b, fine resolution
+>>6 leshort 100 \b, normal resolution
+>>11 byte 1 \b, G3 compression
+>>11 byte 2 \b, G32D compression
+
+#------------------------------------------------------------------------------
+# $File: motorola,v 1.10 2009/09/19 16:28:11 christos Exp $
+# motorola: file(1) magic for Motorola 68K and 88K binaries
+#
+# 68K
+#
+0 beshort 0520 mc68k COFF
+>18 beshort ^00000020 object
+>18 beshort &00000020 executable
+>12 belong >0 not stripped
+>168 string .lowmem Apple toolbox
+>20 beshort 0407 (impure)
+>20 beshort 0410 (pure)
+>20 beshort 0413 (demand paged)
+>20 beshort 0421 (standalone)
+0 beshort 0521 mc68k executable (shared)
+>12 belong >0 not stripped
+0 beshort 0522 mc68k executable (shared demand paged)
+>12 belong >0 not stripped
+#
+# Motorola/UniSoft 68K Binary Compatibility Standard (BCS)
+#
+0 beshort 0554 68K BCS executable
+#
+# 88K
+#
+# Motorola/88Open BCS
+#
+0 beshort 0555 88K BCS executable
+#
+# Motorola S-Records, from Gerd Truschinski <gt@freebsd.first.gmd.de>
+0 string S0 Motorola S-Record; binary data in text format
+
+# ATARI ST relocatable PRG
+#
+# from Oskar Schirmer <schirmer@scara.com> Feb 3, 2001
+# (according to Roland Waldi, Oct 21, 1987)
+# besides the magic 0x601a, the text segment size is checked to be
+# not larger than 1 MB (which is a lot on ST).
+# The additional 0x601b distinction I took from Doug Lee's magic.
+0 belong&0xFFFFFFF0 0x601A0000 Atari ST M68K contiguous executable
+>2 belong x (txt=%d,
+>6 belong x dat=%d,
+>10 belong x bss=%d,
+>14 belong x sym=%d)
+0 belong&0xFFFFFFF0 0x601B0000 Atari ST M68K non-contig executable
+>2 belong x (txt=%d,
+>6 belong x dat=%d,
+>10 belong x bss=%d,
+>14 belong x sym=%d)
+
+# Atari ST/TT... program format (sent by Wolfram Kleff <kleff@cs.uni-bonn.de>)
+0 beshort 0x601A Atari 68xxx executable,
+>2 belong x text len %u,
+>6 belong x data len %u,
+>10 belong x BSS len %u,
+>14 belong x symboltab len %u,
+>18 belong 0
+>22 belong &0x01 fastload flag,
+>22 belong &0x02 may be loaded to alternate RAM,
+>22 belong &0x04 malloc may be from alternate RAM,
+>22 belong x flags: 0x%X,
+>26 beshort 0 no relocation tab
+>26 beshort !0 + relocation tab
+>30 string SFX [Self-Extracting LZH SFX archive]
+>38 string SFX [Self-Extracting LZH SFX archive]
+>44 string ZIP! [Self-Extracting ZIP SFX archive]
+
+0 beshort 0x0064 Atari 68xxx CPX file
+>8 beshort x (version %04x)
+
+#------------------------------------------------------------------------------
+# $File: mozilla,v 1.5 2015/01/24 15:48:42 christos Exp $
+# mozilla: file(1) magic for Mozilla XUL fastload files
+# (XUL.mfasl and XPC.mfasl)
+# URL: http://www.mozilla.org/
+# From: Josh Triplett <josh@freedesktop.org>
+
+0 string XPCOM\nMozFASL\r\n\x1A Mozilla XUL fastload data
+0 string mozLz4a Mozilla lz4 compressed bookmark data
+
+#------------------------------------------------------------------------------
+# $File: msdos,v 1.99 2014/06/03 01:40:24 christos Exp $
+# msdos: file(1) magic for MS-DOS files
+#
+
+# .BAT files (Daniel Quinlan, quinlan@yggdrasil.com)
+# updated by Joerg Jenderek at Oct 2008,Apr 2011
+0 string/t @
+>1 string/cW \ echo\ off DOS batch file text
+!:mime text/x-msdos-batch
+>1 string/cW echo\ off DOS batch file text
+!:mime text/x-msdos-batch
+>1 string/cW rem DOS batch file text
+!:mime text/x-msdos-batch
+>1 string/cW set\ DOS batch file text
+!:mime text/x-msdos-batch
+
+
+# OS/2 batch files are REXX. the second regex is a bit generic, oh well
+# the matched commands seem to be common in REXX and uncommon elsewhere
+100 search/0xffff rxfuncadd
+>100 regex/c =^[\ \t]{0,10}call[\ \t]{1,10}rxfunc OS/2 REXX batch file text
+100 search/0xffff say
+>100 regex/c =^[\ \t]{0,10}say\ ['"] OS/2 REXX batch file text
+
+0 leshort 0x14c MS Windows COFF Intel 80386 object file
+#>4 ledate x stamp %s
+0 leshort 0x166 MS Windows COFF MIPS R4000 object file
+#>4 ledate x stamp %s
+0 leshort 0x184 MS Windows COFF Alpha object file
+#>4 ledate x stamp %s
+0 leshort 0x268 MS Windows COFF Motorola 68000 object file
+#>4 ledate x stamp %s
+0 leshort 0x1f0 MS Windows COFF PowerPC object file
+#>4 ledate x stamp %s
+0 leshort 0x290 MS Windows COFF PA-RISC object file
+#>4 ledate x stamp %s
+
+# Tests for various EXE types.
+#
+# Many of the compressed formats were extraced from IDARC 1.23 source code.
+#
+0 string/b MZ
+# All non-DOS EXE extensions have the relocation table more than 0x40 bytes into the file.
+>0x18 leshort <0x40 MS-DOS executable
+!:mime application/x-dosexec
+# These traditional tests usually work but not always. When test quality support is
+# implemented these can be turned on.
+#>>0x18 leshort 0x1c (Borland compiler)
+#>>0x18 leshort 0x1e (MS compiler)
+
+# If the relocation table is 0x40 or more bytes into the file, it's definitely
+# not a DOS EXE.
+>0x18 leshort >0x3f
+
+# Maybe it's a PE?
+>>(0x3c.l) string PE\0\0 PE
+!:mime application/x-dosexec
+>>>(0x3c.l+24) leshort 0x010b \b32 executable
+>>>(0x3c.l+24) leshort 0x020b \b32+ executable
+>>>(0x3c.l+24) leshort 0x0107 ROM image
+>>>(0x3c.l+24) default x Unknown PE signature
+>>>>&0 leshort x 0x%x
+>>>(0x3c.l+22) leshort&0x2000 >0 (DLL)
+>>>(0x3c.l+92) leshort 1 (native)
+>>>(0x3c.l+92) leshort 2 (GUI)
+>>>(0x3c.l+92) leshort 3 (console)
+>>>(0x3c.l+92) leshort 7 (POSIX)
+>>>(0x3c.l+92) leshort 9 (Windows CE)
+>>>(0x3c.l+92) leshort 10 (EFI application)
+>>>(0x3c.l+92) leshort 11 (EFI boot service driver)
+>>>(0x3c.l+92) leshort 12 (EFI runtime driver)
+>>>(0x3c.l+92) leshort 13 (EFI ROM)
+>>>(0x3c.l+92) leshort 14 (XBOX)
+>>>(0x3c.l+92) leshort 15 (Windows boot application)
+>>>(0x3c.l+92) default x (Unknown subsystem
+>>>>&0 leshort x 0x%x)
+>>>(0x3c.l+4) leshort 0x14c Intel 80386
+>>>(0x3c.l+4) leshort 0x166 MIPS R4000
+>>>(0x3c.l+4) leshort 0x168 MIPS R10000
+>>>(0x3c.l+4) leshort 0x184 Alpha
+>>>(0x3c.l+4) leshort 0x1a2 Hitachi SH3
+>>>(0x3c.l+4) leshort 0x1a6 Hitachi SH4
+>>>(0x3c.l+4) leshort 0x1c0 ARM
+>>>(0x3c.l+4) leshort 0x1c2 ARM Thumb
+>>>(0x3c.l+4) leshort 0x1c4 ARMv7 Thumb
+>>>(0x3c.l+4) leshort 0x1f0 PowerPC
+>>>(0x3c.l+4) leshort 0x200 Intel Itanium
+>>>(0x3c.l+4) leshort 0x266 MIPS16
+>>>(0x3c.l+4) leshort 0x268 Motorola 68000
+>>>(0x3c.l+4) leshort 0x290 PA-RISC
+>>>(0x3c.l+4) leshort 0x366 MIPSIV
+>>>(0x3c.l+4) leshort 0x466 MIPS16 with FPU
+>>>(0x3c.l+4) leshort 0xebc EFI byte code
+>>>(0x3c.l+4) leshort 0x8664 x86-64
+>>>(0x3c.l+4) leshort 0xc0ee MSIL
+>>>(0x3c.l+4) default x Unknown processor type
+>>>>&0 leshort x 0x%x
+>>>(0x3c.l+22) leshort&0x0200 >0 (stripped to external PDB)
+>>>(0x3c.l+22) leshort&0x1000 >0 system file
+>>>(0x3c.l+24) leshort 0x010b
+>>>>(0x3c.l+232) lelong >0 Mono/.Net assembly
+>>>(0x3c.l+24) leshort 0x020b
+>>>>(0x3c.l+248) lelong >0 Mono/.Net assembly
+
+# hooray, there's a DOS extender using the PE format, with a valid PE
+# executable inside (which just prints a message and exits if run in win)
+>>>(8.s*16) string 32STUB \b, 32rtm DOS extender
+>>>(8.s*16) string !32STUB \b, for MS Windows
+>>>(0x3c.l+0xf8) string UPX0 \b, UPX compressed
+>>>(0x3c.l+0xf8) search/0x140 PEC2 \b, PECompact2 compressed
+>>>(0x3c.l+0xf8) search/0x140 UPX2
+>>>>(&0x10.l+(-4)) string PK\3\4 \b, ZIP self-extracting archive (Info-Zip)
+>>>(0x3c.l+0xf8) search/0x140 .idata
+>>>>(&0xe.l+(-4)) string PK\3\4 \b, ZIP self-extracting archive (Info-Zip)
+>>>>(&0xe.l+(-4)) string ZZ0 \b, ZZip self-extracting archive
+>>>>(&0xe.l+(-4)) string ZZ1 \b, ZZip self-extracting archive
+>>>(0x3c.l+0xf8) search/0x140 .rsrc
+>>>>(&0x0f.l+(-4)) string a\\\4\5 \b, WinHKI self-extracting archive
+>>>>(&0x0f.l+(-4)) string Rar! \b, RAR self-extracting archive
+>>>>(&0x0f.l+(-4)) search/0x3000 MSCF \b, InstallShield self-extracting archive
+>>>>(&0x0f.l+(-4)) search/32 Nullsoft \b, Nullsoft Installer self-extracting archive
+>>>(0x3c.l+0xf8) search/0x140 .data
+>>>>(&0x0f.l) string WEXTRACT \b, MS CAB-Installer self-extracting archive
+>>>(0x3c.l+0xf8) search/0x140 .petite\0 \b, Petite compressed
+>>>>(0x3c.l+0xf7) byte x
+>>>>>(&0x104.l+(-4)) string =!sfx! \b, ACE self-extracting archive
+>>>(0x3c.l+0xf8) search/0x140 .WISE \b, WISE installer self-extracting archive
+>>>(0x3c.l+0xf8) search/0x140 .dz\0\0\0 \b, Dzip self-extracting archive
+>>>&(0x3c.l+0xf8) search/0x100 _winzip_ \b, ZIP self-extracting archive (WinZip)
+>>>&(0x3c.l+0xf8) search/0x100 SharedD \b, Microsoft Installer self-extracting archive
+>>>0x30 string Inno \b, InnoSetup self-extracting archive
+
+# Hmm, not a PE but the relocation table is too high for a traditional DOS exe,
+# must be one of the unusual subformats.
+>>(0x3c.l) string !PE\0\0 MS-DOS executable
+!:mime application/x-dosexec
+
+>>(0x3c.l) string NE \b, NE
+!:mime application/x-dosexec
+>>>(0x3c.l+0x36) byte 1 for OS/2 1.x
+>>>(0x3c.l+0x36) byte 2 for MS Windows 3.x
+>>>(0x3c.l+0x36) byte 3 for MS-DOS
+>>>(0x3c.l+0x36) byte 4 for Windows 386
+>>>(0x3c.l+0x36) byte 5 for Borland Operating System Services
+>>>(0x3c.l+0x36) default x
+>>>>(0x3c.l+0x36) byte x (unknown OS %x)
+>>>(0x3c.l+0x36) byte 0x81 for MS-DOS, Phar Lap DOS extender
+>>>(0x3c.l+0x0c) leshort&0x8003 0x8002 (DLL)
+>>>(0x3c.l+0x0c) leshort&0x8003 0x8001 (driver)
+>>>&(&0x24.s-1) string ARJSFX \b, ARJ self-extracting archive
+>>>(0x3c.l+0x70) search/0x80 WinZip(R)\ Self-Extractor \b, ZIP self-extracting archive (WinZip)
+
+>>(0x3c.l) string LX\0\0 \b, LX
+!:mime application/x-dosexec
+>>>(0x3c.l+0x0a) leshort <1 (unknown OS)
+>>>(0x3c.l+0x0a) leshort 1 for OS/2
+>>>(0x3c.l+0x0a) leshort 2 for MS Windows
+>>>(0x3c.l+0x0a) leshort 3 for DOS
+>>>(0x3c.l+0x0a) leshort >3 (unknown OS)
+>>>(0x3c.l+0x10) lelong&0x28000 =0x8000 (DLL)
+>>>(0x3c.l+0x10) lelong&0x20000 >0 (device driver)
+>>>(0x3c.l+0x10) lelong&0x300 0x300 (GUI)
+>>>(0x3c.l+0x10) lelong&0x28300 <0x300 (console)
+>>>(0x3c.l+0x08) leshort 1 i80286
+>>>(0x3c.l+0x08) leshort 2 i80386
+>>>(0x3c.l+0x08) leshort 3 i80486
+>>>(8.s*16) string emx \b, emx
+>>>>&1 string x %s
+>>>&(&0x54.l-3) string arjsfx \b, ARJ self-extracting archive
+
+# MS Windows system file, supposedly a collection of LE executables
+>>(0x3c.l) string W3 \b, W3 for MS Windows
+!:mime application/x-dosexec
+
+>>(0x3c.l) string LE\0\0 \b, LE executable
+!:mime application/x-dosexec
+>>>(0x3c.l+0x0a) leshort 1
+# some DOS extenders use LE files with OS/2 header
+>>>>0x240 search/0x100 DOS/4G for MS-DOS, DOS4GW DOS extender
+>>>>0x240 search/0x200 WATCOM\ C/C++ for MS-DOS, DOS4GW DOS extender
+>>>>0x440 search/0x100 CauseWay\ DOS\ Extender for MS-DOS, CauseWay DOS extender
+>>>>0x40 search/0x40 PMODE/W for MS-DOS, PMODE/W DOS extender
+>>>>0x40 search/0x40 STUB/32A for MS-DOS, DOS/32A DOS extender (stub)
+>>>>0x40 search/0x80 STUB/32C for MS-DOS, DOS/32A DOS extender (configurable stub)
+>>>>0x40 search/0x80 DOS/32A for MS-DOS, DOS/32A DOS extender (embedded)
+# this is a wild guess; hopefully it is a specific signature
+>>>>&0x24 lelong <0x50
+>>>>>(&0x4c.l) string \xfc\xb8WATCOM
+>>>>>>&0 search/8 3\xdbf\xb9 \b, 32Lite compressed
+# another wild guess: if real OS/2 LE executables exist, they probably have higher start EIP
+#>>>>(0x3c.l+0x1c) lelong >0x10000 for OS/2
+# fails with DOS-Extenders.
+>>>(0x3c.l+0x0a) leshort 2 for MS Windows
+>>>(0x3c.l+0x0a) leshort 3 for DOS
+>>>(0x3c.l+0x0a) leshort 4 for MS Windows (VxD)
+>>>(&0x7c.l+0x26) string UPX \b, UPX compressed
+>>>&(&0x54.l-3) string UNACE \b, ACE self-extracting archive
+
+# looks like ASCII, probably some embedded copyright message.
+# and definitely not NE/LE/LX/PE
+>>0x3c lelong >0x20000000
+>>>(4.s*512) leshort !0x014c \b, MZ for MS-DOS
+!:mime application/x-dosexec
+# header data too small for extended executable
+>2 long !0
+>>0x18 leshort <0x40
+>>>(4.s*512) leshort !0x014c
+
+>>>>&(2.s-514) string !LE
+>>>>>&-2 string !BW \b, MZ for MS-DOS
+!:mime application/x-dosexec
+>>>>&(2.s-514) string LE \b, LE
+>>>>>0x240 search/0x100 DOS/4G for MS-DOS, DOS4GW DOS extender
+# educated guess since indirection is still not capable enough for complex offset
+# calculations (next embedded executable would be at &(&2*512+&0-2)
+# I suspect there are only LE executables in these multi-exe files
+>>>>&(2.s-514) string BW
+>>>>>0x240 search/0x100 DOS/4G \b, LE for MS-DOS, DOS4GW DOS extender (embedded)
+>>>>>0x240 search/0x100 !DOS/4G \b, BW collection for MS-DOS
+
+# This sequence skips to the first COFF segment, usually .text
+>(4.s*512) leshort 0x014c \b, COFF
+!:mime application/x-dosexec
+>>(8.s*16) string go32stub for MS-DOS, DJGPP go32 DOS extender
+>>(8.s*16) string emx
+>>>&1 string x for DOS, Win or OS/2, emx %s
+>>&(&0x42.l-3) byte x
+>>>&0x26 string UPX \b, UPX compressed
+# and yet another guess: small .text, and after large .data is unusal, could be 32lite
+>>&0x2c search/0xa0 .text
+>>>&0x0b lelong <0x2000
+>>>>&0 lelong >0x6000 \b, 32lite compressed
+
+>(8.s*16) string $WdX \b, WDos/X DOS extender
+
+# By now an executable type should have been printed out. The executable
+# may be a self-uncompressing archive, so look for evidence of that and
+# print it out.
+#
+# Some signatures below from Greg Roelofs, newt@uchicago.edu.
+#
+>0x35 string \x8e\xc0\xb9\x08\x00\xf3\xa5\x4a\x75\xeb\x8e\xc3\x8e\xd8\x33\xff\xbe\x30\x00\x05 \b, aPack compressed
+>0xe7 string LH/2\ Self-Extract \b, %s
+>0x1c string UC2X \b, UCEXE compressed
+>0x1c string WWP\ \b, WWPACK compressed
+>0x1c string RJSX \b, ARJ self-extracting archive
+>0x1c string diet \b, diet compressed
+>0x1c string LZ09 \b, LZEXE v0.90 compressed
+>0x1c string LZ91 \b, LZEXE v0.91 compressed
+>0x1c string tz \b, TinyProg compressed
+>0x1e string Copyright\ 1989-1990\ PKWARE\ Inc. Self-extracting PKZIP archive
+!:mime application/zip
+# Yes, this really is "Copr", not "Corp."
+>0x1e string PKLITE\ Copr. Self-extracting PKZIP archive
+!:mime application/zip
+# winarj stores a message in the stub instead of the sig in the MZ header
+>0x20 search/0xe0 aRJsfX \b, ARJ self-extracting archive
+>0x20 string AIN
+>>0x23 string 2 \b, AIN 2.x compressed
+>>0x23 string <2 \b, AIN 1.x compressed
+>>0x23 string >2 \b, AIN 1.x compressed
+>0x24 string LHa's\ SFX \b, LHa self-extracting archive
+!:mime application/x-lha
+>0x24 string LHA's\ SFX \b, LHa self-extracting archive
+!:mime application/x-lha
+>0x24 string \ $ARX \b, ARX self-extracting archive
+>0x24 string \ $LHarc \b, LHarc self-extracting archive
+>0x20 string SFX\ by\ LARC \b, LARC self-extracting archive
+>0x40 string aPKG \b, aPackage self-extracting archive
+>0x64 string W\ Collis\0\0 \b, Compack compressed
+>0x7a string Windows\ self-extracting\ ZIP \b, ZIP self-extracting archive
+>>&0xf4 search/0x140 \x0\x40\x1\x0
+>>>(&0.l+(4)) string MSCF \b, WinHKI CAB self-extracting archive
+>1638 string -lh5- \b, LHa self-extracting archive v2.13S
+>0x17888 string Rar! \b, RAR self-extracting archive
+
+# Skip to the end of the EXE. This will usually work fine in the PE case
+# because the MZ image is hardcoded into the toolchain and almost certainly
+# won't match any of these signatures.
+>(4.s*512) long x
+>>&(2.s-517) byte x
+>>>&0 string PK\3\4 \b, ZIP self-extracting archive
+>>>&0 string Rar! \b, RAR self-extracting archive
+>>>&0 string =!\x11 \b, AIN 2.x self-extracting archive
+>>>&0 string =!\x12 \b, AIN 2.x self-extracting archive
+>>>&0 string =!\x17 \b, AIN 1.x self-extracting archive
+>>>&0 string =!\x18 \b, AIN 1.x self-extracting archive
+>>>&7 search/400 **ACE** \b, ACE self-extracting archive
+>>>&0 search/0x480 UC2SFX\ Header \b, UC2 self-extracting archive
+
+# a few unknown ZIP sfxes, no idea if they are needed or if they are
+# already captured by the generic patterns above
+>(8.s*16) search/0x20 PKSFX \b, ZIP self-extracting archive (PKZIP)
+# TODO: how to add this? >FileSize-34 string Windows\ Self-Installing\ Executable \b, ZIP self-extracting archive
+#
+
+# TELVOX Teleinformatica CODEC self-extractor for OS/2:
+>49801 string \x79\xff\x80\xff\x76\xff \b, CODEC archive v3.21
+>>49824 leshort =1 \b, 1 file
+>>49824 leshort >1 \b, %u files
+
+# added by Joerg Jenderek of http://www.freedos.org/software/?prog=kc
+# and http://www.freedos.org/software/?prog=kpdos
+# for FreeDOS files like KEYBOARD.SYS, KEYBRD2.SYS, KEYBRD3.SYS, *.KBD
+0 string/b KCF FreeDOS KEYBoard Layout collection
+# only version=0x100 found
+>3 uleshort x \b, version 0x%x
+# length of string containing author,info and special characters
+>6 ubyte >0
+#>>6 pstring x \b, name=%s
+>>7 string >\0 \b, author=%-.14s
+>>7 search/254 \xff \b, info=
+#>>>&0 string x \b%-s
+>>>&0 string x \b%-.15s
+# for FreeDOS *.KL files
+0 string/b KLF FreeDOS KEYBoard Layout file
+# only version=0x100 or 0x101 found
+>3 uleshort x \b, version 0x%x
+# stringlength
+>5 ubyte >0
+>>8 string x \b, name=%-.2s
+0 string \xffKEYB\ \ \ \0\0\0\0
+>12 string \0\0\0\0`\004\360 MS-DOS KEYBoard Layout file
+
+# .COM formats (Daniel Quinlan, quinlan@yggdrasil.com)
+# Uncommenting only the first two lines will cover about 2/3 of COM files,
+# but it isn't feasible to match all COM files since there must be at least
+# two dozen different one-byte "magics".
+# test too generic ?
+0 byte 0xe9 DOS executable (COM)
+>0x1FE leshort 0xAA55 \b, boot code
+>6 string SFX\ of\ LHarc (%s)
+
+# DOS device driver updated by Joerg Jenderek at May 2011
+# http://maben.homeip.net/static/S100/IBM/software/DOS/DOS%20techref/CHAPTER.009
+0 ulequad&0x07a0ffffffff 0xffffffff DOS executable (
+>40 search/7 UPX! \bUPX compressed
+# DOS device driver attributes
+>4 uleshort&0x8000 0x0000 \bblock device driver
+# character device
+>4 uleshort&0x8000 0x8000 \b
+>>4 uleshort&0x0008 0x0008 \bclock
+# fast video output by int 29h
+>>4 uleshort&0x0010 0x0010 \bfast
+# standard input/output device
+>>4 uleshort&0x0003 >0 \bstandard
+>>>4 uleshort&0x0001 0x0001 \binput
+>>>4 uleshort&0x0003 0x0003 \b/
+>>>4 uleshort&0x0002 0x0002 \boutput
+>>4 uleshort&0x8000 0x8000 \bcharacter device driver
+>0 ubyte x
+# upx compressed device driver has garbage instead of real in name field of header
+>>40 search/7 UPX!
+>>40 default x
+# leading/trailing nulls, zeros or non ASCII characters in 8-byte name field at offset 10 are skipped
+>>>12 ubyte >0x27 \b
+>>>>10 ubyte >0x20
+>>>>>10 ubyte !0x2E
+>>>>>>10 ubyte !0x2A \b%c
+>>>>11 ubyte >0x20
+>>>>>11 ubyte !0x2E \b%c
+>>>>12 ubyte >0x20
+>>>>>12 ubyte !0x39
+>>>>>>12 ubyte !0x2E \b%c
+>>>13 ubyte >0x20
+>>>>13 ubyte !0x2E \b%c
+>>>>14 ubyte >0x20
+>>>>>14 ubyte !0x2E \b%c
+>>>>15 ubyte >0x20
+>>>>>15 ubyte !0x2E \b%c
+>>>>16 ubyte >0x20
+>>>>>16 ubyte !0x2E
+>>>>>>16 ubyte <0xCB \b%c
+>>>>17 ubyte >0x20
+>>>>>17 ubyte !0x2E
+>>>>>>17 ubyte <0x90 \b%c
+# some character device drivers like ASPICD.SYS, btcdrom.sys and Cr_atapi.sys contain only spaces or points in name field
+>>>4 uleshort&0x8000 0x8000
+>>>>12 ubyte <0x2F
+# they have their real name at offset 22
+>>>>>22 string >\0 \b%-.5s
+>4 uleshort&0x8000 0x0000
+# 32 bit sector addressing ( > 32 MB) for block devices
+>>4 uleshort&0x0002 0x0002 \b,32-bit sector-
+# support by driver functions 13h, 17h, 18h
+>4 uleshort&0x0040 0x0040 \b,IOCTL-
+# open, close, removable media support by driver functions 0Dh, 0Eh, 0Fh
+>4 uleshort&0x0800 0x0800 \b,close media-
+# output until busy support by int 10h for character device driver
+>4 uleshort&0x8000 0x8000
+>>4 uleshort&0x2000 0x2000 \b,until busy-
+# direct read/write support by driver functions 03h,0Ch
+>4 uleshort&0x4000 0x4000 \b,control strings-
+>4 uleshort&0x8000 0x8000
+>>4 uleshort&0x6840 >0 \bsupport
+>4 uleshort&0x8000 0x0000
+>>4 uleshort&0x4842 >0 \bsupport
+>0 ubyte x \b)
+# DOS driver cmd640x.sys has 0x12 instead of 0xffffffff for pointer field to next device header
+# Too weak, matches files that only contain 0's
+#0 ulequad&0x000007a0ffffffed 0x0000000000000000 DOS-executable (
+#>4 uleshort&0x8000 0x8000 \bcharacter device driver
+#>>10 string x %-.8s
+#>4 uleshort&0x4000 0x4000 \b,control strings-support)
+
+# test too generic ?
+0 byte 0x8c DOS executable (COM)
+# updated by Joerg Jenderek at Oct 2008
+0 ulelong 0xffff10eb DR-DOS executable (COM)
+# byte 0xeb conflicts with "sequent" magic leshort 0xn2eb
+0 ubeshort&0xeb8d >0xeb00
+# DR-DOS STACKER.COM SCREATE.SYS missed
+>0 byte 0xeb
+>>0x1FE leshort 0xAA55 DOS executable (COM), boot code
+>>85 string UPX DOS executable (COM), UPX compressed
+>>4 string \ $ARX DOS executable (COM), ARX self-extracting archive
+>>4 string \ $LHarc DOS executable (COM), LHarc self-extracting archive
+>>0x20e string SFX\ by\ LARC DOS executable (COM), LARC self-extracting archive
+# updated by Joerg Jenderek at Oct 2008
+#0 byte 0xb8 COM executable
+0 uleshort&0x80ff 0x00b8
+# modified by Joerg Jenderek
+>1 lelong !0x21cd4cff COM executable for DOS
+# http://syslinux.zytor.com/comboot.php
+# (32-bit COMBOOT) programs *.C32 contain 32-bit code and run in flat-memory 32-bit protected mode
+# start with assembler instructions mov eax,21cd4cffh
+0 uleshort&0xc0ff 0xc0b8
+>1 lelong 0x21cd4cff COM executable (32-bit COMBOOT)
+# syslinux:doc/comboot.txt
+# A COM32R program must start with the byte sequence B8 FE 4C CD 21 (mov
+# eax,21cd4cfeh) as a magic number.
+0 string/b \xb8\xfe\x4c\xcd\x21 COM executable (COM32R)
+# start with assembler instructions mov eax,21cd4cfeh
+0 uleshort&0xc0ff 0xc0b8
+>1 lelong 0x21cd4cfe COM executable (32-bit COMBOOT, relocatable)
+0 string/b \x81\xfc
+>4 string \x77\x02\xcd\x20\xb9
+>>36 string UPX! FREE-DOS executable (COM), UPX compressed
+252 string Must\ have\ DOS\ version DR-DOS executable (COM)
+# added by Joerg Jenderek at Oct 2008
+# GRR search is not working
+#34 search/2 UPX! FREE-DOS executable (COM), UPX compressed
+34 string UPX! FREE-DOS executable (COM), UPX compressed
+35 string UPX! FREE-DOS executable (COM), UPX compressed
+# GRR search is not working
+#2 search/28 \xcd\x21 COM executable for MS-DOS
+#WHICHFAT.cOM
+2 string \xcd\x21 COM executable for DOS
+#DELTREE.cOM DELTREE2.cOM
+4 string \xcd\x21 COM executable for DOS
+#IFMEMDSK.cOM ASSIGN.cOM COMP.cOM
+5 string \xcd\x21 COM executable for DOS
+#DELTMP.COm HASFAT32.cOM
+7 string \xcd\x21
+>0 byte !0xb8 COM executable for DOS
+#COMP.cOM MORE.COm
+10 string \xcd\x21
+>5 string !\xcd\x21 COM executable for DOS
+#comecho.com
+13 string \xcd\x21 COM executable for DOS
+#HELP.COm EDIT.coM
+18 string \xcd\x21 COM executable for MS-DOS
+#NWRPLTRM.COm
+23 string \xcd\x21 COM executable for MS-DOS
+#LOADFIX.cOm LOADFIX.cOm
+30 string \xcd\x21 COM executable for MS-DOS
+#syslinux.com 3.11
+70 string \xcd\x21 COM executable for DOS
+# many compressed/converted COMs start with a copy loop instead of a jump
+0x6 search/0xa \xfc\x57\xf3\xa5\xc3 COM executable for MS-DOS
+0x6 search/0xa \xfc\x57\xf3\xa4\xc3 COM executable for DOS
+>0x18 search/0x10 \x50\xa4\xff\xd5\x73 \b, aPack compressed
+0x3c string W\ Collis\0\0 COM executable for MS-DOS, Compack compressed
+# FIXME: missing diet .com compression
+
+# miscellaneous formats
+0 string/b LZ MS-DOS executable (built-in)
+#0 byte 0xf0 MS-DOS program library data
+#
+
+# AAF files:
+# <stuartc@rd.bbc.co.uk> Stuart Cunningham
+0 string/b \320\317\021\340\241\261\032\341AAFB\015\000OM\006\016\053\064\001\001\001\377 AAF legacy file using MS Structured Storage
+>30 byte 9 (512B sectors)
+>30 byte 12 (4kB sectors)
+0 string/b \320\317\021\340\241\261\032\341\001\002\001\015\000\002\000\000\006\016\053\064\003\002\001\001 AAF file using MS Structured Storage
+>30 byte 9 (512B sectors)
+>30 byte 12 (4kB sectors)
+
+# Popular applications
+2080 string Microsoft\ Word\ 6.0\ Document %s
+!:mime application/msword
+2080 string Documento\ Microsoft\ Word\ 6 Spanish Microsoft Word 6 document data
+!:mime application/msword
+# Pawel Wiecek <coven@i17linuxb.ists.pwr.wroc.pl> (for polish Word)
+2112 string MSWordDoc Microsoft Word document data
+!:mime application/msword
+#
+0 belong 0x31be0000 Microsoft Word Document
+!:mime application/msword
+#
+0 string/b PO^Q` Microsoft Word 6.0 Document
+!:mime application/msword
+#
+0 string/b \376\067\0\043 Microsoft Office Document
+!:mime application/msword
+0 string/b \333\245-\0\0\0 Microsoft Office Document
+!:mime application/msword
+512 string/b \354\245\301 Microsoft Word Document
+!:mime application/msword
+
+#
+0 string/b \xDB\xA5\x2D\x00 Microsoft WinWord 2.0 Document
+!:mime application/msword
+#
+2080 string Microsoft\ Excel\ 5.0\ Worksheet %s
+!:mime application/vnd.ms-excel
+#
+0 string/b \xDB\xA5\x2D\x00 Microsoft WinWord 2.0 Document
+!:mime application/msword
+
+2080 string Foglio\ di\ lavoro\ Microsoft\ Exce %s
+!:mime application/vnd.ms-excel
+#
+# Pawel Wiecek <coven@i17linuxb.ists.pwr.wroc.pl> (for polish Excel)
+2114 string Biff5 Microsoft Excel 5.0 Worksheet
+!:mime application/vnd.ms-excel
+# Italian MS-Excel
+2121 string Biff5 Microsoft Excel 5.0 Worksheet
+!:mime application/vnd.ms-excel
+0 string/b \x09\x04\x06\x00\x00\x00\x10\x00 Microsoft Excel Worksheet
+!:mime application/vnd.ms-excel
+#
+0 belong 0x00001a00 Lotus 1-2-3
+!:mime application/x-123
+>4 belong 0x00100400 wk3 document data
+>4 belong 0x02100400 wk4 document data
+>4 belong 0x07800100 fm3 or fmb document data
+>4 belong 0x07800000 fm3 or fmb document data
+#
+0 belong 0x00000200 Lotus 1-2-3
+!:mime application/x-123
+>4 belong 0x06040600 wk1 document data
+>4 belong 0x06800200 fmt document data
+0 string/b WordPro\0 Lotus WordPro
+!:mime application/vnd.lotus-wordpro
+0 string/b WordPro\r\373 Lotus WordPro
+!:mime application/vnd.lotus-wordpro
+
+
+# Summary: Script used by InstallScield to uninstall applications
+# Extension: .isu
+# Submitted by: unknown
+# Modified by (1): Abel Cheung <abelcheung@gmail.com> (replace useless entry)
+0 string \x71\xa8\x00\x00\x01\x02
+>12 string Stirling\ Technologies, InstallShield Uninstall Script
+
+# Winamp .avs
+#0 string Nullsoft\ AVS\ Preset\ \060\056\061\032 A plug in for Winamp ms-windows Freeware media player
+0 string/b Nullsoft\ AVS\ Preset\ Winamp plug in
+
+# Windows Metafont .WMF
+0 string/b \327\315\306\232 ms-windows metafont .wmf
+0 string/b \002\000\011\000 ms-windows metafont .wmf
+0 string/b \001\000\011\000 ms-windows metafont .wmf
+
+#tz3 files whatever that is (MS Works files)
+0 string/b \003\001\001\004\070\001\000\000 tz3 ms-works file
+0 string/b \003\002\001\004\070\001\000\000 tz3 ms-works file
+0 string/b \003\003\001\004\070\001\000\000 tz3 ms-works file
+
+# PGP sig files .sig
+#0 string \211\000\077\003\005\000\063\237\127 065 to \027\266\151\064\005\045\101\233\021\002 PGP sig
+0 string \211\000\077\003\005\000\063\237\127\065\027\266\151\064\005\045\101\233\021\002 PGP sig
+0 string \211\000\077\003\005\000\063\237\127\066\027\266\151\064\005\045\101\233\021\002 PGP sig
+0 string \211\000\077\003\005\000\063\237\127\067\027\266\151\064\005\045\101\233\021\002 PGP sig
+0 string \211\000\077\003\005\000\063\237\127\070\027\266\151\064\005\045\101\233\021\002 PGP sig
+0 string \211\000\077\003\005\000\063\237\127\071\027\266\151\064\005\045\101\233\021\002 PGP sig
+0 string \211\000\225\003\005\000\062\122\207\304\100\345\042 PGP sig
+
+# windows zips files .dmf
+0 string/b MDIF\032\000\010\000\000\000\372\046\100\175\001\000\001\036\001\000 MS Windows special zipped file
+
+
+#ico files
+0 string/b \102\101\050\000\000\000\056\000\000\000\000\000\000\000 Icon for MS Windows
+
+# Windows icons
+0 name ico-dir
+# not entirely accurate, the number of icons is part of the header
+>0 byte 1 - 1 icon
+>0 ubyte >1 - %d icons
+>2 byte 0 \b, 256x
+>2 byte !0 \b, %dx
+>3 byte 0 \b256
+>3 byte !0 \b%d
+>4 ubyte !0 \b, %d colors
+
+0 belong 0x00000100
+>9 byte 0
+>>0 byte x MS Windows icon resource
+!:mime image/x-icon
+>>4 use ico-dir
+>9 ubyte 0xff
+>>0 byte x MS Windows icon resource
+!:mime image/x-icon
+>>4 use ico-dir
+
+# Windows non-animated cursors
+0 name cur-dir
+# not entirely accurate, the number of icons is part of the header
+>0 byte 1 - 1 icon
+>0 ubyte >1 - %d icons
+>2 byte 0 \b, 256x
+>2 byte !0 \b, %dx
+>3 byte 0 \b256
+>3 byte !0 \b%d
+>6 uleshort x \b, hotspot @%dx
+>8 uleshort x \b%d
+
+0 belong 0x00000200
+>9 byte 0
+>>0 byte x MS Windows cursor resource
+!:mime image/x-cur
+>>4 use cur-dir
+>9 ubyte 0xff
+>>0 byte x MS Windows cursor resource
+!:mime image/x-cur
+>>4 use cur-dir
+
+# .chr files
+0 string/b PK\010\010BGI Borland font
+>4 string >\0 %s
+# then there is a copyright notice
+
+
+# .bgi files
+0 string/b pk\010\010BGI Borland device
+>4 string >\0 %s
+# then there is a copyright notice
+
+
+# Windows Recycle Bin record file (named INFO2)
+# By Abel Cheung (abelcheung AT gmail dot com)
+# Version 4 always has 280 bytes (0x118) per record, version 5 has 800 bytes
+# Since Vista uses another structure, INFO2 structure probably won't change
+# anymore. Detailed analysis in:
+# http://www.cybersecurityinstitute.biz/downloads/INFO2.pdf
+0 lelong 0x00000004
+>12 lelong 0x00000118 Windows Recycle Bin INFO2 file (Win98 or below)
+
+0 lelong 0x00000005
+>12 lelong 0x00000320 Windows Recycle Bin INFO2 file (Win2k - WinXP)
+
+
+##### put in Either Magic/font or Magic/news
+# Acroread or something files wrongly identified as G3 .pfm
+# these have the form \000 \001 any? \002 \000 \000
+# or \000 \001 any? \022 \000 \000
+0 belong&0xffff00ff 0x00010012 PFM data
+>4 string \000\000
+>6 string >\060 - %s
+
+0 belong&0xffff00ff 0x00010002 PFM data
+>4 string \000\000
+>6 string >\060 - %s
+#0 string \000\001 pfm?
+#>3 string \022\000\000Copyright\ yes
+#>3 string \002\000\000Copyright\ yes
+#>3 string >\0 oops, not a font file. Cancel that.
+#it clashes with ttf files so put it lower down.
+
+# From Doug Lee via a FreeBSD pr
+9 string GERBILDOC First Choice document
+9 string GERBILDB First Choice database
+9 string GERBILCLIP First Choice database
+0 string GERBIL First Choice device file
+9 string RABBITGRAPH RabbitGraph file
+0 string DCU1 Borland Delphi .DCU file
+0 string =!<spell> MKS Spell hash list (old format)
+0 string =!<spell2> MKS Spell hash list
+# Too simple - MPi
+#0 string AH Halo(TM) bitmapped font file
+0 lelong 0x08086b70 TurboC BGI file
+0 lelong 0x08084b50 TurboC Font file
+
+# Debian#712046: The magic below identifies "Delphi compiled form data".
+# An additional source of information is available at:
+# http://www.woodmann.com/fravia/dafix_t1.htm
+0 string TPF0
+>4 pstring >\0 Delphi compiled form '%s'
+
+# tests for DBase files moved, updated and merged to database
+
+0 string PMCC Windows 3.x .GRP file
+1 string RDC-meg MegaDots
+>8 byte >0x2F version %c
+>9 byte >0x2F \b.%c file
+0 lelong 0x4C
+>4 lelong 0x00021401 Windows shortcut file
+
+# .PIF files added by Joerg Jenderek from http://smsoft.ru/en/pifdoc.htm
+# only for windows versions equal or greater 3.0
+0x171 string MICROSOFT\ PIFEX\0 Windows Program Information File
+!:mime application/x-dosexec
+#>2 string >\0 \b, Title:%.30s
+>0x24 string >\0 \b for %.63s
+>0x65 string >\0 \b, directory=%.64s
+>0xA5 string >\0 \b, parameters=%.64s
+#>0x181 leshort x \b, offset %x
+#>0x183 leshort x \b, offsetdata %x
+#>0x185 leshort x \b, section length %x
+>0x187 search/0xB55 WINDOWS\ VMM\ 4.0\0
+>>&0x5e ubyte >0
+>>>&-1 string <PIFMGR.DLL \b, icon=%s
+#>>>&-1 string PIFMGR.DLL \b, icon=%s
+>>>&-1 string >PIFMGR.DLL \b, icon=%s
+>>&0xF0 ubyte >0
+>>>&-1 string <Terminal \b, font=%.32s
+#>>>&-1 string =Terminal \b, font=%.32s
+>>>&-1 string >Terminal \b, font=%.32s
+>>&0x110 ubyte >0
+>>>&-1 string <Lucida\ Console \b, TrueTypeFont=%.32s
+#>>>&-1 string =Lucida\ Console \b, TrueTypeFont=%.32s
+>>>&-1 string >Lucida\ Console \b, TrueTypeFont=%.32s
+#>0x187 search/0xB55 WINDOWS\ 286\ 3.0\0 \b, Windows 3.X standard mode-style
+#>0x187 search/0xB55 WINDOWS\ 386\ 3.0\0 \b, Windows 3.X enhanced mode-style
+>0x187 search/0xB55 WINDOWS\ NT\ \ 3.1\0 \b, Windows NT-style
+#>0x187 search/0xB55 WINDOWS\ NT\ \ 4.0\0 \b, Windows NT-style
+>0x187 search/0xB55 CONFIG\ \ SYS\ 4.0\0 \b +CONFIG.SYS
+#>>&06 string x \b:%s
+>0x187 search/0xB55 AUTOEXECBAT\ 4.0\0 \b +AUTOEXEC.BAT
+#>>&06 string x \b:%s
+
+# DOS EPS Binary File Header
+# From: Ed Sznyter <ews@Black.Market.NET>
+0 belong 0xC5D0D3C6 DOS EPS Binary File
+>4 long >0 Postscript starts at byte %d
+>>8 long >0 length %d
+>>>12 long >0 Metafile starts at byte %d
+>>>>16 long >0 length %d
+>>>20 long >0 TIFF starts at byte %d
+>>>>24 long >0 length %d
+
+# TNEF magic From "Joomy" <joomy@se-ed.net>
+# Microsoft Outlook's Transport Neutral Encapsulation Format (TNEF)
+0 leshort 0x223e9f78 TNEF
+!:mime application/vnd.ms-tnef
+
+# Norton Guide (.NG , .HLP) files added by Joerg Jenderek from source NG2HTML.C
+# of http://www.davep.org/norton-guides/ng2h-105.tgz
+# http://en.wikipedia.org/wiki/Norton_Guides
+0 string NG\0\001
+# only value 0x100 found at offset 2
+>2 ulelong 0x00000100 Norton Guide
+# Title[40]
+>>8 string >\0 "%-.40s"
+#>>6 uleshort x \b, MenuCount=%u
+# szCredits[5][66]
+>>48 string >\0 \b, %-.66s
+>>114 string >\0 %-.66s
+
+# 4DOS help (.HLP) files added by Joerg Jenderek from source TPHELP.PAS
+# of http://www.4dos.info/
+# pointer,HelpID[8]=4DHnnnmm
+0 ulelong 0x48443408 4DOS help file
+>4 string x \b, version %-4.4s
+
+# old binary Microsoft (.HLP) files added by Joerg Jenderek from http://file-extension.net/seeker/file_extension_hlp
+0 ulequad 0x3a000000024e4c MS Advisor help file
+
+# HtmlHelp files (.chm)
+0 string/b ITSF\003\000\000\000\x60\000\000\000\001\000\000\000 MS Windows HtmlHelp Data
+
+# GFA-BASIC (Wolfram Kleff)
+2 string/b GFA-BASIC3 GFA-BASIC 3 data
+
+#------------------------------------------------------------------------------
+# From Stuart Caie <kyzer@4u.net> (developer of cabextract)
+# Microsoft Cabinet files
+0 string/b MSCF\0\0\0\0 Microsoft Cabinet archive data
+!:mime application/vnd.ms-cab-compressed
+>8 lelong x \b, %u bytes
+>28 leshort 1 \b, 1 file
+>28 leshort >1 \b, %u files
+
+# InstallShield Cabinet files
+0 string/b ISc( InstallShield Cabinet archive data
+>5 byte&0xf0 =0x60 version 6,
+>5 byte&0xf0 !0x60 version 4/5,
+>(12.l+40) lelong x %u files
+
+# Windows CE package files
+0 string/b MSCE\0\0\0\0 Microsoft WinCE install header
+>20 lelong 0 \b, architecture-independent
+>20 lelong 103 \b, Hitachi SH3
+>20 lelong 104 \b, Hitachi SH4
+>20 lelong 0xA11 \b, StrongARM
+>20 lelong 4000 \b, MIPS R4000
+>20 lelong 10003 \b, Hitachi SH3
+>20 lelong 10004 \b, Hitachi SH3E
+>20 lelong 10005 \b, Hitachi SH4
+>20 lelong 70001 \b, ARM 7TDMI
+>52 leshort 1 \b, 1 file
+>52 leshort >1 \b, %u files
+>56 leshort 1 \b, 1 registry entry
+>56 leshort >1 \b, %u registry entries
+
+
+# Windows Enhanced Metafile (EMF)
+# See msdn.microsoft.com/archive/en-us/dnargdi/html/msdn_enhmeta.asp
+# for further information.
+0 ulelong 1
+>40 string \ EMF Windows Enhanced Metafile (EMF) image data
+>>44 ulelong x version 0x%x
+
+# from http://filext.com by Derek M Jones <derek@knosof.co.uk>
+# False positive with PPT (also currently this string is too long)
+#0 string/b \xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x3E\x00\x03\x00\xFE\xFF\x09\x00\x06 Microsoft Installer
+0 string/b \320\317\021\340\241\261\032\341 Microsoft Office Document
+#>48 byte 0x1B Excel Document
+#!:mime application/vnd.ms-excel
+>546 string bjbj Microsoft Word Document
+!:mime application/msword
+>546 string jbjb Microsoft Word Document
+!:mime application/msword
+
+0 string/b \224\246\056 Microsoft Word Document
+!:mime application/msword
+
+512 string R\0o\0o\0t\0\ \0E\0n\0t\0r\0y Microsoft Word Document
+!:mime application/msword
+
+# From: "Nelson A. de Oliveira" <naoliv@gmail.com>
+# Magic type for Dell's BIOS .hdr files
+# Dell's .hdr
+0 string/b $RBU
+>23 string Dell %s system BIOS
+>5 byte 2
+>>48 byte x version %d.
+>>49 byte x \b%d.
+>>50 byte x \b%d
+>5 byte <2
+>>48 string x version %.3s
+
+# Type: Microsoft DirectDraw Surface
+# URL: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/directx9_c/directx/graphics/reference/DDSFileReference/ddsfileformat.asp
+# From: Morten Hustveit <morten@debian.org>
+0 string/b DDS\040\174\000\000\000 Microsoft DirectDraw Surface (DDS),
+>16 lelong >0 %d x
+>12 lelong >0 %d,
+>84 string x %.4s
+
+# Type: Microsoft Document Imaging Format (.mdi)
+# URL: http://en.wikipedia.org/wiki/Microsoft_Document_Imaging_Format
+# From: Daniele Sempione <scrows@oziosi.org>
+0 short 0x5045 Microsoft Document Imaging Format
+
+# MS eBook format (.lit)
+0 string/b ITOLITLS Microsoft Reader eBook Data
+>8 lelong x \b, version %u
+!:mime application/x-ms-reader
+
+# Windows CE Binary Image Data Format
+# From: Dr. Jesus <j@hug.gs>
+0 string/b B000FF\n Windows Embedded CE binary image
+
+# Windows Imaging (WIM) Image
+0 string/b MSWIM\000\000\000 Windows imaging (WIM) image
+
+# The second byte of these signatures is a file version; I don't know what,
+# if anything, produced files with version numbers 0-2.
+# From: John Elliott <johne@seasip.demon.co.uk>
+0 string \xfc\x03\x00 Mallard BASIC program data (v1.11)
+0 string \xfc\x04\x00 Mallard BASIC program data (v1.29+)
+0 string \xfc\x03\x01 Mallard BASIC protected program data (v1.11)
+0 string \xfc\x04\x01 Mallard BASIC protected program data (v1.29+)
+
+0 string MIOPEN Mallard BASIC Jetsam data
+0 string Jetsam0 Mallard BASIC Jetsam index data
+
+
+#------------------------------------------------------------------------------
+# $File: msooxml,v 1.4 2014/01/06 18:16:24 rrt Exp $
+# msooxml: file(1) magic for Microsoft Office XML
+# From: Ralf Brown <ralf.brown@gmail.com>
+
+# .docx, .pptx, and .xlsx are XML plus other files inside a ZIP
+# archive. The first member file is normally "[Content_Types].xml".
+# but some libreoffice generated files put this later. Perhaps skip
+# the "[Content_Types].xml" test?
+# Since MSOOXML doesn't have anything like the uncompressed "mimetype"
+# file of ePub or OpenDocument, we'll have to scan for a filename
+# which can distinguish between the three types
+
+# start by checking for ZIP local file header signature
+0 string PK\003\004
+!:strength +10
+# make sure the first file is correct
+>0x1E regex \\[Content_Types\\]\\.xml|_rels/\\.rels
+# skip to the second local file header
+# since some documents include a 520-byte extra field following the file
+# header, we need to scan for the next header
+>>(18.l+49) search/2000 PK\003\004
+# now skip to the *third* local file header; again, we need to scan due to a
+# 520-byte extra field following the file header
+>>>&26 search/1000 PK\003\004
+# and check the subdirectory name to determine which type of OOXML
+# file we have. Correct the mimetype with the registered ones:
+# http://technet.microsoft.com/en-us/library/cc179224.aspx
+>>>>&26 string word/ Microsoft Word 2007+
+!:mime application/vnd.openxmlformats-officedocument.wordprocessingml.document
+>>>>&26 string ppt/ Microsoft PowerPoint 2007+
+!:mime application/vnd.openxmlformats-officedocument.presentationml.presentation
+>>>>&26 string xl/ Microsoft Excel 2007+
+!:mime application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
+>>>>&26 default x Microsoft OOXML
+
+#------------------------------------------------------------------------------
+# $File$
+# msvc: file(1) magic for msvc
+# "H. Nanosecond" <aldomel@ix.netcom.com>
+# Microsoft visual C
+#
+# I have version 1.0
+
+# .aps
+0 string HWB\000\377\001\000\000\000 Microsoft Visual C .APS file
+
+# .ide
+#too long 0 string \102\157\162\154\141\156\144\040\103\053\053\040\120\162\157\152\145\143\164\040\106\151\154\145\012\000\032\000\002\000\262\000\272\276\372\316 MSVC .ide
+0 string \102\157\162\154\141\156\144\040\103\053\053\040\120\162\157 MSVC .ide
+
+# .res
+0 string \000\000\000\000\040\000\000\000\377 MSVC .res
+0 string \377\003\000\377\001\000\020\020\350 MSVC .res
+0 string \377\003\000\377\001\000\060\020\350 MSVC .res
+
+#.lib
+0 string \360\015\000\000 Microsoft Visual C library
+0 string \360\075\000\000 Microsoft Visual C library
+0 string \360\175\000\000 Microsoft Visual C library
+
+#.pch
+0 string DTJPCH0\000\022\103\006\200 Microsoft Visual C .pch
+
+# .pdb
+# too long 0 string Microsoft\ C/C++\ program\ database\
+0 string Microsoft\ C/C++\ MSVC program database
+>18 string program\ database\
+>33 string >\0 ver %s
+
+#.sbr
+0 string \000\002\000\007\000 MSVC .sbr
+>5 string >\0 %s
+
+#.bsc
+0 string \002\000\002\001 MSVC .bsc
+
+#.wsp
+0 string 1.00\ .0000.0000\000\003 MSVC .wsp version 1.0000.0000
+# these seem to start with the version and contain menus
+
+#------------------------------------------------------------------------------
+# msx: file(1) magic for the MSX Home Computer
+# v1.1
+# Fabio R. Schmidlin <sd-snatcher@users.sourceforge.net>
+
+############## MSX Music file formats ##############
+
+# Gigamix MGSDRV music file
+0 string MGS MSX Gigamix MGSDRV3 music file,
+>6 ubeshort 0x0D0A
+>>3 byte x \bv%c
+>>4 byte x \b.%c
+>>5 byte x \b%c
+>>8 string >\0 \b, title: %s
+
+1 string mgs2\ MSX Gigamix MGSDRV2 music file
+>6 uleshort 0x80
+>>0x2E uleshort 0
+>>>0x30 string >\0 \b, title: %s
+
+# KSS music file
+0 string KSCC KSS music file v1.03
+>0xE byte 0
+>>0xF byte&0x02 0 \b, soundchips: AY-3-8910, SCC(+)
+>>0xF byte&0x02 2 \b, soundchip(s): SN76489
+>>>0xF byte&0x04 4 stereo
+>>0xF byte&0x01 1 \b, YM2413
+>>0xF byte&0x08 8 \b, Y8950
+
+0 string KSSX KSS music file v1.20
+>0xE byte&0xEF 0
+>>0xF byte&0x40 0x00 \b, 60Hz
+>>0xF byte&0x40 0x40 \b, 50Hz
+>>0xF byte&0x02 0 \b, soundchips: AY-3-8910, SCC(+)
+>>0xF byte&0x02 0x02 \b, soundchips: SN76489
+>>>0xF byte&0x04 0x04 stereo
+>>0xF byte&0x01 0x01 \b,
+>>>0xF byte&0x18 0x00 \bYM2413
+>>>0xF byte&0x18 0x08 \bYM2413, Y8950
+>>>0xF byte&0x18 0x18 \bYM2413+Y8950 pseudostereo
+>>0xF byte&0x18 0x10 \b, Majyutsushi DAC
+
+# Moonblaster for Moonsound
+0 string MBMS
+>4 byte 0x10 MSX Moonblaster for MoonSound music
+
+# Music Player K-kaz
+0 string MPK MSX Music Player K-kaz song
+>6 ubeshort 0x0D0A
+>>3 byte x v%c
+>>4 byte x \b.%c
+>>5 byte x \b%c
+
+# I don't know why these don't work
+#0 search/0xFFFF \r\n.FM9
+#>0 search/0xFFFF \r\n#FORMAT MSX Music Player K-kaz source MML file
+#0 search/0xFFFF \r\nFM1\ \=
+#>0 search/0xFFFF \r\nPSG1\=
+#>>0 search/0xFFFF \r\nSCC1\= MSX MuSiCa MML source file
+
+# OPX Music file
+0x35 beshort 0x0d0a
+>0x7B beshort 0x0d0a
+>>0x7D byte 0x1a
+>>>0x87 uleshort 0 MSX OPX Music file
+>>>>0x86 byte 0 v1.5
+>>>>>0 string >\32 \b, title: %s
+>>>>0x86 byte 1 v2.4
+>>>>>0 string >\32 \b, title: %s
+
+# SCMD music file
+0x8B string SCMD
+>0xCE uleshort 0 MSX SCMD Music file
+#>>-2 uleshort 0x6a71 ; The file must end with this value. How to code this here?
+>>0x8F string >\0 \b, title: %s
+
+0 search/0xFFFF \r\n@title
+>&0 search/0xFFFF \r\n@m=[ MSX SCMD source MML file
+
+
+############## MSX image file formats ##############
+
+# MSX raw VRAM dump
+0 ubyte 0xFE
+>1 uleshort 0
+>>5 uleshort 0
+>>>3 uleshort 0x37FF MSX SC2/GRP raw image
+>>>3 uleshort 0x6A00 MSX Graph Saurus SR5 raw image
+>>>3 uleshort >0x769E
+>>>>3 uleshort <0x8000 MSX GE5/GE6 raw image
+>>>>>3 uleshort 0x7FFF \b, with sprite patterns
+>>>3 uleshort 0xD3FF MSX screen 7-12 raw image
+>>>3 uleshort 0xD400 MSX Graph Saurus SR7/SR8/SRS raw image
+
+# Graph Saurus compressed images
+0 ubyte 0xFD
+>1 uleshort 0
+>>5 uleshort 0
+>>>3 uleshort >0x013D MSX Graph Saurus compressed image
+
+# Maki-chan Graphic format
+0 string MAKI02\ \ Maki-chan image,
+>8 byte x system ID: %c
+>9 byte x \b%c
+>10 byte x \b%c
+>11 byte x \b%c,
+>13 search/0x200 \x1A
+# >>&3 ubyte 0 , video mode: PC-98 400 lines, 16 analog colors
+# >>&3 ubyte 1 , video mode: MSX SC7, 16 analog colors
+# >>&3 ubyte 2 , video mode: VM-98 400 lines, 8 analog colors
+# >>&3 ubyte 3 , video mode: PC-88 analog, 200 lines, 8 analog colors
+# >>&3 ubyte 4 , video mode: 400 lines, 16 digital colors
+# >>&3 ubyte 5 , video mode: 200 lines, 16 digital colors
+# >>&3 ubyte 6 , video mode: old PC-98 digital 400 lines, 8 colors
+# >>&3 ubyte 7 , video mode: PC-88 400 lines, 8 digital colors
+>>&8 uleshort+1 x %dx
+>>&10 uleshort+1 x \b%d,
+>>&3 ubyte&0x82 0x80 256 colors
+>>&3 ubyte&0x82 0x00 16 colors
+>>&3 ubyte&0x82 0x01 8 colors
+>>&3 ubyte&0x04 4 digital
+>>&3 ubyte&0x04 0 analog
+>>&3 ubyte&0x01 1 \b, 2:1 dot aspect ratio
+
+# Japanese PIC file
+0 string PIC\x1A
+>4 lelong 0 Japanese PIC image file
+
+# MSX G9B image file
+0 string G9B
+>1 uleshort 11
+>>3 uleshort >10
+>>>5 ubyte >0 MSX G9B image, depth=%d
+>>>>8 uleshort x \b, %dx
+>>>>10 uleshort x \b%d
+>>>>5 ubyte <9
+>>>>>6 ubyte 0
+>>>>>>7 ubyte x \b, codec=%d RGB color palettes
+>>>>>6 ubyte 64 \b, codec=RGB fixed color
+>>>>>6 ubyte 128 \b, codec=YJK
+>>>>>6 ubyte 192 \b, codec=YUV
+>>>>5 ubyte >8 codec=RGB fixed color
+>>>>12 ubyte 0 \b, raw
+>>>>12 ubyte 1 \b, bitbuster compression
+
+############## Other MSX file formats ##############
+
+# MSX ROMs
+0 string AB
+>2 uleshort 0x0010 MSX ROM
+>>2 uleshort x \b, init=0x%4x
+>>4 uleshort >0 \b, stat=0x%4x
+>>6 uleshort >0 \b, dev=0x%4x
+>>8 uleshort >0 \b, bas=0x%4x
+>2 uleshort 0x4010 MSX ROM
+>>2 uleshort x \b, init=0x%04x
+>>4 uleshort >0 \b, stat=0x%04x
+>>6 uleshort >0 \b, dev=0x%04x
+>>8 uleshort >0 \b, bas=0x%04x
+>2 uleshort 0x8010 MSX ROM
+>>2 uleshort x \b, init=0x%04x
+>>4 uleshort >0 \b, stat=0x%04x
+>>6 uleshort >0 \b, dev=0x%04x
+>>8 uleshort >0 \b, bas=0x%04x
+
+0 string AB
+#>2 string 5JSuperLAYDOCK MSX Super Laydock ROM
+#>3 string @HYDLIDE3MSX MSX Hydlide-3 ROM
+#>3 string @3\x80IA862 Golvellius MSX1 ROM
+>2 uleshort >10
+>>10 string \0\0\0\0\0\0 MSX ROM
+>>>0x10 string YZ\0\0\0\0 Konami Game Master 2 MSX ROM
+>>>0x10 string CD \b, Konami RC-
+>>>>0x12 ubyte x \b%d
+>>>>0x13 ubyte/16 x \b%d
+>>>>0x13 ubyte&0xF x \b%d
+>>>0x10 string EF \b, Konami RC-
+>>>>0x12 ubyte x \b%d
+>>>>0x13 ubyte/16 x \b%d
+>>>>0x13 ubyte&0xF x \b%d
+>>>2 uleshort x \b, init=0x%04x
+>>>4 uleshort >0 \b, stat=0x%04x
+>>>6 uleshort >0 \b, dev=0x%04x
+>>>8 uleshort >0 \b, bas=0x%04x
+>2 uleshort 0
+>>4 uleshort 0
+>>>6 uleshort 0
+>>>>8 uleshort >0 MSX BASIC program in ROM, bas=0x%04x
+
+0x4000 string AB
+>0x4002 uleshort >0x4010
+>>0x400A string \0\0\0\0\0\0 MSX MegaROM with nonstandard page order
+>>0x4002 uleshort x \b, init=0x%04x
+>>0x4004 uleshort >0 \b, stat=0x%04x
+>>0x4006 uleshort >0 \b, dev=0x%04x
+>>0x4008 uleshort >0 \b, bas=0x%04x
+
+0x8000 string AB
+>0x8002 uleshort >0x4010
+>>0x800A string \0\0\0\0\0\0 MSX MegaROM with nonstandard page order
+>>0x8002 uleshort x \b, init=0x%04x
+>>0x8004 uleshort >0 \b, stat=0x%04x
+>>0x8006 uleshort >0 \b, dev=0x%04x
+>>0x8008 uleshort >0 \b, bas=0x%04x
+
+
+0x3C000 string AB
+>0x3C008 string \0\0\0\0\0\0\0\0 MSX MegaROM with nonstandard page order
+>>0x3C002 uleshort x \b, init=0x%04x
+>>0x3C004 uleshort >0 \b, stat=0x%04x
+>>0x3C006 uleshort >0 \b, dev=0x%04x
+>>0x3C008 uleshort >0 \b, bas=0x%04x
+
+# MSX BIN file
+#0 byte 0xFE
+#>1 uleshort >0x8000
+#>>3 uleshort >0x8004
+#>>>5 uleshort >0x8000 MSX BIN file
+
+# MSX-BASIC file
+0 byte 0xFF
+>3 uleshort 0x000A
+>>1 uleshort >0x8000 MSX-BASIC program
+
+# MSX .CAS file
+0 string \x1F\xA6\xDE\xBA\xCC\x13\x7D\x74 MSX cassette archive
+
+# Mega-Assembler file
+0 byte 0xFE
+>1 uleshort 0x0001
+>>5 uleshort 0xffff
+>>>6 byte 0x0A MSX Mega-Assembler source
+
+# Execrom Patchfile
+0 string ExecROM\ patchfile\x1A MSX ExecROM patchfile
+>0x12 ubyte/16 x v%d
+>0x12 ubyte&0xF x \b.%d
+>0x13 ubyte x \b, contains %d patches
+
+# Konami's King's Valley-2 custom stage (ELG file)
+4 uleshort 0x0900
+>0xF byte 1
+>>0x14 byte 0
+>>>0x1E string \ \ \
+>>>>0x23 byte 1
+>>>>>0x25 byte 0
+>>>>>>0x15 string >\x30
+>>>>>>>0x15 string <\x5A Konami King's Valley-2 custom stage, title: "%-8.8s"
+>>>>>>>>0x1D byte <32 \b, theme: %d
+
+# Metal Gear 1 savegame
+#0x4F string \x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF
+#>>0x60 string \xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF
+#>>>0x7B string \0x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00 Metal Gear 1 savegame
+
+# ------------------------------------------------------------------------
+# $File$
+# mup: file(1) magic for Mup (Music Publisher) input file.
+#
+# From: Abel Cheung <abel (@) oaka.org>
+#
+# NOTE: This header is mainly proposed in the Arkkra mailing list,
+# and is not a mandatory header because of old mup input file
+# compatibility. Noteedit also use mup format, but is not forcing
+# user to use any header as well.
+#
+0 search/1 //!Mup Mup music publication program input text
+>6 string -Arkkra (Arkkra)
+>>13 string -
+>>>16 string .
+>>>>14 string x \b, need V%.4s
+>>>15 string .
+>>>>14 string x \b, need V%.3s
+>6 string -
+>>9 string .
+>>>7 string x \b, need V%.4s
+>>8 string .
+>>>7 string x \b, need V%.3s
+#------------------------------------------------------------------------------
+# $File: cracklib,v 1.7 2009/09/19 16:28:08 christos Exp $
+# music: file (1) magic for music formats
+
+# BWW format used by Bagpipe Music Writer Gold by Robert MacNeil Musicworks
+# and Bagpipe Writer by Doug Wickstrom
+#
+0 string Bagpipe Bagpipe
+>8 string Reader Reader
+>>15 string >\0 (version %.3s)
+>8 string Music\ Writer Music Writer
+>>20 string :
+>>>21 string >\0 (version %.3s)
+>>21 string Gold Gold
+>>>25 string :
+>>>>26 string >\0 (version %.3s)
+
+
+#-----------------------------------------------------------------------------
+# $File: natinst,v 1.5 2013/02/06 14:18:52 christos Exp $
+# natinst: file(1) magic for National Instruments Code Files
+
+#
+# From <egamez@fcfm.buap.mx> Enrique Gamez-Flores
+# version 1
+# Many formats still missing, we use, for the moment LabVIEW
+# We guess VXI format file. VISA, LabWindowsCVI, BridgeVIEW, etc, are missing
+#
+0 string RSRC National Instruments,
+# Check if it's a LabVIEW File
+>8 string LV LabVIEW File,
+# Check which kind of file it is
+>>10 string SB Code Resource File, data
+>>10 string IN Virtual Instrument Program, data
+>>10 string AR VI Library, data
+# This is for Menu Libraries
+>8 string LMNULBVW Portable File Names, data
+# This is for General Resources
+>8 string rsc Resources File, data
+# This is for VXI Package
+0 string VMAP National Instruments, VXI File, data
+
+#------------------------------------------------------------------------------
+# $File: ncr,v 1.7 2009/09/19 16:28:11 christos Exp $
+# ncr: file(1) magic for NCR Tower objects
+#
+# contributed by
+# Michael R. Wayne *** TMC & Associates *** INTERNET: wayne@ford-vax.arpa
+# uucp: {philabs | pyramid} !fmsrl7!wayne OR wayne@fmsrl7.UUCP
+#
+0 beshort 000610 Tower/XP rel 2 object
+>12 belong >0 not stripped
+>20 beshort 0407 executable
+>20 beshort 0410 pure executable
+>22 beshort >0 - version %d
+0 beshort 000615 Tower/XP rel 2 object
+>12 belong >0 not stripped
+>20 beshort 0407 executable
+>20 beshort 0410 pure executable
+>22 beshort >0 - version %d
+0 beshort 000620 Tower/XP rel 3 object
+>12 belong >0 not stripped
+>20 beshort 0407 executable
+>20 beshort 0410 pure executable
+>22 beshort >0 - version %d
+0 beshort 000625 Tower/XP rel 3 object
+>12 belong >0 not stripped
+>20 beshort 0407 executable
+>20 beshort 0410 pure executable
+>22 beshort >0 - version %d
+0 beshort 000630 Tower32/600/400 68020 object
+>12 belong >0 not stripped
+>20 beshort 0407 executable
+>20 beshort 0410 pure executable
+>22 beshort >0 - version %d
+0 beshort 000640 Tower32/800 68020
+>18 beshort &020000 w/68881 object
+>18 beshort &040000 compatible object
+>18 beshort &060000 object
+>20 beshort 0407 executable
+>20 beshort 0413 pure executable
+>12 belong >0 not stripped
+>22 beshort >0 - version %d
+0 beshort 000645 Tower32/800 68010
+>18 beshort &040000 compatible object
+>18 beshort &060000 object
+>20 beshort 0407 executable
+>20 beshort 0413 pure executable
+>12 belong >0 not stripped
+>22 beshort >0 - version %d
+
+#------------------------------------------------------------
+# $File: java,v 1.12 2009/09/19 16:28:10 christos Exp $
+
+# From: Mikhail Gusarov <dottedmag@dottedmag.net>
+# NekoVM (http://nekovm.org/) bytecode
+0 string NEKO NekoVM bytecode
+>4 lelong x (%d global symbols,
+>8 lelong x %d global fields,
+>12 lelong x %d bytecode ops)
+!:mime application/x-nekovm-bytecode
+
+
+#------------------------------------------------------------------------------
+# $File: netbsd,v 1.21 2014/03/29 15:40:34 christos Exp $
+# netbsd: file(1) magic for NetBSD objects
+#
+# All new-style magic numbers are in network byte order.
+# The old-style magic numbers are indistinguishable from the same magic
+# numbers used in other systems, and are handled, for all those systems,
+# in aout.
+#
+
+0 belong&0377777777 041400413 a.out NetBSD/i386 demand paged
+>0 byte &0x80
+>>20 lelong <4096 shared library
+>>20 lelong =4096 dynamically linked executable
+>>20 lelong >4096 dynamically linked executable
+>0 byte ^0x80 executable
+>16 lelong >0 not stripped
+0 belong&0377777777 041400410 a.out NetBSD/i386 pure
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80 executable
+>16 lelong >0 not stripped
+0 belong&0377777777 041400407 a.out NetBSD/i386
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80
+>>0 byte &0x40 position independent
+>>20 lelong !0 executable
+>>20 lelong =0 object file
+>16 lelong >0 not stripped
+0 belong&0377777777 041400507 a.out NetBSD/i386 core
+>12 string >\0 from '%s'
+>32 lelong !0 (signal %d)
+
+0 belong&0377777777 041600413 a.out NetBSD/m68k demand paged
+>0 byte &0x80
+>>20 belong <8192 shared library
+>>20 belong =8192 dynamically linked executable
+>>20 belong >8192 dynamically linked executable
+>0 byte ^0x80 executable
+>16 belong >0 not stripped
+0 belong&0377777777 041600410 a.out NetBSD/m68k pure
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80 executable
+>16 belong >0 not stripped
+0 belong&0377777777 041600407 a.out NetBSD/m68k
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80
+>>0 byte &0x40 position independent
+>>20 belong !0 executable
+>>20 belong =0 object file
+>16 belong >0 not stripped
+0 belong&0377777777 041600507 a.out NetBSD/m68k core
+>12 string >\0 from '%s'
+>32 belong !0 (signal %d)
+
+0 belong&0377777777 042000413 a.out NetBSD/m68k4k demand paged
+>0 byte &0x80
+>>20 belong <4096 shared library
+>>20 belong =4096 dynamically linked executable
+>>20 belong >4096 dynamically linked executable
+>0 byte ^0x80 executable
+>16 belong >0 not stripped
+0 belong&0377777777 042000410 a.out NetBSD/m68k4k pure
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80 executable
+>16 belong >0 not stripped
+0 belong&0377777777 042000407 a.out NetBSD/m68k4k
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80
+>>0 byte &0x40 position independent
+>>20 belong !0 executable
+>>20 belong =0 object file
+>16 belong >0 not stripped
+0 belong&0377777777 042000507 a.out NetBSD/m68k4k core
+>12 string >\0 from '%s'
+>32 belong !0 (signal %d)
+
+0 belong&0377777777 042200413 a.out NetBSD/ns32532 demand paged
+>0 byte &0x80
+>>20 lelong <4096 shared library
+>>20 lelong =4096 dynamically linked executable
+>>20 lelong >4096 dynamically linked executable
+>0 byte ^0x80 executable
+>16 lelong >0 not stripped
+0 belong&0377777777 042200410 a.out NetBSD/ns32532 pure
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80 executable
+>16 lelong >0 not stripped
+0 belong&0377777777 042200407 a.out NetBSD/ns32532
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80
+>>0 byte &0x40 position independent
+>>20 lelong !0 executable
+>>20 lelong =0 object file
+>16 lelong >0 not stripped
+0 belong&0377777777 042200507 a.out NetBSD/ns32532 core
+>12 string >\0 from '%s'
+>32 lelong !0 (signal %d)
+
+0 belong&0377777777 045200507 a.out NetBSD/powerpc core
+>12 string >\0 from '%s'
+
+0 belong&0377777777 042400413 a.out NetBSD/SPARC demand paged
+>0 byte &0x80
+>>20 belong <8192 shared library
+>>20 belong =8192 dynamically linked executable
+>>20 belong >8192 dynamically linked executable
+>0 byte ^0x80 executable
+>16 belong >0 not stripped
+0 belong&0377777777 042400410 a.out NetBSD/SPARC pure
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80 executable
+>16 belong >0 not stripped
+0 belong&0377777777 042400407 a.out NetBSD/SPARC
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80
+>>0 byte &0x40 position independent
+>>20 belong !0 executable
+>>20 belong =0 object file
+>16 belong >0 not stripped
+0 belong&0377777777 042400507 a.out NetBSD/SPARC core
+>12 string >\0 from '%s'
+>32 belong !0 (signal %d)
+
+0 belong&0377777777 042600413 a.out NetBSD/pmax demand paged
+>0 byte &0x80
+>>20 lelong <4096 shared library
+>>20 lelong =4096 dynamically linked executable
+>>20 lelong >4096 dynamically linked executable
+>0 byte ^0x80 executable
+>16 lelong >0 not stripped
+0 belong&0377777777 042600410 a.out NetBSD/pmax pure
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80 executable
+>16 lelong >0 not stripped
+0 belong&0377777777 042600407 a.out NetBSD/pmax
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80
+>>0 byte &0x40 position independent
+>>20 lelong !0 executable
+>>20 lelong =0 object file
+>16 lelong >0 not stripped
+0 belong&0377777777 042600507 a.out NetBSD/pmax core
+>12 string >\0 from '%s'
+>32 lelong !0 (signal %d)
+
+0 belong&0377777777 043000413 a.out NetBSD/vax 1k demand paged
+>0 byte &0x80
+>>20 lelong <4096 shared library
+>>20 lelong =4096 dynamically linked executable
+>>20 lelong >4096 dynamically linked executable
+>0 byte ^0x80 executable
+>16 lelong >0 not stripped
+0 belong&0377777777 043000410 a.out NetBSD/vax 1k pure
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80 executable
+>16 lelong >0 not stripped
+0 belong&0377777777 043000407 a.out NetBSD/vax 1k
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80
+>>0 byte &0x40 position independent
+>>20 lelong !0 executable
+>>20 lelong =0 object file
+>16 lelong >0 not stripped
+0 belong&0377777777 043000507 a.out NetBSD/vax 1k core
+>12 string >\0 from '%s'
+>32 lelong !0 (signal %d)
+
+0 belong&0377777777 045400413 a.out NetBSD/vax 4k demand paged
+>0 byte &0x80
+>>20 lelong <4096 shared library
+>>20 lelong =4096 dynamically linked executable
+>>20 lelong >4096 dynamically linked executable
+>0 byte ^0x80 executable
+>16 lelong >0 not stripped
+0 belong&0377777777 045400410 a.out NetBSD/vax 4k pure
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80 executable
+>16 lelong >0 not stripped
+0 belong&0377777777 045400407 a.out NetBSD/vax 4k
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80
+>>0 byte &0x40 position independent
+>>20 lelong !0 executable
+>>20 lelong =0 object file
+>16 lelong >0 not stripped
+0 belong&0377777777 045400507 a.out NetBSD/vax 4k core
+>12 string >\0 from '%s'
+>32 lelong !0 (signal %d)
+
+# NetBSD/alpha does not support (and has never supported) a.out objects,
+# so no rules are provided for them. NetBSD/alpha ELF objects are
+# dealt with in "elf".
+0 lelong 0x00070185 ECOFF NetBSD/alpha binary
+>10 leshort 0x0001 not stripped
+>10 leshort 0x0000 stripped
+0 belong&0377777777 043200507 a.out NetBSD/alpha core
+>12 string >\0 from '%s'
+>32 lelong !0 (signal %d)
+
+0 belong&0377777777 043400413 a.out NetBSD/mips demand paged
+>0 byte &0x80
+>>20 belong <8192 shared library
+>>20 belong =8192 dynamically linked executable
+>>20 belong >8192 dynamically linked executable
+>0 byte ^0x80 executable
+>16 belong >0 not stripped
+0 belong&0377777777 043400410 a.out NetBSD/mips pure
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80 executable
+>16 belong >0 not stripped
+0 belong&0377777777 043400407 a.out NetBSD/mips
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80
+>>0 byte &0x40 position independent
+>>20 belong !0 executable
+>>20 belong =0 object file
+>16 belong >0 not stripped
+0 belong&0377777777 043400507 a.out NetBSD/mips core
+>12 string >\0 from '%s'
+>32 belong !0 (signal %d)
+
+0 belong&0377777777 043600413 a.out NetBSD/arm32 demand paged
+>0 byte &0x80
+>>20 lelong <4096 shared library
+>>20 lelong =4096 dynamically linked executable
+>>20 lelong >4096 dynamically linked executable
+>0 byte ^0x80 executable
+>16 lelong >0 not stripped
+0 belong&0377777777 043600410 a.out NetBSD/arm32 pure
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80 executable
+>16 lelong >0 not stripped
+0 belong&0377777777 043600407 a.out NetBSD/arm32
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80
+>>0 byte &0x40 position independent
+>>20 lelong !0 executable
+>>20 lelong =0 object file
+>16 lelong >0 not stripped
+# NetBSD/arm26 has always used ELF objects, but it shares a core file
+# format with NetBSD/arm32.
+0 belong&0377777777 043600507 a.out NetBSD/arm core
+>12 string >\0 from '%s'
+>32 lelong !0 (signal %d)
+
+# Kernel core dump format
+0 belong&0x0000ffff 0x00008fca NetBSD kernel core file
+>0 belong&0x03ff0000 0x00000000 \b, Unknown
+>0 belong&0x03ff0000 0x00010000 \b, sun 68010/68020
+>0 belong&0x03ff0000 0x00020000 \b, sun 68020
+>0 belong&0x03ff0000 0x00640000 \b, 386 PC
+>0 belong&0x03ff0000 0x00860000 \b, i386 BSD
+>0 belong&0x03ff0000 0x00870000 \b, m68k BSD (8K pages)
+>0 belong&0x03ff0000 0x00880000 \b, m68k BSD (4K pages)
+>0 belong&0x03ff0000 0x00890000 \b, ns32532 BSD
+>0 belong&0x03ff0000 0x008a0000 \b, SPARC/32 BSD
+>0 belong&0x03ff0000 0x008b0000 \b, pmax BSD
+>0 belong&0x03ff0000 0x008c0000 \b, vax BSD (1K pages)
+>0 belong&0x03ff0000 0x008d0000 \b, alpha BSD
+>0 belong&0x03ff0000 0x008e0000 \b, mips BSD (Big Endian)
+>0 belong&0x03ff0000 0x008f0000 \b, arm6 BSD
+>0 belong&0x03ff0000 0x00900000 \b, m68k BSD (2K pages)
+>0 belong&0x03ff0000 0x00910000 \b, sh3 BSD
+>0 belong&0x03ff0000 0x00950000 \b, ppc BSD (Big Endian)
+>0 belong&0x03ff0000 0x00960000 \b, vax BSD (4K pages)
+>0 belong&0x03ff0000 0x00970000 \b, mips1 BSD
+>0 belong&0x03ff0000 0x00980000 \b, mips2 BSD
+>0 belong&0x03ff0000 0x00990000 \b, m88k BSD
+>0 belong&0x03ff0000 0x00920000 \b, parisc BSD
+>0 belong&0x03ff0000 0x009b0000 \b, sh5/64 BSD
+>0 belong&0x03ff0000 0x009c0000 \b, SPARC/64 BSD
+>0 belong&0x03ff0000 0x009d0000 \b, amd64 BSD
+>0 belong&0x03ff0000 0x009e0000 \b, sh5/32 BSD
+>0 belong&0x03ff0000 0x009f0000 \b, ia64 BSD
+>0 belong&0x03ff0000 0x00b70000 \b, aarch64 BSD
+>0 belong&0x03ff0000 0x00b80000 \b, or1k BSD
+>0 belong&0x03ff0000 0x00b90000 \b, Risk-V BSD
+>0 belong&0x03ff0000 0x00c80000 \b, hp200 BSD
+>0 belong&0x03ff0000 0x012c0000 \b, hp300 BSD
+>0 belong&0x03ff0000 0x020b0000 \b, hp800 HP-UX
+>0 belong&0x03ff0000 0x020c0000 \b, hp200/hp300 HP-UX
+>0 belong&0xfc000000 0x04000000 \b, CPU
+>0 belong&0xfc000000 0x08000000 \b, DATA
+>0 belong&0xfc000000 0x10000000 \b, STACK
+>4 leshort x \b, (headersize = %d
+>6 leshort x \b, segmentsize = %d
+>6 lelong x \b, segments = %d)
+
+#------------------------------------------------------------------------------
+# $File$
+# netscape: file(1) magic for Netscape files
+# "H. Nanosecond" <aldomel@ix.netcom.com>
+# version 3 and 4 I think
+#
+
+# Netscape Address book .nab
+0 string \000\017\102\104\000\000\000\000\000\000\001\000\000\000\000\002\000\000\000\002\000\000\004\000 Netscape Address book
+
+# Netscape Communicator address book
+0 string \000\017\102\111 Netscape Communicator address book
+
+# .snm Caches
+0 string #\ Netscape\ folder\ cache Netscape folder cache
+0 string \000\036\204\220\000 Netscape folder cache
+# .n2p
+# Net 2 Phone
+#0 string 123\130\071\066\061\071\071\071\060\070\061\060\061\063\060
+0 string SX961999 Net2phone
+
+#
+#This is files ending in .art, FIXME add more rules
+0 string JG\004\016\0\0\0\0 ART
+
+#------------------------------------------------------------------------------
+# $File$
+# netware: file(1) magic for NetWare Loadable Modules (NLMs)
+# From: Mads Martin Joergensen <mmj@suse.de>
+
+0 string NetWare\ Loadable\ Module NetWare Loadable Module
+
+#------------------------------------------------------------------------------
+# $File$
+# news: file(1) magic for SunOS NeWS fonts (not "news" as in "netnews")
+#
+0 string StartFontMetrics ASCII font metrics
+0 string StartFont ASCII font bits
+0 belong 0x137A2944 NeWS bitmap font
+0 belong 0x137A2947 NeWS font family
+0 belong 0x137A2950 scalable OpenFont binary
+0 belong 0x137A2951 encrypted scalable OpenFont binary
+8 belong 0x137A2B45 X11/NeWS bitmap font
+8 belong 0x137A2B48 X11/NeWS font family
+
+#------------------------------------------------------------------------------
+# $File: nitpicker,v 1.5 2014/04/28 12:04:50 christos Exp $
+# nitpicker: file(1) magic for Flowfiles.
+# From: Christian Jachmann <C.Jachmann@gmx.net> http://www.nitpicker.de
+0 string NPFF NItpicker Flow File
+>4 byte x V%d.
+>5 byte x %d
+>6 bedate x started: %s
+>10 bedate x stopped: %s
+>14 belong x Bytes: %u
+>18 belong x Bytes1: %u
+>22 belong x Flows: %u
+>26 belong x Pkts: %u
+
+#------------------------------------------------------------------------------
+# $File: oasis,v 1.1 2011/03/15 02:09:38 christos Exp $
+# OASIS
+# Summary: OASIS stream file
+# Long description: Open Artwork System Interchange Standard
+# File extension: .oas
+# Full name: Ben Cowley (bcowley@broadcom.com)
+# Philip Dixon (pdixon@broadcom.com)
+# Reference: http://www.wrcad.com/oasis/oasis-3626-042303-draft.pdf
+# (see page 3)
+0 string %SEMI-OASIS\r\n OASIS Stream file
+
+#------------------------------------------------------------------------------
+# $File: ocaml,v 1.4 2009/09/19 16:28:11 christos Exp $
+# ocaml: file(1) magic for Objective Caml files.
+0 string Caml1999 OCaml
+>8 string X exec file
+>8 string I interface file (.cmi)
+>8 string O object file (.cmo)
+>8 string A library file (.cma)
+>8 string Y native object file (.cmx)
+>8 string Z native library file (.cmxa)
+>8 string M abstract syntax tree implementation file
+>8 string N abstract syntax tree interface file
+>9 string >\0 (Version %3.3s)
+
+#------------------------------------------------------------------------------
+# $File$
+# octave binary data file(1) magic, from Dirk Eddelbuettel <edd@debian.org>
+0 string Octave-1-L Octave binary data (little endian)
+0 string Octave-1-B Octave binary data (big endian)
+
+#------------------------------------------------------------------------------
+# $File$
+# Microsoft OLE 2 Compound Documents : file(1) magic for Microsoft Structured
+# storage (http://en.wikipedia.org/wiki/Structured_Storage)
+# Additional tests for OLE 2 Compound Documents should be under this recipe.
+
+0 string \320\317\021\340\241\261\032\341 OLE 2 Compound Document
+# - Microstation V8 DGN files (www.bentley.com)
+# Last update on 10/23/2006 by Lester Hightower
+> 0x480 string D\000g\000n\000~\000H : Microstation V8 DGN
+# - Visio documents
+# Last update on 10/23/2006 by Lester Hightower
+> 0x480 string V\000i\000s\000i\000o\000D\000o\000c : Visio Document
+
+#------------------------------------------------------------------------------
+# $File$
+# olf: file(1) magic for OLF executables
+#
+# We have to check the byte order flag to see what byte order all the
+# other stuff in the header is in.
+#
+# MIPS R3000 may also be for MIPS R2000.
+# What're the correct byte orders for the nCUBE and the Fujitsu VPP500?
+#
+# Created by Erik Theisen <etheisen@openbsd.org>
+# Based on elf from Daniel Quinlan <quinlan@yggdrasil.com>
+0 string \177OLF OLF
+>4 byte 0 invalid class
+>4 byte 1 32-bit
+>4 byte 2 64-bit
+>7 byte 0 invalid os
+>7 byte 1 OpenBSD
+>7 byte 2 NetBSD
+>7 byte 3 FreeBSD
+>7 byte 4 4.4BSD
+>7 byte 5 Linux
+>7 byte 6 SVR4
+>7 byte 7 esix
+>7 byte 8 Solaris
+>7 byte 9 Irix
+>7 byte 10 SCO
+>7 byte 11 Dell
+>7 byte 12 NCR
+>5 byte 0 invalid byte order
+>5 byte 1 LSB
+>>16 leshort 0 no file type,
+>>16 leshort 1 relocatable,
+>>16 leshort 2 executable,
+>>16 leshort 3 shared object,
+# Core handling from Peter Tobias <tobias@server.et-inf.fho-emden.de>
+# corrections by Christian 'Dr. Disk' Hechelmann <drdisk@ds9.au.s.shuttle.de>
+>>16 leshort 4 core file
+>>>(0x38+0xcc) string >\0 of '%s'
+>>>(0x38+0x10) lelong >0 (signal %d),
+>>16 leshort &0xff00 processor-specific,
+>>18 leshort 0 no machine,
+>>18 leshort 1 AT&T WE32100 - invalid byte order,
+>>18 leshort 2 SPARC - invalid byte order,
+>>18 leshort 3 Intel 80386,
+>>18 leshort 4 Motorola 68000 - invalid byte order,
+>>18 leshort 5 Motorola 88000 - invalid byte order,
+>>18 leshort 6 Intel 80486,
+>>18 leshort 7 Intel 80860,
+>>18 leshort 8 MIPS R3000_BE - invalid byte order,
+>>18 leshort 9 Amdahl - invalid byte order,
+>>18 leshort 10 MIPS R3000_LE,
+>>18 leshort 11 RS6000 - invalid byte order,
+>>18 leshort 15 PA-RISC - invalid byte order,
+>>18 leshort 16 nCUBE,
+>>18 leshort 17 VPP500,
+>>18 leshort 18 SPARC32PLUS,
+>>18 leshort 20 PowerPC,
+>>18 leshort 0x9026 Alpha,
+>>20 lelong 0 invalid version
+>>20 lelong 1 version 1
+>>36 lelong 1 MathCoPro/FPU/MAU Required
+>8 string >\0 (%s)
+>5 byte 2 MSB
+>>16 beshort 0 no file type,
+>>16 beshort 1 relocatable,
+>>16 beshort 2 executable,
+>>16 beshort 3 shared object,
+>>16 beshort 4 core file,
+>>>(0x38+0xcc) string >\0 of '%s'
+>>>(0x38+0x10) belong >0 (signal %d),
+>>16 beshort &0xff00 processor-specific,
+>>18 beshort 0 no machine,
+>>18 beshort 1 AT&T WE32100,
+>>18 beshort 2 SPARC,
+>>18 beshort 3 Intel 80386 - invalid byte order,
+>>18 beshort 4 Motorola 68000,
+>>18 beshort 5 Motorola 88000,
+>>18 beshort 6 Intel 80486 - invalid byte order,
+>>18 beshort 7 Intel 80860,
+>>18 beshort 8 MIPS R3000_BE,
+>>18 beshort 9 Amdahl,
+>>18 beshort 10 MIPS R3000_LE - invalid byte order,
+>>18 beshort 11 RS6000,
+>>18 beshort 15 PA-RISC,
+>>18 beshort 16 nCUBE,
+>>18 beshort 17 VPP500,
+>>18 beshort 18 SPARC32PLUS,
+>>18 beshort 20 PowerPC or cisco 4500,
+>>18 beshort 21 cisco 7500,
+>>18 beshort 24 cisco SVIP,
+>>18 beshort 25 cisco 7200,
+>>18 beshort 36 cisco 12000,
+>>18 beshort 0x9026 Alpha,
+>>20 belong 0 invalid version
+>>20 belong 1 version 1
+>>36 belong 1 MathCoPro/FPU/MAU Required
+
+#------------------------------------------------------------------------------
+# $File: os2,v 1.7 2009/09/19 16:28:11 christos Exp $
+# os2: file(1) magic for OS/2 files
+#
+
+# Provided 1998/08/22 by
+# David Mediavilla <davidme.news@REMOVEIFNOTSPAMusa.net>
+1 search/1 InternetShortcut MS Windows 95 Internet shortcut text
+>17 search/100 URL= (URL=<
+>>&0 string x \b%s>)
+
+# OS/2 URL objects
+# Provided 1998/08/22 by
+# David Mediavilla <davidme.news@REMOVEIFNOTSPAMusa.net>
+#0 string http: OS/2 URL object text
+#>5 string >\ (WWW) <http:%s>
+#0 string mailto: OS/2 URL object text
+#>7 string >\ (email) <%s>
+#0 string news: OS/2 URL object text
+#>5 string >\ (Usenet) <%s>
+#0 string ftp: OS/2 URL object text
+#>4 string >\ (FTP) <ftp:%s>
+#0 string file: OS/2 URL object text
+#>5 string >\ (Local file) <%s>
+
+# >>>>> OS/2 INF/HLP <<<<< (source: Daniel Dissett ddissett@netcom.com)
+# Carl Hauser (chauser.parc@xerox.com) and
+# Marcus Groeber (marcusg@ph-cip.uni-koeln.de)
+# list the following header format in inf02a.doc:
+#
+# int16 ID; // ID magic word (5348h = "HS")
+# int8 unknown1; // unknown purpose, could be third letter of ID
+# int8 flags; // probably a flag word...
+# // bit 0: set if INF style file
+# // bit 4: set if HLP style file
+# // patching this byte allows reading HLP files
+# // using the VIEW command, while help files
+# // seem to work with INF settings here as well.
+# int16 hdrsize; // total size of header
+# int16 unknown2; // unknown purpose
+#
+0 string HSP\x01\x9b\x00 OS/2 INF
+>107 string >0 (%s)
+0 string HSP\x10\x9b\x00 OS/2 HLP
+>107 string >0 (%s)
+
+# OS/2 INI (this is a guess)
+0 string \xff\xff\xff\xff\x14\0\0\0 OS/2 INI
+
+#------------------------------------------------------------------------------
+# $File$
+# os400: file(1) magic for IBM OS/400 files
+#
+# IBM OS/400 (i5/OS) Save file (SAVF) - gerardo.cacciari@gmail.com
+# In spite of its quite variable format (due to internal memory page
+# length differences between CISC and RISC versions of the OS) the
+# SAVF structure hasn't suitable offsets to identify the catalog
+# header in the first descriptor where there are some useful infos,
+# so we must search in a somewhat large area for a particular string
+# that represents the EBCDIC encoding of 'QSRDSSPC' (save/restore
+# descriptor space) preceded by a two byte constant.
+#
+1090 search/7393 \x19\xDB\xD8\xE2\xD9\xC4\xE2\xE2\xD7\xC3 IBM OS/400 save file data
+>&212 byte 0x01 \b, created with SAVOBJ
+>&212 byte 0x02 \b, created with SAVLIB
+>&212 byte 0x07 \b, created with SAVCFG
+>&212 byte 0x08 \b, created with SAVSECDTA
+>&212 byte 0x0A \b, created with SAVSECDTA
+>&212 byte 0x0B \b, created with SAVDLO
+>&212 byte 0x0D \b, created with SAVLICPGM
+>&212 byte 0x11 \b, created with SAVCHGOBJ
+>&213 byte 0x44 \b, at least V5R4 to open
+>&213 byte 0x43 \b, at least V5R3 to open
+>&213 byte 0x42 \b, at least V5R2 to open
+>&213 byte 0x41 \b, at least V5R1 to open
+>&213 byte 0x40 \b, at least V4R5 to open
+>&213 byte 0x3F \b, at least V4R4 to open
+>&213 byte 0x3E \b, at least V4R3 to open
+>&213 byte 0x3C \b, at least V4R2 to open
+>&213 byte 0x3D \b, at least V4R1M4 to open
+>&213 byte 0x3B \b, at least V4R1 to open
+>&213 byte 0x3A \b, at least V3R7 to open
+>&213 byte 0x35 \b, at least V3R6 to open
+>&213 byte 0x36 \b, at least V3R2 to open
+>&213 byte 0x34 \b, at least V3R1 to open
+>&213 byte 0x31 \b, at least V3R0M5 to open
+>&213 byte 0x30 \b, at least V2R3 to open
+
+#------------------------------------------------------------------------------
+# $File: os9,v 1.6 2009/09/19 16:28:11 christos Exp $
+#
+# Copyright (c) 1996 Ignatios Souvatzis. 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 ``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 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.
+#
+#
+#
+# OS9/6809 module descriptions:
+#
+0 beshort 0x87CD OS9/6809 module:
+#
+>6 byte&0x0f 0x00 non-executable
+>6 byte&0x0f 0x01 machine language
+>6 byte&0x0f 0x02 BASIC I-code
+>6 byte&0x0f 0x03 Pascal P-code
+>6 byte&0x0f 0x04 C I-code
+>6 byte&0x0f 0x05 COBOL I-code
+>6 byte&0x0f 0x06 Fortran I-code
+#
+>6 byte&0xf0 0x10 program executable
+>6 byte&0xf0 0x20 subroutine
+>6 byte&0xf0 0x30 multi-module
+>6 byte&0xf0 0x40 data module
+#
+>6 byte&0xf0 0xC0 system module
+>6 byte&0xf0 0xD0 file manager
+>6 byte&0xf0 0xE0 device driver
+>6 byte&0xf0 0xF0 device descriptor
+#
+# OS9/m68k stuff (to be continued)
+#
+0 beshort 0x4AFC OS9/68K module:
+#
+# attr
+>0x14 byte&0x80 0x80 re-entrant
+>0x14 byte&0x40 0x40 ghost
+>0x14 byte&0x20 0x20 system-state
+#
+# lang:
+#
+>0x13 byte 1 machine language
+>0x13 byte 2 BASIC I-code
+>0x13 byte 3 Pascal P-code
+>0x13 byte 4 C I-code
+>0x13 byte 5 COBOL I-code
+>0x13 byte 6 Fortran I-code
+#
+#
+# type:
+#
+>0x12 byte 1 program executable
+>0x12 byte 2 subroutine
+>0x12 byte 3 multi-module
+>0x12 byte 4 data module
+>0x12 byte 11 trap library
+>0x12 byte 12 system module
+>0x12 byte 13 file manager
+>0x12 byte 14 device driver
+>0x12 byte 15 device descriptor
+
+#------------------------------------------------------------------------------
+# $File$
+#
+# Mach magic number info
+#
+0 long 0xefbe OSF/Rose object
+# I386 magic number info
+#
+0 short 0565 i386 COFF object
+
+#------------------------------------------------------------------------------
+# $File: palm,v 1.12 2014/03/28 19:11:40 christos Exp $
+# palm: file(1) magic for PalmOS {.prc,.pdb}: applications, docfiles, and hacks
+#
+# Brian Lalor <blalor@hcirisc.cs.binghamton.edu>
+
+# These are weak, byte 59 is not guaranteed to be 0 and there are
+# 8 character identifiers at byte 60, one I found for appl is BIGb.
+# What are the possibilities and where is this documented?
+
+# The common header format for PalmOS .pdb/.prc files is
+# {
+# char name[ 32 ];
+# Word attributes;
+# Word version;
+# DWord creationDate;
+# DWord modificationDate;
+# DWord lastBackupDate;
+# DWord modificationNumber;
+# DWord appInfoID;
+# DWord sortInfoID;
+# char type[4];
+# char creator[4];
+# DWord uniqueIDSeed;
+# RecordListType recordList;
+# };
+#
+# Datestamps are unsigned seconds since the MacOS epoch (Jan 1, 1904),
+# or Unix/POSIX time + 2082844800.
+
+0 name aportisdoc
+# date is supposed to be big-endian seconds since 1 Jan 1904, but many
+# files contain the timestamp in little-endian or a completely
+# nonsensical value...
+#>36 bedate-2082844800 >0 \b, created %s
+# compression: 1=uncomp, 2=orig, 0x4448=HuffDic
+>(78.L) beshort =1 \b, uncompressed
+# compressed
+>(78.L) beshort >1
+>>(78.L+4) belong x \b, %d bytes uncompressed
+
+# appl
+#60 string appl PalmOS application
+#>0 string >\0 "%s"
+
+# HACK
+#60 string HACK HackMaster hack
+#>0 string >\0 "%s"
+
+# iSiloX e-book
+60 string SDocSilX iSiloX E-book
+>0 string >\0 "%s"
+
+# Mobipocket (www.mobipocket.com), donated by Carl Witty
+# expanded by Ralf Brown
+60 string BOOKMOBI Mobipocket E-book
+# MobiPocket stores a full title, pointed at by the belong at offset
+# 0x54 in its header at (78.L), with length given by the belong at
+# offset 0x58.
+# there's no guarantee that the title string is null-terminated, but
+# we currently can't specify a variable-length string where the length
+# field is not at the start of the string; in practice, the data
+# following the string always seems to start with a zero byte
+>(78.L) belong x
+>>&(&0x50.L-4) string >\0 "%s"
+>0 use aportisdoc
+>>(78.L+0x68) belong >0 \b, version %d
+>>(78.L+0x1C) belong !0 \b, codepage %d
+>>(78.L+0x0C) beshort >0 \b, encrypted (type %d)
+
+# AportisDoc/PalmDOC
+60 string TEXtREAd AportisDoc/PalmDOC E-book
+>0 string >\0 "%s"
+>0 use aportisdoc
+
+# Variety of PalmOS document types
+# Michael-John Turner <mj@debian.org>
+# Thanks to Hasan Umit Ezerce <humit@tr-net.net.tr> for his DocType
+60 string BVokBDIC BDicty PalmOS document
+>0 string >\0 "%s"
+60 string DB99DBOS DB PalmOS document
+>0 string >\0 "%s"
+60 string vIMGView FireViewer/ImageViewer PalmOS document
+>0 string >\0 "%s"
+60 string PmDBPmDB HanDBase PalmOS document
+>0 string >\0 "%s"
+60 string InfoINDB InfoView PalmOS document
+>0 string >\0 "%s"
+60 string ToGoToGo iSilo PalmOS document
+>0 string >\0 "%s"
+60 string JfDbJBas JFile PalmOS document
+>0 string >\0 "%s"
+60 string JfDbJFil JFile Pro PalmOS document
+>0 string >\0 "%s"
+60 string DATALSdb List PalmOS document
+>0 string >\0 "%s"
+60 string Mdb1Mdb1 MobileDB PalmOS document
+>0 string >\0 "%s"
+60 string PNRdPPrs PeanutPress PalmOS document
+>0 string >\0 "%s"
+60 string DataPlkr Plucker PalmOS document
+>0 string >\0 "%s"
+60 string DataSprd QuickSheet PalmOS document
+>0 string >\0 "%s"
+60 string SM01SMem SuperMemo PalmOS document
+>0 string >\0 "%s"
+60 string TEXtTlDc TealDoc PalmOS document
+>0 string >\0 "%s"
+60 string InfoTlIf TealInfo PalmOS document
+>0 string >\0 "%s"
+60 string DataTlMl TealMeal PalmOS document
+>0 string >\0 "%s"
+60 string DataTlPt TealPaint PalmOS document
+>0 string >\0 "%s"
+60 string dataTDBP ThinkDB PalmOS document
+>0 string >\0 "%s"
+60 string TdatTide Tides PalmOS document
+>0 string >\0 "%s"
+60 string ToRaTRPW TomeRaider PalmOS document
+>0 string >\0 "%s"
+
+# A GutenPalm zTXT etext for use on Palm Pilots (http://gutenpalm.sf.net)
+# For version 1.xx zTXTs, outputs version and numbers of bookmarks and
+# annotations.
+# For other versions, just outputs version.
+#
+60 string zTXT A GutenPalm zTXT e-book
+>0 string >\0 "%s"
+>(0x4E.L) byte 0
+>>(0x4E.L+1) byte x (v0.%02d)
+>(0x4E.L) byte 1
+>>(0x4E.L+1) byte x (v1.%02d)
+>>>(0x4E.L+10) beshort >0
+>>>>(0x4E.L+10) beshort <2 - 1 bookmark
+>>>>(0x4E.L+10) beshort >1 - %d bookmarks
+>>>(0x4E.L+14) beshort >0
+>>>>(0x4E.L+14) beshort <2 - 1 annotation
+>>>>(0x4E.L+14) beshort >1 - %d annotations
+>(0x4E.L) byte >1 (v%d.
+>>(0x4E.L+1) byte x %02d)
+
+# Palm OS .prc file types
+60 string libr
+# flags, only bit 0 or bit 6
+# http://en.wikipedia.org/wiki/PRC_%28Palm_OS%29
+# http://web.mit.edu/tytso/www/pilot/prc-format.html
+>0x20 beshort&0xffbe 0
+>>0 string >\0 Palm OS dynamic library data "%s"
+60 string ptch Palm OS operating system patch data
+>0 string >\0 "%s"
+
+# Mobipocket (www.mobipocket.com), donated by Carl Witty
+60 string BOOKMOBI Mobipocket E-book
+>0 string >\0 "%s"
+
+#------------------------------------------------------------------------------
+# $File$
+#
+# Parix COFF executables
+# From: Ignatios Souvatzis <ignatios@cs.uni-bonn.de>
+#
+0 beshort&0xfff 0xACE PARIX
+>0 byte&0xf0 0x80 T800
+>0 byte&0xf0 0x90 T9000
+>19 byte&0x02 0x02 executable
+>19 byte&0x02 0x00 object
+>19 byte&0x0c 0x00 not stripped
+#------------------------------------------------------------------------------
+# $File$
+# parrot: file(1) magic for Parrot Virtual Machine
+# URL: http://www.lua.org/
+# From: Lubomir Rintel <lkundrak@v3.sk>
+
+# Compiled Parrot byte code
+0 string \376PBC\r\n\032\n Parrot bytecode
+>64 byte x %d.
+>72 byte x \b%d,
+>8 byte >0 %d byte words,
+>16 byte 0 little-endian,
+>16 byte 1 big-endian,
+>32 byte 0 IEEE-754 8 byte double floats,
+>32 byte 1 x86 12 byte long double floats,
+>32 byte 2 IEEE-754 16 byte long double floats,
+>32 byte 3 MIPS 16 byte long double floats,
+>32 byte 4 AIX 16 byte long double floats,
+>32 byte 5 4-byte floats,
+>40 byte x Parrot %d.
+>48 byte x \b%d.
+>56 byte x \b%d
+#------------------------------------------------------------------------------
+# $File: pascal,v 1.1 2011/12/08 12:12:46 rrt Exp $
+# pascal: file(1) magic for Pascal source
+#
+0 search/8192 (input, Pascal source text
+!:mime text/x-pascal
+#0 regex \^program Pascal source text
+#!:mime text/x-pascal
+#0 regex \^record Pascal source text
+#!:mime text/x-pascal
+
+#------------------------------------------------------------------------------
+# $File: cubemap,v 1.1 2012/06/06 13:03:20 christos Exp $
+# file(1) magic(5) data for OpenStreetMap
+
+# OpenStreetMap Protocolbuffer Binary Format (.osm.pbf)
+# http://wiki.openstreetmap.org/wiki/PBF_Format
+# From: Markus Heidelberg <markus.heidelberg@web.de>
+0 belong 0x0000000D
+>4 beshort 0x0A09
+>>6 string OSMHeader OpenStreetMap Protocolbuffer Binary Format
+
+#------------------------------------------------------------------------------
+# $File$
+# pbm: file(1) magic for Portable Bitmap files
+#
+# XXX - byte order?
+#
+0 short 0x2a17 "compact bitmap" format (Poskanzer)
+
+#------------------------------------------------------------------------------
+# $File: pdf,v 1.7 2013/08/22 07:47:26 christos Exp $
+# pdf: file(1) magic for Portable Document Format
+#
+
+0 string %PDF- PDF document
+!:mime application/pdf
+>5 byte x \b, version %c
+>7 byte x \b.%c
+
+0 string \012%PDF- PDF document
+!:mime application/pdf
+>6 byte x \b, version %c
+>8 byte x \b.%c
+
+# From: Nick Schmalenberger <nick@schmalenberger.us>
+# Forms Data Format
+0 string %FDF- FDF document
+!:mime application/vnd.fdf
+>5 byte x \b, version %c
+>7 byte x \b.%c
+
+#------------------------------------------------------------------------------
+# $File: pdp,v 1.9 2013/04/19 20:11:43 christos Exp $
+# pdp: file(1) magic for PDP-11 executable/object and APL workspace
+#
+0 lelong 0101555 PDP-11 single precision APL workspace
+0 lelong 0101554 PDP-11 double precision APL workspace
+#
+# PDP-11 a.out
+#
+0 leshort 0407 PDP-11 executable
+>8 leshort >0 not stripped
+>15 byte >0 - version %d
+
+# updated by Joerg Jenderek at Mar 2013
+# GRR: line below too general as it catches also Windows precompiled setup information *.PNF
+0 leshort 0401
+# skip *.PNF with WinDirPathOffset 58h
+>68 ulelong !0x00000058 PDP-11 UNIX/RT ldp
+# skip *.PNF with high byte of InfVersionDatumCount zero
+#>>15 byte !0 PDP-11 UNIX/RT ldp
+0 leshort 0405 PDP-11 old overlay
+
+0 leshort 0410 PDP-11 pure executable
+>8 leshort >0 not stripped
+>15 byte >0 - version %d
+
+0 leshort 0411 PDP-11 separate I&D executable
+>8 leshort >0 not stripped
+>15 byte >0 - version %d
+
+0 leshort 0437 PDP-11 kernel overlay
+
+# These last three are derived from 2.11BSD file(1)
+0 leshort 0413 PDP-11 demand-paged pure executable
+>8 leshort >0 not stripped
+
+0 leshort 0430 PDP-11 overlaid pure executable
+>8 leshort >0 not stripped
+
+0 leshort 0431 PDP-11 overlaid separate executable
+>8 leshort >0 not stripped
+#------------------------------------------------------------------------------
+# $File: perl,v 1.22 2014/04/28 12:04:35 christos Exp $
+# perl: file(1) magic for Larry Wall's perl language.
+#
+# The `eval' lines recognizes an outrageously clever hack.
+# Keith Waclena <keith@cerberus.uchicago.edu>
+# Send additions to <perl5-porters@perl.org>
+0 search/1 eval\ "exec\ /bin/perl Perl script text
+!:mime text/x-perl
+0 search/1 eval\ "exec\ /usr/bin/perl Perl script text
+!:mime text/x-perl
+0 search/1 eval\ "exec\ /usr/local/bin/perl Perl script text
+!:mime text/x-perl
+0 search/1 eval\ '(exit\ $?0)'\ &&\ eval\ 'exec Perl script text
+!:mime text/x-perl
+0 search/1 #!/usr/bin/env\ perl Perl script text executable
+!:mime text/x-perl
+0 search/1 #!\ /usr/bin/env\ perl Perl script text executable
+!:mime text/x-perl
+0 search/1 #!
+>0 regex \^#!.*/bin/perl([[:space:]].*)*$ Perl script text executable
+!:mime text/x-perl
+
+# by Dmitry V. Levin and Alexey Tourbin
+# check the first line
+0 search/1 package
+>0 regex \^package[\ \t]+[0-9A-Za-z_:]+\ *; Perl5 module source text
+!:strength + 10
+# not 'p', check other lines
+0 search/1 !p
+>0 regex \^package[\ \t]+[0-9A-Za-z_:]+\ *;
+>>0 regex \^1\ *;|\^(use|sub|my)\ .*[(;{=] Perl5 module source text
+!:strength + 10
+
+# Perl POD documents
+# From: Tom Hukins <tom@eborcom.com>
+0 search/1/W \=pod\n Perl POD document text
+0 search/1/W \n\=pod\n Perl POD document text
+0 search/1/W \=head1\ Perl POD document text
+0 search/1/W \n\=head1\ Perl POD document text
+0 search/1/W \=head2\ Perl POD document text
+0 search/1/W \n\=head2\ Perl POD document text
+0 search/1/W \=encoding\ Perl POD document text
+0 search/1/W \n\=encoding\ Perl POD document text
+
+
+# Perl Storable data files.
+0 string perl-store perl Storable (v0.6) data
+>4 byte >0 (net-order %d)
+>>4 byte &01 (network-ordered)
+>>4 byte =3 (major 1)
+>>4 byte =2 (major 1)
+
+0 string pst0 perl Storable (v0.7) data
+>4 byte >0
+>>4 byte &01 (network-ordered)
+>>4 byte =5 (major 2)
+>>4 byte =4 (major 2)
+>>5 byte >0 (minor %d)
+
+# This is Debian #742949 by Zefram <zefram@fysh.org>:
+# -----------------------------------------------------------
+# The Perl module Hash::SharedMem
+# <https://metacpan.org/release/Hash-SharedMem> defines a file format
+# for a key/value store. Details of the file format are in the "DESIGN"
+# file in the module distribution. Magic:
+0 bequad =0xa58afd185cbf5af7 Hash::SharedMem master file, big-endian
+>8 bequad <0x1000000
+>>15 byte >2 \b, line size 2^%d byte
+>>14 byte >2 \b, page size 2^%d byte
+>>13 byte &1
+>>>13 byte >1 \b, max fanout %d
+0 lequad =0xa58afd185cbf5af7 Hash::SharedMem master file, little-endian
+>8 lequad <0x1000000
+>>8 byte >2 \b, line size 2^%d byte
+>>9 byte >2 \b, page size 2^%d byte
+>>10 byte &1
+>>>10 byte >1 \b, max fanout %d
+0 bequad =0xc693dac5ed5e47c2 Hash::SharedMem data file, big-endian
+>8 bequad <0x1000000
+>>15 byte >2 \b, line size 2^%d byte
+>>14 byte >2 \b, page size 2^%d byte
+>>13 byte &1
+>>>13 byte >1 \b, max fanout %d
+0 lequad =0xc693dac5ed5e47c2 Hash::SharedMem data file, little-endian
+>8 lequad <0x1000000
+>>8 byte >2 \b, line size 2^%d byte
+>>9 byte >2 \b, page size 2^%d byte
+>>10 byte &1
+>>>10 byte >1 \b, max fanout %d
+
+#------------------------------------------------------------------------------
+# $File: matroska,v 1.8 2013/02/08 17:25:16 christos Exp $
+# pgf: file(1) magic for Progressive Graphics File (PGF)
+#
+# <http://www.libpgf.org/uploads/media/PGF_Details_01.pdf>
+# 2013 by Philipp Hahn <pmhahn debian org>
+0 string PGF Progressive Graphics image data,
+!:mime image/x-pgf
+>3 string 2 version %s,
+>3 string 4 version %s,
+>3 string 5 version %s,
+>3 string 6 version %s,
+# PGFPreHeader
+#>>4 lelong x header size %d,
+# PGFHeader
+>>8 lelong x %d x
+>>12 lelong x %d,
+>>16 byte x %d levels,
+>>17 byte x compression level %d,
+>>18 byte x %d bpp,
+>>19 byte x %d channels,
+>>20 clear x
+>>20 byte 0 bitmap,
+>>20 byte 1 gray scale,
+>>20 byte 2 indexed color,
+>>20 byte 3 RGB color,
+>>20 byte 4 CYMK color,
+>>20 byte 5 HSL color,
+>>20 byte 6 HSB color,
+>>20 byte 7 multi-channel,
+>>20 byte 8 duo tone,
+>>20 byte 9 LAB color,
+>>20 byte 10 gray scale 16,
+>>20 byte 11 RGB color 48,
+>>20 byte 12 LAB color 48,
+>>20 byte 13 CYMK color 64,
+>>20 byte 14 deep multi-channel,
+>>20 byte 15 duo tone 16,
+>>20 byte 17 RGBA color,
+>>20 byte 18 gray scale 32,
+>>20 byte 19 RGB color 12,
+>>20 byte 20 RGB color 16,
+>>20 byte 255 unknown format,
+>>20 default x format
+>>>20 byte x \b %d,
+>>21 byte x %d bpc
+# PGFPostHeader
+# Level-Sizes
+#>>(4.l+4) lelong x level 0 size: %d
+#>>(4.l+8) lelong x level 1 size: %d
+#>>(4.l+12) lelong x level 2 size: %d
+
+#------------------------------------------------------------------------------
+# $File: pgp,v 1.10 2014/10/14 16:50:37 christos Exp $
+# pgp: file(1) magic for Pretty Good Privacy
+# see http://lists.gnupg.org/pipermail/gnupg-devel/1999-September/016052.html
+#
+0 beshort 0x9900 PGP key public ring
+!:mime application/x-pgp-keyring
+0 beshort 0x9501 PGP key security ring
+!:mime application/x-pgp-keyring
+0 beshort 0x9500 PGP key security ring
+!:mime application/x-pgp-keyring
+0 beshort 0xa600 PGP encrypted data
+#!:mime application/pgp-encrypted
+#0 string -----BEGIN\040PGP text/PGP armored data
+!:mime text/PGP # encoding: armored data
+#>15 string PUBLIC\040KEY\040BLOCK- public key block
+#>15 string MESSAGE- message
+#>15 string SIGNED\040MESSAGE- signed message
+#>15 string PGP\040SIGNATURE- signature
+
+2 string ---BEGIN\ PGP\ PUBLIC\ KEY\ BLOCK- PGP public key block
+!:mime application/pgp-keys
+>10 search/100 \n\n
+>>&0 use pgp
+0 string -----BEGIN\040PGP\40MESSAGE- PGP message
+!:mime application/pgp
+>10 search/100 \n\n
+>>&0 use pgp
+0 string -----BEGIN\040PGP\40SIGNATURE- PGP signature
+!:mime application/pgp-signature
+>10 search/100 \n\n
+>>&0 use pgp
+
+# Decode the type of the packet based on it's base64 encoding.
+# Idea from Mark Martinec
+# The specification is in RFC 4880, section 4.2 and 4.3:
+# http://tools.ietf.org/html/rfc4880#section-4.2
+
+0 name pgp
+>0 byte 0x67 Reserved (old)
+>0 byte 0x68 Public-Key Encrypted Session Key (old)
+>0 byte 0x69 Signature (old)
+>0 byte 0x6a Symmetric-Key Encrypted Session Key (old)
+>0 byte 0x6b One-Pass Signature (old)
+>0 byte 0x6c Secret-Key (old)
+>0 byte 0x6d Public-Key (old)
+>0 byte 0x6e Secret-Subkey (old)
+>0 byte 0x6f Compressed Data (old)
+>0 byte 0x70 Symmetrically Encrypted Data (old)
+>0 byte 0x71 Marker (old)
+>0 byte 0x72 Literal Data (old)
+>0 byte 0x73 Trust (old)
+>0 byte 0x74 User ID (old)
+>0 byte 0x75 Public-Subkey (old)
+>0 byte 0x76 Unused (old)
+>0 byte 0x77
+>>1 byte&0xc0 0x00 Reserved
+>>1 byte&0xc0 0x40 Public-Key Encrypted Session Key
+>>1 byte&0xc0 0x80 Signature
+>>1 byte&0xc0 0xc0 Symmetric-Key Encrypted Session Key
+>0 byte 0x78
+>>1 byte&0xc0 0x00 One-Pass Signature
+>>1 byte&0xc0 0x40 Secret-Key
+>>1 byte&0xc0 0x80 Public-Key
+>>1 byte&0xc0 0xc0 Secret-Subkey
+>0 byte 0x79
+>>1 byte&0xc0 0x00 Compressed Data
+>>1 byte&0xc0 0x40 Symmetrically Encrypted Data
+>>1 byte&0xc0 0x80 Marker
+>>1 byte&0xc0 0xc0 Literal Data
+>0 byte 0x7a
+>>1 byte&0xc0 0x00 Trust
+>>1 byte&0xc0 0x40 User ID
+>>1 byte&0xc0 0x80 Public-Subkey
+>>1 byte&0xc0 0xc0 Unused [z%x]
+>0 byte 0x30
+>>1 byte&0xc0 0x00 Unused [0%x]
+>>1 byte&0xc0 0x40 User Attribute
+>>1 byte&0xc0 0x80 Sym. Encrypted and Integrity Protected Data
+>>1 byte&0xc0 0xc0 Modification Detection Code
+
+# magic signatures to detect PGP crypto material (from stef)
+# detects and extracts metadata from:
+# - symmetric encrypted packet header
+# - RSA (e=65537) secret (sub-)keys
+
+# 1024b RSA encrypted data
+
+0 string \x84\x8c\x03 PGP RSA encrypted session key -
+>3 lelong x keyid: %X
+>7 lelong x %X
+>11 byte 0x01 RSA (Encrypt or Sign) 1024b
+>11 byte 0x02 RSA Encrypt-Only 1024b
+>12 string \x04\x00
+>12 string \x03\xff
+>12 string \x03\xfe
+>12 string \x03\xfd
+>12 string \x03\xfc
+>12 string \x03\xfb
+>12 string \x03\xfa
+>12 string \x03\xf9
+>142 byte 0xd2 .
+
+# 2048b RSA encrypted data
+
+0 string \x85\x01\x0c\x03 PGP RSA encrypted session key -
+>4 lelong x keyid: %X
+>8 lelong x %X
+>12 byte 0x01 RSA (Encrypt or Sign) 2048b
+>12 byte 0x02 RSA Encrypt-Only 2048b
+>13 string \x08\x00
+>13 string \x07\xff
+>13 string \x07\xfe
+>13 string \x07\xfd
+>13 string \x07\xfc
+>13 string \x07\xfb
+>13 string \x07\xfa
+>13 string \x07\xf9
+>271 byte 0xd2 .
+
+# 3072b RSA encrypted data
+
+0 string \x85\x01\x8c\x03 PGP RSA encrypted session key -
+>4 lelong x keyid: %X
+>8 lelong x %X
+>12 byte 0x01 RSA (Encrypt or Sign) 3072b
+>12 byte 0x02 RSA Encrypt-Only 3072b
+>13 string \x0c\x00
+>13 string \x0b\xff
+>13 string \x0b\xfe
+>13 string \x0b\xfd
+>13 string \x0b\xfc
+>13 string \x0b\xfb
+>13 string \x0b\xfa
+>13 string \x0b\xf9
+>399 byte 0xd2 .
+
+# 3072b RSA encrypted data
+
+0 string \x85\x02\x0c\x03 PGP RSA encrypted session key -
+>4 lelong x keyid: %X
+>8 lelong x %X
+>12 byte 0x01 RSA (Encrypt or Sign) 4096b
+>12 byte 0x02 RSA Encrypt-Only 4096b
+>13 string \x10\x00
+>13 string \x0f\xff
+>13 string \x0f\xfe
+>13 string \x0f\xfd
+>13 string \x0f\xfc
+>13 string \x0f\xfb
+>13 string \x0f\xfa
+>13 string \x0f\xf9
+>527 byte 0xd2 .
+
+# 4096b RSA encrypted data
+
+0 string \x85\x04\x0c\x03 PGP RSA encrypted session key -
+>4 lelong x keyid: %X
+>8 lelong x %X
+>12 byte 0x01 RSA (Encrypt or Sign) 8129b
+>12 byte 0x02 RSA Encrypt-Only 8129b
+>13 string \x20\x00
+>13 string \x1f\xff
+>13 string \x1f\xfe
+>13 string \x1f\xfd
+>13 string \x1f\xfc
+>13 string \x1f\xfb
+>13 string \x1f\xfa
+>13 string \x1f\xf9
+>1039 byte 0xd2 .
+
+# crypto algo mapper
+
+0 name crypto
+>0 byte 0x00 Plaintext or unencrypted data
+>0 byte 0x01 IDEA
+>0 byte 0x02 TripleDES
+>0 byte 0x03 CAST5 (128 bit key)
+>0 byte 0x04 Blowfish (128 bit key, 16 rounds)
+>0 byte 0x07 AES with 128-bit key
+>0 byte 0x08 AES with 192-bit key
+>0 byte 0x09 AES with 256-bit key
+>0 byte 0x0a Twofish with 256-bit key
+
+# hash algo mapper
+
+0 name hash
+>0 byte 0x01 MD5
+>0 byte 0x02 SHA-1
+>0 byte 0x03 RIPE-MD/160
+>0 byte 0x08 SHA256
+>0 byte 0x09 SHA384
+>0 byte 0x0a SHA512
+>0 byte 0x0b SHA224
+
+# pgp symmetric encrypted data
+
+0 byte 0x8c PGP symmetric key encrypted data -
+>1 byte 0x0d
+>1 byte 0x0c
+>2 byte 0x04
+>3 use crypto
+>4 byte 0x01 salted -
+>>5 use hash
+>>14 byte 0xd2 .
+>>14 byte 0xc9 .
+>4 byte 0x03 salted & iterated -
+>>5 use hash
+>>15 byte 0xd2 .
+>>15 byte 0xc9 .
+
+# encrypted keymaterial needs s2k & can be checksummed/hashed
+
+0 name chkcrypto
+>0 use crypto
+>1 byte 0x00 Simple S2K
+>1 byte 0x01 Salted S2K
+>1 byte 0x03 Salted&Iterated S2K
+>2 use hash
+
+# all PGP keys start with this prolog
+# containing version, creation date, and purpose
+
+0 name keyprolog
+>0 byte 0x04
+>1 beldate x created on %s -
+>5 byte 0x01 RSA (Encrypt or Sign)
+>5 byte 0x02 RSA Encrypt-Only
+
+# end of secret keys known signature
+# contains e=65537 and the prolog to
+# the encrypted parameters
+
+0 name keyend
+>0 string \x00\x11\x01\x00\x01 e=65537
+>5 use crypto
+>5 byte 0xff checksummed
+>>6 use chkcrypto
+>5 byte 0xfe hashed
+>>6 use chkcrypto
+
+# PGP secret keys contain also the public parts
+# these vary by bitsize of the key
+
+0 name x1024
+>0 use keyprolog
+>6 string \x03\xfe
+>6 string \x03\xff
+>6 string \x04\x00
+>136 use keyend
+
+0 name x2048
+>0 use keyprolog
+>6 string \x80\x00
+>6 string \x07\xfe
+>6 string \x07\xff
+>264 use keyend
+
+0 name x3072
+>0 use keyprolog
+>6 string \x0b\xfe
+>6 string \x0b\xff
+>6 string \x0c\x00
+>392 use keyend
+
+0 name x4096
+>0 use keyprolog
+>6 string \x10\x00
+>6 string \x0f\xfe
+>6 string \x0f\xff
+>520 use keyend
+
+# \x00|\x1f[\xfe\xff]).{1024})'
+0 name x8192
+>0 use keyprolog
+>6 string \x20\x00
+>6 string \x1f\xfe
+>6 string \x1f\xff
+>1032 use keyend
+
+# depending on the size of the pkt
+# we branch into the proper key size
+# signatures defined as x{keysize}
+
+>0 name pgpkey
+>0 string \x01\xd8 1024b
+>>2 use x1024
+>0 string \x01\xeb 1024b
+>>2 use x1024
+>0 string \x01\xfb 1024b
+>>2 use x1024
+>0 string \x01\xfd 1024b
+>>2 use x1024
+>0 string \x01\xf3 1024b
+>>2 use x1024
+>0 string \x01\xee 1024b
+>>2 use x1024
+>0 string \x01\xfe 1024b
+>>2 use x1024
+>0 string \x01\xf4 1024b
+>>2 use x1024
+>0 string \x02\x0d 1024b
+>>2 use x1024
+>0 string \x02\x03 1024b
+>>2 use x1024
+>0 string \x02\x05 1024b
+>>2 use x1024
+>0 string \x02\x15 1024b
+>>2 use x1024
+>0 string \x02\x00 1024b
+>>2 use x1024
+>0 string \x02\x10 1024b
+>>2 use x1024
+>0 string \x02\x04 1024b
+>>2 use x1024
+>0 string \x02\x06 1024b
+>>2 use x1024
+>0 string \x02\x16 1024b
+>>2 use x1024
+>0 string \x03\x98 2048b
+>>2 use x2048
+>0 string \x03\xab 2048b
+>>2 use x2048
+>0 string \x03\xbb 2048b
+>>2 use x2048
+>0 string \x03\xbd 2048b
+>>2 use x2048
+>0 string \x03\xcd 2048b
+>>2 use x2048
+>0 string \x03\xb3 2048b
+>>2 use x2048
+>0 string \x03\xc3 2048b
+>>2 use x2048
+>0 string \x03\xc5 2048b
+>>2 use x2048
+>0 string \x03\xd5 2048b
+>>2 use x2048
+>0 string \x03\xae 2048b
+>>2 use x2048
+>0 string \x03\xbe 2048b
+>>2 use x2048
+>0 string \x03\xc0 2048b
+>>2 use x2048
+>0 string \x03\xd0 2048b
+>>2 use x2048
+>0 string \x03\xb4 2048b
+>>2 use x2048
+>0 string \x03\xc4 2048b
+>>2 use x2048
+>0 string \x03\xc6 2048b
+>>2 use x2048
+>0 string \x03\xd6 2048b
+>>2 use x2048
+>0 string \x05X 3072b
+>>2 use x3072
+>0 string \x05k 3072b
+>>2 use x3072
+>0 string \x05{ 3072b
+>>2 use x3072
+>0 string \x05} 3072b
+>>2 use x3072
+>0 string \x05\x8d 3072b
+>>2 use x3072
+>0 string \x05s 3072b
+>>2 use x3072
+>0 string \x05\x83 3072b
+>>2 use x3072
+>0 string \x05\x85 3072b
+>>2 use x3072
+>0 string \x05\x95 3072b
+>>2 use x3072
+>0 string \x05n 3072b
+>>2 use x3072
+>0 string \x05\x7e 3072b
+>>2 use x3072
+>0 string \x05\x80 3072b
+>>2 use x3072
+>0 string \x05\x90 3072b
+>>2 use x3072
+>0 string \x05t 3072b
+>>2 use x3072
+>0 string \x05\x84 3072b
+>>2 use x3072
+>0 string \x05\x86 3072b
+>>2 use x3072
+>0 string \x05\x96 3072b
+>>2 use x3072
+>0 string \x07[ 4096b
+>>2 use x4096
+>0 string \x07\x18 4096b
+>>2 use x4096
+>0 string \x07+ 4096b
+>>2 use x4096
+>0 string \x07; 4096b
+>>2 use x4096
+>0 string \x07= 4096b
+>>2 use x4096
+>0 string \x07M 4096b
+>>2 use x4096
+>0 string \x073 4096b
+>>2 use x4096
+>0 string \x07C 4096b
+>>2 use x4096
+>0 string \x07E 4096b
+>>2 use x4096
+>0 string \x07U 4096b
+>>2 use x4096
+>0 string \x07. 4096b
+>>2 use x4096
+>0 string \x07> 4096b
+>>2 use x4096
+>0 string \x07@ 4096b
+>>2 use x4096
+>0 string \x07P 4096b
+>>2 use x4096
+>0 string \x074 4096b
+>>2 use x4096
+>0 string \x07D 4096b
+>>2 use x4096
+>0 string \x07F 4096b
+>>2 use x4096
+>0 string \x07V 4096b
+>>2 use x4096
+>0 string \x0e[ 8192b
+>>2 use x8192
+>0 string \x0e\x18 8192b
+>>2 use x8192
+>0 string \x0e+ 8192b
+>>2 use x8192
+>0 string \x0e; 8192b
+>>2 use x8192
+>0 string \x0e= 8192b
+>>2 use x8192
+>0 string \x0eM 8192b
+>>2 use x8192
+>0 string \x0e3 8192b
+>>2 use x8192
+>0 string \x0eC 8192b
+>>2 use x8192
+>0 string \x0eE 8192b
+>>2 use x8192
+>0 string \x0eU 8192b
+>>2 use x8192
+>0 string \x0e. 8192b
+>>2 use x8192
+>0 string \x0e> 8192b
+>>2 use x8192
+>0 string \x0e@ 8192b
+>>2 use x8192
+>0 string \x0eP 8192b
+>>2 use x8192
+>0 string \x0e4 8192b
+>>2 use x8192
+>0 string \x0eD 8192b
+>>2 use x8192
+>0 string \x0eF 8192b
+>>2 use x8192
+>0 string \x0eV 8192b
+>>2 use x8192
+
+# PGP RSA (e=65537) secret (sub-)key header
+
+0 byte 0x95 PGP Secret Key -
+>1 use pgpkey
+0 byte 0x97 PGP Secret Sub-key -
+>1 use pgpkey
+0 byte 0x9d PGP Secret Sub-key -
+>1 use pgpkey
+
+#------------------------------------------------------------------------------
+# $File$
+# pkgadd: file(1) magic for SysV R4 PKG Datastreams
+#
+0 string #\ PaCkAgE\ DaTaStReAm pkg Datastream (SVR4)
+!:mime application/x-svr4-package
+
+#------------------------------------------------------------------------------
+# $File$
+# plan9: file(1) magic for AT&T Bell Labs' Plan 9 executables
+# From: "Stefan A. Haubenthal" <polluks@web.de>
+#
+0 belong 0x00000107 Plan 9 executable, Motorola 68k
+0 belong 0x000001EB Plan 9 executable, Intel 386
+0 belong 0x00000247 Plan 9 executable, Intel 960
+0 belong 0x000002AB Plan 9 executable, SPARC
+0 belong 0x00000407 Plan 9 executable, MIPS R3000
+0 belong 0x0000048B Plan 9 executable, AT&T DSP 3210
+0 belong 0x00000517 Plan 9 executable, MIPS R4000 BE
+0 belong 0x000005AB Plan 9 executable, AMD 29000
+0 belong 0x00000647 Plan 9 executable, ARM 7-something
+0 belong 0x000006EB Plan 9 executable, PowerPC
+0 belong 0x00000797 Plan 9 executable, MIPS R4000 LE
+0 belong 0x0000084B Plan 9 executable, DEC Alpha
+
+#------------------------------------------------------------------------------
+# $File$
+# plus5: file(1) magic for Plus Five's UNIX MUMPS
+#
+# XXX - byte order? Paging Hokey....
+#
+0 short 0x259 mumps avl global
+>2 byte >0 (V%d)
+>6 byte >0 with %d byte name
+>7 byte >0 and %d byte data cells
+0 short 0x25a mumps blt global
+>2 byte >0 (V%d)
+>8 short >0 - %d byte blocks
+>15 byte 0x00 - P/D format
+>15 byte 0x01 - P/K/D format
+>15 byte 0x02 - K/D format
+>15 byte >0x02 - Bad Flags
+
+#------------------------------------------------------------------------------
+# $File: printer,v 1.25 2011/05/20 23:31:46 christos Exp $
+# printer: file(1) magic for printer-formatted files
+#
+
+# PostScript, updated by Daniel Quinlan (quinlan@yggdrasil.com)
+0 string %! PostScript document text
+!:mime application/postscript
+!:apple ASPSTEXT
+>2 string PS-Adobe- conforming
+>>11 string >\0 DSC level %.3s
+>>>15 string EPS \b, type %s
+>>>15 string Query \b, type %s
+>>>15 string ExitServer \b, type %s
+>>>15 search/1000 %%LanguageLevel:\
+>>>>&0 string >\0 \b, Level %s
+# Some PCs have the annoying habit of adding a ^D as a document separator
+0 string \004%! PostScript document text
+!:mime application/postscript
+!:apple ASPSTEXT
+>3 string PS-Adobe- conforming
+>>12 string >\0 DSC level %.3s
+>>>16 string EPS \b, type %s
+>>>16 string Query \b, type %s
+>>>16 string ExitServer \b, type %s
+>>>16 search/1000 %%LanguageLevel:\
+>>>>&0 string >\0 \b, Level %s
+0 string \033%-12345X%!PS PostScript document
+
+# DOS EPS Binary File Header
+# From: Ed Sznyter <ews@Black.Market.NET>
+0 belong 0xC5D0D3C6 DOS EPS Binary File
+>4 long >0 Postscript starts at byte %d
+>>8 long >0 length %d
+>>>12 long >0 Metafile starts at byte %d
+>>>>16 long >0 length %d
+>>>20 long >0 TIFF starts at byte %d
+>>>>24 long >0 length %d
+
+# Summary: Adobe's PostScript Printer Description File
+# Extension: .ppd
+# Reference: http://partners.adobe.com/public/developer/en/ps/5003.PPD_Spec_v4.3.pdf, Section 3.8
+# Submitted by: Yves Arrouye <arrouye@marin.fdn.fr>
+#
+0 string *PPD-Adobe:\x20 PPD file
+>&0 string x \b, version %s
+
+# HP Printer Job Language
+0 string \033%-12345X@PJL HP Printer Job Language data
+# HP Printer Job Language
+# The header found on Win95 HP plot files is the "Silliest Thing possible"
+# (TM)
+# Every driver puts the language at some random position, with random case
+# (LANGUAGE and Language)
+# For example the LaserJet 5L driver puts the "PJL ENTER LANGUAGE" in line 10
+# From: Uwe Bonnes <bon@elektron.ikp.physik.th-darmstadt.de>
+#
+0 string \033%-12345X@PJL HP Printer Job Language data
+>&0 string >\0 %s
+>>&0 string >\0 %s
+>>>&0 string >\0 %s
+>>>>&0 string >\0 %s
+#>15 string \ ENTER\ LANGUAGE\ =
+#>31 string PostScript PostScript
+
+# From: Stefan Thurner <thurners@nicsys.de>
+0 string \033%-12345X@PJL
+>&0 search/10000 %! PJL encapsulated PostScript document text
+
+# Rick Richardson <rickrich@gmail.com>
+
+# For Fuji-Xerox Printers - HBPL stands for Host Based Printer Language
+# For Oki Data Printers - HIPERC
+# For Konica Minolta Printers - LAVAFLOW
+# For Samsung Printers - QPDL
+# For HP Printers - ZJS stands for Zenographics ZJStream
+0 string \033%-12345X@PJL HP Printer Job Language data
+>0 search/10000 @PJL\ ENTER\ LANGUAGE=HBPL - HBPL
+>0 search/10000 @PJL\ ENTER\ LANGUAGE=HIPERC - Oki Data HIPERC
+>0 search/10000 @PJL\ ENTER\ LANGUAGE=LAVAFLOW - Konica Minolta LAVAFLOW
+>0 search/10000 @PJL\ ENTER\ LANGUAGE=QPDL - Samsung QPDL
+>0 search/10000 @PJL\ ENTER\ LANGUAGE\ =\ QPDL - Samsung QPDL
+>0 search/10000 @PJL\ ENTER\ LANGUAGE=ZJS - HP ZJS
+
+
+# HP Printer Control Language, Daniel Quinlan (quinlan@yggdrasil.com)
+0 string \033E\033 HP PCL printer data
+>3 string \&l0A - default page size
+>3 string \&l1A - US executive page size
+>3 string \&l2A - US letter page size
+>3 string \&l3A - US legal page size
+>3 string \&l26A - A4 page size
+>3 string \&l80A - Monarch envelope size
+>3 string \&l81A - No. 10 envelope size
+>3 string \&l90A - Intl. DL envelope size
+>3 string \&l91A - Intl. C5 envelope size
+>3 string \&l100A - Intl. B5 envelope size
+>3 string \&l-81A - No. 10 envelope size (landscape)
+>3 string \&l-90A - Intl. DL envelope size (landscape)
+
+# IMAGEN printer-ready files:
+0 string @document( Imagen printer
+# this only works if "language xxx" is first item in Imagen header.
+>10 string language\ impress (imPRESS data)
+>10 string language\ daisy (daisywheel text)
+>10 string language\ diablo (daisywheel text)
+>10 string language\ printer (line printer emulation)
+>10 string language\ tektronix (Tektronix 4014 emulation)
+# Add any other languages that your Imagen uses - remember
+# to keep the word `text' if the file is human-readable.
+# [GRR 950115: missing "postscript" or "ultrascript" (whatever it was called)]
+#
+# Now magic for IMAGEN font files...
+0 string Rast RST-format raster font data
+>45 string >0 face %s
+# From Jukka Ukkonen
+0 string \033[K\002\0\0\017\033(a\001\0\001\033(g Canon Bubble Jet BJC formatted data
+
+# From <mike@flyn.org>
+# These are the /etc/magic entries to decode data sent to an Epson printer.
+0 string \x1B\x40\x1B\x28\x52\x08\x00\x00REMOTE1P Epson Stylus Color 460 data
+
+
+#------------------------------------------------------------------------------
+# zenographics: file(1) magic for Zenographics ZjStream printer data
+# Rick Richardson <rickrich@gmail.com>
+0 string JZJZ
+>0x12 string ZZ Zenographics ZjStream printer data (big-endian)
+0 string ZJZJ
+>0x12 string ZZ Zenographics ZjStream printer data (little-endian)
+
+
+#------------------------------------------------------------------------------
+# Oak Technologies printer stream
+# Rick Richardson <rickrich@gmail.com>
+0 string OAK
+>0x07 byte 0
+>0x0b byte 0 Oak Technologies printer stream
+
+# This would otherwise be recognized as PostScript - nick@debian.org
+0 string %!VMF SunClock's Vector Map Format data
+
+#------------------------------------------------------------------------------
+# HP LaserJet 1000 series downloadable firmware file
+0 string \xbe\xefABCDEFGH HP LaserJet 1000 series downloadable firmware
+
+# From: Paolo <oopla@users.sf.net>
+# Epson ESC/Page, ESC/PageColor
+0 string \x1b\x01@EJL Epson ESC/Page language printer data
+
+#------------------------------------------------------------------------------
+# $File$
+# project: file(1) magic for Project management
+#
+# Magic strings for ftnchek project files. Alexander Mai
+0 string FTNCHEK_\ P project file for ftnchek
+>10 string 1 version 2.7
+>10 string 2 version 2.8 to 2.10
+>10 string 3 version 2.11 or later
+
+#------------------------------------------------------------------------------
+# $File$
+# psdbms: file(1) magic for psdatabase
+#
+0 belong&0xff00ffff 0x56000000 ps database
+>1 string >\0 version %s
+>4 string >\0 from kernel %s
+
+#------------------------------------------------------------------------------
+# $File$
+# pulsar: file(1) magic for Pulsar POP3 daemon binary files
+#
+# http://pulsar.sourceforge.net
+# mailto:rok.papez@lugos.si
+#
+
+0 belong 0x1ee7f11e Pulsar POP3 daemon mailbox cache file.
+>4 ubelong x Version: %d.
+>8 ubelong x \b%d
+
+
+#------------------------------------------------------------------------------
+# $File: vax,v 1.7 2009/09/19 16:28:13 christos Exp $
+# pwsafe: file(1) magic for passwordsafe file
+#
+# Password Safe
+# http://passwordsafe.sourceforge.net/
+# file format specs
+# http://passwordsafe.svn.sourceforge.net/viewvc/passwordsafe/trunk/pwsafe/pwsafe/docs/formatV3.txt
+# V2 http://passwordsafe.svn.sourceforge.net/viewvc/passwordsafe/trunk/pwsafe/pwsafe/docs/formatV2.txt
+# V1 http://passwordsafe.svn.sourceforge.net/viewvc/passwordsafe/trunk/pwsafe/pwsafe/docs/notes.txt
+# V2 and V1 have no easy identifier that I can find
+# .psafe3
+0 string PWS3 Password Safe V3 database
+
+#------------------------------------------------------------------------------
+# $File$
+# pyramid: file(1) magic for Pyramids
+#
+# XXX - byte order?
+#
+0 long 0x50900107 Pyramid 90x family executable
+0 long 0x50900108 Pyramid 90x family pure executable
+>16 long >0 not stripped
+0 long 0x5090010b Pyramid 90x family demand paged pure executable
+>16 long >0 not stripped
+
+#------------------------------------------------------------------------------
+# $File: python,v 1.25 2014/05/06 16:08:32 christos Exp $
+# python: file(1) magic for python
+#
+# Outlook puts """ too for urgent messages
+# From: David Necas <yeti@physics.muni.cz>
+# often the module starts with a multiline string
+0 string/t """ Python script text executable
+# MAGIC as specified in Python/import.c (1.5 to 2.7a0 and 3.1a0, assuming
+# that Py_UnicodeFlag is off for Python 2)
+# 20121 ( YEAR - 1995 ) + MONTH + DAY (little endian followed by "\r\n"
+0 belong 0x994e0d0a python 1.5/1.6 byte-compiled
+0 belong 0x87c60d0a python 2.0 byte-compiled
+0 belong 0x2aeb0d0a python 2.1 byte-compiled
+0 belong 0x2ded0d0a python 2.2 byte-compiled
+0 belong 0x3bf20d0a python 2.3 byte-compiled
+0 belong 0x6df20d0a python 2.4 byte-compiled
+0 belong 0xb3f20d0a python 2.5 byte-compiled
+0 belong 0xd1f20d0a python 2.6 byte-compiled
+0 belong 0x03f30d0a python 2.7 byte-compiled
+0 belong 0x3b0c0d0a python 3.0 byte-compiled
+0 belong 0x4f0c0d0a python 3.1 byte-compiled
+0 belong 0x6c0c0d0a python 3.2 byte-compiled
+0 belong 0x9e0c0d0a python 3.3 byte-compiled
+0 belong 0xee0c0d0a python 3.4 byte-compiled
+
+0 search/1/w #!\ /usr/bin/python Python script text executable
+!:mime text/x-python
+0 search/1/w #!\ /usr/local/bin/python Python script text executable
+!:mime text/x-python
+0 search/1 #!/usr/bin/env\ python Python script text executable
+!:mime text/x-python
+0 search/1 #!\ /usr/bin/env\ python Python script text executable
+!:mime text/x-python
+
+
+# from module.submodule import func1, func2
+0 regex \^from\\s+(\\w|\\.)+\\s+import.*$ Python script text executable
+!:mime text/x-python
+
+# def __init__ (self, ...):
+0 search/4096 def\ __init__
+>&0 search/64 self Python script text executable
+!:mime text/x-python
+
+# comments
+#0 search/4096 '''
+#>&0 regex .*'''$ Python script text executable
+#!:mime text/x-python
+
+#0 search/4096 """
+#>&0 regex .*"""$ Python script text executable
+#!:mime text/x-python
+
+# try:
+# except: or finally:
+# block
+0 search/4096 try:
+>&0 regex \^\\s*except.*: Python script text executable
+!:mime text/x-python
+>&0 search/4096 finally: Python script text executable
+!:mime text/x-python
+
+# def name(args, args):
+0 regex \^(\ |\\t){0,50}def\ {1,50}[a-zA-Z]{1,100}
+>&0 regex \ {0,50}\\(([a-zA-Z]|,|\ ){1,255}\\):$ Python script text executable
+!:mime text/x-python
+
+#------------------------------------------------------------------------------
+# $File: qt,v 1.1 2014/12/12 16:48:39 christos Exp $
+# qt: file(1) magic for Qt
+
+# http://doc.qt.io/qt-5/resources.html
+0 string \<!DOCTYPE\040RCC\> Qt Resource Collection file
+
+# https://qt.gitorious.org/qt/qtbase/source/\
+# 5367fa356233da4c0f28172a8f817791525f5457:\
+# src/tools/rcc/rcc.cpp#L840
+0 string qres\0\0 Qt Binary Resource file
+0 search/1024 The\040Resource\040Compiler\040for\040Qt Qt C-code resource file
+
+# https://qt.gitorious.org/qt/qtbase/source/\
+# 5367fa356233da4c0f28172a8f817791525f5457:\
+# src/corelib/kernel/qtranslator.cpp#L62
+0 string \x3c\xb8\x64\x18\xca\xef\x9c\x95
+>8 string \xcd\x21\x1c\xbf\x60\xa1\xbd\xdd Qt Translation file
+
+#------------------------------------------------------------------------------
+# $File: revision,v 1.8 2010/11/25 15:00:12 christos Exp $
+# file(1) magic for revision control files
+# From Hendrik Scholz <hendrik@scholz.net>
+0 string/t /1\ :pserver: cvs password text file
+
+# Conary changesets
+# From: Jonathan Smith <smithj@rpath.com>
+0 belong 0xea3f81bb Conary changeset data
+
+# Type: Git bundles (git-bundle)
+# From: Josh Triplett <josh@freedesktop.org>
+0 string #\ v2\ git\ bundle\n Git bundle
+
+# Type: Git pack
+# From: Adam Buchbinder <adam.buchbinder@gmail.com>
+# The actual magic is 'PACK', but that clashes with Doom/Quake packs. However,
+# those have a little-endian offset immediately following the magic 'PACK',
+# the first byte of which is never 0, while the first byte of the Git pack
+# version, since it's a tiny number stored in big-endian format, is always 0.
+0 string PACK\0 Git pack
+>4 belong >0 \b, version %d
+>>8 belong >0 \b, %d objects
+
+# Type: Git pack index
+# From: Adam Buchbinder <adam.buchbinder@gmail.com>
+0 string \377tOc Git pack index
+>4 belong =2 \b, version 2
+
+# Type: Git index file
+# From: Frederic Briare <fbriere@fbriere.net>
+0 string DIRC Git index
+>4 belong >0 \b, version %d
+>>8 belong >0 \b, %d entries
+
+# Type: Mercurial bundles
+# From: Seo Sanghyeon <tinuviel@sparcs.kaist.ac.kr>
+0 string HG10 Mercurial bundle,
+>4 string UN uncompressed
+>4 string BZ bzip2 compressed
+
+# Type: Subversion (SVN) dumps
+# From: Uwe Zeisberger <zeisberg@informatik.uni-freiburg.de>
+0 string SVN-fs-dump-format-version: Subversion dumpfile
+>28 string >\0 (version: %s)
+
+# Type: Bazaar revision bundles and merge requests
+# URL: http://www.bazaar-vcs.org/
+# From: Jelmer Vernooij <jelmer@samba.org>
+0 string #\ Bazaar\ revision\ bundle\ v Bazaar Bundle
+0 string #\ Bazaar\ merge\ directive\ format Bazaar merge directive
+
+#------------------------------------------------------------------------------
+# $File: riff,v 1.30 2014/09/23 17:02:12 christos Exp $
+# riff: file(1) magic for RIFF format
+# See
+#
+# http://www.seanet.com/users/matts/riffmci/riffmci.htm
+#
+
+# audio format tag. Assume limits: max 1024 bit, 128 channels, 1 MHz
+0 name riff-wave
+>0 leshort 1 \b, Microsoft PCM
+>>14 leshort >0
+>>>14 leshort <1024 \b, %d bit
+>0 leshort 2 \b, Microsoft ADPCM
+>0 leshort 6 \b, ITU G.711 A-law
+>0 leshort 7 \b, ITU G.711 mu-law
+>0 leshort 8 \b, Microsoft DTS
+>0 leshort 17 \b, IMA ADPCM
+>0 leshort 20 \b, ITU G.723 ADPCM (Yamaha)
+>0 leshort 49 \b, GSM 6.10
+>0 leshort 64 \b, ITU G.721 ADPCM
+>0 leshort 80 \b, MPEG
+>0 leshort 85 \b, MPEG Layer 3
+>0 leshort 0x2001 \b, DTS
+>2 leshort =1 \b, mono
+>2 leshort =2 \b, stereo
+>2 leshort >2
+>>2 leshort <128 \b, %d channels
+>4 lelong >0
+>>4 lelong <1000000 %d Hz
+
+# try to find "fmt "
+0 name riff-walk
+>0 string fmt\x20
+>>4 lelong <0x80
+>>>8 use riff-wave
+>0 string LIST
+>>&(4.l+4) use riff-walk
+>0 string DISP
+>>&(4.l+4) use riff-walk
+>0 string bext
+>>&(4.l+4) use riff-walk
+>0 string Fake
+>>&(4.l+4) use riff-walk
+>0 string fact
+>>&(4.l+4) use riff-walk
+>0 string VP8
+>>11 byte 0x9d
+>>>12 byte 0x01
+>>>>13 byte 0x2a \b, VP8 encoding
+>>>>>14 leshort&0x3fff x \b, %d
+>>>>>16 leshort&0x3fff x \bx%d, Scaling:
+>>>>>14 leshort&0xc000 0x0000 \b [none]
+>>>>>14 leshort&0xc000 0x1000 \b [5/4]
+>>>>>14 leshort&0xc000 0x2000 \b [5/3]
+>>>>>14 leshort&0xc000 0x3000 \b [2]
+>>>>>14 leshort&0xc000 0x0000 \bx[none]
+>>>>>14 leshort&0xc000 0x1000 \bx[5/4]
+>>>>>14 leshort&0xc000 0x2000 \bx[5/3]
+>>>>>14 leshort&0xc000 0x3000 \bx[2]
+>>>>>15 byte&0x80 =0x00 \b, YUV color
+>>>>>15 byte&0x80 =0x80 \b, bad color specification
+>>>>>15 byte&0x40 =0x40 \b, no clamping required
+>>>>>15 byte&0x40 =0x00 \b, decoders should clamp
+#>0 string x we got %s
+#>>&(4.l+4) use riff-walk
+
+# AVI section extended by Patrik Radman <patrik+file-magic@iki.fi>
+#
+0 string RIFF RIFF (little-endian) data
+# RIFF Palette format
+>8 string PAL \b, palette
+>>16 leshort x \b, version %d
+>>18 leshort x \b, %d entries
+# RIFF Device Independent Bitmap format
+>8 string RDIB \b, device-independent bitmap
+>>16 string BM
+>>>30 leshort 12 \b, OS/2 1.x format
+>>>>34 leshort x \b, %d x
+>>>>36 leshort x %d
+>>>30 leshort 64 \b, OS/2 2.x format
+>>>>34 leshort x \b, %d x
+>>>>36 leshort x %d
+>>>30 leshort 40 \b, Windows 3.x format
+>>>>34 lelong x \b, %d x
+>>>>38 lelong x %d x
+>>>>44 leshort x %d
+# RIFF MIDI format
+>8 string RMID \b, MIDI
+# RIFF Multimedia Movie File format
+>8 string RMMP \b, multimedia movie
+# RIFF wrapper for MP3
+>8 string RMP3 \b, MPEG Layer 3 audio
+# Microsoft WAVE format (*.wav)
+>8 string WAVE \b, WAVE audio
+!:mime audio/x-wav
+>>12 string >\0
+>>>12 use riff-walk
+# Corel Draw Picture
+>8 string CDRA \b, Corel Draw Picture
+!:mime image/x-coreldraw
+>8 string CDR6 \b, Corel Draw Picture, version 6
+!:mime image/x-coreldraw
+>8 string NUNDROOT \b, Steinberg CuBase
+# AVI == Audio Video Interleave
+>8 string AVI\040 \b, AVI
+!:mime video/x-msvideo
+>>12 string LIST
+>>>20 string hdrlavih
+>>>>&36 lelong x \b, %u x
+>>>>&40 lelong x %u,
+>>>>&4 lelong >1000000 <1 fps,
+>>>>&4 lelong 1000000 1.00 fps,
+>>>>&4 lelong 500000 2.00 fps,
+>>>>&4 lelong 333333 3.00 fps,
+>>>>&4 lelong 250000 4.00 fps,
+>>>>&4 lelong 200000 5.00 fps,
+>>>>&4 lelong 166667 6.00 fps,
+>>>>&4 lelong 142857 7.00 fps,
+>>>>&4 lelong 125000 8.00 fps,
+>>>>&4 lelong 111111 9.00 fps,
+>>>>&4 lelong 100000 10.00 fps,
+# ]9.9,10.1[
+>>>>&4 lelong <101010
+>>>>>&-4 lelong >99010
+>>>>>>&-4 lelong !100000 ~10 fps,
+>>>>&4 lelong 83333 12.00 fps,
+# ]11.9,12.1[
+>>>>&4 lelong <84034
+>>>>>&-4 lelong >82645
+>>>>>>&-4 lelong !83333 ~12 fps,
+>>>>&4 lelong 66667 15.00 fps,
+# ]14.9,15.1[
+>>>>&4 lelong <67114
+>>>>>&-4 lelong >66225
+>>>>>>&-4 lelong !66667 ~15 fps,
+>>>>&4 lelong 50000 20.00 fps,
+>>>>&4 lelong 41708 23.98 fps,
+>>>>&4 lelong 41667 24.00 fps,
+# ]23.9,24.1[
+>>>>&4 lelong <41841
+>>>>>&-4 lelong >41494
+>>>>>>&-4 lelong !41708
+>>>>>>>&-4 lelong !41667 ~24 fps,
+>>>>&4 lelong 40000 25.00 fps,
+# ]24.9,25.1[
+>>>>&4 lelong <40161
+>>>>>&-4 lelong >39841
+>>>>>>&-4 lelong !40000 ~25 fps,
+>>>>&4 lelong 33367 29.97 fps,
+>>>>&4 lelong 33333 30.00 fps,
+# ]29.9,30.1[
+>>>>&4 lelong <33445
+>>>>>&-4 lelong >33223
+>>>>>>&-4 lelong !33367
+>>>>>>>&-4 lelong !33333 ~30 fps,
+>>>>&4 lelong <32224 >30 fps,
+##>>>>&4 lelong x (%lu)
+##>>>>&20 lelong x %lu frames,
+# Note: The tests below assume that the AVI has 1 or 2 streams,
+# "vids" optionally followed by "auds".
+# (Should cover 99.9% of all AVIs.)
+# assuming avih length = 56
+>>>88 string LIST
+>>>>96 string strlstrh
+>>>>>108 string vids video:
+>>>>>>&0 lelong 0 uncompressed
+# skip past vids strh
+>>>>>>(104.l+108) string strf
+>>>>>>>(104.l+132) lelong 1 RLE 8bpp
+>>>>>>>(104.l+132) string/c cvid Cinepak
+>>>>>>>(104.l+132) string/c i263 Intel I.263
+>>>>>>>(104.l+132) string/c iv32 Indeo 3.2
+>>>>>>>(104.l+132) string/c iv41 Indeo 4.1
+>>>>>>>(104.l+132) string/c iv50 Indeo 5.0
+>>>>>>>(104.l+132) string/c mp42 Microsoft MPEG-4 v2
+>>>>>>>(104.l+132) string/c mp43 Microsoft MPEG-4 v3
+>>>>>>>(104.l+132) string/c fmp4 FFMpeg MPEG-4
+>>>>>>>(104.l+132) string/c mjpg Motion JPEG
+>>>>>>>(104.l+132) string/c div3 DivX 3
+>>>>>>>>112 string/c div3 Low-Motion
+>>>>>>>>112 string/c div4 Fast-Motion
+>>>>>>>(104.l+132) string/c divx DivX 4
+>>>>>>>(104.l+132) string/c dx50 DivX 5
+>>>>>>>(104.l+132) string/c xvid XviD
+>>>>>>>(104.l+132) string/c h264 H.264
+>>>>>>>(104.l+132) string/c wmv3 Windows Media Video 9
+>>>>>>>(104.l+132) string/c h264 X.264 or H.264
+>>>>>>>(104.l+132) lelong 0
+##>>>>>>>(104.l+132) string x (%.4s)
+# skip past first (video) LIST
+>>>>(92.l+96) string LIST
+>>>>>(92.l+104) string strlstrh
+>>>>>>(92.l+116) string auds \b, audio:
+# auds strh length = 56:
+>>>>>>>(92.l+172) string strf
+>>>>>>>>(92.l+180) leshort 0x0001 uncompressed PCM
+>>>>>>>>(92.l+180) leshort 0x0002 ADPCM
+>>>>>>>>(92.l+180) leshort 0x0006 aLaw
+>>>>>>>>(92.l+180) leshort 0x0007 uLaw
+>>>>>>>>(92.l+180) leshort 0x0050 MPEG-1 Layer 1 or 2
+>>>>>>>>(92.l+180) leshort 0x0055 MPEG-1 Layer 3
+>>>>>>>>(92.l+180) leshort 0x2000 Dolby AC3
+>>>>>>>>(92.l+180) leshort 0x0161 DivX
+##>>>>>>>>(92.l+180) leshort x (0x%.4x)
+>>>>>>>>(92.l+182) leshort 1 (mono,
+>>>>>>>>(92.l+182) leshort 2 (stereo,
+>>>>>>>>(92.l+182) leshort >2 (%d channels,
+>>>>>>>>(92.l+184) lelong x %d Hz)
+# auds strh length = 64:
+>>>>>>>(92.l+180) string strf
+>>>>>>>>(92.l+188) leshort 0x0001 uncompressed PCM
+>>>>>>>>(92.l+188) leshort 0x0002 ADPCM
+>>>>>>>>(92.l+188) leshort 0x0055 MPEG-1 Layer 3
+>>>>>>>>(92.l+188) leshort 0x2000 Dolby AC3
+>>>>>>>>(92.l+188) leshort 0x0161 DivX
+##>>>>>>>>(92.l+188) leshort x (0x%.4x)
+>>>>>>>>(92.l+190) leshort 1 (mono,
+>>>>>>>>(92.l+190) leshort 2 (stereo,
+>>>>>>>>(92.l+190) leshort >2 (%d channels,
+>>>>>>>>(92.l+192) lelong x %d Hz)
+# Animated Cursor format
+>8 string ACON \b, animated cursor
+# SoundFont 2 <mpruett@sgi.com>
+>8 string sfbk SoundFont/Bank
+# MPEG-1 wrapped in a RIFF, apparently
+>8 string CDXA \b, wrapped MPEG-1 (CDXA)
+>8 string 4XMV \b, 4X Movie file
+# AMV-type AVI file: http://wiki.multimedia.cx/index.php?title=AMV
+>8 string AMV\040 \b, AMV
+>8 string WEBP \b, Web/P image
+!:mime image/webp
+>>12 use riff-walk
+
+#
+# XXX - some of the below may only appear in little-endian form.
+#
+# Also "MV93" appears to be for one form of Macromedia Director
+# files, and "GDMF" appears to be another multimedia format.
+#
+0 string RIFX RIFF (big-endian) data
+# RIFF Palette format
+>8 string PAL \b, palette
+>>16 beshort x \b, version %d
+>>18 beshort x \b, %d entries
+# RIFF Device Independent Bitmap format
+>8 string RDIB \b, device-independent bitmap
+>>16 string BM
+>>>30 beshort 12 \b, OS/2 1.x format
+>>>>34 beshort x \b, %d x
+>>>>36 beshort x %d
+>>>30 beshort 64 \b, OS/2 2.x format
+>>>>34 beshort x \b, %d x
+>>>>36 beshort x %d
+>>>30 beshort 40 \b, Windows 3.x format
+>>>>34 belong x \b, %d x
+>>>>38 belong x %d x
+>>>>44 beshort x %d
+# RIFF MIDI format
+>8 string RMID \b, MIDI
+# RIFF Multimedia Movie File format
+>8 string RMMP \b, multimedia movie
+# Microsoft WAVE format (*.wav)
+>8 string WAVE \b, WAVE audio
+>>20 leshort 1 \b, Microsoft PCM
+>>>34 leshort >0 \b, %d bit
+>>22 beshort =1 \b, mono
+>>22 beshort =2 \b, stereo
+>>22 beshort >2 \b, %d channels
+>>24 belong >0 %d Hz
+# Corel Draw Picture
+>8 string CDRA \b, Corel Draw Picture
+>8 string CDR6 \b, Corel Draw Picture, version 6
+# AVI == Audio Video Interleave
+>8 string AVI\040 \b, AVI
+# Animated Cursor format
+>8 string ACON \b, animated cursor
+# Notation Interchange File Format (big-endian only)
+>8 string NIFF \b, Notation Interchange File Format
+# SoundFont 2 <mpruett@sgi.com>
+>8 string sfbk SoundFont/Bank
+
+#------------------------------------------------------------------------------
+# Sony Wave64
+# see http://www.vcs.de/fileadmin/user_upload/MBS/PDF/Whitepaper/Informations_about_Sony_Wave64.pdf
+# 128 bit RIFF-GUID { 66666972-912E-11CF-A5D6-28DB04C10000 } in little-endian
+0 string riff\x2E\x91\xCF\x11\xA5\xD6\x28\xDB\x04\xC1\x00\x00 Sony Wave64 RIFF data
+# 128 bit + total file size (64 bits) so 24 bytes
+# then WAVE-GUID { 65766177-ACF3-11D3-8CD1-00C04F8EDB8A }
+>24 string wave\xF3\xAC\xD3\x11\x8C\xD1\x00\xC0\x4F\x8E\xDB\x8A \b, WAVE 64 audio
+!:mime audio/x-w64
+# FMT-GUID { 20746D66-ACF3-11D3-8CD1-00C04F8EDB8A }
+>>40 search/256 fmt\x20\xF3\xAC\xD3\x11\x8C\xD1\x00\xC0\x4F\x8E\xDB\x8A \b
+>>>&10 leshort =1 \b, mono
+>>>&10 leshort =2 \b, stereo
+>>>&10 leshort >2 \b, %d channels
+>>>&12 lelong >0 %d Hz
+
+#------------------------------------------------------------------------------
+# MBWF/RF64
+# see EBU TECH 3306 http://tech.ebu.ch/docs/tech/tech3306-2009.pdf
+0 string RF64\xff\xff\xff\xffWAVEds64 MBWF/RF64 audio
+!:mime audio/x-wav
+>40 search/256 fmt\x20 \b
+>>&6 leshort =1 \b, mono
+>>&6 leshort =2 \b, stereo
+>>&6 leshort >2 \b, %d channels
+>>&8 lelong >0 %d Hz
+
+#------------------------------------------------------------------------------
+# $File: rinex,v 1.3 2011/04/04 21:12:03 christos Exp $
+# rinex: file(1) magic for RINEX files
+# http://igscb.jpl.nasa.gov/igscb/data/format/rinex210.txt
+# ftp://cddis.gsfc.nasa.gov/pub/reports/formats/rinex300.pdf
+# data for testing: ftp://cddis.gsfc.nasa.gov/pub/gps/data
+60 string RINEX
+>80 search/256 XXRINEXB RINEX Data, GEO SBAS Broadcast
+>>&32 string x \b, date %15.15s
+>>5 string x \b, version %6.6s
+!:mime rinex/broadcast
+>80 search/256 XXRINEXD RINEX Data, Observation (Hatanaka comp)
+>>&32 string x \b, date %15.15s
+>>5 string x \b, version %6.6s
+!:mime rinex/observation
+>80 search/256 XXRINEXC RINEX Data, Clock
+>>&32 string x \b, date %15.15s
+>>5 string x \b, version %6.6s
+!:mime rinex/clock
+>80 search/256 XXRINEXH RINEX Data, GEO SBAS Navigation
+>>&32 string x \b, date %15.15s
+>>5 string x \b, version %6.6s
+!:mime rinex/navigation
+>80 search/256 XXRINEXG RINEX Data, GLONASS Navigation
+>>&32 string x \b, date %15.15s
+>>5 string x \b, version %6.6s
+!:mime rinex/navigation
+>80 search/256 XXRINEXL RINEX Data, Galileo Navigation
+>>&32 string x \b, date %15.15s
+>>5 string x \b, version %6.6s
+!:mime rinex/navigation
+>80 search/256 XXRINEXM RINEX Data, Meteorological
+>>&32 string x \b, date %15.15s
+>>5 string x \b, version %6.6s
+!:mime rinex/meteorological
+>80 search/256 XXRINEXN RINEX Data, Navigation
+>>&32 string x \b, date %15.15s
+>>5 string x \b, version %6.6s
+!:mime rinex/navigation
+>80 search/256 XXRINEXO RINEX Data, Observation
+>>&32 string x \b, date %15.15s
+>>5 string x \b, version %6.6s
+!:mime rinex/observation
+
+#------------------------------------------------------------------------------
+# $File: rpm,v 1.11 2011/06/14 12:47:41 christos Exp $
+#
+# RPM: file(1) magic for Red Hat Packages Erik Troan (ewt@redhat.com)
+#
+0 belong 0xedabeedb RPM
+!:mime application/x-rpm
+>4 byte x v%d
+>5 byte x \b.%d
+>6 beshort 1 src
+>6 beshort 0 bin
+>>8 beshort 1 i386/x86_64
+>>8 beshort 2 Alpha/Sparc64
+>>8 beshort 3 Sparc
+>>8 beshort 4 MIPS
+>>8 beshort 5 PowerPC
+>>8 beshort 6 68000
+>>8 beshort 7 SGI
+>>8 beshort 8 RS6000
+>>8 beshort 9 IA64
+>>8 beshort 10 Sparc64
+>>8 beshort 11 MIPSel
+>>8 beshort 12 ARM
+>>8 beshort 13 MiNT
+>>8 beshort 14 S/390
+>>8 beshort 15 S/390x
+>>8 beshort 16 PowerPC64
+>>8 beshort 17 SuperH
+>>8 beshort 18 Xtensa
+>>8 beshort 255 noarch
+
+#delta RPM Daniel Novotny (dnovotny@redhat.com)
+0 string drpm Delta RPM
+!:mime application/x-rpm
+>12 string x %s
+>>8 beshort 11 MIPSel
+>>8 beshort 12 ARM
+>>8 beshort 13 MiNT
+>>8 beshort 14 S/390
+>>8 beshort 15 S/390x
+>>8 beshort 16 PowerPC64
+>>8 beshort 17 SuperH
+>>8 beshort 18 Xtensa
+>>10 string x %s
+
+#------------------------------------------------------------------------------
+# $File$
+# rtf: file(1) magic for Rich Text Format (RTF)
+#
+# Duncan P. Simpson, D.P.Simpson@dcs.warwick.ac.uk
+#
+0 string {\\rtf Rich Text Format data,
+!:mime text/rtf
+>5 string 1 version 1,
+>>6 string \\ansi ANSI
+>>6 string \\mac Apple Macintosh
+>>6 string \\pc IBM PC, code page 437
+>>6 string \\pca IBM PS/2, code page 850
+>>6 default x unknown character set
+>5 default x unknown version
+
+#------------------------------------------------------------------------------
+# $File: ruby,v 1.4 2010/07/08 20:24:13 christos Exp $
+# ruby: file(1) magic for Ruby scripting language
+# URL: http://www.ruby-lang.org/
+# From: Reuben Thomas <rrt@sc3d.org>
+
+# Ruby scripts
+0 search/1/w #!\ /usr/bin/ruby Ruby script text executable
+!:mime text/x-ruby
+0 search/1/w #!\ /usr/local/bin/ruby Ruby script text executable
+!:mime text/x-ruby
+0 search/1 #!/usr/bin/env\ ruby Ruby script text executable
+!:mime text/x-ruby
+0 search/1 #!\ /usr/bin/env\ ruby Ruby script text executable
+!:mime text/x-ruby
+
+# What looks like ruby, but does not have a shebang
+# (modules and such)
+# From: Lubomir Rintel <lkundrak@v3.sk>
+0 regex \^[\ \t]*require[\ \t]'[A-Za-z_/]+'
+>0 regex include\ [A-Z]|def\ [a-z]|\ do$
+>>0 regex \^[\ \t]*end([\ \t]*[;#].*)?$ Ruby script text
+!:mime text/x-ruby
+0 regex \^[\ \t]*(class|module)[\ \t][A-Z]
+>0 regex (modul|includ)e\ [A-Z]|def\ [a-z]
+>>0 regex \^[\ \t]*end([\ \t]*[;#].*)?$ Ruby module source text
+!:mime text/x-ruby
+
+#------------------------------------------------------------------------------
+# $File$
+# sc: file(1) magic for "sc" spreadsheet
+#
+38 string Spreadsheet sc spreadsheet file
+!:mime application/x-sc
+
+#------------------------------------------------------------------------------
+# $File$
+# sccs: file(1) magic for SCCS archives
+#
+# SCCS archive structure:
+# \001h01207
+# \001s 00276/00000/00000
+# \001d D 1.1 87/09/23 08:09:20 ian 1 0
+# \001c date and time created 87/09/23 08:09:20 by ian
+# \001e
+# \001u
+# \001U
+# ... etc.
+# Now '\001h' happens to be the same as the 3B20's a.out magic number (0550).
+# *Sigh*. And these both came from various parts of the USG.
+# Maybe we should just switch everybody from SCCS to RCS!
+# Further, you can't just say '\001h0', because the five-digit number
+# is a checksum that could (presumably) have any leading digit,
+# and we don't have regular expression matching yet.
+# Hence the following official kludge:
+8 string \001s\ SCCS archive data
+
+#------------------------------------------------------------------------------
+# $File: scientific,v 1.8 2014/01/06 17:46:23 rrt Exp $
+# scientific: file(1) magic for scientific formats
+#
+# From: Joe Krahn <krahn@niehs.nih.gov>
+
+########################################################
+# CCP4 data and plot files:
+0 string MTZ\040 MTZ reflection file
+
+92 string PLOT%%84 Plot84 plotting file
+>52 byte 1 , Little-endian
+>55 byte 1 , Big-endian
+
+########################################################
+# Electron density MAP/MASK formats
+
+0 string EZD_MAP NEWEZD Electron Density Map
+109 string MAP\040( Old EZD Electron Density Map
+
+0 string/c :-)\040Origin BRIX Electron Density Map
+>170 string >0 , Sigma:%.12s
+#>4 string >0 %.178s
+#>4 addr x %.178s
+
+7 string 18\040!NTITLE XPLOR ASCII Electron Density Map
+9 string \040!NTITLE\012\040REMARK CNS ASCII electron density map
+
+208 string MAP\040 CCP4 Electron Density Map
+# Assumes same stamp for float and double (normal case)
+>212 byte 17 \b, Big-endian
+>212 byte 34 \b, VAX format
+>212 byte 68 \b, Little-endian
+>212 byte 85 \b, Convex native
+
+############################################################
+# X-Ray Area Detector images
+0 string R-AXIS4\ \ \ R-Axis Area Detector Image:
+>796 lelong <20 Little-endian, IP #%d,
+>>768 lelong >0 Size=%dx
+>>772 lelong >0 \b%d
+>796 belong <20 Big-endian, IP #%d,
+>>768 belong >0 Size=%dx
+>>772 belong >0 \b%d
+
+0 string RAXIS\ \ \ \ \ R-Axis Area Detector Image, Win32:
+>796 lelong <20 Little-endian, IP #%d,
+>>768 lelong >0 Size=%dx
+>>772 lelong >0 \b%d
+>796 belong <20 Big-endian, IP #%d,
+>>768 belong >0 Size=%dx
+>>772 belong >0 \b%d
+
+
+1028 string MMX\000\000\000\000\000\000\000\000\000\000\000\000\000 MAR Area Detector Image,
+>1072 ulong >1 Compressed(%d),
+>1100 ulong >1 %d headers,
+>1104 ulong >0 %d x
+>1108 ulong >0 %d,
+>1120 ulong >0 %d bits/pixel
+
+# Type: GEDCOM genealogical (family history) data
+# From: Giuseppe Bilotta
+0 search/1/c 0\ HEAD GEDCOM genealogy text
+>&0 search 1\ GEDC
+>>&0 search 2\ VERS version
+>>>&1 string >\0 %s
+# From: Phil Endecott <phil05@chezphil.org>
+0 string \000\060\000\040\000\110\000\105\000\101\000\104 GEDCOM data
+0 string \060\000\040\000\110\000\105\000\101\000\104\000 GEDCOM data
+0 string \376\377\000\060\000\040\000\110\000\105\000\101\000\104 GEDCOM data
+0 string \377\376\060\000\040\000\110\000\105\000\101\000\104\000 GEDCOM data
+
+# PDB: Protein Data Bank files
+# Adam Buchbinder <adam.buchbinder@gmail.com>
+#
+# http://www.wwpdb.org/documentation/format32/sect2.html
+# http://www.ch.ic.ac.uk/chemime/
+#
+# The PDB file format is fixed-field, 80 columns. From the spec:
+#
+# COLS DATA
+# 1 - 6 "HEADER"
+# 11 - 50 String(40)
+# 51 - 59 Date
+# 63 - 66 IDcode
+#
+# Thus, positions 7-10, 60-62 and 67-80 are spaces. The Date must be in the
+# format DD-MMM-YY, e.g., 01-JAN-70, and the IDcode consists of numbers and
+# uppercase letters. However, examples have been seen without the date string,
+# e.g., the example on the chemime site.
+0 string HEADER\ \ \ \
+>&0 regex/1l \^.{40}
+>>&0 regex/1l [0-9]{2}-[A-Z]{3}-[0-9]{2}\ {3}
+>>>&0 regex/1ls [A-Z0-9]{4}.{14}$
+>>>>&0 regex/1l [A-Z0-9]{4} Protein Data Bank data, ID Code %s
+!:mime chemical/x-pdb
+>>>>0 regex/1l [0-9]{2}-[A-Z]{3}-[0-9]{2} \b, %s
+
+# Type: GDSII Stream file
+0 belong 0x00060002 GDSII Stream file
+>4 byte 0x00
+>>5 byte x version %d.0
+>4 byte >0x00 version %d
+>>5 byte x \b.%d
+
+#------------------------------------------------------------------------------
+# $File$
+0 search/1 -----BEGIN\ CERTIFICATE------ RFC1421 Security Certificate text
+0 search/1 -----BEGIN\ NEW\ CERTIFICATE RFC1421 Security Certificate Signing Request text
+0 belong 0xedfeedfe Sun 'jks' Java Keystore File data
+# Type: SE Linux policy modules *.pp reference policy
+# for Fedora 5 to 9, RHEL5, and Debian Etch and Lenny.
+# URL: http://doc.coker.com.au/computers/selinux-magic
+# From: Russell Coker <russell@coker.com.au>
+
+0 lelong 0xf97cff8f SE Linux modular policy
+>4 lelong x version %d,
+>8 lelong x %d sections,
+>>(12.l) lelong 0xf97cff8d
+>>>(12.l+27) lelong x mod version %d,
+>>>(12.l+31) lelong 0 Not MLS,
+>>>(12.l+31) lelong 1 MLS,
+>>>(12.l+23) lelong 2
+>>>>(12.l+47) string >\0 module name %s
+>>>(12.l+23) lelong 1 base
+
+1 string policy_module( SE Linux policy module source
+2 string policy_module( SE Linux policy module source
+
+0 string ##\ <summary> SE Linux policy interface source
+
+#0 search gen_context( SE Linux policy file contexts
+
+#0 search gen_sens( SE Linux policy MLS constraints source
+
+#------------------------------------------------------------------------------
+# $File$
+# sendmail: file(1) magic for sendmail config files
+#
+# XXX - byte order?
+#
+0 byte 046 Sendmail frozen configuration
+>16 string >\0 - version %s
+0 short 0x271c Sendmail frozen configuration
+>16 string >\0 - version %s
+
+#------------------------------------------------------------------------------
+# sendmail: file(1) magic for sendmail m4(1) files
+#
+# From Hendrik Scholz <hendrik@scholz.net>
+# i.e. files in /usr/share/sendmail/cf/
+#
+0 string divert(-1)\n sendmail m4 text file
+
+
+#------------------------------------------------------------------------------
+# $File: sequent,v 1.11 2014/06/02 19:27:54 christos Exp $
+# sequent: file(1) magic for Sequent machines
+#
+# Sequent information updated by Don Dwiggins <atsun!dwiggins>.
+# For Sequent's multiprocessor systems (incomplete).
+0 lelong 0x00ea BALANCE NS32000 .o
+>16 lelong >0 not stripped
+>124 lelong >0 version %d
+0 lelong 0x10ea BALANCE NS32000 executable (0 @ 0)
+>16 lelong >0 not stripped
+>124 lelong >0 version %d
+0 lelong 0x20ea BALANCE NS32000 executable (invalid @ 0)
+>16 lelong >0 not stripped
+>124 lelong >0 version %d
+0 lelong 0x30ea BALANCE NS32000 standalone executable
+>16 lelong >0 not stripped
+>124 lelong >0 version %d
+#
+# Symmetry information added by Jason Merrill <jason@jarthur.claremont.edu>.
+# Symmetry magic nums will not be reached if DOS COM comes before them;
+# byte 0xeb is matched before these get a chance.
+0 leshort 0x12eb SYMMETRY i386 .o
+>16 lelong >0 not stripped
+>124 lelong >0 version %d
+0 leshort 0x22eb SYMMETRY i386 executable (0 @ 0)
+>16 lelong >0 not stripped
+>124 lelong >0 version %d
+0 leshort 0x32eb SYMMETRY i386 executable (invalid @ 0)
+>16 lelong >0 not stripped
+>124 lelong >0 version %d
+# http://en.wikipedia.org/wiki/Sequent_Computer_Systems
+# below test line conflicts with MS-DOS 2.11 floppies and Acronis loader
+#0 leshort 0x42eb SYMMETRY i386 standalone executable
+0 leshort 0x42eb
+# skip unlike negative version
+>124 lelong >-1
+# assuming version 28867614 is very low probable
+>>124 lelong !28867614 SYMMETRY i386 standalone executable
+>>>16 lelong >0 not stripped
+>>>124 lelong >0 version %d
+
+#------------------------------------------------------------------------------
+# $File: sereal,v 1.2 2014/11/11 20:10:49 christos Exp $
+# sereal: file(1) magic the Sereal binary serialization format
+#
+# From: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
+#
+# See the specification of the format at
+# https://github.com/Sereal/Sereal/blob/master/sereal_spec.pod#document-header-format
+#
+# I'd have liked to do the byte&0xF0 matching against 0, 1, 2 ... by
+# doing (byte&0xF0)>>4 here, but unfortunately that's not
+# supported. So when we print out a message about an unknown format
+# we'll print out e.g. 0x30 instead of the more human-readable
+# 0x30>>4.
+#
+# See https://github.com/Sereal/Sereal/commit/35372ae01d in the
+# Sereal.git repository for test Sereal data.
+0 name sereal
+>4 byte&0x0F x (version %d,
+>4 byte&0xF0 0x00 uncompressed)
+>4 byte&0xF0 0x10 compressed with non-incremental Snappy)
+>4 byte&0xF0 0x20 compressed with incremental Snappy)
+>4 byte&0xF0 >0x20 unknown subformat, flag: %d>>4)
+
+0 string/b \=srl Sereal data packet
+!:mime application/sereal
+>&0 use sereal
+0 string/b \=\xF3rl Sereal data packet
+!:mime application/sereal
+>&0 use sereal
+0 string/b \=\xC3\xB3rl Sereal data packet, UTF-8 encoded
+!:mime application/sereal
+>&0 use sereal
+
+
+#------------------------------------------------------------------------------
+# $File: sgi,v 1.20 2014/03/10 00:53:38 christos Exp $
+# sgi: file(1) magic for Silicon Graphics operating systems and applications
+#
+# Executable images are handled either in aout (for old-style a.out
+# files for 68K; they are indistinguishable from other big-endian 32-bit
+# a.out files) or in mips (for MIPS ECOFF and Ucode files)
+#
+
+# kbd file definitions
+0 string kbd!map kbd map file
+>8 byte >0 Ver %d:
+>10 short >0 with %d table(s)
+
+0 beshort 0x8765 disk quotas file
+
+0 beshort 0x0506 IRIS Showcase file
+>2 byte 0x49 -
+>3 byte x - version %d
+0 beshort 0x0226 IRIS Showcase template
+>2 byte 0x63 -
+>3 byte x - version %d
+0 belong 0x5343464d IRIS Showcase file
+>4 byte x - version %d
+0 belong 0x5443464d IRIS Showcase template
+>4 byte x - version %d
+0 belong 0xdeadbabe IRIX Parallel Arena
+>8 belong >0 - version %d
+
+# core files
+#
+# 32bit core file
+0 belong 0xdeadadb0 IRIX core dump
+>4 belong 1 of
+>16 string >\0 '%s'
+# 64bit core file
+0 belong 0xdeadad40 IRIX 64-bit core dump
+>4 belong 1 of
+>16 string >\0 '%s'
+# N32bit core file
+0 belong 0xbabec0bb IRIX N32 core dump
+>4 belong 1 of
+>16 string >\0 '%s'
+# New style crash dump file
+0 string \x43\x72\x73\x68\x44\x75\x6d\x70 IRIX vmcore dump of
+>36 string >\0 '%s'
+
+# Trusted IRIX info
+0 string SGIAUDIT SGI Audit file
+>8 byte x - version %d
+>9 byte x \b.%d
+#
+0 string WNGZWZSC Wingz compiled script
+0 string WNGZWZSS Wingz spreadsheet
+0 string WNGZWZHP Wingz help file
+#
+0 string #Inventor V IRIS Inventor 1.0 file
+0 string #Inventor V2 Open Inventor 2.0 file
+# GLF is OpenGL stream encoding
+0 string glfHeadMagic(); GLF_TEXT
+4 belong 0x7d000000 GLF_BINARY_LSB_FIRST
+!:strength -30
+4 belong 0x0000007d GLF_BINARY_MSB_FIRST
+!:strength -30
+# GLS is OpenGL stream encoding; GLS is the successor of GLF
+0 string glsBeginGLS( GLS_TEXT
+4 belong 0x10000000 GLS_BINARY_LSB_FIRST
+!:strength -30
+4 belong 0x00000010 GLS_BINARY_MSB_FIRST
+!:strength -30
+
+#
+#
+# Performance Co-Pilot file types
+0 string PmNs PCP compiled namespace (V.0)
+0 string PmN PCP compiled namespace
+>3 string >\0 (V.%1.1s)
+#3 lelong 0x84500526 PCP archive
+3 belong 0x84500526 PCP archive
+>7 byte x (V.%d)
+#>20 lelong -2 temporal index
+#>20 lelong -1 metadata
+#>20 lelong 0 log volume #0
+#>20 lelong >0 log volume #%d
+>20 belong -2 temporal index
+>20 belong -1 metadata
+>20 belong 0 log volume #0
+>20 belong >0 log volume #%d
+>24 string >\0 host: %s
+0 string PCPFolio PCP
+>9 string Version: Archive Folio
+>18 string >\0 (V.%s)
+0 string #pmchart PCP pmchart view
+>9 string Version
+>17 string >\0 (V%-3.3s)
+0 string #kmchart PCP kmchart view
+>9 string Version
+>17 string >\0 (V.%s)
+0 string pmview PCP pmview config
+>7 string Version
+>15 string >\0 (V%-3.3s)
+0 string #pmlogger PCP pmlogger config
+>10 string Version
+>18 string >\0 (V%1.1s)
+0 string #pmdahotproc PCP pmdahotproc config
+>13 string Version
+>21 string >\0 (V%-3.3s)
+0 string PcPh PCP Help
+>4 string 1 Index
+>4 string 2 Text
+>5 string >\0 (V.%1.1s)
+0 string #pmieconf-rules PCP pmieconf rules
+>16 string >\0 (V.%1.1s)
+3 string pmieconf-pmie PCP pmie config
+>17 string >\0 (V.%1.1s)
+
+# SpeedShop data files
+0 lelong 0x13130303 SpeedShop data file
+
+# mdbm files
+0 lelong 0x01023962 mdbm file, version 0 (obsolete)
+0 string mdbm mdbm file,
+>5 byte x version %d,
+>6 byte x 2^%d pages,
+>7 byte x pagesize 2^%d,
+>17 byte x hash %d,
+>11 byte x dataformat %d
+
+# Alias Maya files
+0 string/t //Maya\040ASCII Alias Maya Ascii File,
+>13 string >\0 version %s
+8 string MAYAFOR4 Alias Maya Binary File,
+>32 string >\0 version %s scene
+8 string MayaFOR4 Alias Maya Binary File,
+>32 string >\0 version %s scene
+8 string CIMG Alias Maya Image File
+8 string DEEP Alias Maya Image File
+#------------------------------------------------------------------------------
+# $File: sgml,v 1.29 2012/08/26 10:25:41 christos Exp $
+# Type: SVG Vectorial Graphics
+# From: Noel Torres <tecnico@ejerciciosresueltos.com>
+0 string \<?xml\ version="
+>15 string >\0
+>>19 search/4096 \<svg SVG Scalable Vector Graphics image
+!:mime image/svg+xml
+>>19 search/4096 \<gnc-v2 GnuCash file
+!:mime application/x-gnucash
+
+# Sitemap file
+0 string/t \<?xml\ version="
+>15 string >\0
+>>19 search/4096 \<urlset XML Sitemap document text
+!:mime application/xml-sitemap
+
+# OpenStreetMap XML (.osm)
+# http://wiki.openstreetmap.org/wiki/OSM_XML
+# From: Markus Heidelberg <markus.heidelberg@web.de>
+0 string \<?xml\ version="
+>15 string >\0
+>>19 search/4096 \<osm OpenStreetMap XML data
+
+# xhtml
+0 string/t \<?xml\ version="
+>15 string >\0
+>>19 search/4096/cWbt \<!doctype\ html XHTML document text
+!:mime text/html
+0 string/t \<?xml\ version='
+>15 string >\0
+>>19 search/4096/cWbt \<!doctype\ html XHTML document text
+!:mime text/html
+0 string/t \<?xml\ version="
+>15 string >\0
+>>19 search/4096/cWbt \<html broken XHTML document text
+!:mime text/html
+
+#------------------------------------------------------------------------------
+# sgml: file(1) magic for Standard Generalized Markup Language
+# HyperText Markup Language (HTML) is an SGML document type,
+# from Daniel Quinlan (quinlan@yggdrasil.com)
+# adapted to string extenstions by Anthon van der Neut <anthon@mnt.org)
+0 search/4096/cWt \<!doctype\ html HTML document text
+!:mime text/html
+!:strength + 5
+0 search/4096/cwt \<head HTML document text
+!:mime text/html
+!:strength + 5
+0 search/4096/cwt \<title HTML document text
+!:mime text/html
+!:strength + 5
+0 search/4096/cwt \<html HTML document text
+!:mime text/html
+!:strength + 5
+0 search/4096/cwt \<script HTML document text
+!:mime text/html
+!:strength + 5
+0 search/4096/cwt \<style HTML document text
+!:mime text/html
+!:strength + 5
+0 search/4096/cwt \<table HTML document text
+!:mime text/html
+!:strength + 5
+0 search/4096/cwt \<a\ href= HTML document text
+!:mime text/html
+!:strength + 5
+
+# Extensible markup language (XML), a subset of SGML
+# from Marc Prud'hommeaux (marc@apocalypse.org)
+0 search/1/cwt \<?xml XML document text
+!:mime application/xml
+!:strength + 5
+0 string/t \<?xml\ version\ " XML
+!:mime application/xml
+!:strength + 5
+0 string/t \<?xml\ version=" XML
+!:mime application/xml
+!:strength + 5
+>15 string/t >\0 %.3s document text
+>>23 search/1 \<xsl:stylesheet (XSL stylesheet)
+>>24 search/1 \<xsl:stylesheet (XSL stylesheet)
+0 string \<?xml\ version=' XML
+!:mime application/xml
+!:strength + 5
+>15 string/t >\0 %.3s document text
+>>23 search/1 \<xsl:stylesheet (XSL stylesheet)
+>>24 search/1 \<xsl:stylesheet (XSL stylesheet)
+0 search/1/wbt \<?xml XML document text
+!:mime application/xml
+!:strength - 10
+0 search/1/wt \<?XML broken XML document text
+!:mime application/xml
+!:strength - 10
+
+
+# SGML, mostly from rph@sq
+0 search/4096/cwt \<!doctype exported SGML document text
+0 search/4096/cwt \<!subdoc exported SGML subdocument text
+0 search/4096/cwt \<!-- exported SGML document text
+!:strength - 10
+
+# Web browser cookie files
+# (Mozilla, Galeon, Netscape 4, Konqueror..)
+# Ulf Harnhammar <ulfh@update.uu.se>
+0 search/1 #\ HTTP\ Cookie\ File Web browser cookie text
+0 search/1 #\ Netscape\ HTTP\ Cookie\ File Netscape cookie text
+0 search/1 #\ KDE\ Cookie\ File Konqueror cookie text
+
+#------------------------------------------------------------------------
+# $File: sharc,v 1.6 2009/09/19 16:28:12 christos Exp $
+# file(1) magic for sharc files
+#
+# SHARC DSP, MIDI SysEx and RiscOS filetype definitions added by
+# FutureGroove Music (dsp@futuregroove.de)
+
+#------------------------------------------------------------------------
+#0 string Draw RiscOS Drawfile
+#0 string PACK RiscOS PackdDir archive
+
+#------------------------------------------------------------------------
+# SHARC DSP stuff (based on the FGM SHARC DSP SDK)
+
+#0 string =! Assembler source
+#0 string Analog ADi asm listing file
+0 string .SYSTEM SHARC architecture file
+0 string .system SHARC architecture file
+
+0 leshort 0x521C SHARC COFF binary
+>2 leshort >1 , %d sections
+>>12 lelong >0 , not stripped
+
+#------------------------------------------------------------------------------
+# $File$
+# sinclair: file(1) sinclair QL
+
+# additions to /etc/magic by Thomas M. Ott (ThMO)
+
+# Sinclair QL floppy disk formats (ThMO)
+0 string =QL5 QL disk dump data,
+>3 string =A 720 KB,
+>3 string =B 1.44 MB,
+>3 string =C 3.2 MB,
+>4 string >\0 label:%.10s
+
+# Sinclair QL OS dump (ThMO)
+# (NOTE: if `file' would be able to use indirect references in a endian format
+# differing from the natural host format, this could be written more
+# reliably and faster...)
+#
+# we *can't* lookup QL OS code dumps, because `file' is UNABLE to read more
+# than the first 8K of a file... #-(
+#
+#0 belong =0x30000
+#>49124 belong <47104
+#>>49128 belong <47104
+#>>>49132 belong <47104
+#>>>>49136 belong <47104 QL OS dump data,
+#>>>>>49148 string >\0 type %.3s,
+#>>>>>49142 string >\0 version %.4s
+
+# Sinclair QL firmware executables (ThMO)
+0 string NqNqNq`\004 QL firmware executable (BCPL)
+
+# Sinclair QL libraries (was ThMO)
+0 beshort 0xFB01 QDOS object
+>2 pstring x '%s'
+
+# Sinclair QL executables (was ThMO)
+4 belong 0x4AFB QDOS executable
+>9 pstring x '%s'
+
+# Sinclair QL ROM (ThMO)
+0 belong =0x4AFB0001 QL plugin-ROM data,
+>9 pstring =\0 un-named
+>9 pstring >\0 named: %s
+# Type: SiSU Markup Language
+# URL: http://www.sisudoc.org/
+# From: Ralph Amissah <ralph.amissah@gmail.com>
+
+0 regex \^%?[\ \t]*SiSU[\ \t]+insert SiSU text insert
+>5 regex [0-9.]+ %s
+
+0 regex \^%[\ \t]+SiSU[\ \t]+master SiSU text master
+>5 regex [0-9.]+ %s
+
+0 regex \^%?[\ \t]*SiSU[\ \t]+text SiSU text
+>5 regex [0-9.]+ %s
+
+0 regex \^%?[\ \t]*SiSU[\ \t][0-9.]+ SiSU text
+>5 regex [0-9.]+ %s
+
+0 regex \^%*[\ \t]*sisu-[0-9.]+ SiSU text
+>5 regex [0-9.]+ %s
+
+#------------------------------------------------------------------------------
+# $File$
+# Sketch Drawings: http://sketch.sourceforge.net/
+# From: Edwin Mons <e@ik.nu>
+0 search/1 ##Sketch Sketch document text
+
+#-----------------------------------------------
+# $File$
+# GNU Smalltalk image, starting at version 1.6.2
+# From: catull_us@yahoo.com
+#
+0 string GSTIm\0\0 GNU SmallTalk
+# little-endian
+>7 byte&1 =0 LE image version
+>>10 byte x %d.
+>>9 byte x \b%d.
+>>8 byte x \b%d
+#>>12 lelong x , data: %ld
+#>>16 lelong x , table: %ld
+#>>20 lelong x , memory: %ld
+# big-endian
+>7 byte&1 =1 BE image version
+>>8 byte x %d.
+>>9 byte x \b%d.
+>>10 byte x \b%d
+#>>12 belong x , data: %ld
+#>>16 belong x , table: %ld
+#>>20 belong x , memory: %ld
+
+
+
+#------------------------------------------------------------------------------
+# $File$
+# smile: file(1) magic for Smile serialization
+#
+# The Smile serialization format uses a 4-byte header:
+#
+# Constant byte #0: 0x3A (ASCII ':')
+# Constant byte #1: 0x29 (ASCII ')')
+# Constant byte #2: 0x0A (ASCII linefeed, '\n')
+# Variable byte #3, consisting of bits:
+# Bits 4-7 (4 MSB): 4-bit version number
+# Bits 3: Reserved
+# Bit 2 (mask 0x04): Whether raw binary (unescaped 8-bit) values may be present in content
+# Bit 1 (mask 0x02): Whether shared String value checking was enabled during encoding, default false
+# Bit 0 (mask 0x01): Whether shared property name checking was enabled during encoding, default true
+#
+# Reference: http://wiki.fasterxml.com/SmileFormatSpec
+# Created by: Pierre-Alexandre Meyer <pierre@mouraf.org>
+
+# Detection
+0 string :)\n Smile binary data
+
+# Versioning
+>3 byte&0xF0 x version %d:
+
+# Properties
+>3 byte&0x04 0x04 binary raw,
+>3 byte&0x04 0x00 binary encoded,
+>3 byte&0x02 0x02 shared String values enabled,
+>3 byte&0x02 0x00 shared String values disabled,
+>3 byte&0x01 0x01 shared field names enabled
+>3 byte&0x01 0x00 shared field names disabled
+
+
+#------------------------------------------------------------------------------
+# $File: sniffer,v 1.18 2011/08/08 08:49:27 christos Exp $
+# sniffer: file(1) magic for packet capture files
+#
+# From: guy@alum.mit.edu (Guy Harris)
+#
+
+#
+# Microsoft Network Monitor 1.x capture files.
+#
+0 string RTSS NetMon capture file
+>5 byte x - version %d
+>4 byte x \b.%d
+>6 leshort 0 (Unknown)
+>6 leshort 1 (Ethernet)
+>6 leshort 2 (Token Ring)
+>6 leshort 3 (FDDI)
+>6 leshort 4 (ATM)
+>6 leshort >4 (type %d)
+
+#
+# Microsoft Network Monitor 2.x capture files.
+#
+0 string GMBU NetMon capture file
+>5 byte x - version %d
+>4 byte x \b.%d
+>6 leshort 0 (Unknown)
+>6 leshort 1 (Ethernet)
+>6 leshort 2 (Token Ring)
+>6 leshort 3 (FDDI)
+>6 leshort 4 (ATM)
+>6 leshort 5 (IP-over-IEEE 1394)
+>6 leshort 6 (802.11)
+>6 leshort 7 (Raw IP)
+>6 leshort 8 (Raw IP)
+>6 leshort 9 (Raw IP)
+>6 leshort >9 (type %d)
+
+#
+# Network General Sniffer capture files.
+# Sorry, make that "Network Associates Sniffer capture files."
+# Sorry, make that "Network General old DOS Sniffer capture files."
+#
+0 string TRSNIFF\ data\ \ \ \ \032 Sniffer capture file
+>33 byte 2 (compressed)
+>23 leshort x - version %d
+>25 leshort x \b.%d
+>32 byte 0 (Token Ring)
+>32 byte 1 (Ethernet)
+>32 byte 2 (ARCNET)
+>32 byte 3 (StarLAN)
+>32 byte 4 (PC Network broadband)
+>32 byte 5 (LocalTalk)
+>32 byte 6 (Znet)
+>32 byte 7 (Internetwork Analyzer)
+>32 byte 9 (FDDI)
+>32 byte 10 (ATM)
+
+#
+# Cinco Networks NetXRay capture files.
+# Sorry, make that "Network General Sniffer Basic capture files."
+# Sorry, make that "Network Associates Sniffer Basic capture files."
+# Sorry, make that "Network Associates Sniffer Basic, and Windows
+# Sniffer Pro", capture files."
+# Sorry, make that "Network General Sniffer capture files."
+# Sorry, make that "NetScout Sniffer capture files."
+#
+0 string XCP\0 NetXRay capture file
+>4 string >\0 - version %s
+>44 leshort 0 (Ethernet)
+>44 leshort 1 (Token Ring)
+>44 leshort 2 (FDDI)
+>44 leshort 3 (WAN)
+>44 leshort 8 (ATM)
+>44 leshort 9 (802.11)
+
+#
+# "libpcap" capture files.
+# (We call them "tcpdump capture file(s)" for now, as "tcpdump" is
+# the main program that uses that format, but there are other programs
+# that use "libpcap", or that use the same capture file format.)
+#
+0 name pcap-be
+>4 beshort x - version %d
+>6 beshort x \b.%d
+>20 belong 0 (No link-layer encapsulation
+>20 belong 1 (Ethernet
+>20 belong 2 (3Mb Ethernet
+>20 belong 3 (AX.25
+>20 belong 4 (ProNET
+>20 belong 5 (CHAOS
+>20 belong 6 (Token Ring
+>20 belong 7 (BSD ARCNET
+>20 belong 8 (SLIP
+>20 belong 9 (PPP
+>20 belong 10 (FDDI
+>20 belong 11 (RFC 1483 ATM
+>20 belong 12 (raw IP
+>20 belong 13 (BSD/OS SLIP
+>20 belong 14 (BSD/OS PPP
+>20 belong 19 (Linux ATM Classical IP
+>20 belong 50 (PPP or Cisco HDLC
+>20 belong 51 (PPP-over-Ethernet
+>20 belong 99 (Symantec Enterprise Firewall
+>20 belong 100 (RFC 1483 ATM
+>20 belong 101 (raw IP
+>20 belong 102 (BSD/OS SLIP
+>20 belong 103 (BSD/OS PPP
+>20 belong 104 (BSD/OS Cisco HDLC
+>20 belong 105 (802.11
+>20 belong 106 (Linux Classical IP over ATM
+>20 belong 107 (Frame Relay
+>20 belong 108 (OpenBSD loopback
+>20 belong 109 (OpenBSD IPsec encrypted
+>20 belong 112 (Cisco HDLC
+>20 belong 113 (Linux "cooked"
+>20 belong 114 (LocalTalk
+>20 belong 117 (OpenBSD PFLOG
+>20 belong 119 (802.11 with Prism header
+>20 belong 122 (RFC 2625 IP over Fibre Channel
+>20 belong 123 (SunATM
+>20 belong 127 (802.11 with radiotap header
+>20 belong 129 (Linux ARCNET
+>20 belong 138 (Apple IP over IEEE 1394
+>20 belong 139 (MTP2 with pseudo-header
+>20 belong 140 (MTP2
+>20 belong 141 (MTP3
+>20 belong 142 (SCCP
+>20 belong 143 (DOCSIS
+>20 belong 144 (IrDA
+>20 belong 147 (Private use 0
+>20 belong 148 (Private use 1
+>20 belong 149 (Private use 2
+>20 belong 150 (Private use 3
+>20 belong 151 (Private use 4
+>20 belong 152 (Private use 5
+>20 belong 153 (Private use 6
+>20 belong 154 (Private use 7
+>20 belong 155 (Private use 8
+>20 belong 156 (Private use 9
+>20 belong 157 (Private use 10
+>20 belong 158 (Private use 11
+>20 belong 159 (Private use 12
+>20 belong 160 (Private use 13
+>20 belong 161 (Private use 14
+>20 belong 162 (Private use 15
+>20 belong 163 (802.11 with AVS header
+>20 belong 165 (BACnet MS/TP
+>20 belong 166 (PPPD
+>20 belong 169 (GPRS LLC
+>20 belong 177 (Linux LAPD
+>20 belong 187 (Bluetooth HCI H4
+>20 belong 189 (Linux USB
+>20 belong 192 (PPI
+>20 belong 195 (802.15.4
+>20 belong 196 (SITA
+>20 belong 197 (Endace ERF
+>20 belong 201 (Bluetooth HCI H4 with pseudo-header
+>20 belong 202 (AX.25 with KISS header
+>20 belong 203 (LAPD
+>20 belong 204 (PPP with direction pseudo-header
+>20 belong 205 (Cisco HDLC with direction pseudo-header
+>20 belong 206 (Frame Relay with direction pseudo-header
+>20 belong 209 (Linux IPMB
+>20 belong 215 (802.15.4 with non-ASK PHY header
+>20 belong 220 (Memory-mapped Linux USB
+>20 belong 224 (Fibre Channel FC-2
+>20 belong 225 (Fibre Channel FC-2 with frame delimiters
+>20 belong 226 (Solaris IPNET
+>20 belong 227 (SocketCAN
+>20 belong 228 (Raw IPv4
+>20 belong 229 (Raw IPv6
+>20 belong 230 (802.15.4 without FCS
+>20 belong 231 (D-Bus messages
+>20 belong 235 (DVB-CI
+>20 belong 236 (MUX27010
+>20 belong 237 (STANAG 5066 D_PDUs
+>20 belong 239 (Linux netlink NFLOG messages
+>20 belong 240 (Hilscher netAnalyzer
+>20 belong 241 (Hilscher netAnalyzer with delimiters
+>20 belong 242 (IP-over-Infiniband
+>20 belong 243 (MPEG-2 Transport Stream packets
+>20 belong 244 (ng4t ng40
+>20 belong 245 (NFC LLCP
+>20 belong 247 (Infiniband
+>20 belong 248 (SCTP
+>16 belong x \b, capture length %d)
+
+0 ubelong 0xa1b2c3d4 tcpdump capture file (big-endian)
+!:mime application/vnd.tcpdump.pcap
+>0 use pcap-be
+0 ulelong 0xa1b2c3d4 tcpdump capture file (little-endian)
+!:mime application/vnd.tcpdump.pcap
+>0 use \^pcap-be
+
+#
+# "libpcap"-with-Alexey-Kuznetsov's-patches capture files.
+# (We call them "tcpdump capture file(s)" for now, as "tcpdump" is
+# the main program that uses that format, but there are other programs
+# that use "libpcap", or that use the same capture file format.)
+#
+0 ubelong 0xa1b2cd34 extended tcpdump capture file (big-endian)
+>0 use pcap-be
+0 ulelong 0xa1b2cd34 extended tcpdump capture file (little-endian)
+>0 use \^pcap-be
+
+#
+# "pcap-ng" capture files.
+# http://www.winpcap.org/ntar/draft/PCAP-DumpFileFormat.html
+# Pcap-ng files can contain multiple sections. Printing the endianness,
+# snaplen, or other information from the first SHB may be misleading.
+#
+0 ubelong 0x0a0d0d0a
+>8 ubelong 0x1a2b3c4d pcap-ng capture file
+>>12 beshort x - version %d
+>>14 beshort x \b.%d
+0 ulelong 0x0a0d0d0a
+>8 ulelong 0x1a2b3c4d pcap-ng capture file
+>>12 leshort x - version %d
+>>14 leshort x \b.%d
+
+#
+# AIX "iptrace" capture files.
+#
+0 string iptrace\ 1.0 "iptrace" capture file
+0 string iptrace\ 2.0 "iptrace" capture file
+
+#
+# Novell LANalyzer capture files.
+#
+0 leshort 0x1001 LANalyzer capture file
+0 leshort 0x1007 LANalyzer capture file
+
+#
+# HP-UX "nettl" capture files.
+#
+0 string \x54\x52\x00\x64\x00 "nettl" capture file
+
+#
+# RADCOM WAN/LAN Analyzer capture files.
+#
+0 string \x42\xd2\x00\x34\x12\x66\x22\x88 RADCOM WAN/LAN Analyzer capture file
+
+#
+# NetStumbler log files. Not really packets, per se, but about as
+# close as you can get. These are log files from NetStumbler, a
+# Windows program, that scans for 802.11b networks.
+#
+0 string NetS NetStumbler log file
+>8 lelong x \b, %d stations found
+
+#
+# *Peek tagged capture files.
+#
+0 string \177ver EtherPeek/AiroPeek/OmniPeek capture file
+
+#
+# Visual Networks traffic capture files.
+#
+0 string \x05VNF Visual Networks traffic capture file
+
+#
+# Network Instruments Observer capture files.
+#
+0 string ObserverPktBuffe Network Instruments Observer capture file
+
+#
+# Files from Accellent Group's 5View products.
+#
+0 string \xaa\xaa\xaa\xaa 5View capture file
+
+#------------------------------------------------------------------------------
+# $File$
+# softquad: file(1) magic for SoftQuad Publishing Software
+#
+# Author/Editor and RulesBuilder
+#
+# XXX - byte order?
+#
+0 string \<!SQ\ DTD> Compiled SGML rules file
+>9 string >\0 Type %s
+0 string \<!SQ\ A/E> A/E SGML Document binary
+>9 string >\0 Type %s
+0 string \<!SQ\ STS> A/E SGML binary styles file
+>9 string >\0 Type %s
+0 short 0xc0de Compiled PSI (v1) data
+0 short 0xc0da Compiled PSI (v2) data
+>3 string >\0 (%s)
+# Binary sqtroff font/desc files...
+0 short 0125252 SoftQuad DESC or font file binary
+>2 short >0 - version %d
+# Bitmaps...
+0 search/1 SQ\ BITMAP1 SoftQuad Raster Format text
+#0 string SQ\ BITMAP2 SoftQuad Raster Format data
+# sqtroff intermediate language (replacement for ditroff int. lang.)
+0 string X\ SoftQuad troff Context intermediate
+>2 string 495 for AT&T 495 laser printer
+>2 string hp for Hewlett-Packard LaserJet
+>2 string impr for IMAGEN imPRESS
+>2 string ps for PostScript
+
+# From: Michael Piefel <piefel@debian.org>
+# sqtroff intermediate language (replacement for ditroff int. lang.)
+0 string X\ 495 SoftQuad troff Context intermediate for AT&T 495 laser printer
+0 string X\ hp SoftQuad troff Context intermediate for HP LaserJet
+0 string X\ impr SoftQuad troff Context intermediate for IMAGEN imPRESS
+0 string X\ ps SoftQuad troff Context intermediate for PostScript
+
+#------------------------------------------------------------------------------
+# $File$
+# spec: file(1) magic for SPEC raw results (*.raw, *.rsf)
+#
+# Cloyce D. Spradling <cloyce@headgear.org>
+
+0 string spec SPEC
+>4 string .cpu CPU
+>>8 string <: \b%.4s
+>>12 string . raw result text
+
+17 string version=SPECjbb SPECjbb
+>32 string <: \b%.4s
+>>37 string <: v%.4s raw result text
+
+0 string BEGIN\040SPECWEB SPECweb
+>13 string <: \b%.2s
+>>15 string _SSL \b_SSL
+>>>20 string <: v%.4s raw result text
+>>16 string <: v%.4s raw result text
+
+#------------------------------------------------------------------------------
+# $File: spectrum,v 1.6 2009/09/19 16:28:12 christos Exp $
+# spectrum: file(1) magic for Spectrum emulator files.
+#
+# John Elliott <jce@seasip.demon.co.uk>
+
+#
+# Spectrum +3DOS header
+#
+0 string PLUS3DOS\032 Spectrum +3 data
+>15 byte 0 - BASIC program
+>15 byte 1 - number array
+>15 byte 2 - character array
+>15 byte 3 - memory block
+>>16 belong 0x001B0040 (screen)
+>15 byte 4 - Tasword document
+>15 string TAPEFILE - ZXT tapefile
+#
+# Tape file. This assumes the .TAP starts with a Spectrum-format header,
+# which nearly all will.
+#
+# Update: Sanity-check string contents to be printable.
+# -Adam Buchbinder <adam.buchbinder@gmail.com>
+#
+0 string \023\000\000
+>4 string >\0
+>>4 string <\177 Spectrum .TAP data "%-10.10s"
+>>>3 byte 0 - BASIC program
+>>>3 byte 1 - number array
+>>>3 byte 2 - character array
+>>>3 byte 3 - memory block
+>>>>14 belong 0x001B0040 (screen)
+
+# The following three blocks are from pak21-spectrum@srcf.ucam.org
+# TZX tape images
+0 string ZXTape!\x1a Spectrum .TZX data
+>8 byte x version %d
+>9 byte x \b.%d
+
+# RZX input recording files
+0 string RZX! Spectrum .RZX data
+>4 byte x version %d
+>5 byte x \b.%d
+
+# Floppy disk images
+0 string MV\ -\ CPCEMU\ Disk-Fil Amstrad/Spectrum .DSK data
+0 string MV\ -\ CPC\ format\ Dis Amstrad/Spectrum DU54 .DSK data
+0 string EXTENDED\ CPC\ DSK\ Fil Amstrad/Spectrum Extended .DSK data
+0 string SINCLAIR Spectrum .SCL Betadisk image
+
+# Hard disk images
+0 string RS-IDE\x1a Spectrum .HDF hard disk image
+>7 byte x \b, version 0x%02x
+
+#------------------------------------------------------------------------------
+# $File: sql,v 1.14 2014/04/28 12:04:50 christos Exp $
+# sql: file(1) magic for SQL files
+#
+# From: "Marty Leisner" <mleisner@eng.mc.xerox.com>
+# Recognize some MySQL files.
+# Elan Ruusamae <glen@delfi.ee>, added MariaDB signatures
+# from https://bazaar.launchpad.net/~maria-captains/maria/5.5/view/head:/support-files/magic
+#
+0 beshort 0xfe01 MySQL table definition file
+>2 byte x Version %d
+0 belong&0xffffff00 0xfefe0700 MySQL MyISAM index file
+>3 byte x Version %d
+0 belong&0xffffff00 0xfefe0800 MySQL MyISAM compressed data file
+>3 byte x Version %d
+0 belong&0xffffff00 0xfefe0900 MySQL Maria index file
+>3 byte x Version %d
+0 belong&0xffffff00 0xfefe0A00 MySQL Maria compressed data file
+>3 byte x Version %d
+0 belong&0xffffff00 0xfefe0500 MySQL ISAM index file
+>3 byte x Version %d
+0 belong&0xffffff00 0xfefe0600 MySQL ISAM compressed data file
+>3 byte x Version %d
+0 string \376bin MySQL replication log
+0 belong&0xffffff00 0xfefe0b00
+>4 string MARIALOG MySQL Maria transaction log file
+>>3 byte x Version %d
+0 belong&0xffffff00 0xfefe0c00
+>4 string MACF MySQL Maria control file
+>>3 byte x Version %d
+
+#------------------------------------------------------------------------------
+# iRiver H Series database file
+# From Ken Guest <ken@linux.ie>
+# As observed from iRivNavi.iDB and unencoded firmware
+#
+0 string iRivDB iRiver Database file
+>11 string >\0 Version %s
+>39 string iHP-100 [H Series]
+
+#------------------------------------------------------------------------------
+# SQLite database files
+# Ken Guest <ken@linux.ie>, Ty Sarna, Zack Weinberg
+#
+# Version 1 used GDBM internally; its files cannot be distinguished
+# from other GDBM files.
+#
+# Version 2 used this format:
+0 string **\ This\ file\ contains\ an\ SQLite SQLite 2.x database
+
+# Version 3 of SQLite allows applications to embed their own "user version"
+# number in the database at offset 60. Later, SQLite added an "application id"
+# at offset 68 that is preferred over "user version" for indicating the
+# associated application.
+#
+0 string SQLite\ format\ 3
+>60 belong =0x5f4d544e Monotone source repository - SQLite3 database
+>68 belong =0x0f055112 Fossil checkout - SQLite3 database
+>68 belong =0x0f055113 Fossil global configuration - SQLite3 database
+>68 belong =0x0f055111 Fossil repository - SQLite3 database
+>68 belong =0x42654462 Bentley Systems BeSQLite Database - SQLite3 database
+>68 belong =0x42654c6e Bentley Systems Localization File - SQLite3 database
+>68 belong =0x47504b47 OGC GeoPackage file - SQLite3 database
+>68 default x SQLite 3.x database
+>>68 belong !0 \b, application id %u
+>>60 belong !0 \b, user version %d
+
+# SQLite Write-Ahead Log from SQLite version >= 3.7.0
+# http://www.sqlite.org/fileformat.html#walformat
+0 belong&0xfffffffe 0x377f0682 SQLite Write-Ahead Log,
+>4 belong x version %d
+
+# SQLite Rollback Journal
+# http://www.sqlite.org/fileformat.html#rollbackjournal
+0 string \xd9\xd5\x05\xf9\x20\xa1\x63\xd7 SQLite Rollback Journal
+
+# Panasonic channel list database svl.bin or svl.db added by Joerg Jenderek
+# http://www.ullrich.es/job/service-menue/panasonic/panasonic-sendersortierung-sat-am-pc/
+# pceditor_V2003.jar
+0 string PSDB\0 Panasonic channel list database
+>126 string SQLite\ format\ 3
+>>&-15 indirect x \b; contains
+# Type: OpenSSH key files
+# From: Nicolas Collignon <tsointsoin@gmail.com>
+
+0 string SSH\ PRIVATE\ KEY OpenSSH RSA1 private key,
+>28 string >\0 version %s
+0 string -----BEGIN\ OPENSSH\ PRIVATE\ KEY----- OpenSSH private key
+
+0 string ssh-dss\ OpenSSH DSA public key
+0 string ssh-rsa\ OpenSSH RSA public key
+0 string ecdsa-sha2-nistp256 OpenSSH ECDSA public key
+0 string ecdsa-sha2-nistp384 OpenSSH ECDSA public key
+0 string ecdsa-sha2-nistp521 OpenSSH ECDSA public key
+0 string ssh-ed25519 OpenSSH ED25519 public key
+# Type: OpenSSL certificates/key files
+# From: Nicolas Collignon <tsointsoin@gmail.com>
+
+0 string -----BEGIN\ CERTIFICATE----- PEM certificate
+0 string -----BEGIN\ CERTIFICATE\ REQ PEM certificate request
+0 string -----BEGIN\ RSA\ PRIVATE PEM RSA private key
+0 string -----BEGIN\ DSA\ PRIVATE PEM DSA private key
+0 string -----BEGIN\ EC\ PRIVATE PEM EC private key
+
+#------------------------------------------------------------------------------
+# $File: sun,v 1.26 2014/03/29 15:40:34 christos Exp $
+# sun: file(1) magic for Sun machines
+#
+# Values for big-endian Sun (MC680x0, SPARC) binaries on pre-5.x
+# releases. (5.x uses ELF.) Entries for executables without an
+# architecture type, used before the 68020-based Sun-3's came out,
+# are in aout, as they're indistinguishable from other big-endian
+# 32-bit a.out files.
+#
+0 belong&077777777 0600413 a.out SunOS SPARC demand paged
+>0 byte &0x80
+>>20 belong <4096 shared library
+>>20 belong =4096 dynamically linked executable
+>>20 belong >4096 dynamically linked executable
+>0 byte ^0x80 executable
+>16 belong >0 not stripped
+
+0 belong&077777777 0600410 a.out SunOS SPARC pure
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80 executable
+>16 belong >0 not stripped
+
+0 belong&077777777 0600407 a.out SunOS SPARC
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80 executable
+>16 belong >0 not stripped
+
+0 belong&077777777 0400413 a.out SunOS mc68020 demand paged
+>0 byte &0x80
+>>20 belong <4096 shared library
+>>20 belong =4096 dynamically linked executable
+>>20 belong >4096 dynamically linked executable
+>0 byte ^0x80 executable
+>16 belong >0 not stripped
+
+0 belong&077777777 0400410 a.out SunOS mc68020 pure
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80 executable
+>16 belong >0 not stripped
+
+0 belong&077777777 0400407 a.out SunOS mc68020
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80 executable
+>16 belong >0 not stripped
+
+0 belong&077777777 0200413 a.out SunOS mc68010 demand paged
+>0 byte &0x80
+>>20 belong <4096 shared library
+>>20 belong =4096 dynamically linked executable
+>>20 belong >4096 dynamically linked executable
+>0 byte ^0x80 executable
+>16 belong >0 not stripped
+
+0 belong&077777777 0200410 a.out SunOS mc68010 pure
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80 executable
+>16 belong >0 not stripped
+
+0 belong&077777777 0200407 a.out SunOS mc68010
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80 executable
+>16 belong >0 not stripped
+
+#
+# Core files. "SPARC 4.x BCP" means "core file from a SunOS 4.x SPARC
+# binary executed in compatibility mode under SunOS 5.x".
+#
+0 belong 0x080456 SunOS core file
+>4 belong 432 (SPARC)
+>>132 string >\0 from '%s'
+>>116 belong =3 (quit)
+>>116 belong =4 (illegal instruction)
+>>116 belong =5 (trace trap)
+>>116 belong =6 (abort)
+>>116 belong =7 (emulator trap)
+>>116 belong =8 (arithmetic exception)
+>>116 belong =9 (kill)
+>>116 belong =10 (bus error)
+>>116 belong =11 (segmentation violation)
+>>116 belong =12 (bad argument to system call)
+>>116 belong =29 (resource lost)
+>>120 belong x (T=%dK,
+>>124 belong x D=%dK,
+>>128 belong x S=%dK)
+>4 belong 826 (68K)
+>>128 string >\0 from '%s'
+>4 belong 456 (SPARC 4.x BCP)
+>>152 string >\0 from '%s'
+# Sun SunPC
+0 long 0xfa33c08e SunPC 4.0 Hard Disk
+0 string #SUNPC_CONFIG SunPC 4.0 Properties Values
+# Sun snoop (see RFC 1761, which describes the capture file format,
+# RFC 3827, which describes some additional datalink types, and
+# http://www.iana.org/assignments/snoop-datalink-types/snoop-datalink-types.xml,
+# which is the IANA registry of Snoop datalink types)
+#
+0 string snoop Snoop capture file
+>8 belong >0 - version %d
+>12 belong 0 (IEEE 802.3)
+>12 belong 1 (IEEE 802.4)
+>12 belong 2 (IEEE 802.5)
+>12 belong 3 (IEEE 802.6)
+>12 belong 4 (Ethernet)
+>12 belong 5 (HDLC)
+>12 belong 6 (Character synchronous)
+>12 belong 7 (IBM channel-to-channel adapter)
+>12 belong 8 (FDDI)
+>12 belong 9 (Other)
+>12 belong 10 (type %d)
+>12 belong 11 (type %d)
+>12 belong 12 (type %d)
+>12 belong 13 (type %d)
+>12 belong 14 (type %d)
+>12 belong 15 (type %d)
+>12 belong 16 (Fibre Channel)
+>12 belong 17 (ATM)
+>12 belong 18 (ATM Classical IP)
+>12 belong 19 (type %d)
+>12 belong 20 (type %d)
+>12 belong 21 (type %d)
+>12 belong 22 (type %d)
+>12 belong 23 (type %d)
+>12 belong 24 (type %d)
+>12 belong 25 (type %d)
+>12 belong 26 (IP over Infiniband)
+>12 belong >26 (type %d)
+
+#---------------------------------------------------------------------------
+# The following entries have been tested by Duncan Laurie <duncan@sun.com> (a
+# lead Sun/Cobalt developer) who agrees that they are good and worthy of
+# inclusion.
+
+# Boot ROM images for Sun/Cobalt Linux server appliances
+0 string Cobalt\ Networks\ Inc.\nFirmware\ v Paged COBALT boot rom
+>38 string x V%.4s
+
+# New format for Sun/Cobalt boot ROMs is annoying, it stores the version code
+# at the very end where file(1) can't get it.
+0 string CRfs COBALT boot rom data (Flat boot rom or file system)
+
+#------------------------------------------------------------------------------
+# msx: file(1) magic for the SymbOS operating system
+# http://www.symbos.de
+# Fabio R. Schmidlin <frs@pop.com.br>
+
+# SymbOS EXE file
+0x30 string SymExe SymbOS executable
+>0x36 ubyte x v%c
+>0x37 ubyte x \b.%c
+>0xF string x \b, name: %s
+
+# SymbOS DOX document
+0 string INFOq\0 SymbOS DOX document
+
+# Symbos driver
+0 string SMD1 SymbOS driver
+>19 byte x \b, name: %c
+>20 byte x \b%c
+>21 byte x \b%c
+>22 byte x \b%c
+>23 byte x \b%c
+>24 byte x \b%c
+>25 byte x \b%c
+>26 byte x \b%c
+>27 byte x \b%c
+>28 byte x \b%c
+>29 byte x \b%c
+>30 byte x \b%c
+>31 byte x \b%c
+
+# Symbos video
+0 string SymVid SymbOS video
+>6 ubyte x v%c
+>7 ubyte x \b.%c
+
+# Soundtrakker 128 ST2 music
+0 byte 0
+>0xC string \x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x40\x00 Soundtrakker 128 ST2 music,
+>>1 string x name: %s
+
+
+
+#------------------------------------------------------------------------
+# $File: sysex,v 1.7 2013/09/16 15:12:42 christos Exp $
+# sysex: file(1) magic for MIDI sysex files
+#
+# GRR: original 1 byte test at offset was too general as it catches also many FATs of DOS filesystems
+# where real SYStem EXclusive messages at offset 1 are limited to seven bits
+# http://en.wikipedia.org/wiki/MIDI
+0 ubeshort&0xFF80 0xF000 SysEx File -
+
+# North American Group
+>1 byte 0x01 Sequential
+>1 byte 0x02 IDP
+>1 byte 0x03 OctavePlateau
+>1 byte 0x04 Moog
+>1 byte 0x05 Passport
+>1 byte 0x06 Lexicon
+>1 byte 0x07 Kurzweil/Future Retro
+>>3 byte 0x77 777
+>>4 byte 0x00 Bank
+>>4 byte 0x01 Song
+>>5 byte 0x0f 16
+>>5 byte 0x0e 15
+>>5 byte 0x0d 14
+>>5 byte 0x0c 13
+>>5 byte 0x0b 12
+>>5 byte 0x0a 11
+>>5 byte 0x09 10
+>>5 byte 0x08 9
+>>5 byte 0x07 8
+>>5 byte 0x06 7
+>>5 byte 0x05 6
+>>5 byte 0x04 5
+>>5 byte 0x03 4
+>>5 byte 0x02 3
+>>5 byte 0x01 2
+>>5 byte 0x00 1
+>>5 byte 0x10 (ALL)
+>>2 byte x \b, Channel %d
+>1 byte 0x08 Fender
+>1 byte 0x09 Gulbransen
+>1 byte 0x0a AKG
+>1 byte 0x0b Voyce
+>1 byte 0x0c Waveframe
+>1 byte 0x0d ADA
+>1 byte 0x0e Garfield
+>1 byte 0x0f Ensoniq
+>1 byte 0x10 Oberheim
+>>2 byte 0x06 Matrix 6 series
+>>3 byte 0x0A Dump (All)
+>>3 byte 0x01 Dump (Bank)
+>>4 belong 0x0002040E Matrix 1000
+>>>11 byte <2 User bank %d
+>>>11 byte >1 Preset bank %d
+>1 byte 0x11 Apple
+>1 byte 0x12 GreyMatter
+>1 byte 0x14 PalmTree
+>1 byte 0x15 JLCooper
+>1 byte 0x16 Lowrey
+>1 byte 0x17 AdamsSmith
+>1 byte 0x18 E-mu
+>1 byte 0x19 Harmony
+>1 byte 0x1a ART
+>1 byte 0x1b Baldwin
+>1 byte 0x1c Eventide
+>1 byte 0x1d Inventronics
+>1 byte 0x1f Clarity
+
+# European Group
+>1 byte 0x21 SIEL
+>1 byte 0x22 Synthaxe
+>1 byte 0x24 Hohner
+>1 byte 0x25 Twister
+>1 byte 0x26 Solton
+>1 byte 0x27 Jellinghaus
+>1 byte 0x28 Southworth
+>1 byte 0x29 PPG
+>1 byte 0x2a JEN
+>1 byte 0x2b SSL
+>1 byte 0x2c AudioVertrieb
+
+>1 byte 0x2f ELKA
+>>3 byte 0x09 EK-44
+
+>1 byte 0x30 Dynacord
+>1 byte 0x31 Jomox
+>1 byte 0x33 Clavia
+>1 byte 0x39 Soundcraft
+# Some Waldorf info from http://Stromeko.Synth.net/Downloads#WaldorfDocs
+>1 byte 0x3e Waldorf
+>>2 byte 0x00 microWave
+>>2 byte 0x0E microwave2 / XT
+>>2 byte 0x0F Q / Q+
+>>3 byte =0 (default id)
+>>3 byte >0 (
+>>>3 byte <0x7F \bdevice %d)
+>>>3 byte =0x7F \bbroadcast id)
+>>3 byte 0x7f Microwave I
+>>>4 byte 0x00 SNDR (Sound Request)
+>>>4 byte 0x10 SNDD (Sound Dump)
+>>>4 byte 0x20 SNDP (Sound Parameter Change)
+>>>4 byte 0x30 SNDQ (Sound Parameter Inquiry)
+>>>4 byte 0x70 BOOT (Sound Reserved)
+>>>4 byte 0x01 MULR (Multi Request)
+>>>4 byte 0x11 MULD (Multi Dump)
+>>>4 byte 0x21 MULP (Multi Parameter Change)
+>>>4 byte 0x31 MULQ (Multi Parameter Inquiry)
+>>>4 byte 0x71 OS (Multi Reserved)
+>>>4 byte 0x02 DRMR (Drum Map Request)
+>>>4 byte 0x12 DRMD (Drum Map Dump)
+>>>4 byte 0x22 DRMP (Drum Map Parameter Change)
+>>>4 byte 0x32 DRMQ (Drum Map Parameter Inquiry)
+>>>4 byte 0x72 BIN (Drum Map Reserved)
+>>>4 byte 0x03 PATR (Sequencer Pattern Request)
+>>>4 byte 0x13 PATD (Sequencer Pattern Dump)
+>>>4 byte 0x23 PATP (Sequencer Pattern Parameter Change)
+>>>4 byte 0x33 PATQ (Sequencer Pattern Parameter Inquiry)
+>>>4 byte 0x73 AFM (Sequencer Pattern Reserved)
+>>>4 byte 0x04 GLBR (Global Parameter Request)
+>>>4 byte 0x14 GLBD (Global Parameter Dump)
+>>>4 byte 0x24 GLBP (Global Parameter Parameter Change)
+>>>4 byte 0x34 GLBQ (Global Parameter Parameter Inquiry)
+>>>4 byte 0x07 MODR (Mode Parameter Request)
+>>>4 byte 0x17 MODD (Mode Parameter Dump)
+>>>4 byte 0x27 MODP (Mode Parameter Parameter Change)
+>>>4 byte 0x37 MODQ (Mode Parameter Parameter Inquiry)
+>>2 byte 0x10 microQ
+>>>4 byte 0x00 SNDR (Sound Request)
+>>>4 byte 0x10 SNDD (Sound Dump)
+>>>4 byte 0x20 SNDP (Sound Parameter Change)
+>>>4 byte 0x30 SNDQ (Sound Parameter Inquiry)
+>>>4 byte 0x70 (Sound Reserved)
+>>>4 byte 0x01 MULR (Multi Request)
+>>>4 byte 0x11 MULD (Multi Dump)
+>>>4 byte 0x21 MULP (Multi Parameter Change)
+>>>4 byte 0x31 MULQ (Multi Parameter Inquiry)
+>>>4 byte 0x71 OS (Multi Reserved)
+>>>4 byte 0x02 DRMR (Drum Map Request)
+>>>4 byte 0x12 DRMD (Drum Map Dump)
+>>>4 byte 0x22 DRMP (Drum Map Parameter Change)
+>>>4 byte 0x32 DRMQ (Drum Map Parameter Inquiry)
+>>>4 byte 0x72 BIN (Drum Map Reserved)
+>>>4 byte 0x04 GLBR (Global Parameter Request)
+>>>4 byte 0x14 GLBD (Global Parameter Dump)
+>>>4 byte 0x24 GLBP (Global Parameter Parameter Change)
+>>>4 byte 0x34 GLBQ (Global Parameter Parameter Inquiry)
+>>2 byte 0x11 rackAttack
+>>>4 byte 0x00 SNDR (Sound Parameter Request)
+>>>4 byte 0x10 SNDD (Sound Parameter Dump)
+>>>4 byte 0x20 SNDP (Sound Parameter Parameter Change)
+>>>4 byte 0x30 SNDQ (Sound Parameter Parameter Inquiry)
+>>>4 byte 0x01 PRGR (Program Parameter Request)
+>>>4 byte 0x11 PRGD (Program Parameter Dump)
+>>>4 byte 0x21 PRGP (Program Parameter Parameter Change)
+>>>4 byte 0x31 PRGQ (Program Parameter Parameter Inquiry)
+>>>4 byte 0x71 OS (Program Parameter Reserved)
+>>>4 byte 0x03 PATR (Pattern Parameter Request)
+>>>4 byte 0x13 PATD (Pattern Parameter Dump)
+>>>4 byte 0x23 PATP (Pattern Parameter Parameter Change)
+>>>4 byte 0x33 PATQ (Pattern Parameter Parameter Inquiry)
+>>>4 byte 0x04 GLBR (Global Parameter Request)
+>>>4 byte 0x14 GLBD (Global Parameter Dump)
+>>>4 byte 0x24 GLBP (Global Parameter Parameter Change)
+>>>4 byte 0x34 GLBQ (Global Parameter Parameter Inquiry)
+>>>4 byte 0x05 EFXR (FX Parameter Request)
+>>>4 byte 0x15 EFXD (FX Parameter Dump)
+>>>4 byte 0x25 EFXP (FX Parameter Parameter Change)
+>>>4 byte 0x35 EFXQ (FX Parameter Parameter Inquiry)
+>>>4 byte 0x07 MODR (Mode Command Request)
+>>>4 byte 0x17 MODD (Mode Command Dump)
+>>>4 byte 0x27 MODP (Mode Command Parameter Change)
+>>>4 byte 0x37 MODQ (Mode Command Parameter Inquiry)
+>>2 byte 0x03 Wave
+>>>4 byte 0x00 SBPR (Soundprogram)
+>>>4 byte 0x01 SAPR (Performance)
+>>>4 byte 0x02 SWAVE (Wave)
+>>>4 byte 0x03 SWTBL (Wave control table)
+>>>4 byte 0x04 SVT (Velocity Curve)
+>>>4 byte 0x05 STT (Tuning Table)
+>>>4 byte 0x06 SGLB (Global Parameters)
+>>>4 byte 0x07 SARRMAP (Performance Program Change Map)
+>>>4 byte 0x08 SBPRMAP (Sound Program Change Map)
+>>>4 byte 0x09 SBPRPAR (Sound Parameter)
+>>>4 byte 0x0A SARRPAR (Performance Parameter)
+>>>4 byte 0x0B SINSPAR (Instrument/External Parameter)
+>>>4 byte 0x0F SBULK (Bulk Switch on/off)
+
+# Japanese Group
+>1 byte 0x40 Kawai
+>>3 byte 0x20 K1
+>>3 byte 0x22 K4
+
+>1 byte 0x41 Roland
+>>3 byte 0x14 D-50
+>>3 byte 0x2b U-220
+>>3 byte 0x02 TR-707
+
+>1 byte 0x42 Korg
+>>3 byte 0x19 M1
+
+>1 byte 0x43 Yamaha
+>1 byte 0x44 Casio
+>1 byte 0x46 Kamiya
+>1 byte 0x47 Akai
+>1 byte 0x48 Victor
+>1 byte 0x49 Mesosha
+>1 byte 0x4b Fujitsu
+>1 byte 0x4c Sony
+>1 byte 0x4e Teac
+>1 byte 0x50 Matsushita
+>1 byte 0x51 Fostex
+>1 byte 0x52 Zoom
+>1 byte 0x54 Matsushita
+>1 byte 0x57 Acoustic tech. lab.
+# http://www.midi.org/techspecs/manid.php
+>1 belong&0xffffff00 0x00007400 Ta Horng
+>1 belong&0xffffff00 0x00007500 e-Tek
+>1 belong&0xffffff00 0x00007600 E-Voice
+>1 belong&0xffffff00 0x00007700 Midisoft
+>1 belong&0xffffff00 0x00007800 Q-Sound
+>1 belong&0xffffff00 0x00007900 Westrex
+>1 belong&0xffffff00 0x00007a00 Nvidia*
+>1 belong&0xffffff00 0x00007b00 ESS
+>1 belong&0xffffff00 0x00007c00 Mediatrix
+>1 belong&0xffffff00 0x00007d00 Brooktree
+>1 belong&0xffffff00 0x00007e00 Otari
+>1 belong&0xffffff00 0x00007f00 Key Electronics
+>1 belong&0xffffff00 0x00010000 Shure
+>1 belong&0xffffff00 0x00010100 AuraSound
+>1 belong&0xffffff00 0x00010200 Crystal
+>1 belong&0xffffff00 0x00010300 Rockwell
+>1 belong&0xffffff00 0x00010400 Silicon Graphics
+>1 belong&0xffffff00 0x00010500 Midiman
+>1 belong&0xffffff00 0x00010600 PreSonus
+>1 belong&0xffffff00 0x00010800 Topaz
+>1 belong&0xffffff00 0x00010900 Cast Lightning
+>1 belong&0xffffff00 0x00010a00 Microsoft
+>1 belong&0xffffff00 0x00010b00 Sonic Foundry
+>1 belong&0xffffff00 0x00010c00 Line 6
+>1 belong&0xffffff00 0x00010d00 Beatnik Inc.
+>1 belong&0xffffff00 0x00010e00 Van Koerving
+>1 belong&0xffffff00 0x00010f00 Altech Systems
+>1 belong&0xffffff00 0x00011000 S & S Research
+>1 belong&0xffffff00 0x00011100 VLSI Technology
+>1 belong&0xffffff00 0x00011200 Chromatic
+>1 belong&0xffffff00 0x00011300 Sapphire
+>1 belong&0xffffff00 0x00011400 IDRC
+>1 belong&0xffffff00 0x00011500 Justonic Tuning
+>1 belong&0xffffff00 0x00011600 TorComp
+>1 belong&0xffffff00 0x00011700 Newtek Inc.
+>1 belong&0xffffff00 0x00011800 Sound Sculpture
+>1 belong&0xffffff00 0x00011900 Walker Technical
+>1 belong&0xffffff00 0x00011a00 Digital Harmony
+>1 belong&0xffffff00 0x00011b00 InVision
+>1 belong&0xffffff00 0x00011c00 T-Square
+>1 belong&0xffffff00 0x00011d00 Nemesys
+>1 belong&0xffffff00 0x00011e00 DBX
+>1 belong&0xffffff00 0x00011f00 Syndyne
+>1 belong&0xffffff00 0x00012000 Bitheadz
+>1 belong&0xffffff00 0x00012100 Cakewalk
+>1 belong&0xffffff00 0x00012200 Staccato
+>1 belong&0xffffff00 0x00012300 National Semicon.
+>1 belong&0xffffff00 0x00012400 Boom Theory
+>1 belong&0xffffff00 0x00012500 Virtual DSP Corp
+>1 belong&0xffffff00 0x00012600 Antares
+>1 belong&0xffffff00 0x00012700 Angel Software
+>1 belong&0xffffff00 0x00012800 St Louis Music
+>1 belong&0xffffff00 0x00012900 Lyrrus dba G-VOX
+>1 belong&0xffffff00 0x00012a00 Ashley Audio
+>1 belong&0xffffff00 0x00012b00 Vari-Lite
+>1 belong&0xffffff00 0x00012c00 Summit Audio
+>1 belong&0xffffff00 0x00012d00 Aureal Semicon.
+>1 belong&0xffffff00 0x00012e00 SeaSound
+>1 belong&0xffffff00 0x00012f00 U.S. Robotics
+>1 belong&0xffffff00 0x00013000 Aurisis
+>1 belong&0xffffff00 0x00013100 Nearfield Multimedia
+>1 belong&0xffffff00 0x00013200 FM7 Inc.
+>1 belong&0xffffff00 0x00013300 Swivel Systems
+>1 belong&0xffffff00 0x00013400 Hyperactive
+>1 belong&0xffffff00 0x00013500 MidiLite
+>1 belong&0xffffff00 0x00013600 Radical
+>1 belong&0xffffff00 0x00013700 Roger Linn
+>1 belong&0xffffff00 0x00013800 Helicon
+>1 belong&0xffffff00 0x00013900 Event
+>1 belong&0xffffff00 0x00013a00 Sonic Network
+>1 belong&0xffffff00 0x00013b00 Realtime Music
+>1 belong&0xffffff00 0x00013c00 Apogee Digital
+
+>1 belong&0xffffff00 0x00202b00 Medeli Electronics
+>1 belong&0xffffff00 0x00202c00 Charlie Lab
+>1 belong&0xffffff00 0x00202d00 Blue Chip Music
+>1 belong&0xffffff00 0x00202e00 BEE OH Corp
+>1 belong&0xffffff00 0x00202f00 LG Semicon America
+>1 belong&0xffffff00 0x00203000 TESI
+>1 belong&0xffffff00 0x00203100 EMAGIC
+>1 belong&0xffffff00 0x00203200 Behringer
+>1 belong&0xffffff00 0x00203300 Access Music
+>1 belong&0xffffff00 0x00203400 Synoptic
+>1 belong&0xffffff00 0x00203500 Hanmesoft Corp
+>1 belong&0xffffff00 0x00203600 Terratec
+>1 belong&0xffffff00 0x00203700 Proel SpA
+>1 belong&0xffffff00 0x00203800 IBK MIDI
+>1 belong&0xffffff00 0x00203900 IRCAM
+>1 belong&0xffffff00 0x00203a00 Propellerhead Software
+>1 belong&0xffffff00 0x00203b00 Red Sound Systems
+>1 belong&0xffffff00 0x00203c00 Electron ESI AB
+>1 belong&0xffffff00 0x00203d00 Sintefex Audio
+>1 belong&0xffffff00 0x00203e00 Music and More
+>1 belong&0xffffff00 0x00203f00 Amsaro
+>1 belong&0xffffff00 0x00204000 CDS Advanced Technology
+>1 belong&0xffffff00 0x00204100 Touched by Sound
+>1 belong&0xffffff00 0x00204200 DSP Arts
+>1 belong&0xffffff00 0x00204300 Phil Rees Music
+>1 belong&0xffffff00 0x00204400 Stamer Musikanlagen GmbH
+>1 belong&0xffffff00 0x00204500 Soundart
+>1 belong&0xffffff00 0x00204600 C-Mexx Software
+>1 belong&0xffffff00 0x00204700 Klavis Tech.
+>1 belong&0xffffff00 0x00204800 Noteheads AB
+
+0 string T707 Roland TR-707 Data
+#------------------------------------------------------------------------------
+# file: file(1) magic for Tcl scripting language
+# URL: http://www.tcl.tk/
+# From: gustaf neumann
+
+# Tcl scripts
+0 search/1/w #!\ /usr/bin/tcl Tcl script text executable
+!:mime text/x-tcl
+0 search/1/w #!\ /usr/local/bin/tcl Tcl script text executable
+!:mime text/x-tcl
+0 search/1 #!/usr/bin/env\ tcl Tcl script text executable
+!:mime text/x-tcl
+0 search/1 #!\ /usr/bin/env\ tcl Tcl script text executable
+!:mime text/x-tcl
+0 search/1/w #!\ /usr/bin/wish Tcl/Tk script text executable
+!:mime text/x-tcl
+0 search/1/w #!\ /usr/local/bin/wish Tcl/Tk script text executable
+!:mime text/x-tcl
+0 search/1 #!/usr/bin/env\ wish Tcl/Tk script text executable
+!:mime text/x-tcl
+0 search/1 #!\ /usr/bin/env\ wish Tcl/Tk script text executable
+!:mime text/x-tcl
+
+# check the first line
+0 search/1 package\ req
+>0 regex \^package[\ \t]+req Tcl script
+# not 'p', check other lines
+0 search/1 !p
+>0 regex \^package[\ \t]+req Tcl script
+
+#------------------------------------------------------------------------------
+# $File$
+# teapot: file(1) magic for "teapot" spreadsheet
+#
+0 string #!teapot\012xdr teapot work sheet (XDR format)
+
+#------------------------------------------------------------------------------
+# $File$
+# terminfo: file(1) magic for terminfo
+#
+# XXX - byte order for screen images?
+#
+0 string \032\001 Compiled terminfo entry
+0 short 0433 Curses screen image
+0 short 0434 Curses screen image
+
+#------------------------------------------------------------------------------
+# $File: tex,v 1.19 2013/09/17 17:39:16 christos Exp $
+# tex: file(1) magic for TeX files
+#
+# XXX - needs byte-endian stuff (big-endian and little-endian DVI?)
+#
+# From <conklin@talisman.kaleida.com>
+
+# Although we may know the offset of certain text fields in TeX DVI
+# and font files, we can't use them reliably because they are not
+# zero terminated. [but we do anyway, christos]
+0 string \367\002 TeX DVI file
+!:mime application/x-dvi
+>16 string >\0 (%s)
+0 string \367\203 TeX generic font data
+0 string \367\131 TeX packed font data
+>3 string >\0 (%s)
+0 string \367\312 TeX virtual font data
+0 search/1 This\ is\ TeX, TeX transcript text
+0 search/1 This\ is\ METAFONT, METAFONT transcript text
+
+# There is no way to detect TeX Font Metric (*.tfm) files without
+# breaking them apart and reading the data. The following patterns
+# match most *.tfm files generated by METAFONT or afm2tfm.
+2 string \000\021 TeX font metric data
+!:mime application/x-tex-tfm
+>33 string >\0 (%s)
+2 string \000\022 TeX font metric data
+!:mime application/x-tex-tfm
+>33 string >\0 (%s)
+
+# Texinfo and GNU Info, from Daniel Quinlan (quinlan@yggdrasil.com)
+0 search/1 \\input\ texinfo Texinfo source text
+!:mime text/x-texinfo
+0 search/1 This\ is\ Info\ file GNU Info text
+!:mime text/x-info
+
+# TeX documents, from Daniel Quinlan (quinlan@yggdrasil.com)
+0 search/4096 \\input TeX document text
+!:mime text/x-tex
+!:strength + 15
+0 search/4096 \\begin LaTeX document text
+!:mime text/x-tex
+!:strength + 15
+0 search/4096 \\section LaTeX document text
+!:mime text/x-tex
+!:strength + 18
+0 search/4096 \\setlength LaTeX document text
+!:mime text/x-tex
+!:strength + 15
+0 search/4096 \\documentstyle LaTeX document text
+!:mime text/x-tex
+!:strength + 18
+0 search/4096 \\chapter LaTeX document text
+!:mime text/x-tex
+!:strength + 18
+0 search/4096 \\documentclass LaTeX 2e document text
+!:mime text/x-tex
+!:strength + 15
+0 search/4096 \\relax LaTeX auxiliary file
+!:mime text/x-tex
+!:strength + 15
+0 search/4096 \\contentsline LaTeX table of contents
+!:mime text/x-tex
+!:strength + 15
+0 search/4096 %\ -*-latex-*- LaTeX document text
+!:mime text/x-tex
+
+# Tex document, from Hendrik Scholz <hendrik@scholz.net>
+0 search/1 \\ifx TeX document text
+
+# Index and glossary files
+0 search/4096 \\indexentry LaTeX raw index file
+0 search/4096 \\begin{theindex} LaTeX sorted index
+0 search/4096 \\glossaryentry LaTeX raw glossary
+0 search/4096 \\begin{theglossary} LaTeX sorted glossary
+0 search/4096 This\ is\ makeindex Makeindex log file
+
+# End of TeX
+
+#------------------------------------------------------------------------------
+# file(1) magic for BibTex text files
+# From Hendrik Scholz <hendrik@scholz.net>
+
+0 search/1/c @article{ BibTeX text file
+0 search/1/c @book{ BibTeX text file
+0 search/1/c @inbook{ BibTeX text file
+0 search/1/c @incollection{ BibTeX text file
+0 search/1/c @inproceedings{ BibTeX text file
+0 search/1/c @manual{ BibTeX text file
+0 search/1/c @misc{ BibTeX text file
+0 search/1/c @preamble{ BibTeX text file
+0 search/1/c @phdthesis{ BibTeX text file
+0 search/1/c @techreport{ BibTeX text file
+0 search/1/c @unpublished{ BibTeX text file
+
+73 search/1 %%%\ \ BibTeX-file{ BibTex text file (with full header)
+
+73 search/1 %%%\ \ @BibTeX-style-file{ BibTeX style text file (with full header)
+
+0 search/1 %\ BibTeX\ standard\ bibliography\ BibTeX standard bibliography style text file
+
+0 search/1 %\ BibTeX\ ` BibTeX custom bibliography style text file
+
+0 search/1 @c\ @mapfile{ TeX font aliases text file
+
+0 string #LyX LyX document text
+
+# ConTeXt documents
+# http://wiki.contextgarden.net/
+0 search/4096 \\setupcolors[ ConTeXt document text
+!:strength + 15
+0 search/4096 \\definecolor[ ConTeXt document text
+!:strength + 15
+0 search/4096 \\setupinteraction[ ConTeXt document text
+!:strength + 15
+0 search/4096 \\useURL[ ConTeXt document text
+!:strength + 15
+0 search/4096 \\setuppapersize[ ConTeXt document text
+!:strength + 15
+0 search/4096 \\setuplayout[ ConTeXt document text
+!:strength + 15
+0 search/4096 \\setupfooter[ ConTeXt document text
+!:strength + 15
+0 search/4096 \\setupfootertexts[ ConTeXt document text
+!:strength + 15
+0 search/4096 \\setuppagenumbering[ ConTeXt document text
+!:strength + 15
+0 search/4096 \\setupbodyfont[ ConTeXt document text
+!:strength + 15
+0 search/4096 \\setuphead[ ConTeXt document text
+!:strength + 15
+0 search/4096 \\setupitemize[ ConTeXt document text
+!:strength + 15
+0 search/4096 \\setupwhitespace[ ConTeXt document text
+!:strength + 15
+0 search/4096 \\setupindenting[ ConTeXt document text
+!:strength + 15
+
+#------------------------------------------------------------------------------
+# $File: tgif,v 1.6 2010/09/20 18:55:20 rrt Exp $
+# file(1) magic for tgif(1) files
+# From Hendrik Scholz <hendrik@scholz.net>
+0 string %TGIF\ Tgif file version
+>6 string x %s
+
+#------------------------------------------------------------------------------
+# $File: ti-8x,v 1.6 2009/09/19 16:28:12 christos Exp $
+# ti-8x: file(1) magic for the TI-8x and TI-9x Graphing Calculators.
+#
+# From: Ryan McGuire (rmcguire@freenet.columbus.oh.us).
+#
+# Update: Romain Lievin (roms@lpg.ticalc.org).
+#
+# NOTE: This list is not complete.
+# Files for the TI-80 and TI-81 are pretty rare. I'm not going to put the
+# program/group magic numbers in here because I cannot find any.
+0 string **TI80** TI-80 Graphing Calculator File.
+0 string **TI81** TI-81 Graphing Calculator File.
+#
+# Magic Numbers for the TI-73
+#
+0 string **TI73** TI-73 Graphing Calculator
+>0x00003B byte 0x00 (real number)
+>0x00003B byte 0x01 (list)
+>0x00003B byte 0x02 (matrix)
+>0x00003B byte 0x03 (equation)
+>0x00003B byte 0x04 (string)
+>0x00003B byte 0x05 (program)
+>0x00003B byte 0x06 (assembly program)
+>0x00003B byte 0x07 (picture)
+>0x00003B byte 0x08 (gdb)
+>0x00003B byte 0x0C (complex number)
+>0x00003B byte 0x0F (window settings)
+>0x00003B byte 0x10 (zoom)
+>0x00003B byte 0x11 (table setup)
+>0x00003B byte 0x13 (backup)
+
+# Magic Numbers for the TI-82
+#
+0 string **TI82** TI-82 Graphing Calculator
+>0x00003B byte 0x00 (real)
+>0x00003B byte 0x01 (list)
+>0x00003B byte 0x02 (matrix)
+>0x00003B byte 0x03 (Y-variable)
+>0x00003B byte 0x05 (program)
+>0x00003B byte 0x06 (protected prgm)
+>0x00003B byte 0x07 (picture)
+>0x00003B byte 0x08 (gdb)
+>0x00003B byte 0x0B (window settings)
+>0x00003B byte 0x0C (window settings)
+>0x00003B byte 0x0D (table setup)
+>0x00003B byte 0x0E (screenshot)
+>0x00003B byte 0x0F (backup)
+#
+# Magic Numbers for the TI-83
+#
+0 string **TI83** TI-83 Graphing Calculator
+>0x00003B byte 0x00 (real)
+>0x00003B byte 0x01 (list)
+>0x00003B byte 0x02 (matrix)
+>0x00003B byte 0x03 (Y-variable)
+>0x00003B byte 0x04 (string)
+>0x00003B byte 0x05 (program)
+>0x00003B byte 0x06 (protected prgm)
+>0x00003B byte 0x07 (picture)
+>0x00003B byte 0x08 (gdb)
+>0x00003B byte 0x0B (window settings)
+>0x00003B byte 0x0C (window settings)
+>0x00003B byte 0x0D (table setup)
+>0x00003B byte 0x0E (screenshot)
+>0x00003B byte 0x13 (backup)
+#
+# Magic Numbers for the TI-83+
+#
+0 string **TI83F* TI-83+ Graphing Calculator
+>0x00003B byte 0x00 (real number)
+>0x00003B byte 0x01 (list)
+>0x00003B byte 0x02 (matrix)
+>0x00003B byte 0x03 (equation)
+>0x00003B byte 0x04 (string)
+>0x00003B byte 0x05 (program)
+>0x00003B byte 0x06 (assembly program)
+>0x00003B byte 0x07 (picture)
+>0x00003B byte 0x08 (gdb)
+>0x00003B byte 0x0C (complex number)
+>0x00003B byte 0x0F (window settings)
+>0x00003B byte 0x10 (zoom)
+>0x00003B byte 0x11 (table setup)
+>0x00003B byte 0x13 (backup)
+>0x00003B byte 0x15 (application variable)
+>0x00003B byte 0x17 (group of variable)
+
+#
+# Magic Numbers for the TI-85
+#
+0 string **TI85** TI-85 Graphing Calculator
+>0x00003B byte 0x00 (real number)
+>0x00003B byte 0x01 (complex number)
+>0x00003B byte 0x02 (real vector)
+>0x00003B byte 0x03 (complex vector)
+>0x00003B byte 0x04 (real list)
+>0x00003B byte 0x05 (complex list)
+>0x00003B byte 0x06 (real matrix)
+>0x00003B byte 0x07 (complex matrix)
+>0x00003B byte 0x08 (real constant)
+>0x00003B byte 0x09 (complex constant)
+>0x00003B byte 0x0A (equation)
+>0x00003B byte 0x0C (string)
+>0x00003B byte 0x0D (function GDB)
+>0x00003B byte 0x0E (polar GDB)
+>0x00003B byte 0x0F (parametric GDB)
+>0x00003B byte 0x10 (diffeq GDB)
+>0x00003B byte 0x11 (picture)
+>0x00003B byte 0x12 (program)
+>0x00003B byte 0x13 (range)
+>0x00003B byte 0x17 (window settings)
+>0x00003B byte 0x18 (window settings)
+>0x00003B byte 0x19 (window settings)
+>0x00003B byte 0x1A (window settings)
+>0x00003B byte 0x1B (zoom)
+>0x00003B byte 0x1D (backup)
+>0x00003B byte 0x1E (unknown)
+>0x00003B byte 0x2A (equation)
+>0x000032 string ZS4 - ZShell Version 4 File.
+>0x000032 string ZS3 - ZShell Version 3 File.
+#
+# Magic Numbers for the TI-86
+#
+0 string **TI86** TI-86 Graphing Calculator
+>0x00003B byte 0x00 (real number)
+>0x00003B byte 0x01 (complex number)
+>0x00003B byte 0x02 (real vector)
+>0x00003B byte 0x03 (complex vector)
+>0x00003B byte 0x04 (real list)
+>0x00003B byte 0x05 (complex list)
+>0x00003B byte 0x06 (real matrix)
+>0x00003B byte 0x07 (complex matrix)
+>0x00003B byte 0x08 (real constant)
+>0x00003B byte 0x09 (complex constant)
+>0x00003B byte 0x0A (equation)
+>0x00003B byte 0x0C (string)
+>0x00003B byte 0x0D (function GDB)
+>0x00003B byte 0x0E (polar GDB)
+>0x00003B byte 0x0F (parametric GDB)
+>0x00003B byte 0x10 (diffeq GDB)
+>0x00003B byte 0x11 (picture)
+>0x00003B byte 0x12 (program)
+>0x00003B byte 0x13 (range)
+>0x00003B byte 0x17 (window settings)
+>0x00003B byte 0x18 (window settings)
+>0x00003B byte 0x19 (window settings)
+>0x00003B byte 0x1A (window settings)
+>0x00003B byte 0x1B (zoom)
+>0x00003B byte 0x1D (backup)
+>0x00003B byte 0x1E (unknown)
+>0x00003B byte 0x2A (equation)
+#
+# Magic Numbers for the TI-89
+#
+0 string **TI89** TI-89 Graphing Calculator
+>0x000048 byte 0x00 (expression)
+>0x000048 byte 0x04 (list)
+>0x000048 byte 0x06 (matrix)
+>0x000048 byte 0x0A (data)
+>0x000048 byte 0x0B (text)
+>0x000048 byte 0x0C (string)
+>0x000048 byte 0x0D (graphic data base)
+>0x000048 byte 0x0E (figure)
+>0x000048 byte 0x10 (picture)
+>0x000048 byte 0x12 (program)
+>0x000048 byte 0x13 (function)
+>0x000048 byte 0x14 (macro)
+>0x000048 byte 0x1C (zipped)
+>0x000048 byte 0x21 (assembler)
+#
+# Magic Numbers for the TI-92
+#
+0 string **TI92** TI-92 Graphing Calculator
+>0x000048 byte 0x00 (expression)
+>0x000048 byte 0x04 (list)
+>0x000048 byte 0x06 (matrix)
+>0x000048 byte 0x0A (data)
+>0x000048 byte 0x0B (text)
+>0x000048 byte 0x0C (string)
+>0x000048 byte 0x0D (graphic data base)
+>0x000048 byte 0x0E (figure)
+>0x000048 byte 0x10 (picture)
+>0x000048 byte 0x12 (program)
+>0x000048 byte 0x13 (function)
+>0x000048 byte 0x14 (macro)
+>0x000048 byte 0x1D (backup)
+#
+# Magic Numbers for the TI-92+/V200
+#
+0 string **TI92P* TI-92+/V200 Graphing Calculator
+>0x000048 byte 0x00 (expression)
+>0x000048 byte 0x04 (list)
+>0x000048 byte 0x06 (matrix)
+>0x000048 byte 0x0A (data)
+>0x000048 byte 0x0B (text)
+>0x000048 byte 0x0C (string)
+>0x000048 byte 0x0D (graphic data base)
+>0x000048 byte 0x0E (figure)
+>0x000048 byte 0x10 (picture)
+>0x000048 byte 0x12 (program)
+>0x000048 byte 0x13 (function)
+>0x000048 byte 0x14 (macro)
+>0x000048 byte 0x1C (zipped)
+>0x000048 byte 0x21 (assembler)
+#
+# Magic Numbers for the TI-73/83+/89/92+/V200 FLASH upgrades
+#
+0x0000016 string Advanced TI-XX Graphing Calculator (FLASH)
+0 string **TIFL** TI-XX Graphing Calculator (FLASH)
+>8 byte >0 - Revision %d
+>>9 byte x \b.%d,
+>12 byte >0 Revision date %02x
+>>13 byte x \b/%02x
+>>14 beshort x \b/%04x,
+>17 string >/0 name: '%s',
+>48 byte 0x74 device: TI-73,
+>48 byte 0x73 device: TI-83+,
+>48 byte 0x98 device: TI-89,
+>48 byte 0x88 device: TI-92+,
+>49 byte 0x23 type: OS upgrade,
+>49 byte 0x24 type: application,
+>49 byte 0x25 type: certificate,
+>49 byte 0x3e type: license,
+>74 lelong >0 size: %d bytes
+
+# VTi & TiEmu skins (TI Graphing Calculators).
+# From: Romain Lievin (roms@lpg.ticalc.org).
+# Magic Numbers for the VTi skins
+0 string VTI Virtual TI skin
+>3 string v - Version
+>>4 byte >0 \b %c
+>>6 byte x \b.%c
+# Magic Numbers for the TiEmu skins
+0 string TiEmu TiEmu skin
+>6 string v - Version
+>>7 byte >0 \b %c
+>>9 byte x \b.%c
+>>10 byte x \b%c
+
+#------------------------------------------------------------------------------
+# $File$
+# timezone: file(1) magic for timezone data
+#
+# from Daniel Quinlan (quinlan@yggdrasil.com)
+# this should work on Linux, SunOS, and maybe others
+# Added new official magic number for recent versions of the Olson code
+0 string TZif timezone data
+>4 byte 0 \b, old version
+>4 byte >0 \b, version %c
+>20 belong 0 \b, no gmt time flags
+>20 belong 1 \b, 1 gmt time flag
+>20 belong >1 \b, %d gmt time flags
+>24 belong 0 \b, no std time flags
+>20 belong 1 \b, 1 std time flag
+>24 belong >1 \b, %d std time flags
+>28 belong 0 \b, no leap seconds
+>28 belong 1 \b, 1 leap second
+>28 belong >1 \b, %d leap seconds
+>32 belong 0 \b, no transition times
+>32 belong 1 \b, 1 transition time
+>32 belong >1 \b, %d transition times
+>36 belong 0 \b, no abbreviation chars
+>36 belong 1 \b, 1 abbreviation char
+>36 belong >1 \b, %d abbreviation chars
+0 string \0\0\0\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 old timezone data
+0 string \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\0 old timezone data
+0 string \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\0 old timezone data
+0 string \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\4\0 old timezone data
+0 string \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\5\0 old timezone data
+0 string \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\6\0 old timezone data
+
+#------------------------------------------------------------------------------
+# $File: troff,v 1.10 2009/09/19 16:28:12 christos Exp $
+# troff: file(1) magic for *roff
+#
+# updated by Daniel Quinlan (quinlan@yggdrasil.com)
+
+# troff input
+0 search/1 .\\" troff or preprocessor input text
+!:mime text/troff
+0 search/1 '\\" troff or preprocessor input text
+!:mime text/troff
+0 search/1 '.\\" troff or preprocessor input text
+!:mime text/troff
+0 search/1 \\" troff or preprocessor input text
+!:mime text/troff
+0 search/1 ''' troff or preprocessor input text
+!:mime text/troff
+0 regex/20l \^\\.[A-Za-z0-9][A-Za-z0-9][\ \t] troff or preprocessor input text
+!:mime text/troff
+0 regex/20l \^\\.[A-Za-z0-9][A-Za-z0-9]$ troff or preprocessor input text
+!:mime text/troff
+
+# ditroff intermediate output text
+0 search/1 x\ T ditroff output text
+>4 search/1 cat for the C/A/T phototypesetter
+>4 search/1 ps for PostScript
+>4 search/1 dvi for DVI
+>4 search/1 ascii for ASCII
+>4 search/1 lj4 for LaserJet 4
+>4 search/1 latin1 for ISO 8859-1 (Latin 1)
+>4 search/1 X75 for xditview at 75dpi
+>>7 search/1 -12 (12pt)
+>4 search/1 X100 for xditview at 100dpi
+>>8 search/1 -12 (12pt)
+
+# output data formats
+0 string \100\357 very old (C/A/T) troff output data
+
+#------------------------------------------------------------------------------
+# $File$
+# tuxedo: file(1) magic for BEA TUXEDO data files
+#
+# from Ian Springer <ispringer@hotmail.com>
+#
+0 string \0\0\1\236\0\0\0\0\0\0\0\0\0\0\0\0 BEA TUXEDO DES mask data
+
+#------------------------------------------------------------------------------
+# $File$
+# typeset: file(1) magic for other typesetting
+#
+0 string Interpress/Xerox Xerox InterPress data
+>16 string / (version
+>>17 string >\0 %s)
+
+#------------------------------------------------------------------------------
+# $File: unicode,v 1.5 2009/09/19 16:28:13 christos Exp $
+# Unicode: BOM prefixed text files - Adrian Havill <havill@turbolinux.co.jp>
+# GRR: These types should be recognised in file_ascmagic so these
+# encodings can be treated by text patterns.
+# Missing types are already dealt with internally.
+#
+0 string +/v8 Unicode text, UTF-7
+0 string +/v9 Unicode text, UTF-7
+0 string +/v+ Unicode text, UTF-7
+0 string +/v/ Unicode text, UTF-7
+0 string \335\163\146\163 Unicode text, UTF-8-EBCDIC
+0 string \000\000\376\377 Unicode text, UTF-32, big-endian
+0 string \377\376\000\000 Unicode text, UTF-32, little-endian
+0 string \016\376\377 Unicode text, SCSU (Standard Compression Scheme for Unicode)
+
+#------------------------------------------------------------------------------
+# $File: unknown,v 1.7 2009/09/19 16:28:13 christos Exp $
+# unknown: file(1) magic for unknown machines
+#
+# 0x107 is 0407, 0x108 is 0410, and 0x109 is 0411; those are all PDP-11
+# (executable, pure, and split I&D, respectively), but the PDP-11 version
+# doesn't have the "version %ld", which may be a bogus COFFism (I don't
+# think there was ever COFF for the PDP-11).
+#
+# 0x10B is 0413; that's VAX demand-paged, but this is a short, not a
+# long, as it would be on a VAX. In any case, that could collide with
+# VAX demand-paged files, as the magic number is little-endian on those
+# binaries, so the first 16 bits of the file would contain 0x10B.
+#
+# Therefore, those entries are commented out.
+#
+# 0x10C is 0414 and 0x10E is 0416; those *are* unknown.
+#
+#0 short 0x107 unknown machine executable
+#>8 short >0 not stripped
+#>15 byte >0 - version %ld
+#0 short 0x108 unknown pure executable
+#>8 short >0 not stripped
+#>15 byte >0 - version %ld
+#0 short 0x109 PDP-11 separate I&D
+#>8 short >0 not stripped
+#>15 byte >0 - version %ld
+#0 short 0x10b unknown pure executable
+#>8 short >0 not stripped
+#>15 byte >0 - version %ld
+0 long 0x10c unknown demand paged pure executable
+>16 long >0 not stripped
+0 long 0x10e unknown readable demand paged pure executable
+
+#------------------------------------------------------------------------------
+# $File: uterus,v 1.2 2014/04/28 12:04:50 christos Exp $
+# file(1) magic for uterus files
+# http://freecode.com/projects/uterus
+#
+0 string UTE+ uterus file
+>4 string v \b, version
+>5 byte x %c
+>6 string . \b.
+>7 byte x \b%c
+>8 string \<\> \b, big-endian
+>>16 belong >0 \b, slut size %u
+>8 string \>\< \b, litte-endian
+>>16 lelong >0 \b, slut size %u
+>10 byte &8 \b, compressed
+
+#------------------------------------------------------------------------------
+# $File$
+# uuencode: file(1) magic for ASCII-encoded files
+#
+
+# GRR: the first line of xxencoded files is identical to that in uuencoded
+# files, but the first character in most subsequent lines is 'h' instead of
+# 'M'. (xxencoding uses lowercase letters in place of most of uuencode's
+# punctuation and survives BITNET gateways better.) If regular expressions
+# were supported, this entry could possibly be split into two with
+# "begin\040\.\*\012M" or "begin\040\.\*\012h" (where \. and \* are REs).
+0 search/1 begin\ uuencoded or xxencoded text
+
+# btoa(1) is an alternative to uuencode that requires less space.
+0 search/1 xbtoa\ Begin btoa'd text
+
+# ship(1) is another, much cooler alternative to uuencode.
+# Greg Roelofs, newt@uchicago.edu
+0 search/1 $\012ship ship'd binary text
+
+# bencode(8) is used to encode compressed news batches (Bnews/Cnews only?)
+# Greg Roelofs, newt@uchicago.edu
+0 search/1 Decode\ the\ following\ with\ bdeco bencoded News text
+
+# BinHex is the Macintosh ASCII-encoded file format (see also "apple")
+# Daniel Quinlan, quinlan@yggdrasil.com
+11 search/1 must\ be\ converted\ with\ BinHex BinHex binary text
+>41 search/1 x \b, version %.3s
+
+# GRR: handle BASE64
+
+#------------------------------------------------------------------------------
+# $File: varied.out,v 1.22 2010/07/02 00:06:27 christos Exp $
+# varied.out: file(1) magic for various USG systems
+#
+# Herewith many of the object file formats used by USG systems.
+# Most have been moved to files for a particular processor,
+# and deleted if they duplicate other entries.
+#
+0 short 0610 Perkin-Elmer executable
+# AMD 29K
+0 beshort 0572 amd 29k coff noprebar executable
+0 beshort 01572 amd 29k coff prebar executable
+0 beshort 0160007 amd 29k coff archive
+# Cray
+6 beshort 0407 unicos (cray) executable
+# Ultrix 4.3
+596 string \130\337\377\377 Ultrix core file
+>600 string >\0 from '%s'
+# BeOS and MAcOS PEF executables
+# From: hplus@zilker.net (Jon Watte)
+0 string Joy!peffpwpc header for PowerPC PEF executable
+#
+# ava assembler/linker Uros Platise <uros.platise@ijs.si>
+0 string avaobj AVR assembler object code
+>7 string >\0 version '%s'
+# gnu gmon magic From: Eugen Dedu <dedu@ese-metz.fr>
+0 string gmon GNU prof performance data
+>4 long x - version %d
+# From: Dave Pearson <davep@davep.org>
+# Harbour <URL:http://harbour-project.org/> HRB files.
+0 string \xc0HRB Harbour HRB file
+>4 leshort x version %d
+# Harbour HBV files
+0 string \xc0HBV Harbour variable dump file
+>4 leshort x version %d
+
+# From: Alex Beregszaszi <alex@fsn.hu>
+# 0 string exec BugOS executable
+# 0 string pack BugOS archive
+
+# From: Jason Spence <jspence@lightconsulting.com>
+# Generated by the "examples" in STM's ST40 devkit, and derived code.
+0 lelong 0x13a9f17e ST40 component image format
+>4 string >\0 \b, name '%s'
+
+#------------------------------------------------------------------------------
+# $File: varied.script,v 1.9 2011/12/16 16:32:48 rrt Exp $
+# varied.script: file(1) magic for various interpreter scripts
+
+0 string/t #!\ / a
+>3 string >\0 %s script text executable
+!:strength / 2
+
+0 string/b #!\ / a
+>3 string >\0 %s script executable (binary data)
+!:strength / 2
+
+0 string/t #!\t/ a
+>3 string >\0 %s script text executable
+!:strength / 2
+
+0 string/b #!\t/ a
+>3 string >\0 %s script executable (binary data)
+!:strength / 2
+
+0 string/t #!/ a
+>2 string >\0 %s script text executable
+!:strength / 2
+
+0 string/b #!/ a
+>2 string >\0 %s script executable (binary data)
+!:strength / 2
+
+0 string/t #!\ script text executable
+>3 string >\0 for %s
+!:strength / 3
+
+0 string/b #!\ script executable
+>3 string >\0 for %s (binary data)
+!:strength / 3
+
+# using env
+0 string/t #!/usr/bin/env a
+>15 string/t >\0 %s script text executable
+!:strength / 10
+
+0 string/b #!/usr/bin/env a
+>15 string/b >\0 %s script executable (binary data)
+!:strength / 10
+
+0 string/t #!\ /usr/bin/env a
+>16 string/t >\0 %s script text executable
+!:strength / 10
+
+0 string/b #!\ /usr/bin/env a
+>16 string/b >\0 %s script executable (binary data)
+!:strength / 10
+
+# From: arno <arenevier@fdn.fr>
+# mozilla xpconnect typelib
+# see http://www.mozilla.org/scriptable/typelib_file.html
+0 string XPCOM\nTypeLib\r\n\032 XPConnect Typelib
+>0x10 byte x version %d
+>>0x11 byte x \b.%d
+
+#------------------------------------------------------------------------------
+# $File: vax,v 1.8 2013/01/09 22:37:24 christos Exp $
+# vax: file(1) magic for VAX executable/object and APL workspace
+#
+0 lelong 0101557 VAX single precision APL workspace
+0 lelong 0101556 VAX double precision APL workspace
+
+#
+# VAX a.out (BSD; others collide with 386 and other 32-bit little-endian
+# executables, and are handled in aout)
+#
+0 lelong 0420 a.out VAX demand paged (first page unmapped) pure executable
+>16 lelong >0 not stripped
+
+#
+# VAX COFF
+#
+# The `versions' were commented out, but have been un-commented out.
+# (Was the problem just one of endianness?)
+#
+0 leshort 0570 VAX COFF executable
+>12 lelong >0 not stripped
+>22 leshort >0 - version %d
+0 leshort 0575 VAX COFF pure executable
+>12 lelong >0 not stripped
+>22 leshort >0 - version %d
+
+#------------------------------------------------------------------------------
+# $File$
+# vicar: file(1) magic for VICAR files.
+#
+# From: Ossama Othman <othman@astrosun.tn.cornell.edu
+# VICAR is JPL's in-house spacecraft image processing program
+# VICAR image
+0 string LBLSIZE= VICAR image data
+>32 string BYTE \b, 8 bits = VAX byte
+>32 string HALF \b, 16 bits = VAX word = Fortran INTEGER*2
+>32 string FULL \b, 32 bits = VAX longword = Fortran INTEGER*4
+>32 string REAL \b, 32 bits = VAX longword = Fortran REAL*4
+>32 string DOUB \b, 64 bits = VAX quadword = Fortran REAL*8
+>32 string COMPLEX \b, 64 bits = VAX quadword = Fortran COMPLEX*8
+# VICAR label file
+43 string SFDU_LABEL VICAR label file
+
+#------------------------------------------------------------------------------
+# $File: virtual,v 1.5 2014/04/30 21:41:02 christos Exp $
+# From: James Nobis <quel@quelrod.net>
+# Microsoft hard disk images for:
+# Virtual Server
+# Virtual PC
+# http://technet.microsoft.com/en-us/virtualserver/bb676673.aspx
+# .vhd
+0 string conectix Microsoft Disk Image, Virtual Server or Virtual PC
+
+# libvirt
+# From: Philipp Hahn <hahn@univention.de>
+0 string LibvirtQemudSave Libvirt QEMU Suspend Image
+>0x10 lelong x \b, version %u
+>0x14 lelong x \b, XML length %u
+>0x18 lelong 1 \b, running
+>0x1c lelong 1 \b, compressed
+
+0 string LibvirtQemudPart Libvirt QEMU partial Suspend Image
+# From: Alex Beregszaszi <alex@fsn.hu>
+0 string/b COWD VMWare3
+>4 byte 3 disk image
+>>32 lelong x (%d/
+>>36 lelong x \b%d/
+>>40 lelong x \b%d)
+>4 byte 2 undoable disk image
+>>32 string >\0 (%s)
+
+0 string/b VMDK VMware4 disk image
+0 string/b KDMV VMware4 disk image
+
+#--------------------------------------------------------------------
+# Qemu Emulator Images
+# Lines written by Friedrich Schwittay (f.schwittay@yousable.de)
+# Updated by Adam Buchbinder (adam.buchbinder@gmail.com)
+# Made by reading sources, reading documentation, and doing trial and error
+# on existing QCOW files
+0 string/b QFI\xFB QEMU QCOW Image
+
+# Uncomment the following line to display Magic (only used for debugging
+# this magic number)
+#>0 string/b x , Magic: %s
+
+# There are currently 2 Versions: "1" and "2".
+# http://www.gnome.org/~markmc/qcow-image-format-version-1.html
+>4 belong 1 (v1)
+
+# Using the existence of the Backing File Offset to determine whether
+# to read Backing File Information
+>>12 belong >0 \b, has backing file (
+# Note that this isn't a null-terminated string; the length is actually
+# (16.L). Assuming a null-terminated string happens to work usually, but it
+# may spew junk until it reaches a \0 in some cases.
+>>>(12.L) string >\0 \bpath %s
+
+# Modification time of the Backing File
+# Really useful if you want to know if your backing
+# file is still usable together with this image
+>>>>20 bedate >0 \b, mtime %s)
+>>>>20 default x \b)
+
+# Size is stored in bytes in a big-endian u64.
+>>24 bequad x \b, %lld bytes
+
+# 1 for AES encryption, 0 for none.
+>>36 belong 1 \b, AES-encrypted
+
+# http://www.gnome.org/~markmc/qcow-image-format.html
+>4 belong 2 (v2)
+# Using the existence of the Backing File Offset to determine whether
+# to read Backing File Information
+>>8 bequad >0 \b, has backing file
+# Note that this isn't a null-terminated string; the length is actually
+# (16.L). Assuming a null-terminated string happens to work usually, but it
+# may spew junk until it reaches a \0 in some cases. Also, since there's no
+# .Q modifier, we just use the bottom four bytes as an offset. Note that if
+# the file is over 4G, and the backing file path is stored after the first 4G,
+# the wrong filename will be printed. (This should be (8.Q), when that syntax
+# is introduced.)
+>>>(12.L) string >\0 (path %s)
+>>24 bequad x \b, %lld bytes
+>>32 belong 1 \b, AES-encrypted
+
+>4 belong 3 (v3)
+# Using the existence of the Backing File Offset to determine whether
+# to read Backing File Information
+>>8 bequad >0 \b, has backing file
+# Note that this isn't a null-terminated string; the length is actually
+# (16.L). Assuming a null-terminated string happens to work usually, but it
+# may spew junk until it reaches a \0 in some cases. Also, since there's no
+# .Q modifier, we just use the bottom four bytes as an offset. Note that if
+# the file is over 4G, and the backing file path is stored after the first 4G,
+# the wrong filename will be printed. (This should be (8.Q), when that syntax
+# is introduced.)
+>>>(12.L) string >\0 (path %s)
+>>24 bequad x \b, %lld bytes
+>>32 belong 1 \b, AES-encrypted
+
+>4 default x (unknown version)
+
+0 string/b QEVM QEMU suspend to disk image
+
+# QEMU QED Image
+# http://wiki.qemu.org/Features/QED/Specification
+0 string/b QED\0 QEMU QED Image
+
+# VDI Image
+# Sun xVM VirtualBox Disk Image
+# From: Richard W.M. Jones <rich@annexia.org>
+# VirtualBox Disk Image
+0x40 ulelong 0xbeda107f VirtualBox Disk Image
+>0x44 uleshort >0 \b, major %u
+>0x46 uleshort >0 \b, minor %u
+>0 string >\0 (%s)
+>368 lequad x \b, %lld bytes
+
+0 string/b Bochs\ Virtual\ HD\ Image Bochs disk image,
+>32 string x type %s,
+>48 string x subtype %s
+
+0 lelong 0x02468ace Bochs Sparse disk image
+
+
+#------------------------------------------------------------------------------
+# $File$
+# Virtutech Compressed Random Access File Format
+#
+# From <gustav@virtutech.com>
+0 string \211\277\036\203 Virtutech CRAFF
+>4 belong x v%d
+>20 belong 0 uncompressed
+>20 belong 1 bzipp2ed
+>20 belong 2 gzipped
+>24 belong 0 not clean
+
+#------------------------------------------------------------------------------
+# $File$
+# visx: file(1) magic for Visx format files
+#
+0 short 0x5555 VISX image file
+>2 byte 0 (zero)
+>2 byte 1 (unsigned char)
+>2 byte 2 (short integer)
+>2 byte 3 (float 32)
+>2 byte 4 (float 64)
+>2 byte 5 (signed char)
+>2 byte 6 (bit-plane)
+>2 byte 7 (classes)
+>2 byte 8 (statistics)
+>2 byte 10 (ascii text)
+>2 byte 15 (image segments)
+>2 byte 100 (image set)
+>2 byte 101 (unsigned char vector)
+>2 byte 102 (short integer vector)
+>2 byte 103 (float 32 vector)
+>2 byte 104 (float 64 vector)
+>2 byte 105 (signed char vector)
+>2 byte 106 (bit plane vector)
+>2 byte 121 (feature vector)
+>2 byte 122 (feature vector library)
+>2 byte 124 (chain code)
+>2 byte 126 (bit vector)
+>2 byte 130 (graph)
+>2 byte 131 (adjacency graph)
+>2 byte 132 (adjacency graph library)
+>2 string .VISIX (ascii text)
+
+#------------------------------------------------------------------------------
+# $File: vms,v 1.8 2014/08/17 12:58:54 christos Exp $
+# vms: file(1) magic for VMS executables (experimental)
+#
+# VMS .exe formats, both VAX and AXP (Greg Roelofs, newt@uchicago.edu)
+
+# GRR 950122: I'm just guessing on these, based on inspection of the headers
+# of three executables each for Alpha and VAX architectures. The VAX files
+# all had headers similar to this:
+#
+# 00000 b0 00 30 00 44 00 60 00 00 00 00 00 30 32 30 35 ..0.D.`.....0205
+# 00010 01 01 00 00 ff ff ff ff ff ff ff ff 00 00 00 00 ................
+#
+0 string \xb0\0\x30\0 VMS VAX executable
+>44032 string PK\003\004 \b, Info-ZIP SFX archive v5.12 w/decryption
+#
+# The AXP files all looked like this, except that the byte at offset 0x22
+# was 06 in some of them and 07 in others:
+#
+# 00000 03 00 00 00 00 00 00 00 ec 02 00 00 10 01 00 00 ................
+# 00010 68 00 00 00 98 00 00 00 b8 00 00 00 00 00 00 00 h...............
+# 00020 00 00 07 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+# 00030 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 ................
+# 00040 00 00 00 00 ff ff ff ff ff ff ff ff 02 00 00 00 ................
+#
+# GRR this test is still too general as it catches example adressen.dbt
+0 belong 0x03000000
+>8 ubelong 0xec020000 VMS Alpha executable
+>>75264 string PK\003\004 \b, Info-ZIP SFX archive v5.12 w/decryption
+
+#------------------------------------------------------------------------------
+# $File$
+# VMware specific files (deducted from version 1.1 and log file entries)
+# Anthon van der Neut (anthon@mnt.org)
+0 belong 0x4d52564e VMware nvram
+
+#------------------------------------------------------------------------------
+# $File: vorbis,v 1.20 2014/09/23 16:35:08 christos Exp $
+# vorbis: file(1) magic for Ogg/Vorbis files
+#
+# From Felix von Leitner <leitner@fefe.de>
+# Extended by Beni Cherniavsky <cben@crosswinds.net>
+# Further extended by Greg Wooledge <greg@wooledge.org>
+#
+# Most (everything but the number of channels and bitrate) is commented
+# out with `##' as it's not interesting to the average user. The most
+# probable things advanced users would want to uncomment are probably
+# the number of comments and the encoder version.
+#
+# FIXME: The first match has been made a search, so that it can skip
+# over prepended ID3 tags. This will work for MIME type detection, but
+# won't work for detecting other properties of the file (they all need
+# to be made relative to the search). In any case, if the file has ID3
+# tags, the ID3 information will be printed, not the Ogg information,
+# so until that's fixed, this doesn't matter.
+# FIXME[2]: Disable the above for now, since search assumes text mode.
+#
+# --- Ogg Framing ---
+#0 search/1000 OggS Ogg data
+0 string OggS Ogg data
+>4 byte !0 UNKNOWN REVISION %u
+##>4 byte 0 revision 0
+>4 byte 0
+##>>14 lelong x (Serial %lX)
+# non-Vorbis content: FLAC (Free Lossless Audio Codec, http://flac.sourceforge.net)
+>>28 string \x7fFLAC \b, FLAC audio
+# non-Vorbis content: Theora
+!:mime audio/ogg
+>>28 string \x80theora \b, Theora video
+!:mime video/ogg
+# non-Vorbis content: Kate
+>>28 string \x80kate\0\0\0\0 \b, Kate (Karaoke and Text)
+!:mime application/ogg
+>>>37 ubyte x v%u
+>>>38 ubyte x \b.%u,
+>>>40 byte 0 utf8 encoding,
+>>>40 byte !0 unknown character encoding,
+>>>60 string >\0 language %s,
+>>>60 string \0 no language set,
+>>>76 string >\0 category %s
+>>>76 string \0 no category set
+# non-Vorbis content: Skeleton
+>>28 string fishead\0 \b, Skeleton
+!:mime video/ogg
+>>>36 leshort x v%u
+>>>40 leshort x \b.%u
+# non-Vorbis content: Speex
+>>28 string Speex\ \ \ \b, Speex audio
+!:mime audio/ogg
+# non-Vorbis content: OGM
+>>28 string \x01video\0\0\0 \b, OGM video
+!:mime video/ogg
+>>>37 string/c div3 (DivX 3)
+>>>37 string/c divx (DivX 4)
+>>>37 string/c dx50 (DivX 5)
+>>>37 string/c xvid (XviD)
+# --- First vorbis packet - general header ---
+>>28 string \x01vorbis \b, Vorbis audio,
+!:mime audio/ogg
+>>>35 lelong !0 UNKNOWN VERSION %u,
+##>>>35 lelong 0 version 0,
+>>>35 lelong 0
+>>>>39 ubyte 1 mono,
+>>>>39 ubyte 2 stereo,
+>>>>39 ubyte >2 %u channels,
+>>>>40 lelong x %u Hz
+# Minimal, nominal and maximal bitrates specified when encoding
+>>>>48 string <\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff \b,
+# The above tests if at least one of these is specified:
+>>>>>52 lelong !-1
+# Vorbis RC2 has a bug which puts -1000 in the min/max bitrate fields
+# instead of -1.
+# Vorbis 1.0 uses 0 instead of -1.
+>>>>>>52 lelong !0
+>>>>>>>52 lelong !-1000
+>>>>>>>>52 lelong x <%u
+>>>>>48 lelong !-1
+>>>>>>48 lelong x ~%u
+>>>>>44 lelong !-1
+>>>>>>44 lelong !-1000
+>>>>>>>44 lelong !0
+>>>>>>>>44 lelong x >%u
+>>>>>48 string <\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff bps
+# -- Second vorbis header packet - the comments
+# A kludge to read the vendor string. It's a counted string, not a
+# zero-terminated one, so file(1) can't read it in a generic way.
+# libVorbis is the only one existing currently, so I detect specifically
+# it. The interesting value is the cvs date (8 digits decimal).
+# Post-RC1 Ogg files have the second header packet (and thus the version)
+# in a different place, so we must use an indirect offset.
+>>>(84.b+85) string \x03vorbis
+>>>>(84.b+96) string/c Xiphophorus\ libVorbis\ I \b, created by: Xiphophorus libVorbis I
+>>>>>(84.b+120) string >00000000
+# Map to beta version numbers:
+>>>>>>(84.b+120) string <20000508 (<beta1, prepublic)
+>>>>>>(84.b+120) string 20000508 (1.0 beta 1 or beta 2)
+>>>>>>(84.b+120) string >20000508
+>>>>>>>(84.b+120) string <20001031 (beta2-3)
+>>>>>>(84.b+120) string 20001031 (1.0 beta 3)
+>>>>>>(84.b+120) string >20001031
+>>>>>>>(84.b+120) string <20010225 (beta3-4)
+>>>>>>(84.b+120) string 20010225 (1.0 beta 4)
+>>>>>>(84.b+120) string >20010225
+>>>>>>>(84.b+120) string <20010615 (beta4-RC1)
+>>>>>>(84.b+120) string 20010615 (1.0 RC1)
+>>>>>>(84.b+120) string 20010813 (1.0 RC2)
+>>>>>>(84.b+120) string 20010816 (RC2 - Garf tuned v1)
+>>>>>>(84.b+120) string 20011014 (RC2 - Garf tuned v2)
+>>>>>>(84.b+120) string 20011217 (1.0 RC3)
+>>>>>>(84.b+120) string 20011231 (1.0 RC3)
+# Some pre-1.0 CVS snapshots still had "Xiphphorus"...
+>>>>>>(84.b+120) string >20011231 (pre-1.0 CVS)
+# For the 1.0 release, Xiphophorus is replaced by Xiph.Org
+>>>>(84.b+96) string/c Xiph.Org\ libVorbis\ I \b, created by: Xiph.Org libVorbis I
+>>>>>(84.b+117) string >00000000
+>>>>>>(84.b+117) string <20020717 (pre-1.0 CVS)
+>>>>>>(84.b+117) string 20020717 (1.0)
+>>>>>>(84.b+117) string 20030909 (1.0.1)
+>>>>>>(84.b+117) string 20040629 (1.1.0 RC1)
+
+#------------------------------------------------------------------------------
+# $File$
+# VXL: file(1) magic for VXL binary IO data files
+#
+# from Ian Scott <scottim@sf.net>
+#
+# VXL is a collection of C++ libraries for Computer Vision.
+# See the vsl chapter in the VXL Book for more info
+# http://www.isbe.man.ac.uk/public_vxl_doc/books/vxl/book.html
+# http:/vxl.sf.net
+
+2 lelong 0x472b2c4e VXL data file,
+>0 leshort >0 schema version no %d
+
+#------------------------------------------------------------------------------
+# $File: warc,v 1.2 2009/09/19 16:28:13 christos Exp $
+# warc: file(1) magic for WARC files
+
+0 string WARC/ WARC Archive
+>5 string x version %.4s
+!:mime application/warc
+
+#------------------------------------------------------------------------------
+# Arc File Format from Internet Archive
+# see http://www.archive.org/web/researcher/ArcFileFormat.php
+0 string filedesc:// Internet Archive File
+!:mime application/x-ia-arc
+>11 search/256 \x0A \b
+>>&0 ubyte >0 \b version %c
+
+#------------------------------------------------------------------------------
+# weak: file(1) magic for very weak magic entries, disabled by default
+#
+# These entries are so weak that they might interfere identification of
+# other formats. Example include:
+# - Only identify for 1 or 2 bytes
+# - Match against very wide range of values
+# - Match against generic word in some spoken languages (e.g. English)
+
+# Summary: Computer Graphics Metafile
+# Extension: .cgm
+#0 beshort&0xffe0 0x0020 binary Computer Graphics Metafile
+#0 beshort 0x3020 character Computer Graphics Metafile
+
+#0 string =!! Bennet Yee's "face" format
+
+#------------------------------------------------------------------------------
+# $File: windows,v 1.9 2014/09/23 23:42:44 christos Exp $
+# windows: file(1) magic for Microsoft Windows
+#
+# This file is mainly reserved for files where programs
+# using them are run almost always on MS Windows 3.x or
+# above, or files only used exclusively in Windows OS,
+# where there is no better category to allocate for.
+# For example, even though WinZIP almost run on Windows
+# only, it is better to treat them as "archive" instead.
+# For format usable in DOS, such as generic executable
+# format, please specify under "msdos" file.
+#
+
+
+# Summary: Outlook Express DBX file
+# Extension: .dbx
+# Created by: Christophe Monniez
+0 string \xCF\xAD\x12\xFE MS Outlook Express DBX file
+>4 byte =0xC5 \b, message database
+>4 byte =0xC6 \b, folder database
+>4 byte =0xC7 \b, account information
+>4 byte =0x30 \b, offline database
+
+
+# Summary: Windows crash dump
+# Extension: .dmp
+# Created by: Andreas Schuster (http://computer.forensikblog.de/)
+# Reference (1): http://computer.forensikblog.de/en/2008/02/64bit_magic.html
+# Modified by (1): Abel Cheung (Avoid match with first 4 bytes only)
+0 string PAGE
+>4 string DUMP MS Windows 32bit crash dump
+>>0x05c byte 0 \b, no PAE
+>>0x05c byte 1 \b, PAE
+>>0xf88 lelong 1 \b, full dump
+>>0xf88 lelong 2 \b, kernel dump
+>>0xf88 lelong 3 \b, small dump
+>>0x068 lelong x \b, %d pages
+>4 string DU64 MS Windows 64bit crash dump
+>>0xf98 lelong 1 \b, full dump
+>>0xf98 lelong 2 \b, kernel dump
+>>0xf98 lelong 3 \b, small dump
+>>0x090 lequad x \b, %lld pages
+
+
+# Summary: Vista Event Log
+# Extension: .evtx
+# Created by: Andreas Schuster (http://computer.forensikblog.de/)
+# Reference (1): http://computer.forensikblog.de/en/2007/05/some_magic.html
+0 string ElfFile\0 MS Windows Vista Event Log
+>0x2a leshort x \b, %d chunks
+>>0x10 lelong x \b (no. %d in use)
+>0x18 lelong >1 \b, next record no. %d
+>0x18 lelong =1 \b, empty
+>0x78 lelong &1 \b, DIRTY
+>0x78 lelong &2 \b, FULL
+
+
+# Summary: Windows 3.1 group files
+# Extension: .grp
+# Created by: unknown
+0 string \120\115\103\103 MS Windows 3.1 group files
+
+
+# Summary: Old format help files
+# Extension: .hlp
+# Created by: Dirk Jagdmann <doj@cubic.org>
+0 lelong 0x00035f3f MS Windows 3.x help file
+
+
+# Summary: Hyper terminal
+# Extension: .ht
+# Created by: unknown
+0 string HyperTerminal\
+>15 string 1.0\ --\ HyperTerminal\ data\ file MS Windows HyperTerminal profile
+
+# http://ithreats.files.wordpress.com/2009/05/\
+# lnk_the_windows_shortcut_file_format.pdf
+# Summary: Windows shortcut
+# Extension: .lnk
+# Created by: unknown
+# 'L' + GUUID
+0 string \114\0\0\0\001\024\002\0\0\0\0\0\300\0\0\0\0\0\0\106 MS Windows shortcut
+>20 lelong&1 1 \b, Item id list present
+>20 lelong&2 2 \b, Points to a file or directory
+>20 lelong&4 4 \b, Has Description string
+>20 lelong&8 8 \b, Has Relative path
+>20 lelong&16 16 \b, Has Working directory
+>20 lelong&32 32 \b, Has command line arguments
+>20 lelong&64 64 \b, Icon
+>>56 lelong \b number=%d
+>24 lelong&1 1 \b, Read-Only
+>24 lelong&2 2 \b, Hidden
+>24 lelong&4 4 \b, System
+>24 lelong&8 8 \b, Volume Label
+>24 lelong&16 16 \b, Directory
+>24 lelong&32 32 \b, Archive
+>24 lelong&64 64 \b, Encrypted
+>24 lelong&128 128 \b, Normal
+>24 lelong&256 256 \b, Temporary
+>24 lelong&512 512 \b, Sparse
+>24 lelong&1024 1024 \b, Reparse point
+>24 lelong&2048 2048 \b, Compressed
+>24 lelong&4096 4096 \b, Offline
+>28 leqwdate x \b, ctime=%s
+>36 leqwdate x \b, mtime=%s
+>44 leqwdate x \b, atime=%s
+>52 lelong x \b, length=%u, window=
+>60 lelong&1 1 \bhide
+>60 lelong&2 2 \bnormal
+>60 lelong&4 4 \bshowminimized
+>60 lelong&8 8 \bshowmaximized
+>60 lelong&16 16 \bshownoactivate
+>60 lelong&32 32 \bminimize
+>60 lelong&64 64 \bshowminnoactive
+>60 lelong&128 128 \bshowna
+>60 lelong&256 256 \brestore
+>60 lelong&512 512 \bshowdefault
+#>20 lelong&1 0
+#>>20 lelong&2 2
+#>>>(72.l-64) pstring/h x \b [%s]
+#>20 lelong&1 1
+#>>20 lelong&2 2
+#>>>(72.s) leshort x
+#>>>&75 pstring/h x \b [%s]
+
+# Summary: Outlook Personal Folders
+# Created by: unknown
+0 lelong 0x4E444221 Microsoft Outlook email folder
+>10 leshort 0x0e (<=2002)
+>10 leshort 0x17 (>=2003)
+
+
+# Summary: Windows help cache
+# Created by: unknown
+0 string \164\146\115\122\012\000\000\000\001\000\000\000 MS Windows help cache
+
+
+# Summary: IE cache file
+# Created by: Christophe Monniez
+0 string Client\ UrlCache\ MMF Internet Explorer cache file
+>20 string >\0 version %s
+
+
+# Summary: Registry files
+# Created by: unknown
+# Modified by (1): Joerg Jenderek
+0 string regf MS Windows registry file, NT/2000 or above
+0 string CREG MS Windows 95/98/ME registry file
+0 string SHCC3 MS Windows 3.1 registry file
+
+
+# Summary: Windows Registry text
+# Extension: .reg
+# Submitted by: Abel Cheung <abelcheung@gmail.com>
+0 string REGEDIT4\r\n\r\n Windows Registry text (Win95 or above)
+0 string Windows\ Registry\ Editor\
+>&0 string Version\ 5.00\r\n\r\n Windows Registry text (Win2K or above)
+
+# Windows *.INF *.INI files updated by Joerg Jenderek at Apr 2013
+# empty ,comment , section
+# PR/383: remove unicode BOM because it is not portable across regex impls
+0 regex/s \\`(\\r\\n|;|[[])
+# left bracket in section line
+>&0 search/8192 [
+# http://en.wikipedia.org/wiki/Autorun.inf
+# http://msdn.microsoft.com/en-us/library/windows/desktop/cc144200.aspx
+>>&0 regex/c \^(autorun)]\r\n
+>>>&0 ubyte =0x5b INItialization configuration
+!:mime application/x-wine-extension-ini
+# From: Pal Tamas <folti@balabit.hu>
+# Autorun File
+>>>&0 ubyte !0x5b Microsoft Windows Autorun file
+!:mime application/x-setupscript
+# http://msdn.microsoft.com/en-us/library/windows/hardware/ff549520(v=vs.85).aspx
+# version strings ASCII coded case-independent for Windows setup information script file
+>>&0 regex/c \^(version|strings)] Windows setup INFormation
+!:mime application/x-setupscript
+#!:mime application/inf
+#!:mime application/x-wine-extension-inf
+>>&0 regex/c \^(WinsockCRCList|OEMCPL)] Windows setup INFormation
+!:mime text/inf
+# http://www.winfaq.de/faq_html/Content/tip2500/onlinefaq.php?h=tip2653.htm
+# http://msdn.microsoft.com/en-us/library/windows/desktop/cc144102.aspx
+# .ShellClassInfo DeleteOnCopy LocalizedFileNames ASCII coded case-independent
+>>&0 regex/c \^(\.ShellClassInfo|DeleteOnCopy|LocalizedFileNames)] Windows desktop.ini
+!:mime application/x-wine-extension-ini
+#!:mime text/plain
+# http://support.microsoft.com/kb/84709/
+>>&0 regex/c \^(don't\ load)] Windows CONTROL.INI
+!:mime application/x-wine-extension-ini
+>>&0 regex/c \^(ndishlp\\$|protman\\$|NETBEUI\\$)] Windows PROTOCOL.INI
+!:mime application/x-wine-extension-ini
+# http://technet.microsoft.com/en-us/library/cc722567.aspx
+# http://www.winfaq.de/faq_html/Content/tip0000/onlinefaq.php?h=tip0137.htm
+>>&0 regex/c \^(windows|Compatibility|embedding)] Windows WIN.INI
+!:mime application/x-wine-extension-ini
+# http://en.wikipedia.org/wiki/SYSTEM.INI
+>>&0 regex/c \^(boot|386enh|drivers)] Windows SYSTEM.INI
+!:mime application/x-wine-extension-ini
+# http://www.mdgx.com/newtip6.htm
+>>&0 regex/c \^(SafeList)] Windows IOS.INI
+!:mime application/x-wine-extension-ini
+# http://en.wikipedia.org/wiki/NTLDR Windows Boot Loader information
+>>&0 regex/c \^(boot\x20loader)] Windows boot.ini
+!:mime application/x-wine-extension-ini
+>>>&0 ubyte x
+# http://en.wikipedia.org/wiki/CONFIG.SYS
+>>&0 regex/c \^(menu)]\r\n MS-DOS CONFIG.SYS
+# http://support.microsoft.com/kb/118579/
+>>&0 regex/c \^(Paths)]\r\n MS-DOS MSDOS.SYS
+# VERS string unicoded case-independent
+>>&0 ubequad&0xFFdfFFdfFFdfFFdf 0x0056004500520053
+# ION] string unicoded case-independent
+>>>&0 ubequad&0xFFdfFFdfFFdfFFff 0x0049004f004e005d Windows setup INFormation
+!:mime application/x-setupscript
+# STRI string unicoded case-independent
+>>&0 ubequad&0xFFdfFFdfFFdfFFdf 0x0053005400520049
+# NGS] string unicoded case-independent
+>>>&0 ubequad&0xFFdfFFdfFFdfFFff 0x004e00470053005D Windows setup INFormation
+!:mime application/x-setupscript
+# unknown keyword after opening bracket
+>>&0 default x
+>>>&0 search/8192 [
+# version Strings FileIdentification
+>>>>&0 string/c version Windows setup INFormation
+!:mime application/x-setupscript
+# VERS string unicoded case-independent
+>>>>&0 ubequad&0xFFdfFFdfFFdfFFdf 0x0056004500520053
+# ION] string unicoded case-independent
+>>>>>&0 ubequad&0xFFdfFFdfFFdfFFff 0x0049004f004e005d Windows setup INFormation
+!:mime application/x-setupscript
+# http://en.wikipedia.org/wiki/Initialization_file Windows Initialization File or other
+#>>>>&0 default x Generic INItialization configuration
+#!:mime application/x-wine-extension-ini
+
+# Windows Precompiled INF files *.PNF added by Joerg Jenderek at Mar 2013 of _PNF_HEADER inf.h
+# http://read.pudn.com/downloads3/sourcecode/windows/248345/win2k/private/windows/setup/setupapi/inf.h__.htm
+# GRR: line below too general as it catches also PDP-11 UNIX/RT ldp
+0 leshort&0xFeFe 0x0000
+# test for unused null bits in PNF_FLAGs
+>4 ulelong&0xFCffFe00 0x00000000
+# only found 58h for Offset of WinDirPath immediately after _PNF_HEADER structure
+>>68 ulelong >0x57
+# test for zero high byte of InfValueBlockSize, followed by WinDirPath like
+# C:\WINDOWS (ASCII 0x433a5c.. , unicode 0x43003a005c..) or X:\MININT
+>>>(68.l-1) ubelong&0xffE0C519 =0x00400018 Windows Precompiled iNF
+!:mime application/x-pnf
+# currently only found Major Version=1 and Minor Version=1
+#>>>>0 uleshort =0x0101
+#>>>>>1 ubyte x \b, version %u
+#>>>>>0 ubyte x \b.%u
+>>>>0 uleshort !0x0101
+>>>>>1 ubyte x \b, version %u
+>>>>>0 ubyte x \b.%u
+# 1 ,2 (windows 98 SE)
+#>>>>2 uleshort =2 \b, InfStyle %u
+>>>>2 uleshort !2 \b, InfStyle %u
+# PNF_FLAG_IS_UNICODE 0x00000001
+# PNF_FLAG_HAS_STRINGS 0x00000002
+# PNF_FLAG_SRCPATH_IS_URL 0x00000004
+# PNF_FLAG_HAS_VOLATILE_DIRIDS 0x00000008
+# PNF_FLAG_INF_VERIFIED 0x00000010
+# PNF_FLAG_INF_DIGITALLY_SIGNED 0x00000020
+# ?? 0x00000100
+# ?? 0x01000000
+# ?? 0x02000000
+>>>>4 ulelong&0x00000001 0x00000001 \b, unicoded
+>>>>4 ulelong&0x00000020 0x00000020 \b, digitally signed
+#>>>>8 ulelong x \b, InfSubstValueListOffset 0x%x
+# many 0, 1 lmouusb.PNF, 2 linkfx10.PNF , f webfdr16.PNF
+#>>>>12 uleshort x \b, InfSubstValueCount 0x%x
+# only < 9 found
+#>>>>14 uleshort x \b, InfVersionDatumCount 0x%x
+# only found values lower 0x0000ffff
+#>>>>16 ulelong x \b, InfVersionDataSize 0x%x
+# only found positive values lower 0x00ffFFff for InfVersionDataOffset
+>>>>20 ulelong x \b, at 0x%x
+>>>>4 ulelong&0x00000001 =0x00000001
+# case independent: CatalogFile Class DriverVer layoutfile LayoutFile SetupClass signature Signature
+>>>>>(20.l) lestring16 x "%s"
+>>>>4 ulelong&0x00000001 !0x00000001
+>>>>>(20.l) string x "%s"
+# FILETIME is number of 100-nanosecond intervals since 1 January 1601
+#>>>>24 ulequad x \b, InfVersionLastWriteTime %16.16llx
+# only found values lower 0x00ffFFff
+#>>>>32 ulelong x \b, StringTableBlockOffset 0x%x
+#>>>>36 ulelong x \b, StringTableBlockSize 0x%x
+#>>>>40 ulelong x \b, InfSectionCount 0x%x
+#>>>>44 ulelong x \b, InfSectionBlockOffset 0x%x
+#>>>>48 ulelong x \b, InfSectionBlockSize 0x%x
+#>>>>52 ulelong x \b, InfLineBlockOffset 0x%x
+#>>>>56 ulelong x \b, InfLineBlockSize 0x%x
+#>>>>60 ulelong x \b, InfValueBlockOffset 0x%x
+#>>>>64 ulelong x \b, InfValueBlockSize 0x%x
+# WinDirPathOffset
+#>>>>68 ulelong x \b, at 0x%x
+>>>>68 ulelong >0x57
+>>>>>4 ulelong&0x00000001 =0x00000001
+>>>>>>(68.l) ubequad =0x43003a005c005700
+# normally unicoded C:\Windows
+#>>>>>>>(68.l) lestring16 x \b, WinDirPath "%s"
+>>>>>>(68.l) ubequad !0x43003a005c005700
+>>>>>>>(68.l) lestring16 x \b, WinDirPath "%s"
+>>>>>4 ulelong&0x00000001 !0x00000001
+# normally ASCII C:\WINDOWS
+#>>>>>>(68.l) string =C:\\WINDOWS \b, WinDirPath "%s"
+>>>>>>(68.l) string !C:\\WINDOWS \b, WinDirPath "%s"
+# found OsLoaderPathOffset values often 0 , once 70h corelist.PNF, once 68h ASCII machine.PNF
+#>>>>72 ulelong >0 \b, at 0x%x
+>>>>72 ulelong >0 \b,
+>>>>>4 ulelong&0x00000001 =0x00000001
+>>>>>>(72.l) lestring16 x OsLoaderPath "%s"
+>>>>>4 ulelong&0x00000001 !0x00000001
+# seldom C:\ instead empty
+>>>>>>(72.l) string x OsLoaderPath "%s"
+# 1fdh
+#>>>>76 uleshort x \b, StringTableHashBucketCount 0x%x
+>>>>78 uleshort !0x407 \b, LanguageId %x
+# only 407h found
+#>>>>78 uleshort =0x407 \b, LanguageId %x
+# InfSourcePathOffset often 0
+#>>>>80 ulelong >0 \b, at 0x%x
+>>>>80 ulelong >0 \b,
+>>>>>4 ulelong&0x00000001 =0x00000001
+>>>>>>(80.l) lestring16 x SourcePath "%s"
+>>>>>4 ulelong&0x00000001 !0x00000001
+>>>>>>(80.l) string >\0 SourcePath "%s"
+# OriginalInfNameOffset often 0
+#>>>>84 ulelong >0 \b, at 0x%x
+>>>>84 ulelong >0 \b,
+>>>>>4 ulelong&0x00000001 =0x00000001
+>>>>>>(84.l) lestring16 x InfName "%s"
+>>>>>4 ulelong&0x00000001 !0x00000001
+>>>>>>(84.l) string >\0 InfName "%s"
+
+
+#------------------------------------------------------------------------------
+# $File$
+# wireless-regdb: file(1) magic for CRDA wireless-regdb file format
+#
+0 string RGDB CRDA wireless regulatory database file
+>4 belong 19 (Version 1)
+
+#------------------------------------------------------------------------------
+# $File: wordprocessors,v 1.17 2013/02/06 14:18:52 christos Exp $
+# wordprocessors: file(1) magic fo word processors.
+#
+####### PWP file format used on Smith Corona Personal Word Processors:
+2 string \040\040\040\040\040\040\040\040\040\040\040ML4D\040'92 Smith Corona PWP
+>24 byte 2 \b, single spaced
+>24 byte 3 \b, 1.5 spaced
+>24 byte 4 \b, double spaced
+>25 byte 0x42 \b, letter
+>25 byte 0x54 \b, legal
+>26 byte 0x46 \b, A4
+
+#WordPerfect type files Version 1.6 - PLEASE DO NOT REMOVE THIS LINE
+0 string \377WPC\020\000\000\000\022\012\001\001\000\000\000\000 (WP) loadable file
+>15 byte 0 Optimized for Intel
+>15 byte 1 Optimized for Non-Intel
+1 string WPC (Corel/WP)
+>8 short 257 WordPerfect macro
+>8 short 258 WordPerfect help file
+>8 short 259 WordPerfect keyboard file
+>8 short 266 WordPerfect document
+>8 short 267 WordPerfect dictionary
+>8 short 268 WordPerfect thesaurus
+>8 short 269 WordPerfect block
+>8 short 270 WordPerfect rectangular block
+>8 short 271 WordPerfect column block
+>8 short 272 WordPerfect printer data
+>8 short 275 WordPerfect printer data
+>8 short 276 WordPerfect driver resource data
+>8 short 279 WordPerfect hyphenation code
+>8 short 280 WordPerfect hyphenation data
+>8 short 281 WordPerfect macro resource data
+>8 short 283 WordPerfect hyphenation lex
+>8 short 285 WordPerfect wordlist
+>8 short 286 WordPerfect equation resource data
+>8 short 289 WordPerfect spell rules
+>8 short 290 WordPerfect dictionary rules
+>8 short 295 WordPerfect spell rules (Microlytics)
+>8 short 299 WordPerfect settings file
+>8 short 301 WordPerfect 4.2 document
+>8 short 325 WordPerfect dialog file
+>8 short 332 WordPerfect button bar
+>8 short 513 Shell macro
+>8 short 522 Shell definition
+>8 short 769 Notebook macro
+>8 short 770 Notebook help file
+>8 short 771 Notebook keyboard file
+>8 short 778 Notebook definition
+>8 short 1026 Calculator help file
+>8 short 1538 Calendar help file
+>8 short 1546 Calendar data file
+>8 short 1793 Editor macro
+>8 short 1794 Editor help file
+>8 short 1795 Editor keyboard file
+>8 short 1817 Editor macro resource file
+>8 short 2049 Macro editor macro
+>8 short 2050 Macro editor help file
+>8 short 2051 Macro editor keyboard file
+>8 short 2305 PlanPerfect macro
+>8 short 2306 PlanPerfect help file
+>8 short 2307 PlanPerfect keyboard file
+>8 short 2314 PlanPerfect worksheet
+>8 short 2319 PlanPerfect printer definition
+>8 short 2322 PlanPerfect graphic definition
+>8 short 2323 PlanPerfect data
+>8 short 2324 PlanPerfect temporary printer
+>8 short 2329 PlanPerfect macro resource data
+>8 byte 11 Mail
+>8 short 2818 help file
+>8 short 2821 distribution list
+>8 short 2826 out box
+>8 short 2827 in box
+>8 short 2836 users archived mailbox
+>8 short 2837 archived message database
+>8 short 2838 archived attachments
+>8 short 3083 Printer temporary file
+>8 short 3330 Scheduler help file
+>8 short 3338 Scheduler in file
+>8 short 3339 Scheduler out file
+>8 short 3594 GroupWise settings file
+>8 short 3601 GroupWise directory services
+>8 short 3627 GroupWise settings file
+>8 short 4362 Terminal resource data
+>8 short 4363 Terminal resource data
+>8 short 4395 Terminal resource data
+>8 short 4619 GUI loadable text
+>8 short 4620 graphics resource data
+>8 short 4621 printer settings file
+>8 short 4622 port definition file
+>8 short 4623 print queue parameters
+>8 short 4624 compressed file
+>8 short 5130 Network service msg file
+>8 short 5131 Network service msg file
+>8 short 5132 Async gateway login msg
+>8 short 5134 GroupWise message file
+>8 short 7956 GroupWise admin domain database
+>8 short 7957 GroupWise admin host database
+>8 short 7959 GroupWise admin remote host database
+>8 short 7960 GroupWise admin ADS deferment data file
+>8 short 8458 IntelliTAG (SGML) compiled DTD
+>8 long 18219264 WordPerfect graphic image (1.0)
+>8 long 18219520 WordPerfect graphic image (2.0)
+#end of WordPerfect type files Version 1.6 - PLEASE DO NOT REMOVE THIS LINE
+
+# Hangul (Korean) Word Processor File
+0 string HWP\ Document\ File Hangul (Korean) Word Processor File 3.0
+# From: Won-Kyu Park <wkpark@kldp.org>
+512 string R\0o\0o\0t\0 Hangul (Korean) Word Processor File 2000
+!:mime application/x-hwp
+
+# CosmicBook, from Benoit Rouits
+0 string CSBK Ted Neslson's CosmicBook hypertext file
+
+2 string EYWR AmigaWriter file
+
+# chi: file(1) magic for ChiWriter files
+0 string \\1cw\ ChiWriter file
+>5 string >\0 version %s
+0 string \\1cw ChiWriter file
+
+# Quark Express from http://www.garykessler.net/library/file_sigs.html
+2 string IIXPR3 Intel Quark Express Document (English)
+2 string IIXPRa Intel Quark Express Document (Korean)
+2 string MMXPR3 Motorola Quark Express Document (English)
+!:mime application/x-quark-xpress-3
+2 string MMXPRa Motorola Quark Express Document (Korean)
+
+# adobe indesign (document, whatever...) from querkan
+0 belong 0x0606edf5 Adobe InDesign
+>16 string DOCUMENT Document
+
+#------------------------------------------------------------------------------
+# ichitaro456: file(1) magic for Just System Word Processor Ichitaro
+#
+# Contributor kenzo-:
+# Reversed-engineered JS Ichitaro magic numbers
+#
+
+0 string DOC
+>43 byte 0x14 Just System Word Processor Ichitaro v4
+!:mime application/x-ichitaro4
+>144 string JDASH application/x-ichitaro4
+
+0 string DOC
+>43 byte 0x15 Just System Word Processor Ichitaro v5
+!:mime application/x-ichitaro5
+
+0 string DOC
+>43 byte 0x16 Just System Word Processor Ichitaro v6
+!:mime application/x-ichitaro6
+
+# Type: Freemind mindmap documents
+# From: Jamie Thompson <debian-bugs@jamie-thompson.co.uk>
+0 string/w \<map\ version Freemind document
+!:mime application/x-freemind
+
+# Type: Freeplane mindmap documents
+# From: Felix Natter <fnatter@gmx.net>
+0 string/w \<map\ version="freeplane Freeplane document
+!:mime application/x-freeplane
+
+# Type: Scribus
+# From: Werner Fink <werner@suse.de>
+0 string \<SCRIBUSUTF8\ Version Scribus Document
+0 string \<SCRIBUSUTF8NEW\ Version Scribus Document
+!:mime application/x-scribus
+
+# help files .hlp compiled from html and used by gfxboot added by Joerg Jenderek
+# markups page=0x04,label=0x12, followed by strings like "opt" or "main" and title=0x14
+0 ulelong&0x8080FFFF 0x00001204 gfxboot compiled html help file
+
+#------------------------------------------------------------------------------
+# $File: wsdl,v 1.2 2013/02/05 15:20:47 christos Exp $
+# wsdl: PHP WSDL Cache, http://www.php.net/manual/en/book.soap.php
+# Cache format extracted from source:
+# http://svn.php.net/viewvc/php/php-src/trunk/ext/soap/php_sdl.c?revision=HEAD&view=markup
+# Requires file >= 5.05, see http://mx.gw.com/pipermail/file/2010/000683.html
+# By Elan Ruusamae <glen@delfi.ee>, Patryk Zawadzki <patrys@pld-linux.org>, 2010-2011
+0 string wsdl PHP WSDL cache,
+>4 byte x version 0x%02x
+>6 ledate x \b, created %s
+
+# uri
+>10 lelong <0x7fffffff
+>>10 pstring/l x \b, uri: "%s"
+
+# source
+>>>&0 lelong <0x7fffffff
+>>>>&-4 pstring/l x \b, source: "%s"
+
+# target_ns
+>>>>>&0 lelong <0x7fffffff
+>>>>>>&-4 pstring/l x \b, target_ns: "%s"
+
+#------------------------------------------------------------------------------
+# $File: xdelta,v 1.4 2009/09/19 16:28:13 christos Exp $
+# file(1) magic(5) data for xdelta Josh MacDonald <jmacd@CS.Berkeley.EDU>
+#
+0 string %XDELTA% XDelta binary patch file 0.14
+0 string %XDZ000% XDelta binary patch file 0.18
+0 string %XDZ001% XDelta binary patch file 0.20
+0 string %XDZ002% XDelta binary patch file 1.0
+0 string %XDZ003% XDelta binary patch file 1.0.4
+0 string %XDZ004% XDelta binary patch file 1.1
+
+0 string \xD6\xC3\xC4\x00 VCDIFF binary diff
+
+#------------------------------------------------------------------------------
+# $File$
+# xenix: file(1) magic for Microsoft Xenix
+#
+# "Middle model" stuff, and "Xenix 8086 relocatable or 80286 small
+# model" lifted from "magic.xenix", with comment "derived empirically;
+# treat as folklore until proven"
+#
+# "small model", "large model", "huge model" stuff lifted from XXX
+#
+# XXX - "x.out" collides with PDP-11 archives
+#
+0 string core core file (Xenix)
+0 byte 0x80 8086 relocatable (Microsoft)
+0 leshort 0xff65 x.out
+>2 string __.SYMDEF randomized
+>0 byte x archive
+0 leshort 0x206 Microsoft a.out
+>8 leshort 1 Middle model
+>0x1e leshort &0x10 overlay
+>0x1e leshort &0x2 separate
+>0x1e leshort &0x4 pure
+>0x1e leshort &0x800 segmented
+>0x1e leshort &0x400 standalone
+>0x1e leshort &0x8 fixed-stack
+>0x1c byte &0x80 byte-swapped
+>0x1c byte &0x40 word-swapped
+>0x10 lelong >0 not-stripped
+>0x1e leshort ^0xc000 pre-SysV
+>0x1e leshort &0x4000 V2.3
+>0x1e leshort &0x8000 V3.0
+>0x1c byte &0x4 86
+>0x1c byte &0xb 186
+>0x1c byte &0x9 286
+>0x1c byte &0xa 386
+>0x1f byte <0x040 small model
+>0x1f byte =0x048 large model
+>0x1f byte =0x049 huge model
+>0x1e leshort &0x1 executable
+>0x1e leshort ^0x1 object file
+>0x1e leshort &0x40 Large Text
+>0x1e leshort &0x20 Large Data
+>0x1e leshort &0x120 Huge Objects Enabled
+>0x10 lelong >0 not stripped
+
+0 leshort 0x140 old Microsoft 8086 x.out
+>0x3 byte &0x4 separate
+>0x3 byte &0x2 pure
+>0 byte &0x1 executable
+>0 byte ^0x1 relocatable
+>0x14 lelong >0 not stripped
+
+0 lelong 0x206 b.out
+>0x1e leshort &0x10 overlay
+>0x1e leshort &0x2 separate
+>0x1e leshort &0x4 pure
+>0x1e leshort &0x800 segmented
+>0x1e leshort &0x400 standalone
+>0x1e leshort &0x1 executable
+>0x1e leshort ^0x1 object file
+>0x1e leshort &0x4000 V2.3
+>0x1e leshort &0x8000 V3.0
+>0x1c byte &0x4 86
+>0x1c byte &0xb 186
+>0x1c byte &0x9 286
+>0x1c byte &0x29 286
+>0x1c byte &0xa 386
+>0x1e leshort &0x4 Large Text
+>0x1e leshort &0x2 Large Data
+>0x1e leshort &0x102 Huge Objects Enabled
+
+0 leshort 0x580 XENIX 8086 relocatable or 80286 small model
+
+#------------------------------------------------------------------------------
+# $File: xilinx,v 1.6 2013/11/19 23:15:13 christos Exp $
+# This is Aaron's attempt at a MAGIC file for Xilinx .bit files.
+# Xilinx-Magic@RevRagnarok.com
+# Got the info from FPGA-FAQ 0026
+#
+# Rewritten to use pstring/H instead of hardcoded lengths by O. Freyermuth,
+# fixes at least reading of bitfiles from Spartan 2, 3, 6.
+# http://www.fpga-faq.com/FAQ_Pages/0026_Tell_me_about_bit_files.htm
+#
+# First there is the sync header and its length
+0 beshort 0x0009
+>2 belong =0x0ff00ff0
+>>&0 belong =0x0ff00ff0
+>>>&0 byte =0x00
+>>>&1 beshort =0x0001
+>>>&3 string a Xilinx BIT data
+# Next is a Pascal-style string with the NCD name. We want to capture that.
+>>>>&0 pstring/H x - from %s
+# And then 'b'
+>>>>>&1 string b
+# Then the model / part number:
+>>>>>>&0 pstring/H x - for %s
+# Then 'c'
+>>>>>>>&1 string c
+# Then the build-date
+>>>>>>>>&0 pstring/H x - built %s
+# Then 'd'
+>>>>>>>>>&1 string d
+# Then the build-time
+>>>>>>>>>>&0 pstring/H x \b(%s)
+# Then 'e'
+>>>>>>>>>>>&1 string e
+# And length of data
+>>>>>>>>>>>>&0 belong x - data length 0x%x
+
+# Raw bitstream files
+0 long 0xffffffff
+>&0 belong 0xaa995566 Xilinx RAW bitstream (.BIN)
+
+#------------------------------------------------------------------------------
+# $File$
+# xo65 object files
+# From: "Ullrich von Bassewitz" <uz@cc65.org>
+#
+0 string \x55\x7A\x6E\x61 xo65 object,
+>4 leshort x version %d,
+>6 leshort&0x0001 =0x0001 with debug info
+>6 leshort&0x0001 =0x0000 no debug info
+
+# xo65 library files
+0 string \x6E\x61\x55\x7A xo65 library,
+>4 leshort x version %d
+
+# o65 object files
+0 string \x01\x00\x6F\x36\x35 o65
+>6 leshort&0x1000 =0x0000 executable,
+>6 leshort&0x1000 =0x1000 object,
+>5 byte x version %d,
+>6 leshort&0x8000 =0x8000 65816,
+>6 leshort&0x8000 =0x0000 6502,
+>6 leshort&0x2000 =0x2000 32 bit,
+>6 leshort&0x2000 =0x0000 16 bit,
+>6 leshort&0x4000 =0x4000 page reloc,
+>6 leshort&0x4000 =0x0000 byte reloc,
+>6 leshort&0x0003 =0x0000 alignment 1
+>6 leshort&0x0003 =0x0001 alignment 2
+>6 leshort&0x0003 =0x0002 alignment 4
+>6 leshort&0x0003 =0x0003 alignment 256
+
+#------------------------------------------------------------------------------
+# $File: xwindows,v 1.8 2013/02/08 17:25:57 christos Exp $
+# xwindows: file(1) magic for various X/Window system file formats.
+
+# Compiled X Keymap
+# XKM (compiled X keymap) files (including version and byte ordering)
+1 string mkx Compiled XKB Keymap: lsb,
+>0 byte >0 version %d
+>0 byte =0 obsolete
+0 string xkm Compiled XKB Keymap: msb,
+>3 byte >0 version %d
+>3 byte =0 obsolete
+
+# xfsdump archive
+0 string xFSdump0 xfsdump archive
+>8 belong x (version %d)
+
+# Jaleo XFS files
+0 long 395726 Jaleo XFS file
+>4 long x - version %d
+>8 long x - [%d -
+>20 long x \b%dx
+>24 long x \b%dx
+>28 long 1008 \bYUV422]
+>28 long 1000 \bRGB24]
+
+# Xcursor data
+# X11 mouse cursor format defined in libXcursor, see
+# http://www.x.org/archive/X11R6.8.1/doc/Xcursor.3.html
+# http://cgit.freedesktop.org/xorg/lib/libXcursor/tree/include/X11/Xcursor/Xcursor.h
+0 string Xcur Xcursor data
+!:mime image/x-xcursor
+>10 leshort x version %d
+>>8 leshort x \b.%d
+#------------------------------------------------------------------------------
+# zfs: file(1) magic for ZFS dumps
+#
+# From <rea-fbsd@codelabs.ru>
+# ZFS dump header has the following structure (as per zfs_ioctl.h
+# in FreeBSD with drr_type is set to DRR_BEGIN)
+#
+# enum {
+# DRR_BEGIN, DRR_OBJECT, DRR_FREEOBJECTS,
+# DRR_WRITE, DRR_FREE, DRR_END,
+# } drr_type;
+# uint32_t drr_pad;
+# uint64_t drr_magic;
+# uint64_t drr_version;
+# uint64_t drr_creation_time;
+# dmu_objset_type_t drr_type;
+# uint32_t drr_pad;
+# uint64_t drr_toguid;
+# uint64_t drr_fromguid;
+# char drr_toname[MAXNAMELEN];
+#
+# Backup magic is 0x00000002f5bacbac (quad word)
+# The drr_type is defined as
+# typedef enum dmu_objset_type {
+# DMU_OST_NONE,
+# DMU_OST_META,
+# DMU_OST_ZFS,
+# DMU_OST_ZVOL,
+# DMU_OST_OTHER, /* For testing only! */
+# DMU_OST_ANY, /* Be careful! */
+# DMU_OST_NUMTYPES
+# } dmu_objset_type_t;
+#
+# Almost all uint64_t fields are printed as the 32-bit ones (with high
+# 32 bits zeroed), because there is no simple way to print them as the
+# full 64-bit values.
+
+# Big-endian values
+8 string \000\000\000\002\365\272\313\254 ZFS shapshot (big-endian machine),
+>20 belong x version %u,
+>32 belong 0 type: NONE,
+>32 belong 1 type: META,
+>32 belong 2 type: ZFS,
+>32 belong 3 type: ZVOL,
+>32 belong 4 type: OTHER,
+>32 belong 5 type: ANY,
+>32 belong >5 type: UNKNOWN (%u),
+>40 byte x destination GUID: %02X
+>41 byte x %02X
+>42 byte x %02X
+>43 byte x %02X
+>44 byte x %02X
+>45 byte x %02X
+>46 byte x %02X
+>47 byte x %02X,
+>48 ulong >0
+>>52 ulong >0
+>>>48 byte x source GUID: %02X
+>>>49 byte x %02X
+>>>50 byte x %02X
+>>>51 byte x %02X
+>>>52 byte x %02X
+>>>53 byte x %02X
+>>>54 byte x %02X
+>>>55 byte x %02X,
+>56 string >\0 name: '%s'
+
+# Little-endian values
+8 string \254\313\272\365\002\000\000\000 ZFS shapshot (little-endian machine),
+>16 lelong x version %u,
+>32 lelong 0 type: NONE,
+>32 lelong 1 type: META,
+>32 lelong 2 type: ZFS,
+>32 lelong 3 type: ZVOL,
+>32 lelong 4 type: OTHER,
+>32 lelong 5 type: ANY,
+>32 lelong >5 type: UNKNOWN (%u),
+>47 byte x destination GUID: %02X
+>46 byte x %02X
+>45 byte x %02X
+>44 byte x %02X
+>43 byte x %02X
+>42 byte x %02X
+>41 byte x %02X
+>40 byte x %02X,
+>48 ulong >0
+>>52 ulong >0
+>>>55 byte x source GUID: %02X
+>>>54 byte x %02X
+>>>53 byte x %02X
+>>>52 byte x %02X
+>>>51 byte x %02X
+>>>50 byte x %02X
+>>>49 byte x %02X
+>>>48 byte x %02X,
+>56 string >\0 name: '%s'
+
+#------------------------------------------------------------------------------
+# $File$
+# zilog: file(1) magic for Zilog Z8000.
+#
+# Was it big-endian or little-endian? My Product Specification doesn't
+# say.
+#
+0 long 0xe807 object file (z8000 a.out)
+0 long 0xe808 pure object file (z8000 a.out)
+0 long 0xe809 separate object file (z8000 a.out)
+0 long 0xe805 overlay object file (z8000 a.out)
+
+#------------------------------------------------------------------------------
+# $File$
+# zyxel: file(1) magic for ZyXEL modems
+#
+# From <rob@pe1chl.ampr.org>
+# These are the /etc/magic entries to decode datafiles as used for the
+# ZyXEL U-1496E DATA/FAX/VOICE modems. (This header conforms to a
+# ZyXEL-defined standard)
+
+0 string ZyXEL\002 ZyXEL voice data
+>10 byte 0 - CELP encoding
+>10 byte&0x0B 1 - ADPCM2 encoding
+>10 byte&0x0B 2 - ADPCM3 encoding
+>10 byte&0x0B 3 - ADPCM4 encoding
+>10 byte&0x0B 8 - New ADPCM3 encoding
+>10 byte&0x04 4 with resync
diff --git a/ext/filter/filter.c b/ext/filter/filter.c
index 28fcca4bfd..cdc5e15bb6 100644
--- a/ext/filter/filter.c
+++ b/ext/filter/filter.c
@@ -286,6 +286,8 @@ PHP_MINIT_FUNCTION(filter)
REGISTER_LONG_CONSTANT("FILTER_FLAG_HOSTNAME", FILTER_FLAG_HOSTNAME, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("FILTER_FLAG_EMAIL_UNICODE", FILTER_FLAG_EMAIL_UNICODE, CONST_CS | CONST_PERSISTENT);
+
sapi_register_input_filter(php_sapi_filter, php_sapi_filter_init);
return SUCCESS;
diff --git a/ext/filter/filter_private.h b/ext/filter/filter_private.h
index d97b1535ab..e74d8b431f 100644
--- a/ext/filter/filter_private.h
+++ b/ext/filter/filter_private.h
@@ -57,6 +57,8 @@
#define FILTER_FLAG_HOSTNAME 0x100000
+#define FILTER_FLAG_EMAIL_UNICODE 0x100000
+
#define FILTER_VALIDATE_INT 0x0101
#define FILTER_VALIDATE_BOOLEAN 0x0102
#define FILTER_VALIDATE_FLOAT 0x0103
diff --git a/ext/filter/logical_filters.c b/ext/filter/logical_filters.c
index a0fed76fce..4a01f31384 100644
--- a/ext/filter/logical_filters.c
+++ b/ext/filter/logical_filters.c
@@ -517,7 +517,7 @@ void php_filter_validate_domain(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
void php_filter_validate_url(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
{
php_url *url;
- int old_len = (int)Z_STRLEN_P(value);
+ size_t old_len = Z_STRLEN_P(value);
php_filter_url(value, flags, option_array, charset);
@@ -598,21 +598,31 @@ void php_filter_validate_email(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
* Feel free to use and redistribute this code. But please keep this copyright notice.
*
*/
- const char regexp[] = "/^(?!(?:(?:\\x22?\\x5C[\\x00-\\x7E]\\x22?)|(?:\\x22?[^\\x5C\\x22]\\x22?)){255,})(?!(?:(?:\\x22?\\x5C[\\x00-\\x7E]\\x22?)|(?:\\x22?[^\\x5C\\x22]\\x22?)){65,}@)(?:(?:[\\x21\\x23-\\x27\\x2A\\x2B\\x2D\\x2F-\\x39\\x3D\\x3F\\x5E-\\x7E]+)|(?:\\x22(?:[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F\\x21\\x23-\\x5B\\x5D-\\x7F]|(?:\\x5C[\\x00-\\x7F]))*\\x22))(?:\\.(?:(?:[\\x21\\x23-\\x27\\x2A\\x2B\\x2D\\x2F-\\x39\\x3D\\x3F\\x5E-\\x7E]+)|(?:\\x22(?:[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F\\x21\\x23-\\x5B\\x5D-\\x7F]|(?:\\x5C[\\x00-\\x7F]))*\\x22)))*@(?:(?:(?!.*[^.]{64,})(?:(?:(?:xn--)?[a-z0-9]+(?:-+[a-z0-9]+)*\\.){1,126}){1,}(?:(?:[a-z][a-z0-9]*)|(?:(?:xn--)[a-z0-9]+))(?:-+[a-z0-9]+)*)|(?:\\[(?:(?:IPv6:(?:(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){7})|(?:(?!(?:.*[a-f0-9][:\\]]){7,})(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,5})?::(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,5})?)))|(?:(?:IPv6:(?:(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){5}:)|(?:(?!(?:.*[a-f0-9]:){5,})(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,3})?::(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,3}:)?)))?(?:(?:25[0-5])|(?:2[0-4][0-9])|(?:1[0-9]{2})|(?:[1-9]?[0-9]))(?:\\.(?:(?:25[0-5])|(?:2[0-4][0-9])|(?:1[0-9]{2})|(?:[1-9]?[0-9]))){3}))\\]))$/iD";
pcre *re = NULL;
pcre_extra *pcre_extra = NULL;
int preg_options = 0;
int ovector[150]; /* Needs to be a multiple of 3 */
int matches;
zend_string *sregexp;
-
+ const char regexp0[] = "/^(?!(?:(?:\\x22?\\x5C[\\x00-\\x7E]\\x22?)|(?:\\x22?[^\\x5C\\x22]\\x22?)){255,})(?!(?:(?:\\x22?\\x5C[\\x00-\\x7E]\\x22?)|(?:\\x22?[^\\x5C\\x22]\\x22?)){65,}@)(?:(?:[\\x21\\x23-\\x27\\x2A\\x2B\\x2D\\x2F-\\x39\\x3D\\x3F\\x5E-\\x7E\\pL\\pN]+)|(?:\\x22(?:[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F\\x21\\x23-\\x5B\\x5D-\\x7F\\pL\\pN]|(?:\\x5C[\\x00-\\x7F]))*\\x22))(?:\\.(?:(?:[\\x21\\x23-\\x27\\x2A\\x2B\\x2D\\x2F-\\x39\\x3D\\x3F\\x5E-\\x7E\\pL\\pN]+)|(?:\\x22(?:[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F\\x21\\x23-\\x5B\\x5D-\\x7F\\pL\\pN]|(?:\\x5C[\\x00-\\x7F]))*\\x22)))*@(?:(?:(?!.*[^.]{64,})(?:(?:(?:xn--)?[a-z0-9]+(?:-+[a-z0-9]+)*\\.){1,126}){1,}(?:(?:[a-z][a-z0-9]*)|(?:(?:xn--)[a-z0-9]+))(?:-+[a-z0-9]+)*)|(?:\\[(?:(?:IPv6:(?:(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){7})|(?:(?!(?:.*[a-f0-9][:\\]]){7,})(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,5})?::(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,5})?)))|(?:(?:IPv6:(?:(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){5}:)|(?:(?!(?:.*[a-f0-9]:){5,})(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,3})?::(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,3}:)?)))?(?:(?:25[0-5])|(?:2[0-4][0-9])|(?:1[0-9]{2})|(?:[1-9]?[0-9]))(?:\\.(?:(?:25[0-5])|(?:2[0-4][0-9])|(?:1[0-9]{2})|(?:[1-9]?[0-9]))){3}))\\]))$/iDu";
+ const char regexp1[] = "/^(?!(?:(?:\\x22?\\x5C[\\x00-\\x7E]\\x22?)|(?:\\x22?[^\\x5C\\x22]\\x22?)){255,})(?!(?:(?:\\x22?\\x5C[\\x00-\\x7E]\\x22?)|(?:\\x22?[^\\x5C\\x22]\\x22?)){65,}@)(?:(?:[\\x21\\x23-\\x27\\x2A\\x2B\\x2D\\x2F-\\x39\\x3D\\x3F\\x5E-\\x7E]+)|(?:\\x22(?:[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F\\x21\\x23-\\x5B\\x5D-\\x7F]|(?:\\x5C[\\x00-\\x7F]))*\\x22))(?:\\.(?:(?:[\\x21\\x23-\\x27\\x2A\\x2B\\x2D\\x2F-\\x39\\x3D\\x3F\\x5E-\\x7E]+)|(?:\\x22(?:[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F\\x21\\x23-\\x5B\\x5D-\\x7F]|(?:\\x5C[\\x00-\\x7F]))*\\x22)))*@(?:(?:(?!.*[^.]{64,})(?:(?:(?:xn--)?[a-z0-9]+(?:-+[a-z0-9]+)*\\.){1,126}){1,}(?:(?:[a-z][a-z0-9]*)|(?:(?:xn--)[a-z0-9]+))(?:-+[a-z0-9]+)*)|(?:\\[(?:(?:IPv6:(?:(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){7})|(?:(?!(?:.*[a-f0-9][:\\]]){7,})(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,5})?::(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,5})?)))|(?:(?:IPv6:(?:(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){5}:)|(?:(?!(?:.*[a-f0-9]:){5,})(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,3})?::(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,3}:)?)))?(?:(?:25[0-5])|(?:2[0-4][0-9])|(?:1[0-9]{2})|(?:[1-9]?[0-9]))(?:\\.(?:(?:25[0-5])|(?:2[0-4][0-9])|(?:1[0-9]{2})|(?:[1-9]?[0-9]))){3}))\\]))$/iD";
+ const char *regexp;
+ size_t regexp_len;
+
+ if (flags & FILTER_FLAG_EMAIL_UNICODE) {
+ regexp = regexp0;
+ regexp_len = sizeof(regexp0) - 1;
+ } else {
+ regexp = regexp1;
+ regexp_len = sizeof(regexp1) - 1;
+ }
/* The maximum length of an e-mail address is 320 octets, per RFC 2821. */
if (Z_STRLEN_P(value) > 320) {
RETURN_VALIDATION_FAILED
}
- sregexp = zend_string_init(regexp, sizeof(regexp) - 1, 0);
+ sregexp = zend_string_init(regexp, regexp_len, 0);
re = pcre_get_compiled_regex(sregexp, &pcre_extra, &preg_options);
if (!re) {
zend_string_release(sregexp);
diff --git a/ext/filter/sanitizing_filters.c b/ext/filter/sanitizing_filters.c
index cc13e1acff..a4bcc0f18a 100644
--- a/ext/filter/sanitizing_filters.c
+++ b/ext/filter/sanitizing_filters.c
@@ -111,7 +111,8 @@ static void php_filter_encode_url(zval *value, const unsigned char* chars, const
static void php_filter_strip(zval *value, zend_long flags)
{
unsigned char *str;
- int i, c;
+ size_t i;
+ int c;
zend_string *buf;
/* Optimization for if no strip flags are set */
@@ -158,7 +159,8 @@ static void filter_map_update(filter_map *map, int flag, const unsigned char *al
static void filter_map_apply(zval *value, filter_map *map)
{
unsigned char *str;
- int i, c;
+ size_t i;
+ int c;
zend_string *buf;
str = (unsigned char *)Z_STRVAL_P(value);
diff --git a/ext/filter/tests/058.phpt b/ext/filter/tests/058.phpt
new file mode 100644
index 0000000000..f3fa483ed9
--- /dev/null
+++ b/ext/filter/tests/058.phpt
@@ -0,0 +1,57 @@
+--TEST--
+FILTER_VALIDATE_EMAIL unicode support (https://tools.ietf.org/html/rfc6531)
+--SKIPIF--
+<?php if (!extension_loaded("filter")) die("skip"); ?>
+--FILE--
+<?php
+$values = Array(
+'niceändsimple@example.com',
+'véry.çommon@example.com',
+'a.lîttle.lengthy.but.fiñe@dept.example.com',
+'dîsposable.style.émail.with+symbol@example.com',
+'other.émail-with-dash@example.com',
+'üser@[IPv6:2001:db8:1ff::a0b:dbd0]',
+'"verî.uñusual.@.uñusual.com"@example.com',
+'"verî.(),:;<>[]\".VERÎ.\"verî@\ \"verî\".unüsual"@strange.example.com',
+'tést@example.com',
+'tést.child@example.com',
+'tést@xn--exmple-cua.com',
+'tést@xn----zfa.xe',
+'tést@subexample.wizard',
+'tést@[255.255.255.255]',
+'tést@[IPv6:2001:0db8:85a3:08d3:1319:8a2e:0370:7344]',
+'tést@[IPv6:2001::7344]',
+'tést@[IPv6:1111:2222:3333:4444:5555:6666:255.255.255.255]',
+'tést+reference@example.com',
+'üñîçøðé@example.com',
+'"üñîçøðé"@example.com',
+'DžǼ੧ఘⅧ⒇৪@example.com',
+);
+foreach ($values as $value) {
+ var_dump(filter_var($value, FILTER_VALIDATE_EMAIL, FILTER_FLAG_EMAIL_UNICODE));
+}
+echo "Done\n";
+?>
+--EXPECT--
+string(26) "niceändsimple@example.com"
+string(25) "véry.çommon@example.com"
+string(44) "a.lîttle.lengthy.but.fiñe@dept.example.com"
+string(48) "dîsposable.style.émail.with+symbol@example.com"
+string(34) "other.émail-with-dash@example.com"
+string(35) "üser@[IPv6:2001:db8:1ff::a0b:dbd0]"
+string(43) ""verî.uñusual.@.uñusual.com"@example.com"
+string(74) ""verî.(),:;<>[]\".VERÎ.\"verî@\ \"verî\".unüsual"@strange.example.com"
+string(17) "tést@example.com"
+string(23) "tést.child@example.com"
+string(24) "tést@xn--exmple-cua.com"
+string(18) "tést@xn----zfa.xe"
+string(23) "tést@subexample.wizard"
+string(23) "tést@[255.255.255.255]"
+string(52) "tést@[IPv6:2001:0db8:85a3:08d3:1319:8a2e:0370:7344]"
+string(23) "tést@[IPv6:2001::7344]"
+string(58) "tést@[IPv6:1111:2222:3333:4444:5555:6666:255.255.255.255]"
+string(27) "tést+reference@example.com"
+string(26) "üñîçøðé@example.com"
+string(28) ""üñîçøðé"@example.com"
+string(31) "DžǼ੧ఘⅧ⒇৪@example.com"
+Done
diff --git a/ext/filter/tests/filter_ipv4_rfc6890.phpt b/ext/filter/tests/filter_ipv4_rfc6890.phpt
index b1920c762d..157de77829 100644
--- a/ext/filter/tests/filter_ipv4_rfc6890.phpt
+++ b/ext/filter/tests/filter_ipv4_rfc6890.phpt
@@ -27,51 +27,51 @@ foreach ($privateRanges as $key => $range) {
$reservedRanges = array();
// 0.0.0.0/8
-$reserverRanges['0.0.0.0/8'] = array('0.0.0.0', '0.255.255.255');
+$reservedRanges['0.0.0.0/8'] = array('0.0.0.0', '0.255.255.255');
// 10.0.0.0/8
-$reserverdRanges['10.0.0.0/8'] = array('10.0.0.0', '10.255.255.255');
+$reservedRanges['10.0.0.0/8'] = array('10.0.0.0', '10.255.255.255');
// 100.64.0.0/10
-$reserverdRanges['10.64.0.0/10'] = array('100.64.0.0', '100.127.255.255');
+$reservedRanges['10.64.0.0/10'] = array('100.64.0.0', '100.127.255.255');
// 127.0.0.0/8
-$reserverdRanges['127.0.0.0/8'] = array('127.0.0.0', '127.255.255.255');
+$reservedRanges['127.0.0.0/8'] = array('127.0.0.0', '127.255.255.255');
// 169.254.0.0/16
-$reserverdRanges['169.254.0.0/16'] = array('169.254.0.0', '169.254.255.255');
+$reservedRanges['169.254.0.0/16'] = array('169.254.0.0', '169.254.255.255');
// 172.16.0.0/12
-$reserverdRanges['172.16.0.0/12'] = array('172.16.0.0', '172.31.0.0');
+$reservedRanges['172.16.0.0/12'] = array('172.16.0.0', '172.31.0.0');
// 192.0.0.0/24
-$reserverdRanges['192.0.0.0/24'] = array('192.0.0.0', '192.0.0.255');
+$reservedRanges['192.0.0.0/24'] = array('192.0.0.0', '192.0.0.255');
// 192.0.0.0/29
-$reserverdRanges['192.0.0.0/29'] = array('192.0.0.0', '192.0.0.7');
+$reservedRanges['192.0.0.0/29'] = array('192.0.0.0', '192.0.0.7');
// 192.0.2.0/24
-$reserverdRanges['192.0.2.0/24'] = array('192.0.2.0', '192.0.2.255');
+$reservedRanges['192.0.2.0/24'] = array('192.0.2.0', '192.0.2.255');
// 198.18.0.0/15
-$reserverdRanges['198.18.0.0/15'] = array('198.18.0.0', '198.19.255.255');
+$reservedRanges['198.18.0.0/15'] = array('198.18.0.0', '198.19.255.255');
// 198.51.100.0/24
-$reserverdRanges['198.51.100.0/24'] = array('198.51.100.0', '198.51.100.255');
+$reservedRanges['198.51.100.0/24'] = array('198.51.100.0', '198.51.100.255');
// 192.88.99.0/24
-$reserverdRanges['192.88.99.0/24'] = array('192.88.99.0', '192.88.99.255');
+$reservedRanges['192.88.99.0/24'] = array('192.88.99.0', '192.88.99.255');
// 192.168.0.0/16
-$reserverdRanges['192.168.0.0/16'] = array('192.168.0.0', '192.168.255.255');
+$reservedRanges['192.168.0.0/16'] = array('192.168.0.0', '192.168.255.255');
// 203.0.113.0/24
-$reserverdRanges['203.0.113.0/24'] = array('203.0.113.0', '203.0.113.255');
+$reservedRanges['203.0.113.0/24'] = array('203.0.113.0', '203.0.113.255');
// 240.0.0.0/4 + 255.255.255.255/32
-$reserverdRanges['240.0.0.0/4'] = array('224.0.0.0', '255.255.255.255');
+$reservedRanges['240.0.0.0/4'] = array('224.0.0.0', '255.255.255.255');
-foreach ($reserverdRanges as $key => $range) {
+foreach ($reservedRanges as $key => $range) {
list($min, $max) = $range;
var_dump($key);
var_dump(filter_var($min, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_NO_RES_RANGE));
@@ -93,6 +93,9 @@ bool(false)
string(14) "192.168.0.0/16"
bool(false)
bool(false)
+string(9) "0.0.0.0/8"
+bool(false)
+bool(false)
string(10) "10.0.0.0/8"
string(8) "10.0.0.0"
string(14) "10.255.255.255"
diff --git a/ext/ftp/config.w32 b/ext/ftp/config.w32
index b09d688180..767c320efd 100644
--- a/ext/ftp/config.w32
+++ b/ext/ftp/config.w32
@@ -7,9 +7,9 @@ if (PHP_FTP != "no") {
EXTENSION("ftp", "php_ftp.c ftp.c");
- if (CHECK_HEADER_ADD_INCLUDE("openssl/ssl.h", "CFLAGS_FTP") &&
- CHECK_LIB("ssleay32.lib", "ftp", PHP_FTP) &&
- CHECK_LIB("libeay32.lib", "ftp", PHP_FTP)) {
+ var ret = SETUP_OPENSSL("ftp", PHP_FTP);
+
+ if (ret > 0) {
MESSAGE("Enabling SSL support for ext\\ftp");
AC_DEFINE('HAVE_FTP_SSL', 1, 'Have FTP over SSL support');
}
diff --git a/ext/ftp/ftp.c b/ext/ftp/ftp.c
index 3af841f047..af7ccd8623 100644
--- a/ext/ftp/ftp.c
+++ b/ext/ftp/ftp.c
@@ -869,7 +869,7 @@ ftp_get(ftpbuf_t *ftp, php_stream *outstream, const char *path, ftptype_t type,
}
while ((rcvd = my_recv(ftp, data->fd, data->buf, FTP_BUFSIZE))) {
- if (rcvd == -1) {
+ if (rcvd == (size_t)-1) {
goto bail;
}
@@ -1836,7 +1836,7 @@ ftp_genlist(ftpbuf_t *ftp, const char *cmd, const char *path)
lines = 0;
lastch = 0;
while ((rcvd = my_recv(ftp, data->fd, data->buf, FTP_BUFSIZE))) {
- if (rcvd == -1 || rcvd > ((size_t)(-1))-size) {
+ if (rcvd == (size_t)-1 || rcvd > ((size_t)(-1))-size) {
goto bail;
}
@@ -1965,7 +1965,7 @@ ftp_nb_continue_read(ftpbuf_t *ftp)
lastch = ftp->lastch;
if ((rcvd = my_recv(ftp, data->fd, data->buf, FTP_BUFSIZE))) {
- if (rcvd == -1) {
+ if (rcvd == (size_t)-1) {
goto bail;
}
diff --git a/ext/ftp/php_ftp.c b/ext/ftp/php_ftp.c
index e9eace1735..3ca775f631 100644
--- a/ext/ftp/php_ftp.c
+++ b/ext/ftp/php_ftp.c
@@ -1491,7 +1491,7 @@ PHP_FUNCTION(ftp_set_option)
RETURN_TRUE;
break;
default:
- php_error_docref(NULL, E_WARNING, "Unknown option '%pd'", option);
+ php_error_docref(NULL, E_WARNING, "Unknown option '" ZEND_LONG_FMT "'", option);
RETURN_FALSE;
break;
}
@@ -1525,7 +1525,7 @@ PHP_FUNCTION(ftp_get_option)
RETURN_BOOL(ftp->usepasvaddress);
break;
default:
- php_error_docref(NULL, E_WARNING, "Unknown option '%pd'", option);
+ php_error_docref(NULL, E_WARNING, "Unknown option '" ZEND_LONG_FMT "'", option);
RETURN_FALSE;
break;
}
diff --git a/ext/gd/gd.c b/ext/gd/gd.c
index ec2a037357..9d0dcca690 100644
--- a/ext/gd/gd.c
+++ b/ext/gd/gd.c
@@ -361,13 +361,13 @@ ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_imagegif, 0, 0, 1)
ZEND_ARG_INFO(0, im)
- ZEND_ARG_INFO(0, filename)
+ ZEND_ARG_INFO(0, to)
ZEND_END_ARG_INFO()
#ifdef HAVE_GD_PNG
ZEND_BEGIN_ARG_INFO_EX(arginfo_imagepng, 0, 0, 1)
ZEND_ARG_INFO(0, im)
- ZEND_ARG_INFO(0, filename)
+ ZEND_ARG_INFO(0, to)
ZEND_ARG_INFO(0, quality)
ZEND_ARG_INFO(0, filters)
ZEND_END_ARG_INFO()
@@ -376,32 +376,32 @@ ZEND_END_ARG_INFO()
#ifdef HAVE_GD_WEBP
ZEND_BEGIN_ARG_INFO_EX(arginfo_imagewebp, 0, 0, 1)
ZEND_ARG_INFO(0, im)
- ZEND_ARG_INFO(0, filename)
+ ZEND_ARG_INFO(0, to)
ZEND_END_ARG_INFO()
#endif
#ifdef HAVE_GD_JPG
ZEND_BEGIN_ARG_INFO_EX(arginfo_imagejpeg, 0, 0, 1)
ZEND_ARG_INFO(0, im)
- ZEND_ARG_INFO(0, filename)
+ ZEND_ARG_INFO(0, to)
ZEND_ARG_INFO(0, quality)
ZEND_END_ARG_INFO()
#endif
ZEND_BEGIN_ARG_INFO_EX(arginfo_imagewbmp, 0, 0, 1)
ZEND_ARG_INFO(0, im)
- ZEND_ARG_INFO(0, filename)
+ ZEND_ARG_INFO(0, to)
ZEND_ARG_INFO(0, foreground)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_imagegd, 0, 0, 1)
ZEND_ARG_INFO(0, im)
- ZEND_ARG_INFO(0, filename)
+ ZEND_ARG_INFO(0, to)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_imagegd2, 0, 0, 1)
ZEND_ARG_INFO(0, im)
- ZEND_ARG_INFO(0, filename)
+ ZEND_ARG_INFO(0, to)
ZEND_ARG_INFO(0, chunk_size)
ZEND_ARG_INFO(0, type)
ZEND_END_ARG_INFO()
@@ -994,7 +994,7 @@ ZEND_GET_MODULE(gd)
/* {{{ PHP_INI_BEGIN */
PHP_INI_BEGIN()
- PHP_INI_ENTRY("gd.jpeg_ignore_warning", "0", PHP_INI_ALL, NULL)
+ PHP_INI_ENTRY("gd.jpeg_ignore_warning", "1", PHP_INI_ALL, NULL)
PHP_INI_END()
/* }}} */
@@ -2683,7 +2683,7 @@ PHP_FUNCTION(imagexbm)
}
/* }}} */
-/* {{{ proto bool imagegif(resource im [, string filename])
+/* {{{ proto bool imagegif(resource im [, mixed to])
Output GIF image to browser or file */
PHP_FUNCTION(imagegif)
{
@@ -2692,7 +2692,7 @@ PHP_FUNCTION(imagegif)
/* }}} */
#ifdef HAVE_GD_PNG
-/* {{{ proto bool imagepng(resource im [, string filename])
+/* {{{ proto bool imagepng(resource im [, mixed to])
Output PNG image to browser or file */
PHP_FUNCTION(imagepng)
{
@@ -2703,7 +2703,7 @@ PHP_FUNCTION(imagepng)
#ifdef HAVE_GD_WEBP
-/* {{{ proto bool imagewebp(resource im [, string filename[, int quality]] )
+/* {{{ proto bool imagewebp(resource im [, mixed to[, int quality]] )
Output WEBP image to browser or file */
PHP_FUNCTION(imagewebp)
{
@@ -2714,7 +2714,7 @@ PHP_FUNCTION(imagewebp)
#ifdef HAVE_GD_JPG
-/* {{{ proto bool imagejpeg(resource im [, string filename [, int quality]])
+/* {{{ proto bool imagejpeg(resource im [, mixed to [, int quality]])
Output JPEG image to browser or file */
PHP_FUNCTION(imagejpeg)
{
@@ -2723,7 +2723,7 @@ PHP_FUNCTION(imagejpeg)
/* }}} */
#endif /* HAVE_GD_JPG */
-/* {{{ proto bool imagewbmp(resource im [, string filename [, int foreground]])
+/* {{{ proto bool imagewbmp(resource im [, mixed to [, int foreground]])
Output WBMP image to browser or file */
PHP_FUNCTION(imagewbmp)
{
@@ -2731,7 +2731,7 @@ PHP_FUNCTION(imagewbmp)
}
/* }}} */
-/* {{{ proto bool imagegd(resource im [, string filename])
+/* {{{ proto bool imagegd(resource im [, mixed to])
Output GD image to browser or file */
PHP_FUNCTION(imagegd)
{
@@ -2739,7 +2739,7 @@ PHP_FUNCTION(imagegd)
}
/* }}} */
-/* {{{ proto bool imagegd2(resource im [, string filename [, int chunk_size [, int type]]])
+/* {{{ proto bool imagegd2(resource im [, mixed to [, int chunk_size [, int type]]])
Output GD2 image to browser or file */
PHP_FUNCTION(imagegd2)
{
@@ -2837,14 +2837,14 @@ PHP_FUNCTION(imagecolorat)
if (im->tpixels && gdImageBoundsSafe(im, x, y)) {
RETURN_LONG(gdImageTrueColorPixel(im, x, y));
} else {
- php_error_docref(NULL, E_NOTICE, "%pd,%pd is out of bounds", x, y);
+ php_error_docref(NULL, E_NOTICE, "" ZEND_LONG_FMT "," ZEND_LONG_FMT " is out of bounds", x, y);
RETURN_FALSE;
}
} else {
if (im->pixels && gdImageBoundsSafe(im, x, y)) {
RETURN_LONG(im->pixels[y][x]);
} else {
- php_error_docref(NULL, E_NOTICE, "%pd,%pd is out of bounds", x, y);
+ php_error_docref(NULL, E_NOTICE, "" ZEND_LONG_FMT "," ZEND_LONG_FMT " is out of bounds", x, y);
RETURN_FALSE;
}
}
@@ -4905,7 +4905,7 @@ PHP_FUNCTION(imageaffinematrixget)
}
default:
- php_error_docref(NULL, E_WARNING, "Invalid type for element %li", type);
+ php_error_docref(NULL, E_WARNING, "Invalid type for element " ZEND_LONG_FMT, type);
RETURN_FALSE;
}
diff --git a/ext/gd/tests/001-mb.phpt b/ext/gd/tests/001-mb.phpt
new file mode 100644
index 0000000000..743df7d2c7
--- /dev/null
+++ b/ext/gd/tests/001-mb.phpt
@@ -0,0 +1,25 @@
+--TEST--
+imagecreatefrompng() and empty/missing file
+--SKIPIF--
+<?php if (!function_exists("imagecreatefrompng")) print "skip"; ?>
+--FILE--
+<?php
+
+$file = dirname(__FILE__)."/001私はガラスを食べられます.test";
+@unlink($file);
+
+var_dump(imagecreatefrompng($file));
+touch($file);
+var_dump(imagecreatefrompng($file));
+
+@unlink($file);
+
+echo "Done\n";
+?>
+--EXPECTF--
+Warning: imagecreatefrompng(%s001私はガラスを食べられます.test): failed to open stream: No such file or directory in %s on line %d
+bool(false)
+
+Warning: imagecreatefrompng(): '%s001私はガラスを食べられます.test' is not a valid PNG file in %s on line %d
+bool(false)
+Done
diff --git a/ext/gd/tests/Tuffy私はガラスを食べられます.ttf b/ext/gd/tests/Tuffy私はガラスを食べられます.ttf
new file mode 100644
index 0000000000..8ea647090f
--- /dev/null
+++ b/ext/gd/tests/Tuffy私はガラスを食べられます.ttf
Binary files differ
diff --git a/ext/gd/tests/bug22544-mb.phpt b/ext/gd/tests/bug22544-mb.phpt
new file mode 100644
index 0000000000..0847214dc3
--- /dev/null
+++ b/ext/gd/tests/bug22544-mb.phpt
@@ -0,0 +1,20 @@
+--TEST--
+Bug #22544 (TrueColor transparency in PNG images).
+--SKIPIF--
+<?php
+ if (!extension_loaded('gd')) {
+ die("skip gd extension not available\n");
+ }
+?>
+--FILE--
+<?php
+ $image = imageCreateTruecolor(640, 100);
+ $transparent = imageColorAllocate($image, 0, 0, 0);
+ $red = imageColorAllocate($image, 255, 50, 50);
+ imageColorTransparent($image, $transparent);
+ imageFilledRectangle($image, 0, 0, 640-1, 100-1, $transparent);
+ include_once __DIR__ . '/func.inc';
+ test_image_equals_file(__DIR__ . '/bug22544私はガラスを食べられます.png', $image);
+?>
+--EXPECT--
+The images are equal.
diff --git a/ext/gd/tests/bug22544私はガラスを食べられます.png b/ext/gd/tests/bug22544私はガラスを食べられます.png
new file mode 100644
index 0000000000..5e6251f440
--- /dev/null
+++ b/ext/gd/tests/bug22544私はガラスを食べられます.png
Binary files differ
diff --git a/ext/gd/tests/bug36697-mb.phpt b/ext/gd/tests/bug36697-mb.phpt
new file mode 100644
index 0000000000..b0b69a394f
--- /dev/null
+++ b/ext/gd/tests/bug36697-mb.phpt
@@ -0,0 +1,28 @@
+--TEST--
+Bug #36697 (TrueColor transparency with GIF palette output).
+--SKIPIF--
+<?php
+ if (!extension_loaded('gd')) {
+ die("skip gd extension not available\n");
+ }
+?>
+--FILE--
+<?php
+$dest = dirname(__FILE__) . "/36697私はガラスを食べられます.gif";
+
+$im = imagecreatetruecolor(192, 36);
+$trans_color = imagecolorallocate($im, 255, 0, 0);
+$color = imagecolorallocate($im, 255, 255, 255);
+imagecolortransparent($im, $trans_color);
+imagefilledrectangle($im, 0,0, 192,36, $trans_color);
+$c = imagecolorat($im, 191,35);
+imagegif($im, $dest);
+imagedestroy($im);
+$im = imagecreatefromgif($dest);
+$c = imagecolorat($im, 191, 35);
+$colors = imagecolorsforindex($im, $c);
+echo $colors['red'] . ' ' . $colors['green'] . ' ' . $colors['blue'];
+@unlink($dest);
+?>
+--EXPECT--
+255 0 0
diff --git a/ext/gd/tests/bug37346-mb.phpt b/ext/gd/tests/bug37346-mb.phpt
new file mode 100644
index 0000000000..90efc6391b
--- /dev/null
+++ b/ext/gd/tests/bug37346-mb.phpt
@@ -0,0 +1,12 @@
+--TEST--
+Bug #37346 (gdimagecreatefromgif, bad colormap)
+--SKIPIF--
+<?php
+ if (!extension_loaded('gd')) die("skip gd extension not available\n");
+?>
+--FILE--
+<?php
+$im = imagecreatefromgif(dirname(__FILE__) . '/bug37346私はガラスを食べられます.gif');
+?>
+--EXPECTF--
+Warning: imagecreatefromgif(): '%sbug37346私はガラスを食べられます.gif' is not a valid GIF file in %sbug37346-mb.php on line %d
diff --git a/ext/gd/tests/bug37346私はガラスを食べられます.gif b/ext/gd/tests/bug37346私はガラスを食べられます.gif
new file mode 100644
index 0000000000..b8453f015e
--- /dev/null
+++ b/ext/gd/tests/bug37346私はガラスを食べられます.gif
@@ -0,0 +1,4 @@
+GIF89a
+<
+
+, Ҷ˵˲ \ No newline at end of file
diff --git a/ext/gd/tests/bug38212-mb.phpt b/ext/gd/tests/bug38212-mb.phpt
new file mode 100644
index 0000000000..905865f28f
--- /dev/null
+++ b/ext/gd/tests/bug38212-mb.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Bug #38212 (Seg Fault on invalid imagecreatefromgd2part() parameters)
+--SKIPIF--
+<?php
+ if (!function_exists('imagecopy')) die("skip gd extension not available\n");
+?>
+--FILE--
+<?php
+$file = dirname(__FILE__) . '/bug38212私はガラスを食べられます.gd2';
+$im1 = imagecreatetruecolor(10,100);
+imagefill($im1, 0,0, 0xffffff);
+imagegd2($im1, $file);
+$im = imagecreatefromgd2part($file, 0,0, -25,10);
+unlink($file);
+?>
+--EXPECTF--
+
+Warning: imagecreatefromgd2part(): Zero width or height not allowed in %s on line %d
diff --git a/ext/gd/tests/bug39286-mb.phpt b/ext/gd/tests/bug39286-mb.phpt
new file mode 100644
index 0000000000..d926fac38e
--- /dev/null
+++ b/ext/gd/tests/bug39286-mb.phpt
@@ -0,0 +1,13 @@
+--TEST--
+Bug #39508 (imagefill crashes with small images 3 pixels or less)
+--SKIPIF--
+<?php
+ if (!extension_loaded('gd')) die("skip gd extension not available\n");
+?>
+--FILE--
+<?php
+$img =imagecreatefromgd2part("foo私はガラスを食べられます.png",0, 100, 0, 100);
+?>
+--EXPECTF--
+
+Warning: imagecreatefromgd2part(): Zero width or height not allowed in %s on line %d
diff --git a/ext/gd/tests/bug48732-mb.phpt b/ext/gd/tests/bug48732-mb.phpt
new file mode 100644
index 0000000000..42871ee206
--- /dev/null
+++ b/ext/gd/tests/bug48732-mb.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Bug #48732 (TTF Bounding box wrong for letters below baseline)
+--SKIPIF--
+<?php
+ if(!extension_loaded('gd')){ die('skip gd extension not available'); }
+ if(!function_exists('imagefttext')) die('skip imagefttext() not available');
+ if (substr(PHP_OS, 0, 3) == 'WIN') die('skip UTF-8 font file names not yet supported on Windows');
+?>
+--FILE--
+<?php
+$cwd = dirname(__FILE__);
+$font = "$cwd/Tuffy私はガラスを食べられます.ttf";
+$g = imagecreate(100, 50);
+$bgnd = imagecolorallocate($g, 255, 255, 255);
+$black = imagecolorallocate($g, 0, 0, 0);
+$bbox = imagettftext($g, 12, 0, 0, 20, $black, $font, "ABCEDFGHIJKLMN\nopqrstu\n");
+imagepng($g, "$cwd/bug48732私はガラスを食べられます.png");
+echo 'Left Bottom: (' . $bbox[0] . ', ' . $bbox[1] . ')';
+?>
+--CLEAN--
+<?php @unlink(dirname(__FILE__) . '/bug48732私はガラスを食べられます.png'); ?>
+--EXPECTF--
+Left Bottom: (0, 46)
diff --git a/ext/gd/tests/bug48801-mb.phpt b/ext/gd/tests/bug48801-mb.phpt
new file mode 100644
index 0000000000..a2f6d28a82
--- /dev/null
+++ b/ext/gd/tests/bug48801-mb.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Bug #48801 (Problem with imagettfbbox)
+--SKIPIF--
+<?php
+ if(!extension_loaded('gd')){ die('skip gd extension not available'); }
+ if(!function_exists('imageftbbox')) die('skip imageftbbox() not available');
+ if (substr(PHP_OS, 0, 3) == 'WIN') die('skip UTF-8 font file names not yet supported on Windows');
+?>
+--FILE--
+<?php
+$cwd = dirname(__FILE__);
+$font = "$cwd/Tuffy私はガラスを食べられます.ttf";
+$bbox = imageftbbox(50, 0, $font, "image");
+echo '(' . $bbox[0] . ', ' . $bbox[1] . ")\n";
+echo '(' . $bbox[2] . ', ' . $bbox[3] . ")\n";
+echo '(' . $bbox[4] . ', ' . $bbox[5] . ")\n";
+echo '(' . $bbox[6] . ', ' . $bbox[7] . ")\n";
+?>
+--EXPECTREGEX--
+\(4, 15\)
+\(16[0-1], 15\)
+\(16[0-1], -4[7-8]\)
+\(4, -4[7-8]\)
diff --git a/ext/gd/tests/bug66339-mb.phpt b/ext/gd/tests/bug66339-mb.phpt
new file mode 100644
index 0000000000..76e3af7c8f
--- /dev/null
+++ b/ext/gd/tests/bug66339-mb.phpt
@@ -0,0 +1,31 @@
+--TEST--
+Bug #66339 (PHP segfaults in imagexbm)
+--SKIPIF--
+<?php
+if (!extension_loaded('gd')) die('skip gd extension not available');
+?>
+--FILE--
+<?php
+$im = imagecreate(8, 8);
+imagecolorallocate($im, 0, 0, 0); // background
+$white = imagecolorallocate($im, 255, 255, 255);
+imagefilledrectangle($im, 2, 2, 6, 6, $white);
+imagexbm($im, NULL);
+echo "------------\n";
+imagexbm($im, './bug66339私はガラスを食べられます.xbm');
+echo file_get_contents('./bug66339私はガラスを食べられます.xbm');
+?>
+--CLEAN--
+<?php
+unlink('./bug66339私はガラスを食べられます.xbm');
+?>
+--EXPECTF--
+#define image_width 8
+#define image_height 8
+static unsigned char image_bits[] = {
+ 0xFF, 0xFF, 0x83, 0x83, 0x83, 0x83, 0x83, 0xFF};
+------------
+#define bug66339%swidth 8
+#define bug66339%sheight 8
+static unsigned char bug66339%sbits[] = {
+ 0xFF, 0xFF, 0x83, 0x83, 0x83, 0x83, 0x83, 0xFF};
diff --git a/ext/gd/tests/bug71912-mb.phpt b/ext/gd/tests/bug71912-mb.phpt
new file mode 100644
index 0000000000..5714e107ce
--- /dev/null
+++ b/ext/gd/tests/bug71912-mb.phpt
@@ -0,0 +1,16 @@
+--TEST--
+Bug #71912 (libgd: signedness vulnerability)
+--SKIPIF--
+<?php
+ if(!extension_loaded('gd')){ die('skip gd extension not available'); }
+ if(!function_exists('imagecreatefromgd2')) die('skip imagecreatefromgd2() not available');
+?>
+--FILE--
+<?php
+imagecreatefromgd2(__DIR__ . DIRECTORY_SEPARATOR . "invalid_neg_size私はガラスを食べられます.gd2");
+?>
+OK
+--EXPECTF--
+
+Warning: imagecreatefromgd2(): '%s%einvalid_neg_size私はガラスを食べられます.gd2' is not a valid GD2 file in %s%ebug71912-mb.php on line %d
+OK
diff --git a/ext/gd/tests/conv_test私はガラスを食べられます.jpeg b/ext/gd/tests/conv_test私はガラスを食べられます.jpeg
new file mode 100644
index 0000000000..7283d1a475
--- /dev/null
+++ b/ext/gd/tests/conv_test私はガラスを食べられます.jpeg
Binary files differ
diff --git a/ext/gd/tests/createfromwbmp-mb.phpt b/ext/gd/tests/createfromwbmp-mb.phpt
new file mode 100644
index 0000000000..50e8a18780
--- /dev/null
+++ b/ext/gd/tests/createfromwbmp-mb.phpt
@@ -0,0 +1,17 @@
+--TEST--
+imagecreatefromwbmp
+--SKIPIF--
+<?php
+ if (!function_exists('imagecreatefromwbmp')) die("skip gd extension not available\n");
+?>
+--FILE--
+<?php
+$file = dirname(__FILE__) . '/src私はガラスを食べられます.wbmp';
+
+$im2 = imagecreatefromwbmp($file);
+echo 'test create from wbmp: ';
+echo imagecolorat($im2, 3,3) == 0x0 ? 'ok' : 'failed';
+echo "\n";
+?>
+--EXPECT--
+test create from wbmp: ok
diff --git a/ext/gd/tests/imagecopyresampled_variation1.phpt b/ext/gd/tests/imagecopyresampled_variation1.phpt
new file mode 100644
index 0000000000..98547f8931
--- /dev/null
+++ b/ext/gd/tests/imagecopyresampled_variation1.phpt
@@ -0,0 +1,70 @@
+--TEST--
+Test for correct colors of imagecopyresampled() wrt. alpha
+--SKIPIF--
+<?php
+if (!extension_loaded('gd')) die('skip ext/gd required');
+?>
+--FILE--
+<?php
+
+const EXP_RED = 66;
+const EXP_GREEN = 66;
+const EXP_BLUE = 133;
+const EXP_ALPHA = 32;
+
+/* create the source image */
+$im = imagecreatetruecolor(10, 10);
+imagealphablending($im, false);
+$solid = imagecolorallocate($im, 0, 100, 150);
+$transparent = imagecolorallocatealpha($im, 200, 0, 100, 64);
+
+/* draw a checker pattern */
+for ($i = 0; $i < imagesx($im); $i++) {
+ for ($j = 0; $j < imagesy($im); $j++) {
+ imagesetpixel($im, $i, $j, ($i%2 != $j%2 ? $solid : $transparent));
+ }
+}
+
+/* create the destination image */
+$copy = imagecreatetruecolor(5, 5);
+imagealphablending($copy, false);
+imagesavealpha($copy, true);
+imagecopyresampled($copy, $im, 0,0, 0,0, 5,5, 10, 10);
+
+/* assert all pixels have the same color */
+$color = imagecolorat($copy, 3, 3);
+for ($i = 0; $i < imagesx($copy); $i++) {
+ for ($j = 0; $j < imagesy($copy); $j++) {
+ if (imagecolorat($copy, $i, $j) != $color) {
+ echo 'different pixel values', PHP_EOL;
+ }
+ }
+}
+
+/* assign actual component values */
+$red = ($color & 0xFF0000) >> 16;
+$green = ($color & 0x00FF00) >> 8;
+$blue = ($color & 0x0000FF);
+$alpha = ($color & 0x7F000000) >> 24;
+
+/* test for expected component values */
+if (!($red >= EXP_RED - 1 && $red <= EXP_RED + 1)) {
+ printf("red: expected roughly %d, got %d\n", EXP_RED, $red);
+}
+if (!($green >= EXP_GREEN - 1 && $green <= EXP_GREEN + 1)) {
+ printf("green: expected roughly %d, got %d\n", EXP_GREEN, $green);
+}
+if (!($blue >= EXP_BLUE - 1 && $blue <= EXP_BLUE + 1)) {
+ printf("blue: expected roughly %d, got %d\n", EXP_BLUE, $blue);
+}
+if (!($alpha >= EXP_ALPHA - 1 && $alpha <= EXP_ALPHA + 1)) {
+ printf("alpha: expected roughly %d, got %d\n", EXP_ALPHA, $alpha);
+}
+
+imagedestroy($copy);
+imagedestroy($im);
+
+echo 'DONE';
+?>
+--EXPECT--
+DONE
diff --git a/ext/gd/tests/imagewbmp-mb.phpt b/ext/gd/tests/imagewbmp-mb.phpt
new file mode 100644
index 0000000000..bb6cfdc655
--- /dev/null
+++ b/ext/gd/tests/imagewbmp-mb.phpt
@@ -0,0 +1,30 @@
+--TEST--
+imagewbmp
+--SKIPIF--
+<?php
+ if (!function_exists('imagecreatefromwbmp')) die("skip gd extension not available\n");
+?>
+--FILE--
+<?php
+$file = dirname(__FILE__) . '/im私はガラスを食べられます.wbmp';
+
+$im = imagecreatetruecolor(6,6);
+imagefill($im, 0,0, 0xffffff);
+imagesetpixel($im, 3,3, 0x0);
+imagewbmp($im, $file);
+
+$im2 = imagecreatefromwbmp($file);
+echo 'test create wbmp: ';
+$c = imagecolorsforindex($im2, imagecolorat($im2, 3,3));
+$failed = false;
+foreach ($c as $v) {
+ if ($v != 0) {
+ $failed = true;
+ }
+}
+echo !$failed ? 'ok' : 'failed';
+echo "\n";
+unlink($file);
+?>
+--EXPECT--
+test create wbmp: ok
diff --git a/ext/gd/tests/invalid_neg_size私はガラスを食べられます.gd2 b/ext/gd/tests/invalid_neg_size私はガラスを食べられます.gd2
new file mode 100644
index 0000000000..3075f15a81
--- /dev/null
+++ b/ext/gd/tests/invalid_neg_size私はガラスを食べられます.gd2
Binary files differ
diff --git a/ext/gd/tests/jpeg2wbmp_error2-mb.phpt b/ext/gd/tests/jpeg2wbmp_error2-mb.phpt
new file mode 100644
index 0000000000..a1c91b1397
--- /dev/null
+++ b/ext/gd/tests/jpeg2wbmp_error2-mb.phpt
@@ -0,0 +1,31 @@
+--TEST--
+Test jpeg2wbmp() function : wrong origin filename param
+--CREDITS--
+Levi Fukumori <levi [at] fukumori [dot] com [dot] br>
+#testfest PHPSP on 2009-06-20
+--SKIPIF--
+<?php
+if(!extension_loaded('gd')) {
+ die('skip gd extension is not loaded');
+}
+if(!function_exists('jpeg2wbmp')) {
+ die('skip jpeg2wbmp function is not available');
+}
+?>
+--FILE--
+<?php
+$file = dirname(__FILE__) .'/simpletext私はガラスを食べられます.wbmp';
+jpeg2wbmp('', $file, 20, 120, 8);
+jpeg2wbmp(null, $file, 20, 120, 8);
+jpeg2wbmp(false, $file, 20, 120, 8);
+?>
+--EXPECTF--
+Warning: jpeg2wbmp(): Unable to open '' for reading in %s on line %d
+
+Warning: jpeg2wbmp(): Unable to open '' for reading in %s on line %d
+
+Warning: jpeg2wbmp(): Unable to open '' for reading in %s on line %d
+--CLEAN--
+<?php
+unlink(dirname(__FILE__) .'/simpletext私はガラスを食べられます.wbmp');
+?>
diff --git a/ext/gd/tests/jpg2gd-mb.phpt b/ext/gd/tests/jpg2gd-mb.phpt
new file mode 100644
index 0000000000..8dee6b3bd5
--- /dev/null
+++ b/ext/gd/tests/jpg2gd-mb.phpt
@@ -0,0 +1,42 @@
+--TEST--
+jpeg <--> gd1/gd2 conversion test
+--SKIPIF--
+<?php
+ if (!extension_loaded('gd')) {
+ die("skip gd extension not available.");
+ }
+
+ if (!function_exists("imagecreatefromjpeg") || !function_exists("imagejpeg")) {
+ die("skip jpeg support unavailable");
+ }
+?>
+--FILE--
+<?php
+ $cwd = dirname(__FILE__);
+
+ echo "JPEG to GD1 conversion: ";
+ echo imagegd(imagecreatefromjpeg($cwd . "/conv_test私はガラスを食べられます.jpeg"), $cwd . "/test私はガラスを食べられます.gd1") ? 'ok' : 'failed';
+ echo "\n";
+
+ echo "JPEG to GD2 conversion: ";
+ echo imagegd2(imagecreatefromjpeg($cwd . "/conv_test私はガラスを食べられます.jpeg"), $cwd . "/test私はガラスを食べられます.gd2") ? 'ok' : 'failed';
+ echo "\n";
+
+ echo "GD1 to JPEG conversion: ";
+ echo imagejpeg(imagecreatefromgd($cwd . "/test私はガラスを食べられます.gd1"), $cwd . "/test_gd1.jpeg") ? 'ok' : 'failed';
+ echo "\n";
+
+ echo "GD2 to JPEG conversion: ";
+ echo imagejpeg(imagecreatefromgd2($cwd . "/test私はガラスを食べられます.gd2"), $cwd . "/test_gd2.jpeg") ? 'ok' : 'failed';
+ echo "\n";
+
+ @unlink($cwd . "/test私はガラスを食べられます.gd1");
+ @unlink($cwd . "/test私はガラスを食べられます.gd2");
+ @unlink($cwd . "/test_gd1.jpeg");
+ @unlink($cwd . "/test_gd2.jpeg");
+?>
+--EXPECT--
+JPEG to GD1 conversion: ok
+JPEG to GD2 conversion: ok
+GD1 to JPEG conversion: ok
+GD2 to JPEG conversion: ok
diff --git a/ext/gd/tests/libgd00094-mb.phpt b/ext/gd/tests/libgd00094-mb.phpt
new file mode 100644
index 0000000000..c18b4fd3d1
--- /dev/null
+++ b/ext/gd/tests/libgd00094-mb.phpt
@@ -0,0 +1,16 @@
+--TEST--
+libgd #94 (imagecreatefromxbm can crash if gdImageCreate fails)
+--SKIPIF--
+<?php
+ if (!extension_loaded('gd')) die("skip gd extension not available\n");
+ if (!GD_BUNDLED) die("skip requires bundled GD library\n");
+?>
+--FILE--
+<?php
+$im = imagecreatefromxbm(dirname(__FILE__) . '/libgd00094私はガラスを食べられます.xbm');
+var_dump($im);
+?>
+--EXPECTF--
+Warning: imagecreatefromxbm(): '%slibgd00094私はガラスを食べられます.xbm' is not a valid XBM file in %slibgd00094-mb.php on line %d
+bool(false)
+
diff --git a/ext/gd/tests/libgd00094私はガラスを食べられます.xbm b/ext/gd/tests/libgd00094私はガラスを食べられます.xbm
new file mode 100644
index 0000000000..4d0f5bdd87
--- /dev/null
+++ b/ext/gd/tests/libgd00094私はガラスを食べられます.xbm
@@ -0,0 +1,3 @@
+#define width 255
+#define height 1073741824
+static unsigned char bla = {
diff --git a/ext/gd/tests/png2wbmp_error1-mb.phpt b/ext/gd/tests/png2wbmp_error1-mb.phpt
new file mode 100644
index 0000000000..a19cf5e017
--- /dev/null
+++ b/ext/gd/tests/png2wbmp_error1-mb.phpt
@@ -0,0 +1,42 @@
+--TEST--
+Test png2wbmp() function : wrong threshold value param
+--CREDITS--
+Levi Fukumori <levi [at] fukumori [dot] com [dot] br>
+#testfest PHPSP on 2009-06-20
+--SKIPIF--
+<?php
+if(!extension_loaded('gd')) {
+ die('skip gd extension is not loaded');
+}
+if(!function_exists('png2wbmp')) {
+ die('skip png2wbmp function is not available');
+}
+?>
+--FILE--
+<?php
+// Create a blank image and add some text
+$im = imagecreatetruecolor(120, 20);
+$text_color = imagecolorallocate($im, 255, 255, 255);
+imagestring($im, 1, 5, 5, 'A Simple Text String', $text_color);
+
+$file = dirname(__FILE__) .'/simpletext私はガラスを食べられます.png';
+$file2 = dirname(__FILE__) .'/simpletext私はガラスを食べられます.wbmp';
+
+// Save the image as 'simpletext.png'
+imagepng($im, $file);
+
+// Free up memory
+imagedestroy($im);
+
+png2wbmp($file, $file2, 20, 120, 9);
+png2wbmp($file, $file2, 20, 120, -1);
+?>
+--EXPECTF--
+Warning: png2wbmp(): Invalid threshold value '9' in %s on line %d
+
+Warning: png2wbmp(): Invalid threshold value '-1' in %s on line %d
+--CLEAN--
+<?php
+unlink(dirname(__FILE__) .'/simpletext私はガラスを食べられます.png');
+unlink(dirname(__FILE__) .'/simpletext私はガラスを食べられます.wbmp');
+?>
diff --git a/ext/gd/tests/simpletext私はガラスを食べられます.jpg b/ext/gd/tests/simpletext私はガラスを食べられます.jpg
new file mode 100644
index 0000000000..c527d7982f
--- /dev/null
+++ b/ext/gd/tests/simpletext私はガラスを食べられます.jpg
Binary files differ
diff --git a/ext/gd/tests/src私はガラスを食べられます.wbmp b/ext/gd/tests/src私はガラスを食べられます.wbmp
new file mode 100644
index 0000000000..d38c74268a
--- /dev/null
+++ b/ext/gd/tests/src私はガラスを食べられます.wbmp
Binary files differ
diff --git a/ext/gmp/gmp.c b/ext/gmp/gmp.c
index fa73d83873..7fb1795d80 100644
--- a/ext/gmp/gmp.c
+++ b/ext/gmp/gmp.c
@@ -1038,7 +1038,7 @@ ZEND_FUNCTION(gmp_init)
}
if (base && (base < 2 || base > GMP_MAX_BASE)) {
- php_error_docref(NULL, E_WARNING, "Bad base for conversion: %pd (should be between 2 and %d)", base, GMP_MAX_BASE);
+ php_error_docref(NULL, E_WARNING, "Bad base for conversion: " ZEND_LONG_FMT " (should be between 2 and %d)", base, GMP_MAX_BASE);
RETURN_FALSE;
}
@@ -1054,7 +1054,7 @@ int gmp_import_export_validate(zend_long size, zend_long options, int *order, in
{
if (size < 1) {
php_error_docref(NULL, E_WARNING,
- "Word size must be positive, %pd given", size);
+ "Word size must be positive, " ZEND_LONG_FMT " given", size);
return FAILURE;
}
@@ -1195,7 +1195,7 @@ ZEND_FUNCTION(gmp_strval)
/* Although the maximum base in general in GMP is 62, mpz_get_str()
* is explicitly limited to -36 when dealing with negative bases. */
if ((base < 2 && base > -2) || base > GMP_MAX_BASE || base < -36) {
- php_error_docref(NULL, E_WARNING, "Bad base for conversion: %pd (should be between 2 and %d or -2 and -36)", base, GMP_MAX_BASE);
+ php_error_docref(NULL, E_WARNING, "Bad base for conversion: " ZEND_LONG_FMT " (should be between 2 and %d or -2 and -36)", base, GMP_MAX_BASE);
RETURN_FALSE;
}
diff --git a/ext/gmp/tests/cast.phpt b/ext/gmp/tests/cast.phpt
index eb1832c4dd..8606538baf 100644
--- a/ext/gmp/tests/cast.phpt
+++ b/ext/gmp/tests/cast.phpt
@@ -19,4 +19,4 @@ string(2) "42"
int(42)
float(42)
-Catchable fatal error: Object of class GMP could not be converted to boolean in %s on line %d
+Recoverable fatal error: Object of class GMP could not be converted to boolean in %s on line %d
diff --git a/ext/hash/config.m4 b/ext/hash/config.m4
index 5174db3b71..703cf14a30 100644
--- a/ext/hash/config.m4
+++ b/ext/hash/config.m4
@@ -27,11 +27,11 @@ if test "$PHP_HASH" != "no"; then
EXT_HASH_SOURCES="hash.c hash_md.c hash_sha.c hash_ripemd.c hash_haval.c \
hash_tiger.c hash_gost.c hash_snefru.c hash_whirlpool.c hash_adler32.c \
- hash_crc32.c hash_fnv.c hash_joaat.c"
+ hash_crc32.c hash_fnv.c hash_joaat.c hash_sha3.c"
EXT_HASH_HEADERS="php_hash.h php_hash_md.h php_hash_sha.h php_hash_ripemd.h \
php_hash_haval.h php_hash_tiger.h php_hash_gost.h php_hash_snefru.h \
php_hash_whirlpool.h php_hash_adler32.h php_hash_crc32.h \
- php_hash_fnv.h php_hash_joaat.h"
+ php_hash_fnv.h php_hash_joaat.h php_hash_sha3.h"
PHP_NEW_EXTENSION(hash, $EXT_HASH_SOURCES, $ext_shared)
ifdef([PHP_INSTALL_HEADERS], [
diff --git a/ext/hash/config.w32 b/ext/hash/config.w32
index 8e9d4c3d48..17711facd8 100644
--- a/ext/hash/config.w32
+++ b/ext/hash/config.w32
@@ -15,10 +15,10 @@ if (PHP_HASH != "no") {
AC_DEFINE('HAVE_HASH_EXT', 1);
EXTENSION("hash", "hash.c hash_md.c hash_sha.c hash_ripemd.c hash_haval.c "
+ "hash_tiger.c hash_gost.c hash_snefru.c hash_whirlpool.c "
- + "hash_adler32.c hash_crc32.c hash_joaat.c hash_fnv.c");
+ + "hash_adler32.c hash_crc32.c hash_joaat.c hash_fnv.c hash_sha3.c");
PHP_INSTALL_HEADERS("ext/hash/", "php_hash.h php_hash_md.h php_hash_sha.h php_hash_ripemd.h " +
"php_hash_haval.h php_hash_tiger.h php_hash_gost.h php_hash_snefru.h " +
- "php_hash_whirlpool.h php_hash_adler32.h php_hash_crc32.h");
+ "php_hash_whirlpool.h php_hash_adler32.h php_hash_crc32.h php_hash_sha3.h");
}
diff --git a/ext/hash/hash.c b/ext/hash/hash.c
index 59d2fbfb78..a86297e92c 100644
--- a/ext/hash/hash.c
+++ b/ext/hash/hash.c
@@ -31,12 +31,6 @@
static int php_hash_le_hash;
HashTable php_hash_hashtable;
-#if (PHP_MAJOR_VERSION >= 5)
-# define DEFAULT_CONTEXT FG(default_context)
-#else
-# define DEFAULT_CONTEXT NULL
-#endif
-
#ifdef PHP_MHASH_BC
struct mhash_bc_entry {
char *mhash_name;
@@ -140,7 +134,7 @@ static void php_hash_do_hash(INTERNAL_FUNCTION_PARAMETERS, int isfilename, zend_
php_error_docref(NULL, E_WARNING, "Invalid path");
RETURN_FALSE;
}
- stream = php_stream_open_wrapper_ex(data, "rb", REPORT_ERRORS, NULL, DEFAULT_CONTEXT);
+ stream = php_stream_open_wrapper_ex(data, "rb", REPORT_ERRORS, NULL, FG(default_context));
if (!stream) {
/* Stream will report errors opening file */
RETURN_FALSE;
@@ -214,7 +208,7 @@ static inline void php_hash_string_xor(unsigned char *out, const unsigned char *
static inline void php_hash_hmac_prep_key(unsigned char *K, const php_hash_ops *ops, void *context, const unsigned char *key, const size_t key_len) {
memset(K, 0, ops->block_size);
- if (key_len > ops->block_size) {
+ if (key_len > (size_t)ops->block_size) {
/* Reduce the key first */
ops->hash_init(context);
ops->hash_update(context, key, key_len);
@@ -259,7 +253,7 @@ static void php_hash_do_hash_hmac(INTERNAL_FUNCTION_PARAMETERS, int isfilename,
php_error_docref(NULL, E_WARNING, "Invalid path");
RETURN_FALSE;
}
- stream = php_stream_open_wrapper_ex(data, "rb", REPORT_ERRORS, NULL, DEFAULT_CONTEXT);
+ stream = php_stream_open_wrapper_ex(data, "rb", REPORT_ERRORS, NULL, FG(default_context));
if (!stream) {
/* Stream will report errors opening file */
RETURN_FALSE;
@@ -373,7 +367,7 @@ PHP_FUNCTION(hash_init)
memset(K, 0, ops->block_size);
- if (key_len > ops->block_size) {
+ if (key_len > (size_t)ops->block_size) {
/* Reduce the key first */
ops->hash_update(context, (unsigned char *) key, key_len);
ops->hash_final((unsigned char *) K, context);
@@ -603,6 +597,122 @@ PHP_FUNCTION(hash_algos)
}
/* }}} */
+static inline zend_bool php_hash_is_crypto(const char *algo, size_t algo_len) {
+
+ char *blacklist[] = { "adler32", "crc32", "crc32b", "fnv132", "fnv1a32", "fnv164", "fnv1a64", "joaat", NULL };
+ char *lower = zend_str_tolower_dup(algo, algo_len);
+ int i = 0;
+
+ while (blacklist[i]) {
+ if (strcmp(lower, blacklist[i]) == 0) {
+ efree(lower);
+ return 0;
+ }
+
+ i++;
+ }
+
+ efree(lower);
+ return 1;
+}
+
+/* {{{ proto string hash_hkdf(string algo, string ikm [, int length = 0, string info = '', string salt = ''])
+RFC5869 HMAC-based key derivation function */
+PHP_FUNCTION(hash_hkdf)
+{
+ zend_string *returnval, *ikm, *algo, *info = NULL, *salt = NULL;
+ zend_long length = 0;
+ unsigned char *prk, *digest, *K;
+ int i, rounds;
+ const php_hash_ops *ops;
+ void *context;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS|lSS", &algo, &ikm, &length, &info, &salt) == FAILURE) {
+ return;
+ }
+
+ ops = php_hash_fetch_ops(ZSTR_VAL(algo), ZSTR_LEN(algo));
+ if (!ops) {
+ php_error_docref(NULL, E_WARNING, "Unknown hashing algorithm: %s", ZSTR_VAL(algo));
+ RETURN_FALSE;
+ }
+
+ if (!php_hash_is_crypto(ZSTR_VAL(algo), ZSTR_LEN(algo))) {
+ php_error_docref(NULL, E_WARNING, "Non-cryptographic hashing algorithm: %s", ZSTR_VAL(algo));
+ RETURN_FALSE;
+ }
+
+ if (ZSTR_LEN(ikm) == 0) {
+ php_error_docref(NULL, E_WARNING, "Input keying material cannot be empty");
+ RETURN_FALSE;
+ }
+
+ if (length < 0) {
+ php_error_docref(NULL, E_WARNING, "Length must be greater than or equal to 0: " ZEND_LONG_FMT, length);
+ RETURN_FALSE;
+ } else if (length == 0) {
+ length = ops->digest_size;
+ } else if (length > ops->digest_size * 255) {
+ php_error_docref(NULL, E_WARNING, "Length must be less than or equal to %d: " ZEND_LONG_FMT, ops->digest_size * 255, length);
+ RETURN_FALSE;
+ }
+
+ context = emalloc(ops->context_size);
+
+ // Extract
+ ops->hash_init(context);
+ K = emalloc(ops->block_size);
+ php_hash_hmac_prep_key(K, ops, context,
+ (unsigned char *) (salt ? ZSTR_VAL(salt) : ""), salt ? ZSTR_LEN(salt) : 0);
+
+ prk = emalloc(ops->digest_size);
+ php_hash_hmac_round(prk, ops, context, K, (unsigned char *) ZSTR_VAL(ikm), ZSTR_LEN(ikm));
+ php_hash_string_xor_char(K, K, 0x6A, ops->block_size);
+ php_hash_hmac_round(prk, ops, context, K, prk, ops->digest_size);
+ ZEND_SECURE_ZERO(K, ops->block_size);
+
+ // Expand
+ returnval = zend_string_alloc(length, 0);
+ digest = emalloc(ops->digest_size);
+ for (i = 1, rounds = (length - 1) / ops->digest_size + 1; i <= rounds; i++) {
+ // chr(i)
+ unsigned char c[1];
+ c[0] = (i & 0xFF);
+
+ php_hash_hmac_prep_key(K, ops, context, prk, ops->digest_size);
+ ops->hash_init(context);
+ ops->hash_update(context, K, ops->block_size);
+
+ if (i > 1) {
+ ops->hash_update(context, digest, ops->digest_size);
+ }
+
+ if (info != NULL && ZSTR_LEN(info) > 0) {
+ ops->hash_update(context, (unsigned char *) ZSTR_VAL(info), ZSTR_LEN(info));
+ }
+
+ ops->hash_update(context, c, 1);
+ ops->hash_final(digest, context);
+ php_hash_string_xor_char(K, K, 0x6A, ops->block_size);
+ php_hash_hmac_round(digest, ops, context, K, digest, ops->digest_size);
+ memcpy(
+ ZSTR_VAL(returnval) + ((i - 1) * ops->digest_size),
+ digest,
+ (i == rounds ? length - ((i - 1) * ops->digest_size) : ops->digest_size)
+ );
+ }
+
+ ZEND_SECURE_ZERO(K, ops->block_size);
+ ZEND_SECURE_ZERO(digest, ops->digest_size);
+ ZEND_SECURE_ZERO(prk, ops->digest_size);
+ efree(K);
+ efree(context);
+ efree(prk);
+ efree(digest);
+ ZSTR_VAL(returnval)[length] = 0;
+ RETURN_STR(returnval);
+}
+
/* {{{ proto string hash_pbkdf2(string algo, string password, string salt, int iterations [, int length = 0, bool raw_output = false])
Generate a PBKDF2 hash of the given password and salt
Returns lowercase hexits by default */
@@ -735,7 +845,8 @@ PHP_FUNCTION(hash_equals)
{
zval *known_zval, *user_zval;
char *known_str, *user_str;
- int result = 0, j;
+ int result = 0;
+ size_t j;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &known_zval, &user_zval) == FAILURE) {
return;
@@ -1013,7 +1124,13 @@ PHP_MINIT_FUNCTION(hash)
php_hash_register_algo("sha224", &php_hash_sha224_ops);
php_hash_register_algo("sha256", &php_hash_sha256_ops);
php_hash_register_algo("sha384", &php_hash_sha384_ops);
+ php_hash_register_algo("sha512/224", &php_hash_sha512_224_ops);
+ php_hash_register_algo("sha512/256", &php_hash_sha512_256_ops);
php_hash_register_algo("sha512", &php_hash_sha512_ops);
+ php_hash_register_algo("sha3-224", &php_hash_sha3_224_ops);
+ php_hash_register_algo("sha3-256", &php_hash_sha3_256_ops);
+ php_hash_register_algo("sha3-384", &php_hash_sha3_384_ops);
+ php_hash_register_algo("sha3-512", &php_hash_sha3_512_ops);
php_hash_register_algo("ripemd128", &php_hash_ripemd128_ops);
php_hash_register_algo("ripemd160", &php_hash_ripemd160_ops);
php_hash_register_algo("ripemd256", &php_hash_ripemd256_ops);
@@ -1204,6 +1321,14 @@ ZEND_BEGIN_ARG_INFO(arginfo_hash_equals, 0)
ZEND_ARG_INFO(0, user_string)
ZEND_END_ARG_INFO()
+ZEND_BEGIN_ARG_INFO_EX(arginfo_hash_hkdf, 0, 0, 2)
+ ZEND_ARG_INFO(0, ikm)
+ ZEND_ARG_INFO(0, algo)
+ ZEND_ARG_INFO(0, length)
+ ZEND_ARG_INFO(0, string)
+ ZEND_ARG_INFO(0, salt)
+ZEND_END_ARG_INFO()
+
/* BC Land */
#ifdef PHP_MHASH_BC
ZEND_BEGIN_ARG_INFO(arginfo_mhash_get_block_size, 0)
@@ -1252,6 +1377,7 @@ const zend_function_entry hash_functions[] = {
PHP_FE(hash_algos, arginfo_hash_algos)
PHP_FE(hash_pbkdf2, arginfo_hash_pbkdf2)
PHP_FE(hash_equals, arginfo_hash_equals)
+ PHP_FE(hash_hkdf, arginfo_hash_hkdf)
/* BC Land */
#ifdef PHP_HASH_MD5_NOT_IN_CORE
diff --git a/ext/hash/hash_adler32.c b/ext/hash/hash_adler32.c
index af2268beac..3cb9ddfb7e 100644
--- a/ext/hash/hash_adler32.c
+++ b/ext/hash/hash_adler32.c
@@ -29,7 +29,7 @@ PHP_HASH_API void PHP_ADLER32Init(PHP_ADLER32_CTX *context)
PHP_HASH_API void PHP_ADLER32Update(PHP_ADLER32_CTX *context, const unsigned char *input, size_t len)
{
- php_hash_uint32 i, s[2];
+ uint32_t i, s[2];
s[0] = context->state & 0xffff;
s[1] = (context->state >> 16) & 0xffff;
diff --git a/ext/hash/hash_fnv.c b/ext/hash/hash_fnv.c
index ac67db2f61..4a7619de16 100644
--- a/ext/hash/hash_fnv.c
+++ b/ext/hash/hash_fnv.c
@@ -147,8 +147,8 @@ PHP_HASH_API void PHP_FNV164Final(unsigned char digest[8], PHP_FNV164_CTX * cont
* returns:
* 32 bit hash as a static hash type
*/
-static php_hash_uint32
-fnv_32_buf(void *buf, size_t len, php_hash_uint32 hval, int alternate)
+static uint32_t
+fnv_32_buf(void *buf, size_t len, uint32_t hval, int alternate)
{
unsigned char *bp = (unsigned char *)buf; /* start of buffer */
unsigned char *be = bp + len; /* beyond end of buffer */
@@ -163,10 +163,10 @@ fnv_32_buf(void *buf, size_t len, php_hash_uint32 hval, int alternate)
hval *= PHP_FNV_32_PRIME;
/* xor the bottom with the current octet */
- hval ^= (php_hash_uint32)*bp++;
+ hval ^= (uint32_t)*bp++;
} else {
/* xor the bottom with the current octet */
- hval ^= (php_hash_uint32)*bp++;
+ hval ^= (uint32_t)*bp++;
/* multiply by the 32 bit FNV magic prime mod 2^32 */
hval *= PHP_FNV_32_PRIME;
@@ -189,8 +189,8 @@ fnv_32_buf(void *buf, size_t len, php_hash_uint32 hval, int alternate)
* returns:
* 64 bit hash as a static hash type
*/
-static php_hash_uint64
-fnv_64_buf(void *buf, size_t len, php_hash_uint64 hval, int alternate)
+static uint64_t
+fnv_64_buf(void *buf, size_t len, uint64_t hval, int alternate)
{
unsigned char *bp = (unsigned char *)buf; /* start of buffer */
unsigned char *be = bp + len; /* beyond end of buffer */
@@ -205,10 +205,10 @@ fnv_64_buf(void *buf, size_t len, php_hash_uint64 hval, int alternate)
hval *= PHP_FNV_64_PRIME;
/* xor the bottom with the current octet */
- hval ^= (php_hash_uint64)*bp++;
+ hval ^= (uint64_t)*bp++;
} else {
/* xor the bottom with the current octet */
- hval ^= (php_hash_uint64)*bp++;
+ hval ^= (uint64_t)*bp++;
/* multiply by the 64 bit FNV magic prime mod 2^64 */
hval *= PHP_FNV_64_PRIME;
diff --git a/ext/hash/hash_gost.c b/ext/hash/hash_gost.c
index c17133786c..95c61a9399 100644
--- a/ext/hash/hash_gost.c
+++ b/ext/hash/hash_gost.c
@@ -207,10 +207,10 @@
AA(v, l, r); \
}
-static inline void Gost(PHP_GOST_CTX *context, php_hash_uint32 data[8])
+static inline void Gost(PHP_GOST_CTX *context, uint32_t data[8])
{
int i;
- php_hash_uint32 l, r, t, key[8], u[8], v[8], w[8], s[8], *h = context->state, *m = data;
+ uint32_t l, r, t, key[8], u[8], v[8], w[8], s[8], *h = context->state, *m = data;
memcpy(u, context->state, sizeof(u));
memcpy(v, data, sizeof(v));
@@ -227,11 +227,11 @@ static inline void Gost(PHP_GOST_CTX *context, php_hash_uint32 data[8])
static inline void GostTransform(PHP_GOST_CTX *context, const unsigned char input[32])
{
int i, j;
- php_hash_uint32 data[8], temp = 0;
+ uint32_t data[8], temp = 0;
for (i = 0, j = 0; i < 8; ++i, j += 4) {
- data[i] = ((php_hash_uint32) input[j]) | (((php_hash_uint32) input[j + 1]) << 8) |
- (((php_hash_uint32) input[j + 2]) << 16) | (((php_hash_uint32) input[j + 3]) << 24);
+ data[i] = ((uint32_t) input[j]) | (((uint32_t) input[j + 1]) << 8) |
+ (((uint32_t) input[j + 2]) << 16) | (((uint32_t) input[j + 3]) << 24);
context->state[i + 8] += data[i] + temp;
temp = context->state[i + 8] < data[i] ? 1 : (context->state[i + 8] == data[i] ? temp : 0);
}
@@ -251,7 +251,7 @@ PHP_HASH_API void PHP_GOSTInitCrypto(PHP_GOST_CTX *context)
context->tables = &tables_crypto;
}
-static const php_hash_uint32 MAX32 = 0xffffffffLU;
+static const uint32_t MAX32 = 0xffffffffLU;
PHP_HASH_API void PHP_GOSTUpdate(PHP_GOST_CTX *context, const unsigned char *input, size_t len)
{
@@ -287,7 +287,7 @@ PHP_HASH_API void PHP_GOSTUpdate(PHP_GOST_CTX *context, const unsigned char *inp
PHP_HASH_API void PHP_GOSTFinal(unsigned char digest[32], PHP_GOST_CTX *context)
{
- php_hash_uint32 i, j, l[8] = {0};
+ uint32_t i, j, l[8] = {0};
if (context->length) {
GostTransform(context, context->buffer);
diff --git a/ext/hash/hash_haval.c b/ext/hash/hash_haval.c
index e8dc92c26a..2b10e5f2c2 100644
--- a/ext/hash/hash_haval.c
+++ b/ext/hash/hash_haval.c
@@ -31,28 +31,28 @@ static const unsigned char PADDING[128] ={
0, 0, 0, 0, 0, 0, 0, 0, 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 const php_hash_uint32 D0[8] = {
+static const uint32_t D0[8] = {
0x243F6A88, 0x85A308D3, 0x13198A2E, 0x03707344, 0xA4093822, 0x299F31D0, 0x082EFA98, 0xEC4E6C89 };
-static const php_hash_uint32 K2[32] = {
+static const uint32_t K2[32] = {
0x452821E6, 0x38D01377, 0xBE5466CF, 0x34E90C6C, 0xC0AC29B7, 0xC97C50DD, 0x3F84D5B5, 0xB5470917,
0x9216D5D9, 0x8979FB1B, 0xD1310BA6, 0x98DFB5AC, 0x2FFD72DB, 0xD01ADFB7, 0xB8E1AFED, 0x6A267E96,
0xBA7C9045, 0xF12C7F99, 0x24A19947, 0xB3916CF7, 0x0801F2E2, 0x858EFC16, 0x636920D8, 0x71574E69,
0xA458FEA3, 0xF4933D7E, 0x0D95748F, 0x728EB658, 0x718BCD58, 0x82154AEE, 0x7B54A41D, 0xC25A59B5 };
-static const php_hash_uint32 K3[32] = {
+static const uint32_t K3[32] = {
0x9C30D539, 0x2AF26013, 0xC5D1B023, 0x286085F0, 0xCA417918, 0xB8DB38EF, 0x8E79DCB0, 0x603A180E,
0x6C9E0E8B, 0xB01E8A3E, 0xD71577C1, 0xBD314B27, 0x78AF2FDA, 0x55605C60, 0xE65525F3, 0xAA55AB94,
0x57489862, 0x63E81440, 0x55CA396A, 0x2AAB10B6, 0xB4CC5C34, 0x1141E8CE, 0xA15486AF, 0x7C72E993,
0xB3EE1411, 0x636FBC2A, 0x2BA9C55D, 0x741831F6, 0xCE5C3E16, 0x9B87931E, 0xAFD6BA33, 0x6C24CF5C };
-static const php_hash_uint32 K4[32] = {
+static const uint32_t K4[32] = {
0x7A325381, 0x28958677, 0x3B8F4898, 0x6B4BB9AF, 0xC4BFE81B, 0x66282193, 0x61D809CC, 0xFB21A991,
0x487CAC60, 0x5DEC8032, 0xEF845D5D, 0xE98575B1, 0xDC262302, 0xEB651B88, 0x23893E81, 0xD396ACC5,
0x0F6D6FF3, 0x83F44239, 0x2E0B4482, 0xA4842004, 0x69C8F04A, 0x9E1F9B5E, 0x21C66842, 0xF6E96C9A,
0x670C9C61, 0xABD388F0, 0x6A51A0D2, 0xD8542F68, 0x960FA728, 0xAB5133A3, 0x6EEF0B6C, 0x137A3BE4 };
-static const php_hash_uint32 K5[32] = {
+static const uint32_t K5[32] = {
0xBA3BF050, 0x7EFB2A98, 0xA1F1651D, 0x39AF0176, 0x66CA593E, 0x82430E88, 0x8CEE8619, 0x456F9FB4,
0x7D84A5C3, 0x3B8B5EBE, 0xE06F75D8, 0x85C12073, 0x401A449F, 0x56C16AA6, 0x4ED3AA62, 0x363F7706,
0x1BFEDF72, 0x429B023D, 0x37D0D724, 0xD00A1248, 0xDB0FEAD3, 0x49F1C09B, 0x075372C9, 0x80991B7B,
@@ -95,10 +95,10 @@ static const short M7[32] = { 7, 6, 5, 4, 3, 2, 1, 0, 7, 6, 5, 4, 3, 2, 1, 0,
7, 6, 5, 4, 3, 2, 1, 0, 7, 6, 5, 4, 3, 2, 1, 0 };
/* {{{ Encode
- Encodes input (php_hash_uint32) into output (unsigned char). Assumes len is
+ Encodes input (uint32_t) into output (unsigned char). Assumes len is
a multiple of 4.
*/
-static void Encode(unsigned char *output, php_hash_uint32 *input, unsigned int len)
+static void Encode(unsigned char *output, uint32_t *input, unsigned int len)
{
unsigned int i, j;
@@ -112,16 +112,16 @@ static void Encode(unsigned char *output, php_hash_uint32 *input, unsigned int l
/* }}} */
/* {{{ Decode
- Decodes input (unsigned char) into output (php_hash_uint32). Assumes len is
+ Decodes input (unsigned char) into output (uint32_t). Assumes len is
a multiple of 4.
*/
-static void Decode(php_hash_uint32 *output, const unsigned char *input, unsigned int len)
+static void Decode(uint32_t *output, const unsigned char *input, unsigned int len)
{
unsigned int i, j;
for (i = 0, j = 0; j < len; i++, j += 4) {
- output[i] = ((php_hash_uint32) input[j]) | (((php_hash_uint32) input[j + 1]) << 8) |
- (((php_hash_uint32) input[j + 2]) << 16) | (((php_hash_uint32) input[j + 3]) << 24);
+ output[i] = ((uint32_t) input[j]) | (((uint32_t) input[j + 1]) << 8) |
+ (((uint32_t) input[j + 2]) << 16) | (((uint32_t) input[j + 3]) << 24);
}
}
/* }}} */
@@ -141,10 +141,10 @@ static void Decode(php_hash_uint32 *output, const unsigned char *input, unsigned
/* {{{ PHP_3HAVALTransform
*/
-static void PHP_3HAVALTransform(php_hash_uint32 state[8], const unsigned char block[128])
+static void PHP_3HAVALTransform(uint32_t state[8], const unsigned char block[128])
{
- php_hash_uint32 E[8];
- php_hash_uint32 x[32];
+ uint32_t E[8];
+ uint32_t x[32];
int i;
Decode(x, block, 128);
@@ -175,10 +175,10 @@ static void PHP_3HAVALTransform(php_hash_uint32 state[8], const unsigned char bl
/* {{{ PHP_4HAVALTransform
*/
-static void PHP_4HAVALTransform(php_hash_uint32 state[8], const unsigned char block[128])
+static void PHP_4HAVALTransform(uint32_t state[8], const unsigned char block[128])
{
- php_hash_uint32 E[8];
- php_hash_uint32 x[32];
+ uint32_t E[8];
+ uint32_t x[32];
int i;
Decode(x, block, 128);
@@ -212,10 +212,10 @@ static void PHP_4HAVALTransform(php_hash_uint32 state[8], const unsigned char bl
/* {{{ PHP_5HAVALTransform
*/
-static void PHP_5HAVALTransform(php_hash_uint32 state[8], const unsigned char block[128])
+static void PHP_5HAVALTransform(uint32_t state[8], const unsigned char block[128])
{
- php_hash_uint32 E[8];
- php_hash_uint32 x[32];
+ uint32_t E[8];
+ uint32_t x[32];
int i;
Decode(x, block, 128);
@@ -289,10 +289,10 @@ PHP_HASH_API void PHP_HAVALUpdate(PHP_HAVAL_CTX *context, const unsigned char *i
/* Compute number of bytes mod 128 */
index = (unsigned int) ((context->count[0] >> 3) & 0x7F);
/* Update number of bits */
- if ((context->count[0] += ((php_hash_uint32) inputLen << 3)) < ((php_hash_uint32) inputLen << 3)) {
+ if ((context->count[0] += ((uint32_t) inputLen << 3)) < ((uint32_t) inputLen << 3)) {
context->count[1]++;
}
- context->count[1] += ((php_hash_uint32) inputLen >> 29);
+ context->count[1] += ((uint32_t) inputLen >> 29);
partLen = 128 - index;
diff --git a/ext/hash/hash_joaat.c b/ext/hash/hash_joaat.c
index b1a095aac9..43199465b5 100644
--- a/ext/hash/hash_joaat.c
+++ b/ext/hash/hash_joaat.c
@@ -70,8 +70,8 @@ PHP_HASH_API void PHP_JOAATFinal(unsigned char digest[4], PHP_JOAAT_CTX * contex
* returns:
* 32 bit hash as a static hash type
*/
-static php_hash_uint32
-joaat_buf(void *buf, size_t len, php_hash_uint32 hval)
+static uint32_t
+joaat_buf(void *buf, size_t len, uint32_t hval)
{
size_t i;
unsigned char *input = (unsigned char *)buf;
diff --git a/ext/hash/hash_md.c b/ext/hash/hash_md.c
index 439387d82e..073715d196 100644
--- a/ext/hash/hash_md.c
+++ b/ext/hash/hash_md.c
@@ -61,10 +61,10 @@ static const unsigned char PADDING[64] =
};
/* {{{ Encode
- Encodes input (php_hash_uint32) into output (unsigned char). Assumes len is
+ Encodes input (uint32_t) into output (unsigned char). Assumes len is
a multiple of 4.
*/
-static void Encode(unsigned char *output, php_hash_uint32 *input, unsigned int len)
+static void Encode(unsigned char *output, uint32_t *input, unsigned int len)
{
unsigned int i, j;
@@ -78,16 +78,16 @@ static void Encode(unsigned char *output, php_hash_uint32 *input, unsigned int l
/* }}} */
/* {{{ Decode
- Decodes input (unsigned char) into output (php_hash_uint32). Assumes len is
+ Decodes input (unsigned char) into output (uint32_t). Assumes len is
a multiple of 4.
*/
-static void Decode(php_hash_uint32 *output, const unsigned char *input, unsigned int len)
+static void Decode(uint32_t *output, const unsigned char *input, unsigned int len)
{
unsigned int i, j;
for (i = 0, j = 0; j < len; i++, j += 4)
- output[i] = ((php_hash_uint32) input[j]) | (((php_hash_uint32) input[j + 1]) << 8) |
- (((php_hash_uint32) input[j + 2]) << 16) | (((php_hash_uint32) input[j + 3]) << 24);
+ output[i] = ((uint32_t) input[j]) | (((uint32_t) input[j + 1]) << 8) |
+ (((uint32_t) input[j + 2]) << 16) | (((uint32_t) input[j + 3]) << 24);
}
/* }}} */
@@ -224,7 +224,7 @@ PHP_NAMED_FUNCTION(php_if_md5_file)
#define S43 15
#define S44 21
-static void MD5Transform(php_hash_uint32[4], const unsigned char[64]);
+static void MD5Transform(uint32_t[4], const unsigned char[64]);
/* F, G, H and I are basic MD5 functions.
*/
@@ -241,22 +241,22 @@ static void MD5Transform(php_hash_uint32[4], const unsigned char[64]);
Rotation is separate from addition to prevent recomputation.
*/
#define FF(a, b, c, d, x, s, ac) { \
- (a) += F ((b), (c), (d)) + (x) + (php_hash_uint32)(ac); \
+ (a) += F ((b), (c), (d)) + (x) + (uint32_t)(ac); \
(a) = ROTATE_LEFT ((a), (s)); \
(a) += (b); \
}
#define GG(a, b, c, d, x, s, ac) { \
- (a) += G ((b), (c), (d)) + (x) + (php_hash_uint32)(ac); \
+ (a) += G ((b), (c), (d)) + (x) + (uint32_t)(ac); \
(a) = ROTATE_LEFT ((a), (s)); \
(a) += (b); \
}
#define HH(a, b, c, d, x, s, ac) { \
- (a) += H ((b), (c), (d)) + (x) + (php_hash_uint32)(ac); \
+ (a) += H ((b), (c), (d)) + (x) + (uint32_t)(ac); \
(a) = ROTATE_LEFT ((a), (s)); \
(a) += (b); \
}
#define II(a, b, c, d, x, s, ac) { \
- (a) += I ((b), (c), (d)) + (x) + (php_hash_uint32)(ac); \
+ (a) += I ((b), (c), (d)) + (x) + (uint32_t)(ac); \
(a) = ROTATE_LEFT ((a), (s)); \
(a) += (b); \
}
@@ -290,10 +290,10 @@ PHP_HASH_API void PHP_MD5Update(PHP_MD5_CTX * context, const unsigned char *inpu
index = (unsigned int) ((context->count[0] >> 3) & 0x3F);
/* Update number of bits */
- if ((context->count[0] += ((php_hash_uint32) inputLen << 3))
- < ((php_hash_uint32) inputLen << 3))
+ if ((context->count[0] += ((uint32_t) inputLen << 3))
+ < ((uint32_t) inputLen << 3))
context->count[1]++;
- context->count[1] += ((php_hash_uint32) inputLen >> 29);
+ context->count[1] += ((uint32_t) inputLen >> 29);
partLen = 64 - index;
@@ -352,10 +352,10 @@ PHP_HASH_API void PHP_MD5Final(unsigned char digest[16], PHP_MD5_CTX * context)
* MD5 basic transformation. Transforms state based on block.
*/
static void MD5Transform(state, block)
-php_hash_uint32 state[4];
+uint32_t state[4];
const unsigned char block[64];
{
- php_hash_uint32 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
+ uint32_t a = state[0], b = state[1], c = state[2], d = state[3], x[16];
Decode(x, block, 64);
@@ -455,9 +455,9 @@ const unsigned char block[64];
#define MD4_R2(a,b,c,d,k,s) a = ROTL32(s, a + MD4_G(b,c,d) + x[k] + 0x5A827999)
#define MD4_R3(a,b,c,d,k,s) a = ROTL32(s, a + MD4_H(b,c,d) + x[k] + 0x6ED9EBA1)
-static void MD4Transform(php_hash_uint32 state[4], const unsigned char block[64])
+static void MD4Transform(uint32_t state[4], const unsigned char block[64])
{
- php_hash_uint32 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
+ uint32_t a = state[0], b = state[1], c = state[2], d = state[3], x[16];
Decode(x, block, 64);
@@ -549,10 +549,10 @@ PHP_HASH_API void PHP_MD4Update(PHP_MD4_CTX * context, const unsigned char *inpu
index = (unsigned int) ((context->count[0] >> 3) & 0x3F);
/* Update number of bits */
- if ((context->count[0] += ((php_hash_uint32) inputLen << 3))
- < ((php_hash_uint32) inputLen << 3))
+ if ((context->count[0] += ((uint32_t) inputLen << 3))
+ < ((uint32_t) inputLen << 3))
context->count[1]++;
- context->count[1] += ((php_hash_uint32) inputLen >> 29);
+ context->count[1] += ((uint32_t) inputLen >> 29);
partLen = 64 - index;
diff --git a/ext/hash/hash_ripemd.c b/ext/hash/hash_ripemd.c
index bbe0d419c2..d08cfe43c2 100644
--- a/ext/hash/hash_ripemd.c
+++ b/ext/hash/hash_ripemd.c
@@ -143,9 +143,9 @@ PHP_HASH_API void PHP_RIPEMD320Init(PHP_RIPEMD320_CTX * context)
#define F3(x,y,z) (((x) & (z)) | ((y) & (~(z))))
#define F4(x,y,z) ((x) ^ ((y) | (~(z))))
-static const php_hash_uint32 K_values[5] = { 0x00000000, 0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xA953FD4E }; /* 128, 256, 160, 320 */
-static const php_hash_uint32 KK_values[4] = { 0x50A28BE6, 0x5C4DD124, 0x6D703EF3, 0x00000000 }; /* 128 & 256 */
-static const php_hash_uint32 KK160_values[5] = { 0x50A28BE6, 0x5C4DD124, 0x6D703EF3, 0x7A6D76E9, 0x00000000 }; /* 160 & 320 */
+static const uint32_t K_values[5] = { 0x00000000, 0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xA953FD4E }; /* 128, 256, 160, 320 */
+static const uint32_t KK_values[4] = { 0x50A28BE6, 0x5C4DD124, 0x6D703EF3, 0x00000000 }; /* 128 & 256 */
+static const uint32_t KK160_values[5] = { 0x50A28BE6, 0x5C4DD124, 0x6D703EF3, 0x7A6D76E9, 0x00000000 }; /* 160 & 320 */
#define K(n) K_values[ (n) >> 4]
#define KK(n) KK_values[(n) >> 4]
@@ -184,27 +184,27 @@ static const unsigned char SS[80] = {
#define ROL(n, x) (((x) << n) | ((x) >> (32 - n)))
/* {{{ RIPEMDDecode
- Decodes input (unsigned char) into output (php_hash_uint32). Assumes len is
+ Decodes input (unsigned char) into output (uint32_t). Assumes len is
a multiple of 4.
*/
-static void RIPEMDDecode(php_hash_uint32 *output, const unsigned char *input, unsigned int len)
+static void RIPEMDDecode(uint32_t *output, const unsigned char *input, unsigned int len)
{
unsigned int i, j;
for (i = 0, j = 0; j < len; i++, j += 4)
- output[i] = ((php_hash_uint32) input[j + 0]) | (((php_hash_uint32) input[j + 1]) << 8) |
- (((php_hash_uint32) input[j + 2]) << 16) | (((php_hash_uint32) input[j + 3]) << 24);
+ output[i] = ((uint32_t) input[j + 0]) | (((uint32_t) input[j + 1]) << 8) |
+ (((uint32_t) input[j + 2]) << 16) | (((uint32_t) input[j + 3]) << 24);
}
/* }}} */
/* {{{ RIPEMD128Transform
* ripemd128 basic transformation. Transforms state based on block.
*/
-static void RIPEMD128Transform(php_hash_uint32 state[4], const unsigned char block[64])
+static void RIPEMD128Transform(uint32_t state[4], const unsigned char block[64])
{
- php_hash_uint32 a = state[0], b = state[1], c = state[2], d = state[3];
- php_hash_uint32 aa = state[0], bb = state[1], cc = state[2], dd = state[3];
- php_hash_uint32 tmp, x[16];
+ uint32_t a = state[0], b = state[1], c = state[2], d = state[3];
+ uint32_t aa = state[0], bb = state[1], cc = state[2], dd = state[3];
+ uint32_t tmp, x[16];
int j;
RIPEMDDecode(x, block, 64);
@@ -261,10 +261,10 @@ PHP_HASH_API void PHP_RIPEMD128Update(PHP_RIPEMD128_CTX * context, const unsigne
index = (unsigned int) ((context->count[0] >> 3) & 0x3F);
/* Update number of bits */
- if ((context->count[0] += ((php_hash_uint32) inputLen << 3)) < ((php_hash_uint32) inputLen << 3)) {
+ if ((context->count[0] += ((uint32_t) inputLen << 3)) < ((uint32_t) inputLen << 3)) {
context->count[1]++;
}
- context->count[1] += ((php_hash_uint32) inputLen >> 29);
+ context->count[1] += ((uint32_t) inputLen >> 29);
partLen = 64 - index;
@@ -291,11 +291,11 @@ PHP_HASH_API void PHP_RIPEMD128Update(PHP_RIPEMD128_CTX * context, const unsigne
/* {{{ RIPEMD256Transform
* ripemd256 basic transformation. Transforms state based on block.
*/
-static void RIPEMD256Transform(php_hash_uint32 state[8], const unsigned char block[64])
+static void RIPEMD256Transform(uint32_t state[8], const unsigned char block[64])
{
- php_hash_uint32 a = state[0], b = state[1], c = state[2], d = state[3];
- php_hash_uint32 aa = state[4], bb = state[5], cc = state[6], dd = state[7];
- php_hash_uint32 tmp, x[16];
+ uint32_t a = state[0], b = state[1], c = state[2], d = state[3];
+ uint32_t aa = state[4], bb = state[5], cc = state[6], dd = state[7];
+ uint32_t tmp, x[16];
int j;
RIPEMDDecode(x, block, 64);
@@ -359,10 +359,10 @@ PHP_HASH_API void PHP_RIPEMD256Update(PHP_RIPEMD256_CTX * context, const unsigne
index = (unsigned int) ((context->count[0] >> 3) & 0x3F);
/* Update number of bits */
- if ((context->count[0] += ((php_hash_uint32) inputLen << 3)) < ((php_hash_uint32) inputLen << 3)) {
+ if ((context->count[0] += ((uint32_t) inputLen << 3)) < ((uint32_t) inputLen << 3)) {
context->count[1]++;
}
- context->count[1] += ((php_hash_uint32) inputLen >> 29);
+ context->count[1] += ((uint32_t) inputLen >> 29);
partLen = 64 - index;
@@ -389,11 +389,11 @@ PHP_HASH_API void PHP_RIPEMD256Update(PHP_RIPEMD256_CTX * context, const unsigne
/* {{{ RIPEMD160Transform
* ripemd160 basic transformation. Transforms state based on block.
*/
-static void RIPEMD160Transform(php_hash_uint32 state[5], const unsigned char block[64])
+static void RIPEMD160Transform(uint32_t state[5], const unsigned char block[64])
{
- php_hash_uint32 a = state[0], b = state[1], c = state[2], d = state[3], e = state[4];
- php_hash_uint32 aa = state[0], bb = state[1], cc = state[2], dd = state[3], ee = state[4];
- php_hash_uint32 tmp, x[16];
+ uint32_t a = state[0], b = state[1], c = state[2], d = state[3], e = state[4];
+ uint32_t aa = state[0], bb = state[1], cc = state[2], dd = state[3], ee = state[4];
+ uint32_t tmp, x[16];
int j;
RIPEMDDecode(x, block, 64);
@@ -458,10 +458,10 @@ PHP_HASH_API void PHP_RIPEMD160Update(PHP_RIPEMD160_CTX * context, const unsigne
index = (unsigned int) ((context->count[0] >> 3) & 0x3F);
/* Update number of bits */
- if ((context->count[0] += ((php_hash_uint32) inputLen << 3)) < ((php_hash_uint32) inputLen << 3)) {
+ if ((context->count[0] += ((uint32_t) inputLen << 3)) < ((uint32_t) inputLen << 3)) {
context->count[1]++;
}
- context->count[1] += ((php_hash_uint32) inputLen >> 29);
+ context->count[1] += ((uint32_t) inputLen >> 29);
partLen = 64 - index;
@@ -488,11 +488,11 @@ PHP_HASH_API void PHP_RIPEMD160Update(PHP_RIPEMD160_CTX * context, const unsigne
/* {{{ RIPEMD320Transform
* ripemd320 basic transformation. Transforms state based on block.
*/
-static void RIPEMD320Transform(php_hash_uint32 state[10], const unsigned char block[64])
+static void RIPEMD320Transform(uint32_t state[10], const unsigned char block[64])
{
- php_hash_uint32 a = state[0], b = state[1], c = state[2], d = state[3], e = state[4];
- php_hash_uint32 aa = state[5], bb = state[6], cc = state[7], dd = state[8], ee = state[9];
- php_hash_uint32 tmp, x[16];
+ uint32_t a = state[0], b = state[1], c = state[2], d = state[3], e = state[4];
+ uint32_t aa = state[5], bb = state[6], cc = state[7], dd = state[8], ee = state[9];
+ uint32_t tmp, x[16];
int j;
RIPEMDDecode(x, block, 64);
@@ -566,10 +566,10 @@ PHP_HASH_API void PHP_RIPEMD320Update(PHP_RIPEMD320_CTX * context, const unsigne
index = (unsigned int) ((context->count[0] >> 3) & 0x3F);
/* Update number of bits */
- if ((context->count[0] += ((php_hash_uint32) inputLen << 3)) < ((php_hash_uint32) inputLen << 3)) {
+ if ((context->count[0] += ((uint32_t) inputLen << 3)) < ((uint32_t) inputLen << 3)) {
context->count[1]++;
}
- context->count[1] += ((php_hash_uint32) inputLen >> 29);
+ context->count[1] += ((uint32_t) inputLen >> 29);
partLen = 64 - index;
@@ -601,10 +601,10 @@ static const unsigned char PADDING[64] =
};
/* {{{ RIPEMDEncode
- Encodes input (php_hash_uint32) into output (unsigned char). Assumes len is
+ Encodes input (uint32_t) into output (unsigned char). Assumes len is
a multiple of 4.
*/
-static void RIPEMDEncode(unsigned char *output, php_hash_uint32 *input, unsigned int len)
+static void RIPEMDEncode(unsigned char *output, uint32_t *input, unsigned int len)
{
unsigned int i, j;
diff --git a/ext/hash/hash_sha.c b/ext/hash/hash_sha.c
index 494ebff06e..6f4ff0ef7c 100644
--- a/ext/hash/hash_sha.c
+++ b/ext/hash/hash_sha.c
@@ -35,10 +35,10 @@ static const unsigned char PADDING[128] =
};
/* {{{ SHAEncode32
- Encodes input (php_hash_uint32) into output (unsigned char). Assumes len is
+ Encodes input (uint32_t) into output (unsigned char). Assumes len is
a multiple of 4.
*/
-static void SHAEncode32(unsigned char *output, php_hash_uint32 *input, unsigned int len)
+static void SHAEncode32(unsigned char *output, uint32_t *input, unsigned int len)
{
unsigned int i, j;
@@ -53,16 +53,16 @@ static void SHAEncode32(unsigned char *output, php_hash_uint32 *input, unsigned
/* {{{ SHADecode32
- Decodes input (unsigned char) into output (php_hash_uint32). Assumes len is
+ Decodes input (unsigned char) into output (uint32_t). Assumes len is
a multiple of 4.
*/
-static void SHADecode32(php_hash_uint32 *output, const unsigned char *input, unsigned int len)
+static void SHADecode32(uint32_t *output, const unsigned char *input, unsigned int len)
{
unsigned int i, j;
for (i = 0, j = 0; j < len; i++, j += 4)
- output[i] = ((php_hash_uint32) input[j + 3]) | (((php_hash_uint32) input[j + 2]) << 8) |
- (((php_hash_uint32) input[j + 1]) << 16) | (((php_hash_uint32) input[j]) << 24);
+ output[i] = ((uint32_t) input[j + 3]) | (((uint32_t) input[j + 2]) << 8) |
+ (((uint32_t) input[j + 1]) << 16) | (((uint32_t) input[j]) << 24);
}
/* }}} */
@@ -179,22 +179,22 @@ PHP_FUNCTION(sha1_file)
/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
*/
#define FF(a, b, c, d, e, w) { \
- (e) += F ((b), (c), (d)) + (w) + (php_hash_uint32)(0x5A827999); \
+ (e) += F ((b), (c), (d)) + (w) + (uint32_t)(0x5A827999); \
(e) += ROTATE_LEFT ((a), 5); \
(b) = ROTATE_LEFT((b), 30); \
}
#define GG(a, b, c, d, e, w) { \
- (e) += G ((b), (c), (d)) + (w) + (php_hash_uint32)(0x6ED9EBA1); \
+ (e) += G ((b), (c), (d)) + (w) + (uint32_t)(0x6ED9EBA1); \
(e) += ROTATE_LEFT ((a), 5); \
(b) = ROTATE_LEFT((b), 30); \
}
#define HH(a, b, c, d, e, w) { \
- (e) += H ((b), (c), (d)) + (w) + (php_hash_uint32)(0x8F1BBCDC); \
+ (e) += H ((b), (c), (d)) + (w) + (uint32_t)(0x8F1BBCDC); \
(e) += ROTATE_LEFT ((a), 5); \
(b) = ROTATE_LEFT((b), 30); \
}
#define II(a, b, c, d, e, w) { \
- (e) += I ((b), (c), (d)) + (w) + (php_hash_uint32)(0xCA62C1D6); \
+ (e) += I ((b), (c), (d)) + (w) + (uint32_t)(0xCA62C1D6); \
(e) += ROTATE_LEFT ((a), 5); \
(b) = ROTATE_LEFT((b), 30); \
}
@@ -219,10 +219,10 @@ PHP_HASH_API void PHP_SHA1Init(PHP_SHA1_CTX * context)
/* {{{ SHA1Transform
* SHA1 basic transformation. Transforms state based on block.
*/
-static void SHA1Transform(php_hash_uint32 state[5], const unsigned char block[64])
+static void SHA1Transform(uint32_t state[5], const unsigned char block[64])
{
- php_hash_uint32 a = state[0], b = state[1], c = state[2];
- php_hash_uint32 d = state[3], e = state[4], x[16], tmp;
+ uint32_t a = state[0], b = state[1], c = state[2];
+ uint32_t d = state[3], e = state[4], x[16], tmp;
SHADecode32(x, block, 64);
@@ -339,10 +339,10 @@ PHP_HASH_API void PHP_SHA1Update(PHP_SHA1_CTX * context, const unsigned char *in
index = (unsigned int) ((context->count[0] >> 3) & 0x3F);
/* Update number of bits */
- if ((context->count[0] += ((php_hash_uint32) inputLen << 3))
- < ((php_hash_uint32) inputLen << 3))
+ if ((context->count[0] += ((uint32_t) inputLen << 3))
+ < ((uint32_t) inputLen << 3))
context->count[1]++;
- context->count[1] += ((php_hash_uint32) inputLen >> 29);
+ context->count[1] += ((uint32_t) inputLen >> 29);
partLen = 64 - index;
@@ -445,7 +445,7 @@ const php_hash_ops php_hash_sha224_ops = {
/* OM1 */
#define SHA256_F5(x) (ROTR32(17,(x)) ^ ROTR32(19,(x)) ^ SHR(10,(x)))
-static const php_hash_uint32 SHA256_K[64] = {
+static const uint32_t SHA256_K[64] = {
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
@@ -477,11 +477,11 @@ PHP_HASH_API void PHP_SHA256Init(PHP_SHA256_CTX * context)
/* {{{ SHA256Transform
* SHA256 basic transformation. Transforms state based on block.
*/
-static void SHA256Transform(php_hash_uint32 state[8], const unsigned char block[64])
+static void SHA256Transform(uint32_t state[8], const unsigned char block[64])
{
- php_hash_uint32 a = state[0], b = state[1], c = state[2], d = state[3];
- php_hash_uint32 e = state[4], f = state[5], g = state[6], h = state[7];
- php_hash_uint32 x[16], T1, T2, W[64];
+ uint32_t a = state[0], b = state[1], c = state[2], d = state[3];
+ uint32_t e = state[4], f = state[5], g = state[6], h = state[7];
+ uint32_t x[16], T1, T2, W[64];
int i;
SHADecode32(x, block, 64);
@@ -547,10 +547,10 @@ PHP_HASH_API void PHP_SHA224Update(PHP_SHA224_CTX * context, const unsigned char
index = (unsigned int) ((context->count[0] >> 3) & 0x3F);
/* Update number of bits */
- if ((context->count[0] += ((php_hash_uint32) inputLen << 3)) < ((php_hash_uint32) inputLen << 3)) {
+ if ((context->count[0] += ((uint32_t) inputLen << 3)) < ((uint32_t) inputLen << 3)) {
context->count[1]++;
}
- context->count[1] += ((php_hash_uint32) inputLen >> 29);
+ context->count[1] += ((uint32_t) inputLen >> 29);
partLen = 64 - index;
@@ -624,10 +624,10 @@ PHP_HASH_API void PHP_SHA256Update(PHP_SHA256_CTX * context, const unsigned char
index = (unsigned int) ((context->count[0] >> 3) & 0x3F);
/* Update number of bits */
- if ((context->count[0] += ((php_hash_uint32) inputLen << 3)) < ((php_hash_uint32) inputLen << 3)) {
+ if ((context->count[0] += ((uint32_t) inputLen << 3)) < ((uint32_t) inputLen << 3)) {
context->count[1]++;
}
- context->count[1] += ((php_hash_uint32) inputLen >> 29);
+ context->count[1] += ((uint32_t) inputLen >> 29);
partLen = 64 - index;
@@ -703,7 +703,7 @@ PHP_HASH_API void PHP_SHA256Final(unsigned char digest[32], PHP_SHA256_CTX * con
/* OM1 */
#define SHA512_F5(x) (ROTR64(19, x) ^ ROTR64(61, x) ^ SHR(6, x))
-static const php_hash_uint64 SHA512_K[128] = {
+static const uint64_t SHA512_K[128] = {
L64(0x428a2f98d728ae22), L64(0x7137449123ef65cd), L64(0xb5c0fbcfec4d3b2f), L64(0xe9b5dba58189dbbc),
L64(0x3956c25bf348b538), L64(0x59f111f1b605d019), L64(0x923f82a4af194f9b), L64(0xab1c5ed5da6d8118),
L64(0xd807aa98a3030242), L64(0x12835b0145706fbe), L64(0x243185be4ee4b28c), L64(0x550c7dc3d5ffb4e2),
@@ -726,10 +726,10 @@ static const php_hash_uint64 SHA512_K[128] = {
L64(0x4cc5d4becb3e42b6), L64(0x597f299cfc657e2a), L64(0x5fcb6fab3ad6faec), L64(0x6c44198c4a475817) };
/* {{{ SHAEncode64
- Encodes input (php_hash_uint64) into output (unsigned char). Assumes len is
+ Encodes input (uint64_t) into output (unsigned char). Assumes len is
a multiple of 8.
*/
-static void SHAEncode64(unsigned char *output, php_hash_uint64 *input, unsigned int len)
+static void SHAEncode64(unsigned char *output, uint64_t *input, unsigned int len)
{
unsigned int i, j;
@@ -748,19 +748,19 @@ static void SHAEncode64(unsigned char *output, php_hash_uint64 *input, unsigned
/* {{{ SHADecode64
- Decodes input (unsigned char) into output (php_hash_uint64). Assumes len is
+ Decodes input (unsigned char) into output (uint64_t). Assumes len is
a multiple of 8.
*/
-static void SHADecode64(php_hash_uint64 *output, const unsigned char *input, unsigned int len)
+static void SHADecode64(uint64_t *output, const unsigned char *input, unsigned int len)
{
unsigned int i, j;
for (i = 0, j = 0; j < len; i++, j += 8)
output[i] =
- ((php_hash_uint64) input[j + 7]) | (((php_hash_uint64) input[j + 6]) << 8) |
- (((php_hash_uint64) input[j + 5]) << 16) | (((php_hash_uint64) input[j + 4]) << 24) |
- (((php_hash_uint64) input[j + 3]) << 32) | (((php_hash_uint64) input[j + 2]) << 40) |
- (((php_hash_uint64) input[j + 1]) << 48) | (((php_hash_uint64) input[j]) << 56);
+ ((uint64_t) input[j + 7]) | (((uint64_t) input[j + 6]) << 8) |
+ (((uint64_t) input[j + 5]) << 16) | (((uint64_t) input[j + 4]) << 24) |
+ (((uint64_t) input[j + 3]) << 32) | (((uint64_t) input[j + 2]) << 40) |
+ (((uint64_t) input[j + 1]) << 48) | (((uint64_t) input[j]) << 56);
}
/* }}} */
@@ -787,11 +787,11 @@ PHP_HASH_API void PHP_SHA384Init(PHP_SHA384_CTX * context)
* SHA512 basic transformation. Transforms state based on block.
* SHA384 uses the exact same algorithm
*/
-static void SHA512Transform(php_hash_uint64 state[8], const unsigned char block[128])
+static void SHA512Transform(uint64_t state[8], const unsigned char block[128])
{
- php_hash_uint64 a = state[0], b = state[1], c = state[2], d = state[3];
- php_hash_uint64 e = state[4], f = state[5], g = state[6], h = state[7];
- php_hash_uint64 x[16], T1, T2, W[80];
+ uint64_t a = state[0], b = state[1], c = state[2], d = state[3];
+ uint64_t e = state[4], f = state[5], g = state[6], h = state[7];
+ uint64_t x[16], T1, T2, W[80];
int i;
SHADecode64(x, block, 128);
@@ -838,10 +838,10 @@ PHP_HASH_API void PHP_SHA384Update(PHP_SHA384_CTX * context, const unsigned char
index = (unsigned int) ((context->count[0] >> 3) & 0x7F);
/* Update number of bits */
- if ((context->count[0] += ((php_hash_uint64) inputLen << 3)) < ((php_hash_uint64) inputLen << 3)) {
+ if ((context->count[0] += ((uint64_t) inputLen << 3)) < ((uint64_t) inputLen << 3)) {
context->count[1]++;
}
- context->count[1] += ((php_hash_uint64) inputLen >> 61);
+ context->count[1] += ((uint64_t) inputLen >> 61);
partLen = 128 - index;
@@ -939,6 +939,42 @@ PHP_HASH_API void PHP_SHA512Init(PHP_SHA512_CTX * context)
}
/* }}} */
+/* {{{ PHP_SHA512_256Init
+ * SHA512/245 initialization. Identical algorithm to SHA512, using alternate initval and truncation
+ */
+PHP_HASH_API void PHP_SHA512_256Init(PHP_SHA512_CTX * context)
+{
+ context->count[0] = context->count[1] = 0;
+
+ context->state[0] = L64(0x22312194FC2BF72C);
+ context->state[1] = L64(0x9F555FA3C84C64C2);
+ context->state[2] = L64(0x2393B86B6F53B151);
+ context->state[3] = L64(0x963877195940EABD);
+ context->state[4] = L64(0x96283EE2A88EFFE3);
+ context->state[5] = L64(0xBE5E1E2553863992);
+ context->state[6] = L64(0x2B0199FC2C85B8AA);
+ context->state[7] = L64(0x0EB72DDC81C52CA2);
+}
+/* }}} */
+
+/* {{{ PHP_SHA512_224Init
+ * SHA512/224 initialization. Identical algorithm to SHA512, using alternate initval and truncation
+ */
+PHP_HASH_API void PHP_SHA512_224Init(PHP_SHA512_CTX * context)
+{
+ context->count[0] = context->count[1] = 0;
+
+ context->state[0] = L64(0x8C3D37C819544DA2);
+ context->state[1] = L64(0x73E1996689DCD4D6);
+ context->state[2] = L64(0x1DFAB7AE32FF9C82);
+ context->state[3] = L64(0x679DD514582F9FCF);
+ context->state[4] = L64(0x0F6D2B697BD44DA8);
+ context->state[5] = L64(0x77E36F7304C48942);
+ context->state[6] = L64(0x3F9D85A86A1D36C8);
+ context->state[7] = L64(0x1112E6AD91D692A1);
+}
+/* }}} */
+
/* {{{ PHP_SHA512Update
SHA512 block update operation. Continues an SHA512 message-digest
operation, processing another message block, and updating the
@@ -952,10 +988,10 @@ PHP_HASH_API void PHP_SHA512Update(PHP_SHA512_CTX * context, const unsigned char
index = (unsigned int) ((context->count[0] >> 3) & 0x7F);
/* Update number of bits */
- if ((context->count[0] += ((php_hash_uint64) inputLen << 3)) < ((php_hash_uint64) inputLen << 3)) {
+ if ((context->count[0] += ((uint64_t) inputLen << 3)) < ((uint64_t) inputLen << 3)) {
context->count[1]++;
}
- context->count[1] += ((php_hash_uint64) inputLen >> 61);
+ context->count[1] += ((uint64_t) inputLen >> 61);
partLen = 128 - index;
@@ -1024,6 +1060,28 @@ PHP_HASH_API void PHP_SHA512Final(unsigned char digest[64], PHP_SHA512_CTX * con
}
/* }}} */
+/* {{{ PHP_SHA512_256Final
+ SHA512/256 finalization. Identical to SHA512Final, but with truncation
+ */
+PHP_HASH_API void PHP_SHA512_256Final(unsigned char digest[32], PHP_SHA512_CTX * context)
+{
+ unsigned char full_digest[64];
+ PHP_SHA512Final(full_digest, context);
+ memcpy(digest, full_digest, 32);
+}
+/* }}} */
+
+/* {{{ PHP_SHA512_224Final
+ SHA512/224 finalization. Identical to SHA512Final, but with truncation
+ */
+PHP_HASH_API void PHP_SHA512_224Final(unsigned char digest[28], PHP_SHA512_CTX * context)
+{
+ unsigned char full_digest[64];
+ PHP_SHA512Final(full_digest, context);
+ memcpy(digest, full_digest, 28);
+}
+/* }}} */
+
const php_hash_ops php_hash_sha512_ops = {
(php_hash_init_func_t) PHP_SHA512Init,
(php_hash_update_func_t) PHP_SHA512Update,
@@ -1034,6 +1092,26 @@ const php_hash_ops php_hash_sha512_ops = {
sizeof(PHP_SHA512_CTX)
};
+const php_hash_ops php_hash_sha512_256_ops = {
+ (php_hash_init_func_t) PHP_SHA512_256Init,
+ (php_hash_update_func_t) PHP_SHA512_256Update,
+ (php_hash_final_func_t) PHP_SHA512_256Final,
+ (php_hash_copy_func_t) php_hash_copy,
+ 32,
+ 128,
+ sizeof(PHP_SHA512_CTX)
+};
+
+const php_hash_ops php_hash_sha512_224_ops = {
+ (php_hash_init_func_t) PHP_SHA512_224Init,
+ (php_hash_update_func_t) PHP_SHA512_224Update,
+ (php_hash_final_func_t) PHP_SHA512_224Final,
+ (php_hash_copy_func_t) php_hash_copy,
+ 28,
+ 128,
+ sizeof(PHP_SHA512_CTX)
+};
+
/*
* Local variables:
* tab-width: 4
diff --git a/ext/hash/hash_sha3.c b/ext/hash/hash_sha3.c
new file mode 100644
index 0000000000..d190ae430b
--- /dev/null
+++ b/ext/hash/hash_sha3.c
@@ -0,0 +1,239 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 7 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2017 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Sara Golemon <pollita@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "php_hash.h"
+#include "php_hash_sha3.h"
+
+#if (defined(__APPLE__) || defined(__APPLE_CC__)) && \
+ (defined(__BIG_ENDIAN__) || defined(__LITTLE_ENDIAN__))
+# if defined(__LITTLE_ENDIAN__)
+# undef WORDS_BIGENDIAN
+# else
+# if defined(__BIG_ENDIAN__)
+# define WORDS_BIGENDIAN
+# endif
+# endif
+#endif
+
+static inline uint64_t rol64(uint64_t v, unsigned char b) {
+ return (v << b) | (v >> (64 - b));
+}
+static inline unsigned char idx(unsigned char x, unsigned char y) {
+ return x + (5 * y);
+}
+
+#ifdef WORDS_BIGENDIAN
+static inline uint64_t load64(const unsigned char* x) {
+ char i;
+ uint64_t ret = 0;
+ for (i = 7; i >= 0; --i) {
+ ret <<= 8;
+ ret |= x[i];
+ }
+ return ret;
+}
+static inline void store64(unsigned char* x, uint64_t val) {
+ char i;
+ for (i = 0; i < 8; ++i) {
+ x[i] = val & 0xFF;
+ val >>= 8;
+ }
+}
+static inline void xor64(unsigned char* x, uint64_t val) {
+ char i;
+ for (i = 0; i < 8; ++i) {
+ x[i] ^= val & 0xFF;
+ val >>= 8;
+ }
+}
+# define readLane(x, y) load64(ctx->state+sizeof(uint64_t)*idx(x, y))
+# define writeLane(x, y, v) store64(ctx->state+sizeof(uint64_t)*idx(x, y), v)
+# define XORLane(x, y, v) xor64(ctx->state+sizeof(uint64_t)*idx(x, y), v)
+#else
+# define readLane(x, y) (((uint64_t*)ctx->state)[idx(x,y)])
+# define writeLane(x, y, v) (((uint64_t*)ctx->state)[idx(x,y)] = v)
+# define XORLane(x, y, v) (((uint64_t*)ctx->state)[idx(x,y)] ^= v)
+#endif
+
+static inline char LFSR86540(unsigned char* pLFSR)
+{
+ unsigned char LFSR = *pLFSR;
+ char result = LFSR & 0x01;
+ if (LFSR & 0x80) {
+ // Primitive polynomial over GF(2): x^8+x^6+x^5+x^4+1
+ LFSR = (LFSR << 1) ^ 0x71;
+ } else {
+ LFSR <<= 1;
+ }
+ *pLFSR = LFSR;
+ return result;
+}
+
+static void permute(PHP_SHA3_CTX* ctx) {
+ unsigned char LFSRstate = 0x01;
+ unsigned char round;
+
+ for (round = 0; round < 24; ++round) {
+ { // Theta step (see [Keccak Reference, Section 2.3.2])
+ uint64_t C[5], D;
+ unsigned char x, y;
+ for (x = 0; x < 5; ++x) {
+ C[x] = readLane(x, 0) ^ readLane(x, 1) ^
+ readLane(x, 2) ^ readLane(x, 3) ^ readLane(x, 4);
+ }
+ for (x = 0; x < 5; ++x) {
+ D = C[(x+4)%5] ^ rol64(C[(x+1)%5], 1);
+ for (y = 0; y < 5; ++y) {
+ XORLane(x, y, D);
+ }
+ }
+ }
+
+ { // p and Pi steps (see [Keccak Reference, Sections 2.3.3 and 2.3.4])
+ unsigned char x = 1, y = 0, t;
+ uint64_t current = readLane(x, y);
+ for (t = 0; t < 24; ++t) {
+ unsigned char r = ((t + 1) * (t + 2) / 2) % 64;
+ unsigned char Y = (2*x + 3*y) % 5;
+ uint64_t temp;
+ x = y;
+ y = Y;
+ temp = readLane(x, y);
+ writeLane(x, y, rol64(current, r));
+ current = temp;
+ }
+ }
+
+ { // X step (see [Keccak Reference, Section 2.3.1])
+ unsigned char x, y;
+ for (y = 0; y < 5; ++y) {
+ uint64_t temp[5];
+ for (x = 0; x < 5; ++x) {
+ temp[x] = readLane(x, y);
+ }
+ for (x = 0; x < 5; ++x) {
+ writeLane(x, y, temp[x] ^((~temp[(x+1)%5]) & temp[(x+2)%5]));
+ }
+ }
+ }
+
+ { // i step (see [Keccak Reference, Section 2.3.5])
+ unsigned char j;
+ for (j = 0; j < 7; ++j) {
+ if (LFSR86540(&LFSRstate)) {
+ uint64_t bitPos = (1<<j) - 1;
+ XORLane(0, 0, (uint64_t)1 << bitPos);
+ }
+ }
+ }
+ }
+}
+
+// ==========================================================================
+
+static void PHP_SHA3_Init(PHP_SHA3_CTX* ctx,
+ int bits) {
+ memset(ctx, 0, sizeof(PHP_SHA3_CTX));
+}
+
+static void PHP_SHA3_Update(PHP_SHA3_CTX* ctx,
+ const unsigned char* buf,
+ unsigned int count,
+ size_t block_size) {
+ while (count > 0) {
+ unsigned int len = block_size - ctx->pos;
+ if (len > count) len = count;
+ count -= len;
+ while (len-- > 0) {
+ ctx->state[ctx->pos++] ^= *(buf++);
+ }
+ if (ctx->pos >= block_size) {
+ permute(ctx);
+ ctx->pos = 0;
+ }
+ }
+}
+
+static void PHP_SHA3_Final(unsigned char* digest,
+ PHP_SHA3_CTX* ctx,
+ int block_size,
+ int digest_size) {
+ int len = digest_size;
+
+ // Pad state to finalize
+ ctx->state[ctx->pos++] ^= 0x06;
+ ctx->state[block_size-1] ^= 0x80;
+ permute(ctx);
+
+ // Square output for digest
+ for(;;) {
+ int bs = (len < block_size) ? len : block_size;
+ memcpy(digest, ctx->state, bs);
+ digest += bs;
+ len -= bs;
+ if (!len) break;
+ permute(ctx);
+ }
+
+ // Zero out context
+ memset(ctx, 0, sizeof(PHP_SHA3_CTX));
+}
+
+// ==========================================================================
+
+#define DECLARE_SHA3_OPS(bits) \
+void PHP_SHA3##bits##Init(PHP_SHA3_##bits##_CTX* ctx) { \
+ PHP_SHA3_Init(ctx, bits); \
+} \
+void PHP_SHA3##bits##Update(PHP_SHA3_##bits##_CTX* ctx, \
+ const unsigned char* input, \
+ unsigned int inputLen) { \
+ PHP_SHA3_Update(ctx, input, inputLen, \
+ (1600 - (2 * bits)) >> 3); \
+} \
+void PHP_SHA3##bits##Final(unsigned char* digest, \
+ PHP_SHA3_##bits##_CTX* ctx) { \
+ PHP_SHA3_Final(digest, ctx, \
+ (1600 - (2 * bits)) >> 3, \
+ bits >> 3); \
+} \
+const php_hash_ops php_hash_sha3_##bits##_ops = { \
+ (php_hash_init_func_t) PHP_SHA3##bits##Init, \
+ (php_hash_update_func_t) PHP_SHA3##bits##Update, \
+ (php_hash_final_func_t) PHP_SHA3##bits##Final, \
+ php_hash_copy, \
+ bits >> 3, \
+ (1600 - (2 * bits)) >> 3, \
+ sizeof(PHP_SHA3_##bits##_CTX) \
+}
+
+DECLARE_SHA3_OPS(224);
+DECLARE_SHA3_OPS(256);
+DECLARE_SHA3_OPS(384);
+DECLARE_SHA3_OPS(512);
+
+#undef DECLARE_SHA3_OPS
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */
diff --git a/ext/hash/hash_snefru.c b/ext/hash/hash_snefru.c
index 85b8658560..17f70d6450 100644
--- a/ext/hash/hash_snefru.c
+++ b/ext/hash/hash_snefru.c
@@ -33,7 +33,7 @@
#endif
#if DBG_SNEFRU
-void ph(php_hash_uint32 h[16])
+void ph(uint32_t h[16])
{
int i;
for (i = 0; i < 16; i++)
@@ -41,12 +41,12 @@ void ph(php_hash_uint32 h[16])
}
#endif
-static inline void Snefru(php_hash_uint32 input[16])
+static inline void Snefru(uint32_t input[16])
{
static int shifts[4] = {16, 8, 16, 24};
int b, index, rshift, lshift;
- const php_hash_uint32 *t0,*t1;
- php_hash_uint32 SBE,B00,B01,B02,B03,B04,B05,B06,B07,B08,B09,B10,B11,B12,B13,B14,B15;
+ const uint32_t *t0,*t1;
+ uint32_t SBE,B00,B01,B02,B03,B04,B05,B06,B07,B08,B09,B10,B11,B12,B13,B14,B15;
B00 = input[0];
B01 = input[1];
@@ -129,7 +129,7 @@ static inline void SnefruTransform(PHP_SNEFRU_CTX *context, const unsigned char
((input[i+2] & 0xff) << 8) | (input[i+3] & 0xff);
}
Snefru(context->state);
- memset(&context->state[8], 0, sizeof(php_hash_uint32) * 8);
+ memset(&context->state[8], 0, sizeof(uint32_t) * 8);
}
PHP_HASH_API void PHP_SNEFRUInit(PHP_SNEFRU_CTX *context)
@@ -137,7 +137,7 @@ PHP_HASH_API void PHP_SNEFRUInit(PHP_SNEFRU_CTX *context)
memset(context, 0, sizeof(*context));
}
-static const php_hash_uint32 MAX32 = 0xffffffffLU;
+static const uint32_t MAX32 = 0xffffffffLU;
PHP_HASH_API void PHP_SNEFRUUpdate(PHP_SNEFRU_CTX *context, const unsigned char *input, size_t len)
{
@@ -173,7 +173,7 @@ PHP_HASH_API void PHP_SNEFRUUpdate(PHP_SNEFRU_CTX *context, const unsigned char
PHP_HASH_API void PHP_SNEFRUFinal(unsigned char digest[32], PHP_SNEFRU_CTX *context)
{
- php_hash_uint32 i, j;
+ uint32_t i, j;
if (context->length) {
SnefruTransform(context, context->buffer);
diff --git a/ext/hash/hash_tiger.c b/ext/hash/hash_tiger.c
index 027a0cd88c..c7009a1b29 100644
--- a/ext/hash/hash_tiger.c
+++ b/ext/hash/hash_tiger.c
@@ -42,13 +42,13 @@
#define round(a,b,c,x,mul) \
c ^= x; \
a -= t1[(unsigned char)(c)] ^ \
- t2[(unsigned char)(((php_hash_uint32)(c))>>(2*8))] ^ \
+ t2[(unsigned char)(((uint32_t)(c))>>(2*8))] ^ \
t3[(unsigned char)((c)>>(4*8))] ^ \
- t4[(unsigned char)(((php_hash_uint32)((c)>>(4*8)))>>(2*8))] ; \
- b += t4[(unsigned char)(((php_hash_uint32)(c))>>(1*8))] ^ \
- t3[(unsigned char)(((php_hash_uint32)(c))>>(3*8))] ^ \
- t2[(unsigned char)(((php_hash_uint32)((c)>>(4*8)))>>(1*8))] ^ \
- t1[(unsigned char)(((php_hash_uint32)((c)>>(4*8)))>>(3*8))]; \
+ t4[(unsigned char)(((uint32_t)((c)>>(4*8)))>>(2*8))] ; \
+ b += t4[(unsigned char)(((uint32_t)(c))>>(1*8))] ^ \
+ t3[(unsigned char)(((uint32_t)(c))>>(3*8))] ^ \
+ t2[(unsigned char)(((uint32_t)((c)>>(4*8)))>>(1*8))] ^ \
+ t1[(unsigned char)(((uint32_t)((c)>>(4*8)))>>(3*8))]; \
b *= mul;
#define pass(a,b,c,mul) \
@@ -105,7 +105,7 @@
# define split(str) \
{ \
int i; \
- php_hash_uint64 tmp[8]; \
+ uint64_t tmp[8]; \
\
for (i = 0; i < 64; ++i) { \
((unsigned char *) tmp)[i^7] = ((unsigned char *) str)[i]; \
@@ -118,8 +118,8 @@
#define tiger_compress(passes, str, state) \
{ \
- register php_hash_uint64 a, b, c, tmpa, x0, x1, x2, x3, x4, x5, x6, x7; \
- php_hash_uint64 aa, bb, cc; \
+ register uint64_t a, b, c, tmpa, x0, x1, x2, x3, x4, x5, x6, x7; \
+ uint64_t aa, bb, cc; \
unsigned int pass_no; \
\
a = state[0]; \
@@ -138,7 +138,7 @@
static inline void TigerFinalize(PHP_TIGER_CTX *context)
{
- context->passed += (php_hash_uint64) context->length << 3;
+ context->passed += (uint64_t) context->length << 3;
context->buffer[context->length++] = 0x1;
if (context->length % 8) {
@@ -148,14 +148,14 @@ static inline void TigerFinalize(PHP_TIGER_CTX *context)
if (context->length > 56) {
memset(&context->buffer[context->length], 0, 64 - context->length);
- tiger_compress(context->passes, ((php_hash_uint64 *) context->buffer), context->state);
+ tiger_compress(context->passes, ((uint64_t *) context->buffer), context->state);
memset(context->buffer, 0, 56);
} else {
memset(&context->buffer[context->length], 0, 56 - context->length);
}
#ifndef WORDS_BIGENDIAN
- memcpy(&context->buffer[56], &context->passed, sizeof(php_hash_uint64));
+ memcpy(&context->buffer[56], &context->passed, sizeof(uint64_t));
#else
context->buffer[56] = (unsigned char) (context->passed & 0xff);
context->buffer[57] = (unsigned char) ((context->passed >> 8) & 0xff);
@@ -166,7 +166,7 @@ static inline void TigerFinalize(PHP_TIGER_CTX *context)
context->buffer[62] = (unsigned char) ((context->passed >> 48) & 0xff);
context->buffer[63] = (unsigned char) ((context->passed >> 56) & 0xff);
#endif
- tiger_compress(context->passes, ((php_hash_uint64 *) context->buffer), context->state);
+ tiger_compress(context->passes, ((uint64_t *) context->buffer), context->state);
}
static inline void TigerDigest(unsigned char *digest_str, unsigned int digest_len, PHP_TIGER_CTX *context)
@@ -206,14 +206,14 @@ PHP_HASH_API void PHP_TIGERUpdate(PHP_TIGER_CTX *context, const unsigned char *i
if (context->length) {
i = 64 - context->length;
memcpy(&context->buffer[context->length], input, i);
- tiger_compress(context->passes, ((const php_hash_uint64 *) context->buffer), context->state);
+ tiger_compress(context->passes, ((const uint64_t *) context->buffer), context->state);
ZEND_SECURE_ZERO(context->buffer, 64);
context->passed += 512;
}
for (; i + 64 <= len; i += 64) {
memcpy(context->buffer, &input[i], 64);
- tiger_compress(context->passes, ((const php_hash_uint64 *) context->buffer), context->state);
+ tiger_compress(context->passes, ((const uint64_t *) context->buffer), context->state);
context->passed += 512;
}
ZEND_SECURE_ZERO(&context->buffer[r], 64-r);
diff --git a/ext/hash/hash_whirlpool.c b/ext/hash/hash_whirlpool.c
index e33d908306..415c346f99 100644
--- a/ext/hash/hash_whirlpool.c
+++ b/ext/hash/hash_whirlpool.c
@@ -41,10 +41,10 @@
static void WhirlpoolTransform(PHP_WHIRLPOOL_CTX *context)
{
int i, r;
- php_hash_uint64 K[8]; /* the round key */
- php_hash_uint64 block[8]; /* mu(buffer) */
- php_hash_uint64 state[8]; /* the cipher state */
- php_hash_uint64 L[8];
+ uint64_t K[8]; /* the round key */
+ uint64_t block[8]; /* mu(buffer) */
+ uint64_t state[8]; /* the cipher state */
+ uint64_t L[8];
unsigned char *buffer = context->buffer.data;
/*
@@ -52,14 +52,14 @@ static void WhirlpoolTransform(PHP_WHIRLPOOL_CTX *context)
*/
for (i = 0; i < 8; i++, buffer += 8) {
block[i] =
- (((php_hash_uint64)buffer[0] ) << 56) ^
- (((php_hash_uint64)buffer[1] & 0xffL) << 48) ^
- (((php_hash_uint64)buffer[2] & 0xffL) << 40) ^
- (((php_hash_uint64)buffer[3] & 0xffL) << 32) ^
- (((php_hash_uint64)buffer[4] & 0xffL) << 24) ^
- (((php_hash_uint64)buffer[5] & 0xffL) << 16) ^
- (((php_hash_uint64)buffer[6] & 0xffL) << 8) ^
- (((php_hash_uint64)buffer[7] & 0xffL) );
+ (((uint64_t)buffer[0] ) << 56) ^
+ (((uint64_t)buffer[1] & 0xffL) << 48) ^
+ (((uint64_t)buffer[2] & 0xffL) << 40) ^
+ (((uint64_t)buffer[3] & 0xffL) << 32) ^
+ (((uint64_t)buffer[4] & 0xffL) << 24) ^
+ (((uint64_t)buffer[5] & 0xffL) << 16) ^
+ (((uint64_t)buffer[6] & 0xffL) << 8) ^
+ (((uint64_t)buffer[7] & 0xffL) );
}
/*
* compute and apply K^0 to the cipher state:
@@ -274,7 +274,7 @@ PHP_HASH_API void PHP_WHIRLPOOLInit(PHP_WHIRLPOOL_CTX *context)
PHP_HASH_API void PHP_WHIRLPOOLUpdate(PHP_WHIRLPOOL_CTX *context, const unsigned char *input, size_t len)
{
- php_hash_uint64 sourceBits = len * 8;
+ uint64_t sourceBits = len * 8;
int sourcePos = 0; /* index of leftmost source unsigned char containing data (1 to 8 bits). */
int sourceGap = (8 - ((int)sourceBits & 7)) & 7; /* space on source[sourcePos]. */
int bufferRem = context->buffer.bits & 7; /* occupied bits on buffer[bufferPos]. */
@@ -283,15 +283,15 @@ PHP_HASH_API void PHP_WHIRLPOOLUpdate(PHP_WHIRLPOOL_CTX *context, const unsigned
unsigned char *bitLength = context->bitlength;
int bufferBits = context->buffer.bits;
int bufferPos = context->buffer.pos;
- php_hash_uint32 b, carry;
+ uint32_t b, carry;
int i;
/*
* tally the length of the added data:
*/
- php_hash_uint64 value = sourceBits;
+ uint64_t value = sourceBits;
for (i = 31, carry = 0; i >= 0 && (carry != 0 || value != L64(0)); i--) {
- carry += bitLength[i] + ((php_hash_uint32)value & 0xff);
+ carry += bitLength[i] + ((uint32_t)value & 0xff);
bitLength[i] = (unsigned char)carry;
carry >>= 8;
value >>= 8;
diff --git a/ext/hash/php_hash.h b/ext/hash/php_hash.h
index c08c358c06..45a598c4dd 100644
--- a/ext/hash/php_hash.h
+++ b/ext/hash/php_hash.h
@@ -31,10 +31,6 @@
#define PHP_HASH_HMAC 0x0001
#define L64 INT64_C
-#define php_hash_int32 int32_t
-#define php_hash_uint32 uint32_t
-#define php_hash_int64 int64_t
-#define php_hash_uint64 uint64_t
typedef void (*php_hash_init_func_t)(void *context);
typedef void (*php_hash_update_func_t)(void *context, const unsigned char *buf, unsigned int count);
@@ -68,6 +64,12 @@ extern const php_hash_ops php_hash_sha224_ops;
extern const php_hash_ops php_hash_sha256_ops;
extern const php_hash_ops php_hash_sha384_ops;
extern const php_hash_ops php_hash_sha512_ops;
+extern const php_hash_ops php_hash_sha512_256_ops;
+extern const php_hash_ops php_hash_sha512_224_ops;
+extern const php_hash_ops php_hash_sha3_224_ops;
+extern const php_hash_ops php_hash_sha3_256_ops;
+extern const php_hash_ops php_hash_sha3_384_ops;
+extern const php_hash_ops php_hash_sha3_512_ops;
extern const php_hash_ops php_hash_ripemd128_ops;
extern const php_hash_ops php_hash_ripemd160_ops;
extern const php_hash_ops php_hash_ripemd256_ops;
@@ -128,6 +130,7 @@ extern zend_module_entry hash_module_entry;
PHP_FUNCTION(hash);
PHP_FUNCTION(hash_file);
+PHP_FUNCTION(hash_hkdf);
PHP_FUNCTION(hash_hmac);
PHP_FUNCTION(hash_hmac_file);
PHP_FUNCTION(hash_init);
diff --git a/ext/hash/php_hash_adler32.h b/ext/hash/php_hash_adler32.h
index c232b631e7..8a227edeeb 100644
--- a/ext/hash/php_hash_adler32.h
+++ b/ext/hash/php_hash_adler32.h
@@ -24,7 +24,7 @@
#include "ext/standard/basic_functions.h"
typedef struct {
- php_hash_uint32 state;
+ uint32_t state;
} PHP_ADLER32_CTX;
PHP_HASH_API void PHP_ADLER32Init(PHP_ADLER32_CTX *context);
diff --git a/ext/hash/php_hash_crc32.h b/ext/hash/php_hash_crc32.h
index 70705b428d..f2a11bf789 100644
--- a/ext/hash/php_hash_crc32.h
+++ b/ext/hash/php_hash_crc32.h
@@ -24,7 +24,7 @@
#include "ext/standard/basic_functions.h"
typedef struct {
- php_hash_uint32 state;
+ uint32_t state;
} PHP_CRC32_CTX;
PHP_HASH_API void PHP_CRC32Init(PHP_CRC32_CTX *context);
diff --git a/ext/hash/php_hash_crc32_tables.h b/ext/hash/php_hash_crc32_tables.h
index 9bcaebd223..6cbb619f8e 100644
--- a/ext/hash/php_hash_crc32_tables.h
+++ b/ext/hash/php_hash_crc32_tables.h
@@ -18,7 +18,7 @@
/* $Id$ */
-static const php_hash_uint32 crc32_table[] = { 0x0,
+static const uint32_t crc32_table[] = { 0x0,
0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6,
0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
@@ -72,7 +72,7 @@ static const php_hash_uint32 crc32_table[] = { 0x0,
0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
};
-static const php_hash_uint32 crc32b_table[] = {
+static const uint32_t crc32b_table[] = {
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
diff --git a/ext/hash/php_hash_fnv.h b/ext/hash/php_hash_fnv.h
index 1062dad7b6..1661dd9ae4 100644
--- a/ext/hash/php_hash_fnv.h
+++ b/ext/hash/php_hash_fnv.h
@@ -21,15 +21,15 @@
#ifndef PHP_HASH_FNV_H
#define PHP_HASH_FNV_H
-#define PHP_FNV1_32_INIT ((php_hash_uint32)0x811c9dc5)
+#define PHP_FNV1_32_INIT ((uint32_t)0x811c9dc5)
#define PHP_FNV1_32A_INIT PHP_FNV1_32_INIT
-#define PHP_FNV_32_PRIME ((php_hash_uint32)0x01000193)
+#define PHP_FNV_32_PRIME ((uint32_t)0x01000193)
-#define PHP_FNV1_64_INIT ((php_hash_uint64)0xcbf29ce484222325ULL)
+#define PHP_FNV1_64_INIT ((uint64_t)0xcbf29ce484222325ULL)
#define PHP_FNV1A_64_INIT FNV1_64_INIT
-#define PHP_FNV_64_PRIME ((php_hash_uint64)0x100000001b3ULL)
+#define PHP_FNV_64_PRIME ((uint64_t)0x100000001b3ULL)
/*
@@ -46,11 +46,11 @@ enum php_fnv_type {
};
typedef struct {
- php_hash_uint32 state;
+ uint32_t state;
} PHP_FNV132_CTX;
typedef struct {
- php_hash_uint64 state;
+ uint64_t state;
} PHP_FNV164_CTX;
@@ -64,8 +64,8 @@ PHP_HASH_API void PHP_FNV164Update(PHP_FNV164_CTX *context, const unsigned char
PHP_HASH_API void PHP_FNV1a64Update(PHP_FNV164_CTX *context, const unsigned char *input, unsigned int inputLen);
PHP_HASH_API void PHP_FNV164Final(unsigned char digest[16], PHP_FNV164_CTX * context);
-static php_hash_uint32 fnv_32_buf(void *buf, size_t len, php_hash_uint32 hval, int alternate);
-static php_hash_uint64 fnv_64_buf(void *buf, size_t len, php_hash_uint64 hval, int alternate);
+static uint32_t fnv_32_buf(void *buf, size_t len, uint32_t hval, int alternate);
+static uint64_t fnv_64_buf(void *buf, size_t len, uint64_t hval, int alternate);
#endif
diff --git a/ext/hash/php_hash_gost.h b/ext/hash/php_hash_gost.h
index e8ae4275d7..6fca83a1b1 100644
--- a/ext/hash/php_hash_gost.h
+++ b/ext/hash/php_hash_gost.h
@@ -25,11 +25,11 @@
/* GOST context */
typedef struct {
- php_hash_uint32 state[16];
- php_hash_uint32 count[2];
+ uint32_t state[16];
+ uint32_t count[2];
unsigned char length;
unsigned char buffer[32];
- const php_hash_uint32 (*tables)[4][256];
+ const uint32_t (*tables)[4][256];
} PHP_GOST_CTX;
PHP_HASH_API void PHP_GOSTInit(PHP_GOST_CTX *);
diff --git a/ext/hash/php_hash_gost_tables.h b/ext/hash/php_hash_gost_tables.h
index af6a78b10b..5a8f6e79fd 100644
--- a/ext/hash/php_hash_gost_tables.h
+++ b/ext/hash/php_hash_gost_tables.h
@@ -1,4 +1,4 @@
-static const php_hash_uint32 tables_test[4][256] = {
+static const uint32_t tables_test[4][256] = {
{ /* table 1 */
0x00072000LU, 0x00075000LU, 0x00074800LU, 0x00071000LU, 0x00076800LU, 0x00074000LU, 0x00070000LU, 0x00077000LU,
0x00073000LU, 0x00075800LU, 0x00070800LU, 0x00076000LU, 0x00073800LU, 0x00077800LU, 0x00072800LU, 0x00071800LU,
@@ -137,7 +137,7 @@ static const php_hash_uint32 tables_test[4][256] = {
},
};
-static const php_hash_uint32 tables_crypto[4][256] = {
+static const uint32_t tables_crypto[4][256] = {
{ /* table 1 */
0x0002d000LU, 0x0002a000LU, 0x0002a800LU, 0x0002b000LU, 0x0002c000LU, 0x00028800LU, 0x00029800LU, 0x0002b800LU,
0x0002e800LU, 0x0002e000LU, 0x0002f000LU, 0x00028000LU, 0x0002c800LU, 0x00029000LU, 0x0002d800LU, 0x0002f800LU,
diff --git a/ext/hash/php_hash_haval.h b/ext/hash/php_hash_haval.h
index 93f3b54698..ca95cff4f6 100644
--- a/ext/hash/php_hash_haval.h
+++ b/ext/hash/php_hash_haval.h
@@ -24,13 +24,13 @@
#include "ext/standard/basic_functions.h"
/* HAVAL context. */
typedef struct {
- php_hash_uint32 state[8];
- php_hash_uint32 count[2];
+ uint32_t state[8];
+ uint32_t count[2];
unsigned char buffer[128];
char passes;
short output;
- void (*Transform)(php_hash_uint32 state[8], const unsigned char block[128]);
+ void (*Transform)(uint32_t state[8], const unsigned char block[128]);
} PHP_HAVAL_CTX;
#define PHP_HASH_HAVAL_INIT_DECL(p,b) PHP_HASH_API void PHP_##p##HAVAL##b##Init(PHP_HAVAL_CTX *); \
diff --git a/ext/hash/php_hash_joaat.h b/ext/hash/php_hash_joaat.h
index b61668bff5..b3448249fd 100644
--- a/ext/hash/php_hash_joaat.h
+++ b/ext/hash/php_hash_joaat.h
@@ -22,14 +22,14 @@
#define PHP_HASH_JOAAT_H
typedef struct {
- php_hash_uint32 state;
+ uint32_t state;
} PHP_JOAAT_CTX;
PHP_HASH_API void PHP_JOAATInit(PHP_JOAAT_CTX *context);
PHP_HASH_API void PHP_JOAATUpdate(PHP_JOAAT_CTX *context, const unsigned char *input, unsigned int inputLen);
PHP_HASH_API void PHP_JOAATFinal(unsigned char digest[16], PHP_JOAAT_CTX * context);
-static php_hash_uint32 joaat_buf(void *buf, size_t len, php_hash_uint32 hval);
+static uint32_t joaat_buf(void *buf, size_t len, uint32_t hval);
#endif
diff --git a/ext/hash/php_hash_md.h b/ext/hash/php_hash_md.h
index 0a96eec186..cf91519a60 100644
--- a/ext/hash/php_hash_md.h
+++ b/ext/hash/php_hash_md.h
@@ -60,8 +60,8 @@
/* MD5 context. */
typedef struct {
- php_hash_uint32 state[4]; /* state (ABCD) */
- php_hash_uint32 count[2]; /* number of bits, modulo 2^64 (lsb first) */
+ uint32_t state[4]; /* state (ABCD) */
+ uint32_t count[2]; /* number of bits, modulo 2^64 (lsb first) */
unsigned char buffer[64]; /* input buffer */
} PHP_MD5_CTX;
@@ -76,8 +76,8 @@ PHP_NAMED_FUNCTION(php_if_md5_file);
/* MD4 context */
typedef struct {
- php_hash_uint32 state[4];
- php_hash_uint32 count[2];
+ uint32_t state[4];
+ uint32_t count[2];
unsigned char buffer[64];
} PHP_MD4_CTX;
diff --git a/ext/hash/php_hash_ripemd.h b/ext/hash/php_hash_ripemd.h
index 399f8c1ee0..b4b39899ec 100644
--- a/ext/hash/php_hash_ripemd.h
+++ b/ext/hash/php_hash_ripemd.h
@@ -24,26 +24,26 @@
/* RIPEMD context. */
typedef struct {
- php_hash_uint32 state[4]; /* state (ABCD) */
- php_hash_uint32 count[2]; /* number of bits, modulo 2^64 (lsb first) */
+ uint32_t state[4]; /* state (ABCD) */
+ uint32_t count[2]; /* number of bits, modulo 2^64 (lsb first) */
unsigned char buffer[64]; /* input buffer */
} PHP_RIPEMD128_CTX;
typedef struct {
- php_hash_uint32 state[5]; /* state (ABCD) */
- php_hash_uint32 count[2]; /* number of bits, modulo 2^64 (lsb first) */
+ uint32_t state[5]; /* state (ABCD) */
+ uint32_t count[2]; /* number of bits, modulo 2^64 (lsb first) */
unsigned char buffer[64]; /* input buffer */
} PHP_RIPEMD160_CTX;
typedef struct {
- php_hash_uint32 state[8]; /* state (ABCD) */
- php_hash_uint32 count[2]; /* number of bits, modulo 2^64 (lsb first) */
+ uint32_t state[8]; /* state (ABCD) */
+ uint32_t count[2]; /* number of bits, modulo 2^64 (lsb first) */
unsigned char buffer[64]; /* input buffer */
} PHP_RIPEMD256_CTX;
typedef struct {
- php_hash_uint32 state[10]; /* state (ABCD) */
- php_hash_uint32 count[2]; /* number of bits, modulo 2^64 (lsb first) */
+ uint32_t state[10]; /* state (ABCD) */
+ uint32_t count[2]; /* number of bits, modulo 2^64 (lsb first) */
unsigned char buffer[64]; /* input buffer */
} PHP_RIPEMD320_CTX;
diff --git a/ext/hash/php_hash_sha.h b/ext/hash/php_hash_sha.h
index 8e7219b945..43bb7ce0f8 100644
--- a/ext/hash/php_hash_sha.h
+++ b/ext/hash/php_hash_sha.h
@@ -36,8 +36,8 @@
/* SHA1 context. */
typedef struct {
- php_hash_uint32 state[5]; /* state (ABCD) */
- php_hash_uint32 count[2]; /* number of bits, modulo 2^64 */
+ uint32_t state[5]; /* state (ABCD) */
+ uint32_t count[2]; /* number of bits, modulo 2^64 */
unsigned char buffer[64]; /* input buffer */
} PHP_SHA1_CTX;
@@ -52,8 +52,8 @@ PHP_FUNCTION(sha1_file);
/* SHA224 context. */
typedef struct {
- php_hash_uint32 state[8]; /* state */
- php_hash_uint32 count[2]; /* number of bits, modulo 2^64 */
+ uint32_t state[8]; /* state */
+ uint32_t count[2]; /* number of bits, modulo 2^64 */
unsigned char buffer[64]; /* input buffer */
} PHP_SHA224_CTX;
@@ -63,8 +63,8 @@ PHP_HASH_API void PHP_SHA224Final(unsigned char[28], PHP_SHA224_CTX *);
/* SHA256 context. */
typedef struct {
- php_hash_uint32 state[8]; /* state */
- php_hash_uint32 count[2]; /* number of bits, modulo 2^64 */
+ uint32_t state[8]; /* state */
+ uint32_t count[2]; /* number of bits, modulo 2^64 */
unsigned char buffer[64]; /* input buffer */
} PHP_SHA256_CTX;
@@ -74,8 +74,8 @@ PHP_HASH_API void PHP_SHA256Final(unsigned char[32], PHP_SHA256_CTX *);
/* SHA384 context */
typedef struct {
- php_hash_uint64 state[8]; /* state */
- php_hash_uint64 count[2]; /* number of bits, modulo 2^128 */
+ uint64_t state[8]; /* state */
+ uint64_t count[2]; /* number of bits, modulo 2^128 */
unsigned char buffer[128]; /* input buffer */
} PHP_SHA384_CTX;
@@ -85,8 +85,8 @@ PHP_HASH_API void PHP_SHA384Final(unsigned char[48], PHP_SHA384_CTX *);
/* SHA512 context */
typedef struct {
- php_hash_uint64 state[8]; /* state */
- php_hash_uint64 count[2]; /* number of bits, modulo 2^128 */
+ uint64_t state[8]; /* state */
+ uint64_t count[2]; /* number of bits, modulo 2^128 */
unsigned char buffer[128]; /* input buffer */
} PHP_SHA512_CTX;
@@ -94,4 +94,12 @@ PHP_HASH_API void PHP_SHA512Init(PHP_SHA512_CTX *);
PHP_HASH_API void PHP_SHA512Update(PHP_SHA512_CTX *, const unsigned char *, unsigned int);
PHP_HASH_API void PHP_SHA512Final(unsigned char[64], PHP_SHA512_CTX *);
+PHP_HASH_API void PHP_SHA512_256Init(PHP_SHA512_CTX *);
+#define PHP_SHA512_256Update PHP_SHA512Update
+PHP_HASH_API void PHP_SHA512_256Final(unsigned char[32], PHP_SHA512_CTX *);
+
+PHP_HASH_API void PHP_SHA512_224Init(PHP_SHA512_CTX *);
+#define PHP_SHA512_224Update PHP_SHA512Update
+PHP_HASH_API void PHP_SHA512_224Final(unsigned char[28], PHP_SHA512_CTX *);
+
#endif /* PHP_HASH_SHA_H */
diff --git a/ext/hash/php_hash_sha3.h b/ext/hash/php_hash_sha3.h
new file mode 100644
index 0000000000..8b70ef4a7e
--- /dev/null
+++ b/ext/hash/php_hash_sha3.h
@@ -0,0 +1,58 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 7 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2017 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Sara Golemon <pollita@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef PHP_HASH_SHA3_H
+#define PHP_HASH_SHA3_H
+
+#include "php.h"
+
+typedef struct {
+ unsigned char state[200]; // 5 * 5 * sizeof(uint64)
+ uint32_t pos;
+} PHP_SHA3_CTX;
+
+typedef PHP_SHA3_CTX PHP_SHA3_224_CTX;
+typedef PHP_SHA3_CTX PHP_SHA3_256_CTX;
+typedef PHP_SHA3_CTX PHP_SHA3_384_CTX;
+typedef PHP_SHA3_CTX PHP_SHA3_512_CTX;
+
+PHP_HASH_API void PHP_SHA3224Init(PHP_SHA3_224_CTX*);
+PHP_HASH_API void PHP_SHA3224Update(PHP_SHA3_224_CTX*, const unsigned char*, unsigned int);
+PHP_HASH_API void PHP_SAH3224Final(unsigned char[32], PHP_SHA3_224_CTX*);
+
+PHP_HASH_API void PHP_SHA3256Init(PHP_SHA3_256_CTX*);
+PHP_HASH_API void PHP_SHA3256Update(PHP_SHA3_256_CTX*, const unsigned char*, unsigned int);
+PHP_HASH_API void PHP_SAH3256Final(unsigned char[32], PHP_SHA3_256_CTX*);
+
+PHP_HASH_API void PHP_SHA3384Init(PHP_SHA3_384_CTX*);
+PHP_HASH_API void PHP_SHA3384Update(PHP_SHA3_384_CTX*, const unsigned char*, unsigned int);
+PHP_HASH_API void PHP_SAH3384Final(unsigned char[32], PHP_SHA3_384_CTX*);
+
+PHP_HASH_API void PHP_SHA3512Init(PHP_SHA3_512_CTX*);
+PHP_HASH_API void PHP_SHA3512Update(PHP_SHA3_512_CTX*, const unsigned char*, unsigned int);
+PHP_HASH_API void PHP_SAH3512Final(unsigned char[32], PHP_SHA3_512_CTX*);
+
+#endif
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */
diff --git a/ext/hash/php_hash_snefru.h b/ext/hash/php_hash_snefru.h
index c926022360..02e11c98fb 100644
--- a/ext/hash/php_hash_snefru.h
+++ b/ext/hash/php_hash_snefru.h
@@ -29,8 +29,8 @@
/* SNEFRU context */
typedef struct {
- php_hash_uint32 state[16];
- php_hash_uint32 count[2];
+ uint32_t state[16];
+ uint32_t count[2];
unsigned char length;
unsigned char buffer[32];
} PHP_SNEFRU_CTX;
diff --git a/ext/hash/php_hash_snefru_tables.h b/ext/hash/php_hash_snefru_tables.h
index d96fae19ae..a1bdd9970d 100644
--- a/ext/hash/php_hash_snefru_tables.h
+++ b/ext/hash/php_hash_snefru_tables.h
@@ -18,7 +18,7 @@
/* $Id$ */
-static const php_hash_uint32 tables[16][256]= {
+static const uint32_t tables[16][256]= {
{ /* Start of S Box 0 */
diff --git a/ext/hash/php_hash_tiger.h b/ext/hash/php_hash_tiger.h
index c9a8c7df47..136bf73bd3 100644
--- a/ext/hash/php_hash_tiger.h
+++ b/ext/hash/php_hash_tiger.h
@@ -23,8 +23,8 @@
/* TIGER context */
typedef struct {
- php_hash_uint64 state[3];
- php_hash_uint64 passed;
+ uint64_t state[3];
+ uint64_t passed;
unsigned char buffer[64];
unsigned int passes:1;
unsigned int length:7;
diff --git a/ext/hash/php_hash_tiger_tables.h b/ext/hash/php_hash_tiger_tables.h
index 08290184ed..e3221d024f 100644
--- a/ext/hash/php_hash_tiger_tables.h
+++ b/ext/hash/php_hash_tiger_tables.h
@@ -24,7 +24,7 @@
#define t3 (table+256*2)
#define t4 (table+256*3)
-static const php_hash_uint64 table[4*256] = {
+static const uint64_t table[4*256] = {
L64(0x02AAB17CF7E90C5E) /* 0 */, L64(0xAC424B03E243A8EC) /* 1 */,
L64(0x72CD5BE30DD5FCD3) /* 2 */, L64(0x6D019B93F6F97F3A) /* 3 */,
L64(0xCD9978FFD21F9193) /* 4 */, L64(0x7573A1C9708029E2) /* 5 */,
diff --git a/ext/hash/php_hash_whirlpool.h b/ext/hash/php_hash_whirlpool.h
index c392b20f8d..1579f85cef 100644
--- a/ext/hash/php_hash_whirlpool.h
+++ b/ext/hash/php_hash_whirlpool.h
@@ -23,7 +23,7 @@
/* WHIRLPOOL context */
typedef struct {
- php_hash_uint64 state[8];
+ uint64_t state[8];
unsigned char bitlength[32];
struct {
int pos;
diff --git a/ext/hash/php_hash_whirlpool_tables.h b/ext/hash/php_hash_whirlpool_tables.h
index 82baea07fd..f86d082852 100644
--- a/ext/hash/php_hash_whirlpool_tables.h
+++ b/ext/hash/php_hash_whirlpool_tables.h
@@ -23,7 +23,7 @@
#define R 10
-static const php_hash_uint64 rc[R + 1] = {
+static const uint64_t rc[R + 1] = {
L64(0x0000000000000000),
L64(0x1823c6e887b8014f),
L64(0x36a6d2f5796f9152),
@@ -37,7 +37,7 @@ static const php_hash_uint64 rc[R + 1] = {
L64(0xca2dbf07ad5a8333),
};
-static const php_hash_uint64 C0[256] = {
+static const uint64_t C0[256] = {
L64(0x18186018c07830d8), L64(0x23238c2305af4626), L64(0xc6c63fc67ef991b8), L64(0xe8e887e8136fcdfb),
L64(0x878726874ca113cb), L64(0xb8b8dab8a9626d11), L64(0x0101040108050209), L64(0x4f4f214f426e9e0d),
L64(0x3636d836adee6c9b), L64(0xa6a6a2a6590451ff), L64(0xd2d26fd2debdb90c), L64(0xf5f5f3f5fb06f70e),
@@ -104,7 +104,7 @@ static const php_hash_uint64 C0[256] = {
L64(0x2828a0285d885075), L64(0x5c5c6d5cda31b886), L64(0xf8f8c7f8933fed6b), L64(0x8686228644a411c2),
};
-static const php_hash_uint64 C1[256] = {
+static const uint64_t C1[256] = {
L64(0xd818186018c07830), L64(0x2623238c2305af46), L64(0xb8c6c63fc67ef991), L64(0xfbe8e887e8136fcd),
L64(0xcb878726874ca113), L64(0x11b8b8dab8a9626d), L64(0x0901010401080502), L64(0x0d4f4f214f426e9e),
L64(0x9b3636d836adee6c), L64(0xffa6a6a2a6590451), L64(0x0cd2d26fd2debdb9), L64(0x0ef5f5f3f5fb06f7),
@@ -171,7 +171,7 @@ static const php_hash_uint64 C1[256] = {
L64(0x752828a0285d8850), L64(0x865c5c6d5cda31b8), L64(0x6bf8f8c7f8933fed), L64(0xc28686228644a411),
};
-static const php_hash_uint64 C2[256] = {
+static const uint64_t C2[256] = {
L64(0x30d818186018c078), L64(0x462623238c2305af), L64(0x91b8c6c63fc67ef9), L64(0xcdfbe8e887e8136f),
L64(0x13cb878726874ca1), L64(0x6d11b8b8dab8a962), L64(0x0209010104010805), L64(0x9e0d4f4f214f426e),
L64(0x6c9b3636d836adee), L64(0x51ffa6a6a2a65904), L64(0xb90cd2d26fd2debd), L64(0xf70ef5f5f3f5fb06),
@@ -238,7 +238,7 @@ static const php_hash_uint64 C2[256] = {
L64(0x50752828a0285d88), L64(0xb8865c5c6d5cda31), L64(0xed6bf8f8c7f8933f), L64(0x11c28686228644a4),
};
-static const php_hash_uint64 C3[256] = {
+static const uint64_t C3[256] = {
L64(0x7830d818186018c0), L64(0xaf462623238c2305), L64(0xf991b8c6c63fc67e), L64(0x6fcdfbe8e887e813),
L64(0xa113cb878726874c), L64(0x626d11b8b8dab8a9), L64(0x0502090101040108), L64(0x6e9e0d4f4f214f42),
L64(0xee6c9b3636d836ad), L64(0x0451ffa6a6a2a659), L64(0xbdb90cd2d26fd2de), L64(0x06f70ef5f5f3f5fb),
@@ -305,7 +305,7 @@ static const php_hash_uint64 C3[256] = {
L64(0x8850752828a0285d), L64(0x31b8865c5c6d5cda), L64(0x3fed6bf8f8c7f893), L64(0xa411c28686228644),
};
-static const php_hash_uint64 C4[256] = {
+static const uint64_t C4[256] = {
L64(0xc07830d818186018), L64(0x05af462623238c23), L64(0x7ef991b8c6c63fc6), L64(0x136fcdfbe8e887e8),
L64(0x4ca113cb87872687), L64(0xa9626d11b8b8dab8), L64(0x0805020901010401), L64(0x426e9e0d4f4f214f),
L64(0xadee6c9b3636d836), L64(0x590451ffa6a6a2a6), L64(0xdebdb90cd2d26fd2), L64(0xfb06f70ef5f5f3f5),
@@ -372,7 +372,7 @@ static const php_hash_uint64 C4[256] = {
L64(0x5d8850752828a028), L64(0xda31b8865c5c6d5c), L64(0x933fed6bf8f8c7f8), L64(0x44a411c286862286),
};
-static const php_hash_uint64 C5[256] = {
+static const uint64_t C5[256] = {
L64(0x18c07830d8181860), L64(0x2305af462623238c), L64(0xc67ef991b8c6c63f), L64(0xe8136fcdfbe8e887),
L64(0x874ca113cb878726), L64(0xb8a9626d11b8b8da), L64(0x0108050209010104), L64(0x4f426e9e0d4f4f21),
L64(0x36adee6c9b3636d8), L64(0xa6590451ffa6a6a2), L64(0xd2debdb90cd2d26f), L64(0xf5fb06f70ef5f5f3),
@@ -439,7 +439,7 @@ static const php_hash_uint64 C5[256] = {
L64(0x285d8850752828a0), L64(0x5cda31b8865c5c6d), L64(0xf8933fed6bf8f8c7), L64(0x8644a411c2868622),
};
-static const php_hash_uint64 C6[256] = {
+static const uint64_t C6[256] = {
L64(0x6018c07830d81818), L64(0x8c2305af46262323), L64(0x3fc67ef991b8c6c6), L64(0x87e8136fcdfbe8e8),
L64(0x26874ca113cb8787), L64(0xdab8a9626d11b8b8), L64(0x0401080502090101), L64(0x214f426e9e0d4f4f),
L64(0xd836adee6c9b3636), L64(0xa2a6590451ffa6a6), L64(0x6fd2debdb90cd2d2), L64(0xf3f5fb06f70ef5f5),
@@ -506,7 +506,7 @@ static const php_hash_uint64 C6[256] = {
L64(0xa0285d8850752828), L64(0x6d5cda31b8865c5c), L64(0xc7f8933fed6bf8f8), L64(0x228644a411c28686),
};
-static const php_hash_uint64 C7[256] = {
+static const uint64_t C7[256] = {
L64(0x186018c07830d818), L64(0x238c2305af462623), L64(0xc63fc67ef991b8c6), L64(0xe887e8136fcdfbe8),
L64(0x8726874ca113cb87), L64(0xb8dab8a9626d11b8), L64(0x0104010805020901), L64(0x4f214f426e9e0d4f),
L64(0x36d836adee6c9b36), L64(0xa6a2a6590451ffa6), L64(0xd26fd2debdb90cd2), L64(0xf5f3f5fb06f70ef5),
diff --git a/ext/hash/tests/hash_algos.phpt b/ext/hash/tests/hash_algos.phpt
index f2d6fc3ffb..0014f95b1d 100644
--- a/ext/hash/tests/hash_algos.phpt
+++ b/ext/hash/tests/hash_algos.phpt
@@ -18,7 +18,7 @@ var_dump(hash_algos());
===Done===
--EXPECTF--
*** Testing hash_algos() : basic functionality ***
-array(46) {
+array(52) {
[%d]=>
string(3) "md2"
[%d]=>
@@ -34,8 +34,20 @@ array(46) {
[%d]=>
string(6) "sha384"
[%d]=>
+ string(10) "sha512/224"
+ [%d]=>
+ string(10) "sha512/256"
+ [%d]=>
string(6) "sha512"
[%d]=>
+ string(8) "sha3-224"
+ [%d]=>
+ string(8) "sha3-256"
+ [%d]=>
+ string(8) "sha3-384"
+ [%d]=>
+ string(8) "sha3-512"
+ [%d]=>
string(9) "ripemd128"
[%d]=>
string(9) "ripemd160"
diff --git a/ext/hash/tests/hash_copy_001.phpt b/ext/hash/tests/hash_copy_001.phpt
index f9fe7cc552..b33d449762 100644
--- a/ext/hash/tests/hash_copy_001.phpt
+++ b/ext/hash/tests/hash_copy_001.phpt
@@ -52,9 +52,27 @@ string(64) "d3a13cf52af8e9390caed78b77b6b1e06e102204e3555d111dfd149bc5d54dba"
string(6) "sha384"
string(96) "6950d861ace4102b803ab8b3779d2f471968233010d2608974ab89804cef6f76162b4433d6e554e11e40a7cdcf510ea3"
string(96) "6950d861ace4102b803ab8b3779d2f471968233010d2608974ab89804cef6f76162b4433d6e554e11e40a7cdcf510ea3"
+string(10) "sha512/224"
+string(56) "a2573d0e3f6c3e2d174c935a35a8ea31032f04e9e83499ac3ceda568"
+string(56) "a2573d0e3f6c3e2d174c935a35a8ea31032f04e9e83499ac3ceda568"
+string(10) "sha512/256"
+string(64) "fddacab80b3a610ba024c9d75a5fe0cafe5ae7c789f829b3c5fbea8ef11ccc1a"
+string(64) "fddacab80b3a610ba024c9d75a5fe0cafe5ae7c789f829b3c5fbea8ef11ccc1a"
string(6) "sha512"
string(128) "caced3db8e9e3a5543d5b933bcbe9e7834e6667545c3f5d4087b58ec8d78b4c8a4a5500c9b88f65f7368810ba9905e51f1cff3b25a5dccf76634108fb4e7ce13"
string(128) "caced3db8e9e3a5543d5b933bcbe9e7834e6667545c3f5d4087b58ec8d78b4c8a4a5500c9b88f65f7368810ba9905e51f1cff3b25a5dccf76634108fb4e7ce13"
+string(8) "sha3-224"
+string(56) "7e1126cffee98e5c4b0e9dd5c6efabd5c9356d668e9a2d3cfab724d4"
+string(56) "7e1126cffee98e5c4b0e9dd5c6efabd5c9356d668e9a2d3cfab724d4"
+string(8) "sha3-256"
+string(64) "834abfed9197af09cbe66b7748c65a050a3755ef7a556d6764eb6eabc93b4c7a"
+string(64) "834abfed9197af09cbe66b7748c65a050a3755ef7a556d6764eb6eabc93b4c7a"
+string(8) "sha3-384"
+string(96) "c9016992586f7a8663c5379ed892349c1140ad258f7c44ee82f61f0b8cb75c675012ea94dc1314e06699be2d1465f67b"
+string(96) "c9016992586f7a8663c5379ed892349c1140ad258f7c44ee82f61f0b8cb75c675012ea94dc1314e06699be2d1465f67b"
+string(8) "sha3-512"
+string(128) "5f85341bc9c6621406bf1841c4ce01727ea8759fdf2927106c3e70a75ad9fffd095b87f995aeee844e1a2c287e1195ce809b9bdb1c31258f7fc098175b6de0b4"
+string(128) "5f85341bc9c6621406bf1841c4ce01727ea8759fdf2927106c3e70a75ad9fffd095b87f995aeee844e1a2c287e1195ce809b9bdb1c31258f7fc098175b6de0b4"
string(9) "ripemd128"
string(32) "5f1bc5f5aeaf747574dd34a6535cd94a"
string(32) "5f1bc5f5aeaf747574dd34a6535cd94a"
@@ -190,9 +208,27 @@ string(64) "268e7f4cf88504a53fd77136c4c4748169f46ff7150b376569ada9c374836944"
string(6) "sha384"
string(96) "6950d861ace4102b803ab8b3779d2f471968233010d2608974ab89804cef6f76162b4433d6e554e11e40a7cdcf510ea3"
string(96) "0d44981d04bb11b1ef75d5c2932bd0aa2785e7bc454daac954d77e2ca10047879b58997533fc99650b20049c6cb9a6cc"
+string(10) "sha512/224"
+string(56) "a2573d0e3f6c3e2d174c935a35a8ea31032f04e9e83499ac3ceda568"
+string(56) "cbc2bbf0028ed803af785b0f264962c84ec48d8ee0908322ef995ddb"
+string(10) "sha512/256"
+string(64) "fddacab80b3a610ba024c9d75a5fe0cafe5ae7c789f829b3c5fbea8ef11ccc1a"
+string(64) "2cec704878ffa7128e0c4a61eef87d1f3c823184d364dfa3fed73beb00499b00"
string(6) "sha512"
string(128) "caced3db8e9e3a5543d5b933bcbe9e7834e6667545c3f5d4087b58ec8d78b4c8a4a5500c9b88f65f7368810ba9905e51f1cff3b25a5dccf76634108fb4e7ce13"
string(128) "28d7c721433782a880f840af0c3f3ea2cad4ef55de2114dda9d504cedeb110e1cf2519c49e4b5da3da4484bb6ba4fd1621ceadc6408f4410b2ebe9d83a4202c2"
+string(8) "sha3-224"
+string(56) "7e1126cffee98e5c4b0e9dd5c6efabd5c9356d668e9a2d3cfab724d4"
+string(56) "9a21a5464794c2c9784df50cf89cf72234e11941bddaee93f912753e"
+string(8) "sha3-256"
+string(64) "834abfed9197af09cbe66b7748c65a050a3755ef7a556d6764eb6eabc93b4c7a"
+string(64) "57aa7a90f29b5ab66592760592780da247fd39b4c911773687450f9df8cc8ed0"
+string(8) "sha3-384"
+string(96) "c9016992586f7a8663c5379ed892349c1140ad258f7c44ee82f61f0b8cb75c675012ea94dc1314e06699be2d1465f67b"
+string(96) "5d6d7e42b241288bc707b74c50f90a37d69a4afa854ca72021a22cb379356e53b6233aea1be2f33d393d6effa9b5e36c"
+string(8) "sha3-512"
+string(128) "5f85341bc9c6621406bf1841c4ce01727ea8759fdf2927106c3e70a75ad9fffd095b87f995aeee844e1a2c287e1195ce809b9bdb1c31258f7fc098175b6de0b4"
+string(128) "9b88c689bc13a36e6983b32e8ee9464d63b619f246ca451d1fe2a6c9670f01e71d0c8eb245f3204d27d27c056f2a0fef76a1e3bc30fb74cccbc984dbd4883ae6"
string(9) "ripemd128"
string(32) "5f1bc5f5aeaf747574dd34a6535cd94a"
string(32) "f95f5e22b8875ee0c48219ae97f0674b"
diff --git a/ext/hash/tests/hash_hkdf_basic.phpt b/ext/hash/tests/hash_hkdf_basic.phpt
new file mode 100644
index 0000000000..7a8ffde6a1
--- /dev/null
+++ b/ext/hash/tests/hash_hkdf_basic.phpt
@@ -0,0 +1,94 @@
+--TEST--
+Test hash_hkdf() function: basic functionality
+--SKIPIF--
+<?php extension_loaded('hash') or die('skip: hash extension not loaded.'); ?>
+--FILE--
+<?php
+
+/* Prototype : string hkdf ( string $algo , string $ikm [, int $length , string $info = '' , string $salt = '' ] )
+ * Description: HMAC-based Key Derivation Function
+ * Source code: ext/hash/hash.c
+*/
+
+echo "*** Testing hash_hkdf(): basic functionality ***\n";
+
+$ikm = 'input key material';
+
+echo 'md2: ', bin2hex(hash_hkdf('md2', $ikm)), "\n";
+echo 'md4: ', bin2hex(hash_hkdf('md4', $ikm)), "\n";
+echo 'md5: ', bin2hex(hash_hkdf('md5', $ikm)), "\n";
+echo 'sha1: ', bin2hex(hash_hkdf('sha1', $ikm)), "\n";
+echo 'sha224: ', bin2hex(hash_hkdf('sha224', $ikm)), "\n";
+echo 'sha256: ', bin2hex(hash_hkdf('sha256', $ikm)), "\n";
+echo 'sha384: ', bin2hex(hash_hkdf('sha384', $ikm)), "\n";
+echo 'sha512: ', bin2hex(hash_hkdf('sha512', $ikm)), "\n";
+echo 'ripemd128: ', bin2hex(hash_hkdf('ripemd128', $ikm)), "\n";
+echo 'ripemd160: ', bin2hex(hash_hkdf('ripemd160', $ikm)), "\n";
+echo 'ripemd256: ', bin2hex(hash_hkdf('ripemd256', $ikm)), "\n";
+echo 'ripemd320: ', bin2hex(hash_hkdf('ripemd320', $ikm)), "\n";
+echo 'whirlpool: ', bin2hex(hash_hkdf('whirlpool', $ikm)), "\n";
+echo 'tiger128,3: ', bin2hex(hash_hkdf('tiger128,3', $ikm)), "\n";
+echo 'tiger160,3: ', bin2hex(hash_hkdf('tiger160,3', $ikm)), "\n";
+echo 'tiger192,3: ', bin2hex(hash_hkdf('tiger192,3', $ikm)), "\n";
+echo 'tiger128,4: ', bin2hex(hash_hkdf('tiger128,4', $ikm)), "\n";
+echo 'tiger160,4: ', bin2hex(hash_hkdf('tiger160,4', $ikm)), "\n";
+echo 'tiger192,4: ', bin2hex(hash_hkdf('tiger192,4', $ikm)), "\n";
+echo 'haval128,3: ', bin2hex(hash_hkdf('haval128,3', $ikm)), "\n";
+echo 'haval160,3: ', bin2hex(hash_hkdf('haval160,3', $ikm)), "\n";
+echo 'haval192,3: ', bin2hex(hash_hkdf('haval192,3', $ikm)), "\n";
+echo 'haval224,3: ', bin2hex(hash_hkdf('haval224,3', $ikm)), "\n";
+echo 'haval256,3: ', bin2hex(hash_hkdf('haval256,3', $ikm)), "\n";
+echo 'haval128,4: ', bin2hex(hash_hkdf('haval128,4', $ikm)), "\n";
+echo 'haval160,4: ', bin2hex(hash_hkdf('haval160,4', $ikm)), "\n";
+echo 'haval192,4: ', bin2hex(hash_hkdf('haval192,4', $ikm)), "\n";
+echo 'haval224,4: ', bin2hex(hash_hkdf('haval224,4', $ikm)), "\n";
+echo 'haval256,4: ', bin2hex(hash_hkdf('haval256,4', $ikm)), "\n";
+echo 'haval128,5: ', bin2hex(hash_hkdf('haval128,5', $ikm)), "\n";
+echo 'haval160,5: ', bin2hex(hash_hkdf('haval160,5', $ikm)), "\n";
+echo 'haval192,5: ', bin2hex(hash_hkdf('haval192,5', $ikm)), "\n";
+echo 'haval224,5: ', bin2hex(hash_hkdf('haval224,5', $ikm)), "\n";
+echo 'haval256,5: ', bin2hex(hash_hkdf('haval256,5', $ikm)), "\n";
+echo 'snefru: ', bin2hex(hash_hkdf('snefru', $ikm)), "\n";
+echo 'snefru256: ', bin2hex(hash_hkdf('snefru256', $ikm)), "\n";
+echo 'gost: ', bin2hex(hash_hkdf('gost', $ikm)), "\n";
+
+?>
+--EXPECTF--
+*** Testing hash_hkdf(): basic functionality ***
+md2: 87779851d2377dab25da16fd7aadfdf5
+md4: 422c6bd8dd2a6baae8abadef618c3ede
+md5: 98b16391063ecee006a3ca8ee5776b1e
+sha1: a71863230e3782240265126a53e137af6667e988
+sha224: 51678ceb17e803505187b2cf6451c30fbc572fda165bb69bbd117c7a
+sha256: d8f0bede4b652933c32a92eccf7723f7eeb4701744c81325dc3f0fa9fda24499
+sha384: f600680e677bb417a7a22a4da8b167c0d91823a7a5d56a49aeb1838bb2320c05068d15d6d980824fee542a279d310c3a
+sha512: fb1b86549e941b81821a89ac6ba7c4f93465077b3f2af94352ebf1d041efcd3c5694469c1ae31bb10db4c1d2ab84f07e4518ba33a3eadd4a149425750285c640
+ripemd128: cb6418fc0dc9efaeb7e9654390fa7f14
+ripemd160: ba42dbb34f08e9337ace15295f218754a41d6c39
+ripemd256: f2e96b292935e2395b59833ed89d928ac1197ff62c8031ebc06a3f5bad19513f
+ripemd320: a13a682072525ceb4c4a5fef59096e682096e1096e6e7e238c7bd48a6f6c6a9ba3d7d9fbee6b68c4
+whirlpool: 497c717e04d896c3d582742c614435b7d0963b39de12dcf532540d39164b3b85214014620dfdff4a089a06b06aff43c39a3b4d9b806913cf6309de58ff1151f5
+tiger128,3: e13c2e7262892c6bd8dfc24121e7cb34
+tiger160,3: 48cc5a9f5e5d7029eb0544662222c0ba13822b7b
+tiger192,3: 5a665d23b6cbb405668160e58b01aebef74eba979f4bc70b
+tiger128,4: 8acf517ecf58cccbd65c1186d71e4116
+tiger160,4: cc0e33ee26700a2eb9a994bbb0e6cef29b429441
+tiger192,4: 97fa02d42331321fdc05c7f8dbc756d751ca36ce1aee69b0
+haval128,3: 2accab8029d42fb15fdbe9d3e2a470ca
+haval160,3: 496fd29e7fc8351d2971b96a3733a7b3de000064
+haval192,3: 238a731801439b1f195e1a1568ce75251e1dd719d904a8a2
+haval224,3: d863e596ff6b2bdba1ed7b313df1c3d177176312e81b47e9290f7566
+haval256,3: 96f555fe41255c34fe57b275f1ae40bbb8f07c6a2a6d68c849748fbb393ff443
+haval128,4: 9822af229cc59527a72e231a690fad3b
+haval160,4: 1bbbc4d632daaf94d5ba167efaa70af5b753effe
+haval192,4: dd12a8f8919cbf5632497f0918b30236371dd1b55f71e824
+haval224,4: 8af449fb4eb627eb8887507c1279a116ac4325b5806dd22e2f2af410
+haval256,4: bd74a6d5fa1ec23a92ce1fd76c36bc8be36f5eddbea821545a91810e1f8d6fc5
+haval128,5: 84564f3450a6ccf6041162207dc8acba
+haval160,5: b55cd1b3c514457b9e61c51ad22f302f6ec7cca1
+haval192,5: d1db7a8e69b327455d530d1ac60f774023b8b4bdd6bbbf92
+haval224,5: c5a2576511f1143c6e29f63d82d6e0be8f67d0bea448e27238be5000
+haval256,5: 9dbab73d13f1fd3a1b41398fe90ba1f298329681d861b023373c33f1051bd4d3
+snefru: 798eac954e5ece38e9acb63b50c1c2ecb799d34356358cec5a80eeeea91c8de9
+snefru256: 798eac954e5ece38e9acb63b50c1c2ecb799d34356358cec5a80eeeea91c8de9
+gost: 64edd584b87a2dfdd1f2b44ed2db8bd27af8386aafe751c2aebaed32dfa3852e
diff --git a/ext/hash/tests/hash_hkdf_edges.phpt b/ext/hash/tests/hash_hkdf_edges.phpt
new file mode 100644
index 0000000000..633efa4301
--- /dev/null
+++ b/ext/hash/tests/hash_hkdf_edges.phpt
@@ -0,0 +1,32 @@
+--TEST--
+Test hash_hkdf() function: edge cases
+--SKIPIF--
+<?php extension_loaded('hash') or die('skip: hash extension not loaded.'); ?>
+--FILE--
+<?php
+
+/* Prototype : string hkdf ( string $algo , string $ikm [, int $length , string $info = '' , string $salt = '' ] )
+ * Description: HMAC-based Key Derivation Function
+ * Source code: ext/hash/hash.c
+*/
+
+echo "*** Testing hash_hkdf(): edge cases ***\n";
+
+$ikm = 'input key material';
+
+echo 'Length < digestSize: ', bin2hex(hash_hkdf('md5', $ikm, 7)), "\n";
+echo 'Length % digestSize != 0: ', bin2hex(hash_hkdf('md5', $ikm, 17)), "\n";
+echo 'Algo name case-sensitivity: ', (bin2hex(hash_hkdf('Md5', $ikm, 7)) === '98b16391063ece' ? 'true' : 'false'), "\n";
+echo "Non-crypto algo name case-sensitivity:\n";
+var_dump(hash_hkdf('jOaAt', $ikm));
+
+?>
+--EXPECTF--
+*** Testing hash_hkdf(): edge cases ***
+Length < digestSize: 98b16391063ece
+Length % digestSize != 0: 98b16391063ecee006a3ca8ee5776b1e5f
+Algo name case-sensitivity: true
+Non-crypto algo name case-sensitivity:
+
+Warning: hash_hkdf(): Non-cryptographic hashing algorithm: jOaAt in %s on line %d
+bool(false)
diff --git a/ext/hash/tests/hash_hkdf_error.phpt b/ext/hash/tests/hash_hkdf_error.phpt
new file mode 100644
index 0000000000..ddda8df43b
--- /dev/null
+++ b/ext/hash/tests/hash_hkdf_error.phpt
@@ -0,0 +1,100 @@
+--TEST--
+Test hash_hkdf() function: error conditions
+--SKIPIF--
+<?php extension_loaded('hash') or die('skip: hash extension not loaded.'); ?>
+--FILE--
+<?php
+
+/* Prototype : string hkdf ( string $algo , string $ikm [, int $length , string $info = '' , string $salt = '' ] )
+ * Description: HMAC-based Key Derivation Function
+ * Source code: ext/hash/hash.c
+*/
+
+$ikm = 'input key material';
+
+echo "*** Testing hash_hkdf(): error conditions ***\n";
+
+echo "\n-- Testing hash_hkdf() function with less than expected no. of arguments --\n";
+var_dump(hash_hkdf());
+var_dump(hash_hkdf('sha1'));
+
+echo "\n-- Testing hash_hkdf() function with more than expected no. of arguments --\n";
+var_dump(hash_hkdf('sha1', $ikm, 20, '', '', 'extra parameter'));
+
+echo "\n-- Testing hash_hkdf() function with invalid hash algorithm --\n";
+var_dump(hash_hkdf('foo', $ikm));
+
+echo "\n-- Testing hash_hkdf() function with non-cryptographic hash algorithm --\n";
+var_dump(hash_hkdf('adler32', $ikm));
+var_dump(hash_hkdf('crc32', $ikm));
+var_dump(hash_hkdf('crc32b', $ikm));
+var_dump(hash_hkdf('fnv132', $ikm));
+var_dump(hash_hkdf('fnv1a32', $ikm));
+var_dump(hash_hkdf('fnv164', $ikm));
+var_dump(hash_hkdf('fnv1a64', $ikm));
+var_dump(hash_hkdf('joaat', $ikm));
+
+echo "\n-- Testing hash_hkdf() function with invalid parameters --\n";
+var_dump(hash_hkdf('sha1', ''));
+var_dump(hash_hkdf('sha1', $ikm, -1));
+var_dump(hash_hkdf('sha1', $ikm, 20 * 255 + 1)); // Length can't be more than 255 times the hash digest size
+?>
+===Done===
+--EXPECTF--
+*** Testing hash_hkdf(): error conditions ***
+
+-- Testing hash_hkdf() function with less than expected no. of arguments --
+
+Warning: hash_hkdf() expects at least 2 parameters, 0 given in %s on line %d
+NULL
+
+Warning: hash_hkdf() expects at least 2 parameters, 1 given in %s on line %d
+NULL
+
+-- Testing hash_hkdf() function with more than expected no. of arguments --
+
+Warning: hash_hkdf() expects at most 5 parameters, 6 given in %s on line %d
+NULL
+
+-- Testing hash_hkdf() function with invalid hash algorithm --
+
+Warning: hash_hkdf(): Unknown hashing algorithm: foo in %s on line %d
+bool(false)
+
+-- Testing hash_hkdf() function with non-cryptographic hash algorithm --
+
+Warning: hash_hkdf(): Non-cryptographic hashing algorithm: adler32 in %s on line %d
+bool(false)
+
+Warning: hash_hkdf(): Non-cryptographic hashing algorithm: crc32 in %s on line %d
+bool(false)
+
+Warning: hash_hkdf(): Non-cryptographic hashing algorithm: crc32b in %s on line %d
+bool(false)
+
+Warning: hash_hkdf(): Non-cryptographic hashing algorithm: fnv132 in %s on line %d
+bool(false)
+
+Warning: hash_hkdf(): Non-cryptographic hashing algorithm: fnv1a32 in %s on line %d
+bool(false)
+
+Warning: hash_hkdf(): Non-cryptographic hashing algorithm: fnv164 in %s on line %d
+bool(false)
+
+Warning: hash_hkdf(): Non-cryptographic hashing algorithm: fnv1a64 in %s on line %d
+bool(false)
+
+Warning: hash_hkdf(): Non-cryptographic hashing algorithm: joaat in %s on line %d
+bool(false)
+
+-- Testing hash_hkdf() function with invalid parameters --
+
+Warning: hash_hkdf(): Input keying material cannot be empty in %s on line %d
+bool(false)
+
+Warning: hash_hkdf(): Length must be greater than or equal to 0: -1 in %s on line %d
+bool(false)
+
+Warning: hash_hkdf(): Length must be less than or equal to 5100: 5101 in %s on line %d
+bool(false)
+===Done===
diff --git a/ext/hash/tests/hash_hkdf_rfc5869.phpt b/ext/hash/tests/hash_hkdf_rfc5869.phpt
new file mode 100644
index 0000000000..592d6aee9a
--- /dev/null
+++ b/ext/hash/tests/hash_hkdf_rfc5869.phpt
@@ -0,0 +1,80 @@
+--TEST--
+Test hash_hkdf() function: RFC 5869 test vectors
+--SKIPIF--
+<?php extension_loaded('hash') or die('skip: hash extension not loaded.'); ?>
+--FILE--
+<?php
+
+/* Prototype : string hkdf ( string $algo , string $ikm [, int $length , string $info = '' , string $salt = '' ] )
+ * Description: HMAC-based Key Derivation Function
+ * Source code: ext/hash/hash.c
+*/
+
+echo "*** Testing hash_hkdf(): RFC 5869 test vectors ***\n";
+echo "Test case 1 (SHA-256): ",
+ bin2hex(hash_hkdf(
+ 'sha256',
+ "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b",
+ 42,
+ "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9",
+ "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c"
+ )), "\n";
+echo "Test case 2 (SHA-256 with longer inputs/outputs): ",
+ bin2hex(hash_hkdf(
+ 'sha256',
+ "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f",
+ 82,
+ "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff",
+ "\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf"
+ )), "\n";
+echo "Test case 3 (SHA-256 with zero-length salt, info): ",
+ bin2hex(hash_hkdf(
+ 'sha256',
+ "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b",
+ 42,
+ '',
+ ''
+ )), "\n";
+echo "Test case 4 (SHA-1): ",
+ bin2hex(hash_hkdf(
+ 'sha1',
+ "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b",
+ 42,
+ "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9",
+ "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c"
+ )), "\n";
+echo "Test case 5 (SHA-1 with longer inputs/outputs): ",
+ bin2hex(hash_hkdf(
+ 'sha1',
+ "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f",
+ 82,
+ "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff",
+ "\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf"
+ )), "\n";
+echo "Test case 6 (SHA-1 with zero-length salt, info): ",
+ bin2hex(hash_hkdf(
+ 'sha1',
+ "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b",
+ 42,
+ '',
+ ''
+ )), "\n";
+echo "Test case 7 (SHA-1 with zero-length info, salt not provided): ",
+ bin2hex(hash_hkdf(
+ 'sha1',
+ "\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c",
+ 42,
+ ''
+ )), "\n";
+?>
+===Done===
+--EXPECTF--
+*** Testing hash_hkdf(): RFC 5869 test vectors ***
+Test case 1 (SHA-256): 3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865
+Test case 2 (SHA-256 with longer inputs/outputs): b11e398dc80327a1c8e7f78c596a49344f012eda2d4efad8a050cc4c19afa97c59045a99cac7827271cb41c65e590e09da3275600c2f09b8367793a9aca3db71cc30c58179ec3e87c14c01d5c1f3434f1d87
+Test case 3 (SHA-256 with zero-length salt, info): 8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d9d201395faa4b61a96c8
+Test case 4 (SHA-1): 085a01ea1b10f36933068b56efa5ad81a4f14b822f5b091568a9cdd4f155fda2c22e422478d305f3f896
+Test case 5 (SHA-1 with longer inputs/outputs): 0bd770a74d1160f7c9f12cd5912a06ebff6adcae899d92191fe4305673ba2ffe8fa3f1a4e5ad79f3f334b3b202b2173c486ea37ce3d397ed034c7f9dfeb15c5e927336d0441f4c4300e2cff0d0900b52d3b4
+Test case 6 (SHA-1 with zero-length salt, info): 0ac1af7002b3d761d1e55298da9d0506b9ae52057220a306e07b6b87e8df21d0ea00033de03984d34918
+Test case 7 (SHA-1 with zero-length info, salt not provided): 2c91117204d745f3500d636a62f64f0ab3bae548aa53d423b0d1f27ebba6f5e5673a081d70cce7acfc48
+===Done===
diff --git a/ext/hash/tests/sha3.phpt b/ext/hash/tests/sha3.phpt
new file mode 100644
index 0000000000..67fb22f988
--- /dev/null
+++ b/ext/hash/tests/sha3.phpt
@@ -0,0 +1,56 @@
+--TEST--
+sha3 algorithm
+--SKIPIF--
+<?php if(!extension_loaded("hash")) print "skip"; ?>
+--FILE--
+<?php
+
+// Test vectors taken from a combination of NIST FIPS-202,
+// Wikipedia reference vectors,
+// and output from reference implementation
+$subjects = [
+ '',
+ 'a',
+ 'The quick brown fox jumps over the lazy dog',
+ 'The quick brown fox jumps over the lazy dog.',
+ str_repeat('a', 257),
+ str_repeat("\xA3", 200),
+];
+foreach ($subjects as $subject) {
+ echo '== ', urlencode($subject), " ==\n";
+ foreach ([224, 256, 384, 512] as $bits) {
+ echo $bits, ': ', hash("sha3-$bits", $subject), "\n";
+ }
+}
+--EXPECT--
+== ==
+224: 6b4e03423667dbb73b6e15454f0eb1abd4597f9a1b078e3f5b5a6bc7
+256: a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a
+384: 0c63a75b845e4f7d01107d852e4c2485c51a50aaaa94fc61995e71bbee983a2ac3713831264adb47fb6bd1e058d5f004
+512: a69f73cca23a9ac5c8b567dc185a756e97c982164fe25859e0d1dcc1475c80a615b2123af1f5f94c11e3e9402c3ac558f500199d95b6d3e301758586281dcd26
+== a ==
+224: 9e86ff69557ca95f405f081269685b38e3a819b309ee942f482b6a8b
+256: 80084bf2fba02475726feb2cab2d8215eab14bc6bdd8bfb2c8151257032ecd8b
+384: 1815f774f320491b48569efec794d249eeb59aae46d22bf77dafe25c5edc28d7ea44f93ee1234aa88f61c91912a4ccd9
+512: 697f2d856172cb8309d6b8b97dac4de344b549d4dee61edfb4962d8698b7fa803f4f93ff24393586e28b5b957ac3d1d369420ce53332712f997bd336d09ab02a
+== The+quick+brown+fox+jumps+over+the+lazy+dog ==
+224: d15dadceaa4d5d7bb3b48f446421d542e08ad8887305e28d58335795
+256: 69070dda01975c8c120c3aada1b282394e7f032fa9cf32f4cb2259a0897dfc04
+384: 7063465e08a93bce31cd89d2e3ca8f602498696e253592ed26f07bf7e703cf328581e1471a7ba7ab119b1a9ebdf8be41
+512: 01dedd5de4ef14642445ba5f5b97c15e47b9ad931326e4b0727cd94cefc44fff23f07bf543139939b49128caf436dc1bdee54fcb24023a08d9403f9b4bf0d450
+== The+quick+brown+fox+jumps+over+the+lazy+dog. ==
+224: 2d0708903833afabdd232a20201176e8b58c5be8a6fe74265ac54db0
+256: a80f839cd4f83f6c3dafc87feae470045e4eb0d366397d5c6ce34ba1739f734d
+384: 1a34d81695b622df178bc74df7124fe12fac0f64ba5250b78b99c1273d4b080168e10652894ecad5f1f4d5b965437fb9
+512: 18f4f4bd419603f95538837003d9d254c26c23765565162247483f65c50303597bc9ce4d289f21d1c2f1f458828e33dc442100331b35e7eb031b5d38ba6460f8
+== aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ==
+224: 10dd422d71c42ee102a0c4bd398b5b85470341a0794702c954b022ba
+256: 6d115e8744deef792419e8bdb8567d74844e0fa5c2d5e474a19de87ac001449f
+384: c9df41fa389101cde63447257835464d89fd3974e5813f3f58d30e0296e89486e2d4bfc2b4089cd3bb860a20263322b8
+512: 5008048b64c14975181175f157be4a780c3d443d2177edf323d57884bc7e3979b9b53bca1325e880df3da0d97c435693441cb5527fbe950f5585678dfbb37785
+== %A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3 ==
+224: 9376816aba503f72f96ce7eb65ac095deee3be4bf9bbc2a1cb7e11e0
+256: 79f38adec5c20307a98ef76e8324afbfd46cfd81b22e3973c65fa1bd9de31787
+384: 1881de2ca7e41ef95dc4732b8f5f002b189cc1e42b74168ed1732649ce1dbcdd76197a31fd55ee989f2d7050dd473e8f
+512: e76dfad22084a8b1467fcf2ffa58361bec7628edf5f3fdc0e4805dc48caeeca81b7c13c30adf52a3659584739a2df46be589c51ca1a4a8416df6545a1ce8ba00
+
diff --git a/ext/hash/tests/sha512-224.phpt b/ext/hash/tests/sha512-224.phpt
new file mode 100644
index 0000000000..3769832ee6
--- /dev/null
+++ b/ext/hash/tests/sha512-224.phpt
@@ -0,0 +1,13 @@
+--TEST--
+sha512/224 algorithm
+--SKIPIF--
+<?php if(!extension_loaded("hash")) print "skip"; ?>
+--FILE--
+<?php
+echo hash('sha512/224', '') . "\n";
+echo hash('sha512/224', 'abc') . "\n";
+echo hash('sha512/224', 'abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu') . "\n";
+--EXPECT--
+6ed0dd02806fa89e25de060c19d3ac86cabb87d6a0ddd05c333b84f4
+4634270f707b6a54daae7530460842e20e37ed265ceee9a43e8924aa
+23fec5bb94d60b23308192640b0c453335d664734fe40e7268674af9
diff --git a/ext/hash/tests/sha512-256.phpt b/ext/hash/tests/sha512-256.phpt
new file mode 100644
index 0000000000..33ae5f1770
--- /dev/null
+++ b/ext/hash/tests/sha512-256.phpt
@@ -0,0 +1,13 @@
+--TEST--
+sha512/256 algorithm
+--SKIPIF--
+<?php if(!extension_loaded("hash")) print "skip"; ?>
+--FILE--
+<?php
+echo hash('sha512/256', '') . "\n";
+echo hash('sha512/256', 'abc') . "\n";
+echo hash('sha512/256', 'abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu') . "\n";
+--EXPECT--
+c672b8d1ef56ed28ab87c3622c5114069bdd3ad7b8f9737498d0c01ecef0967a
+53048e2681941ef99b2e29b76b4c7dabe4c2d0c634fc6d46e0e2f13107e7af23
+3928e184fb8690f840da3988121d31be65cb9d3ef83ee6146feac861e19b563a
diff --git a/ext/iconv/iconv.c b/ext/iconv/iconv.c
index 47aa983ab1..53bebcea2c 100644
--- a/ext/iconv/iconv.c
+++ b/ext/iconv/iconv.c
@@ -720,7 +720,6 @@ PHP_ICONV_API php_iconv_err_t php_iconv_string(const char *in_p, size_t in_len,
default:
/* other error */
- retval = PHP_ICONV_ERR_UNKNOWN;
zend_string_free(out_buf);
return PHP_ICONV_ERR_UNKNOWN;
}
@@ -2121,7 +2120,7 @@ PHP_FUNCTION(iconv_substr)
PHP_FUNCTION(iconv_strpos)
{
char *charset = get_internal_encoding();
- size_t charset_len = 0;
+ size_t charset_len = 0, haystk_len;
zend_string *haystk;
zend_string *ndl;
zend_long offset = 0;
@@ -2142,8 +2141,17 @@ PHP_FUNCTION(iconv_strpos)
}
if (offset < 0) {
- php_error_docref(NULL, E_WARNING, "Offset not contained in string.");
- RETURN_FALSE;
+ /* Convert negative offset (counted from the end of string) */
+ err = _php_iconv_strlen(&haystk_len, ZSTR_VAL(haystk), ZSTR_LEN(haystk), charset);
+ if (err != PHP_ICONV_ERR_SUCCESS) {
+ _php_iconv_show_error(err, GENERIC_SUPERSET_NAME, charset);
+ RETURN_FALSE;
+ }
+ offset += haystk_len;
+ if (offset < 0) { /* If offset before start */
+ php_error_docref(NULL, E_WARNING, "Offset not contained in string.");
+ RETURN_FALSE;
+ }
}
if (ZSTR_LEN(ndl) < 1) {
diff --git a/ext/iconv/tests/iconv_strpos.phpt b/ext/iconv/tests/iconv_strpos.phpt
index 6965f6fae1..28e3fe2320 100644
--- a/ext/iconv/tests/iconv_strpos.phpt
+++ b/ext/iconv/tests/iconv_strpos.phpt
@@ -24,6 +24,7 @@ function foo($haystk, $needle, $offset, $to_charset = false, $from_charset = fal
}
}
foo("abecdbcdabef", "bcd", -1);
+foo("abecdbcdabef", "bcd", -7);
foo("abecdbcdabef", "bcd", 100000);
foo("abcabcabcdabcababcdabc", "bcd", 0);
foo("abcabcabcdabcababcdabc", "bcd", 10);
@@ -37,10 +38,10 @@ var_dump(iconv_strpos("", "string"));
?>
--EXPECTF--
-2: %s
bool(false)
-2: %s
bool(false)
+int(5)
+int(5)
2: %s
bool(false)
bool(false)
diff --git a/ext/iconv/tests/iconv_strpos_variation3.phpt b/ext/iconv/tests/iconv_strpos_variation3.phpt
index aa9bc1ae1b..6f27b74a52 100644
--- a/ext/iconv/tests/iconv_strpos_variation3.phpt
+++ b/ext/iconv/tests/iconv_strpos_variation3.phpt
@@ -48,56 +48,58 @@ $fp = fopen(__FILE__, "r");
$inputs = array(
// int data
-/*1*/ 0,
+ 0,
1,
12345,
+ -5,
-2345,
// float data
-/*5*/ 10.5,
- -10.5,
+ 10.5,
+ -9.5,
+ -100.3,
12.3456789000e10,
12.3456789000E-10,
.5,
// null data
-/*10*/ NULL,
+ NULL,
null,
// boolean data
-/*12*/ true,
+ true,
false,
TRUE,
FALSE,
// empty data
-/*16*/ "",
+ "",
'',
// string data
-/*18*/ "string",
+ "string",
'string',
$heredoc,
// object data
-/*21*/ new classA(),
+ new classA(),
// undefined data
-/*22*/ @$undefined_var,
+ @$undefined_var,
// unset data
-/*23*/ @$unset_var,
+ @$unset_var,
// resource variable
-/*24*/ $fp
+ $fp
);
// loop through each element of $inputs to check the behavior of iconv_strpos()
-$iterator = 1;
+
foreach($inputs as $input) {
- echo "\n-- Iteration $iterator --\n";
+ echo "--\n";
+ var_dump($input);
var_dump( iconv_strpos($haystack, $needle, $input, $encoding));
- $iterator++;
};
fclose($fp);
@@ -106,96 +108,103 @@ echo "Done";
?>
--EXPECTF--
*** Testing iconv_strpos() : usage variations ***
-
--- Iteration 1 --
+--
+int(0)
int(8)
-
--- Iteration 2 --
+--
+int(1)
int(8)
-
--- Iteration 3 --
+--
+int(12345)
bool(false)
-
--- Iteration 4 --
+--
+int(-5)
+int(8)
+--
+int(-2345)
Warning: iconv_strpos(): Offset not contained in string. in %s on line %d
bool(false)
-
--- Iteration 5 --
+--
+float(10.5)
bool(false)
-
--- Iteration 6 --
+--
+float(-9.5)
+int(8)
+--
+float(-100.3)
Warning: iconv_strpos(): Offset not contained in string. in %s on line %d
bool(false)
-
--- Iteration 7 --
+--
+float(123456789000)
Warning: iconv_strpos() expects parameter 3 to be integer, float given in %s on line %d
bool(false)
-
--- Iteration 8 --
+--
+float(1.23456789E-9)
int(8)
-
--- Iteration 9 --
+--
+float(0.5)
int(8)
-
--- Iteration 10 --
+--
+NULL
int(8)
-
--- Iteration 11 --
+--
+NULL
int(8)
-
--- Iteration 12 --
+--
+bool(true)
int(8)
-
--- Iteration 13 --
+--
+bool(false)
int(8)
-
--- Iteration 14 --
+--
+bool(true)
int(8)
-
--- Iteration 15 --
+--
+bool(false)
int(8)
-
--- Iteration 16 --
+--
+string(0) ""
Warning: iconv_strpos() expects parameter 3 to be integer, string given in %s on line %d
bool(false)
-
--- Iteration 17 --
+--
+string(0) ""
Warning: iconv_strpos() expects parameter 3 to be integer, string given in %s on line %d
bool(false)
-
--- Iteration 18 --
+--
+string(6) "string"
Warning: iconv_strpos() expects parameter 3 to be integer, string given in %s on line %d
bool(false)
-
--- Iteration 19 --
+--
+string(6) "string"
Warning: iconv_strpos() expects parameter 3 to be integer, string given in %s on line %d
bool(false)
-
--- Iteration 20 --
+--
+string(11) "hello world"
Warning: iconv_strpos() expects parameter 3 to be integer, string given in %s on line %d
bool(false)
-
--- Iteration 21 --
+--
+object(classA)#%d (%d) {
+}
Warning: iconv_strpos() expects parameter 3 to be integer, object given in %s on line %d
bool(false)
-
--- Iteration 22 --
+--
+NULL
int(8)
-
--- Iteration 23 --
+--
+NULL
int(8)
-
--- Iteration 24 --
+--
+resource(%d) of type (stream)
Warning: iconv_strpos() expects parameter 3 to be integer, resource given in %s on line %d
bool(false)
-Done \ No newline at end of file
+Done
diff --git a/ext/iconv/tests/iconv_strpos_variation3_64bit.phpt b/ext/iconv/tests/iconv_strpos_variation3_64bit.phpt
index d915339275..2704493235 100644
--- a/ext/iconv/tests/iconv_strpos_variation3_64bit.phpt
+++ b/ext/iconv/tests/iconv_strpos_variation3_64bit.phpt
@@ -48,56 +48,58 @@ $fp = fopen(__FILE__, "r");
$inputs = array(
// int data
-/*1*/ 0,
+ 0,
1,
12345,
+ -5,
-2345,
// float data
-/*5*/ 10.5,
- -10.5,
+ 10.5,
+ -9.5,
+ -100.3,
12.3456789000e10,
12.3456789000E-10,
.5,
// null data
-/*10*/ NULL,
+ NULL,
null,
// boolean data
-/*12*/ true,
+ true,
false,
TRUE,
FALSE,
// empty data
-/*16*/ "",
+ "",
'',
// string data
-/*18*/ "string",
+ "string",
'string',
$heredoc,
// object data
-/*21*/ new classA(),
+ new classA(),
// undefined data
-/*22*/ @$undefined_var,
+ @$undefined_var,
// unset data
-/*23*/ @$unset_var,
+ @$unset_var,
// resource variable
-/*24*/ $fp
+ $fp
);
// loop through each element of $inputs to check the behavior of iconv_strpos()
-$iterator = 1;
+
foreach($inputs as $input) {
- echo "\n-- Iteration $iterator --\n";
+ echo "--\n";
+ var_dump($input);
var_dump( iconv_strpos($haystack, $needle, $input, $encoding));
- $iterator++;
};
fclose($fp);
@@ -106,93 +108,100 @@ echo "Done";
?>
--EXPECTF--
*** Testing iconv_strpos() : usage variations ***
-
--- Iteration 1 --
+--
+int(0)
int(8)
-
--- Iteration 2 --
+--
+int(1)
int(8)
-
--- Iteration 3 --
+--
+int(12345)
bool(false)
-
--- Iteration 4 --
+--
+int(-5)
+int(8)
+--
+int(-2345)
Warning: iconv_strpos(): Offset not contained in string. in %s on line %d
bool(false)
-
--- Iteration 5 --
+--
+float(10.5)
bool(false)
-
--- Iteration 6 --
+--
+float(-9.5)
+int(8)
+--
+float(-100.3)
Warning: iconv_strpos(): Offset not contained in string. in %s on line %d
bool(false)
-
--- Iteration 7 --
+--
+float(123456789000)
bool(false)
-
--- Iteration 8 --
+--
+float(1.23456789E-9)
int(8)
-
--- Iteration 9 --
+--
+float(0.5)
int(8)
-
--- Iteration 10 --
+--
+NULL
int(8)
-
--- Iteration 11 --
+--
+NULL
int(8)
-
--- Iteration 12 --
+--
+bool(true)
int(8)
-
--- Iteration 13 --
+--
+bool(false)
int(8)
-
--- Iteration 14 --
+--
+bool(true)
int(8)
-
--- Iteration 15 --
+--
+bool(false)
int(8)
-
--- Iteration 16 --
+--
+string(0) ""
Warning: iconv_strpos() expects parameter 3 to be integer, string given in %s on line %d
bool(false)
-
--- Iteration 17 --
+--
+string(0) ""
Warning: iconv_strpos() expects parameter 3 to be integer, string given in %s on line %d
bool(false)
-
--- Iteration 18 --
+--
+string(6) "string"
Warning: iconv_strpos() expects parameter 3 to be integer, string given in %s on line %d
bool(false)
-
--- Iteration 19 --
+--
+string(6) "string"
Warning: iconv_strpos() expects parameter 3 to be integer, string given in %s on line %d
bool(false)
-
--- Iteration 20 --
+--
+string(11) "hello world"
Warning: iconv_strpos() expects parameter 3 to be integer, string given in %s on line %d
bool(false)
-
--- Iteration 21 --
+--
+object(classA)#%d (%d) {
+}
Warning: iconv_strpos() expects parameter 3 to be integer, object given in %s on line %d
bool(false)
-
--- Iteration 22 --
+--
+NULL
int(8)
-
--- Iteration 23 --
+--
+NULL
int(8)
-
--- Iteration 24 --
+--
+resource(%d) of type (stream)
Warning: iconv_strpos() expects parameter 3 to be integer, resource given in %s on line %d
bool(false)
diff --git a/ext/iconv/tests/iconv_strpos_variation5.phpt b/ext/iconv/tests/iconv_strpos_variation5.phpt
index 3db0634215..fcd5aaecae 100644
--- a/ext/iconv/tests/iconv_strpos_variation5.phpt
+++ b/ext/iconv/tests/iconv_strpos_variation5.phpt
@@ -32,10 +32,9 @@ $needle_mb = base64_decode(b'44CC');
/*
* Loop through integers as multiples of ten for $offset argument
- * iconv_strpos should not be able to accept negative values as $offset.
* 60 is larger than *BYTE* count for $string_mb
*/
-for ($i = -10; $i <= 60; $i += 10) {
+for ($i = -30; $i <= 60; $i += 10) {
echo "\n**-- Offset is: $i --**\n";
echo "-- ASCII String --\n";
var_dump(iconv_strpos($string_ascii, $needle_ascii, $i));
@@ -49,7 +48,7 @@ echo "Done";
--EXPECTF--
*** Testing iconv_strpos() : usage variations ***
-**-- Offset is: -10 --**
+**-- Offset is: -30 --**
-- ASCII String --
Warning: iconv_strpos(): Offset not contained in string. in %s on line %d
@@ -59,6 +58,18 @@ bool(false)
Warning: iconv_strpos(): Offset not contained in string. in %s on line %d
bool(false)
+**-- Offset is: -20 --**
+-- ASCII String --
+int(9)
+--Multibyte String --
+int(9)
+
+**-- Offset is: -10 --**
+-- ASCII String --
+int(20)
+--Multibyte String --
+int(20)
+
**-- Offset is: 0 --**
-- ASCII String --
int(9)
diff --git a/ext/imap/php_imap.c b/ext/imap/php_imap.c
index 9799112e01..0f6ac9a2d0 100644
--- a/ext/imap/php_imap.c
+++ b/ext/imap/php_imap.c
@@ -2912,7 +2912,7 @@ PHP_FUNCTION(imap_utf7_decode)
#if PHP_DEBUG
/* warn if we computed outlen incorrectly */
if (outp - out != outlen) {
- php_error_docref(NULL, E_WARNING, "outp - out [%ld] != outlen [%d]", outp - out, outlen);
+ php_error_docref(NULL, E_WARNING, "outp - out [%zd] != outlen [%d]", outp - out, outlen);
}
#endif
@@ -4446,7 +4446,7 @@ static zend_string* _php_rfc822_write_address(ADDRESS *addresslist)
char address[SENDBUFLEN];
if (_php_imap_address_size(addresslist) >= SENDBUFLEN) {
- php_error_docref(NULL, E_ERROR, "Address buffer overflow");
+ zend_throw_error(NULL, "Address buffer overflow");
return NULL;
}
address[0] = 0;
diff --git a/ext/interbase/config.m4 b/ext/interbase/config.m4
index ace3047e65..5b4cde5852 100644
--- a/ext/interbase/config.m4
+++ b/ext/interbase/config.m4
@@ -3,39 +3,54 @@ PHP_ARG_WITH(interbase,for Firebird support,
install directory [/opt/firebird]])
if test "$PHP_INTERBASE" != "no"; then
- if test "$PHP_INTERBASE" = "yes"; then
- IBASE_INCDIR=/opt/firebird/include
- IBASE_LIBDIR=/opt/firebird/lib
+
+ AC_PATH_PROG(FB_CONFIG, fb_config, no)
+
+ if test -x "$FB_CONFIG" && test "$PHP_INTERBASE" = "yes"; then
+ AC_MSG_CHECKING(for libfbconfig)
+ FB_CFLAGS=`$FB_CONFIG --cflags`
+ FB_LIBDIR=`$FB_CONFIG --libs`
+ FB_VERSION=`$FB_CONFIG --version`
+ AC_MSG_RESULT(version $FB_VERSION)
+ PHP_EVAL_LIBLINE($FB_LIBDIR, INTERBASE_SHARED_LIBADD)
+ PHP_EVAL_INCLINE($FB_CFLAGS)
+
else
- IBASE_INCDIR=$PHP_INTERBASE/include
- IBASE_LIBDIR=$PHP_INTERBASE/$PHP_LIBDIR
- fi
+ if test "$PHP_INTERBASE" = "yes"; then
+ IBASE_INCDIR=/opt/firebird/include
+ IBASE_LIBDIR=/opt/firebird/lib
+ else
+ IBASE_INCDIR=$PHP_INTERBASE/include
+ IBASE_LIBDIR=$PHP_INTERBASE/$PHP_LIBDIR
+ fi
- PHP_CHECK_LIBRARY(fbclient, isc_detach_database,
- [
- IBASE_LIBNAME=fbclient
- ], [
- PHP_CHECK_LIBRARY(gds, isc_detach_database,
+ PHP_CHECK_LIBRARY(fbclient, isc_detach_database,
[
- IBASE_LIBNAME=gds
+ IBASE_LIBNAME=fbclient
], [
- PHP_CHECK_LIBRARY(ib_util, isc_detach_database,
+ PHP_CHECK_LIBRARY(gds, isc_detach_database,
[
- IBASE_LIBNAME=ib_util
+ IBASE_LIBNAME=gds
], [
- AC_MSG_ERROR([libfbclient, libgds or libib_util not found! Check config.log for more information.])
+ PHP_CHECK_LIBRARY(ib_util, isc_detach_database,
+ [
+ IBASE_LIBNAME=ib_util
+ ], [
+ AC_MSG_ERROR([libfbclient, libgds or libib_util not found! Check config.log for more information.])
+ ], [
+ -L$IBASE_LIBDIR
+ ])
], [
-L$IBASE_LIBDIR
])
], [
-L$IBASE_LIBDIR
])
- ], [
- -L$IBASE_LIBDIR
- ])
- PHP_ADD_LIBRARY_WITH_PATH($IBASE_LIBNAME, $IBASE_LIBDIR, INTERBASE_SHARED_LIBADD)
- PHP_ADD_INCLUDE($IBASE_INCDIR)
+ PHP_ADD_LIBRARY_WITH_PATH($IBASE_LIBNAME, $IBASE_LIBDIR, INTERBASE_SHARED_LIBADD)
+ PHP_ADD_INCLUDE($IBASE_INCDIR)
+ fi
+
AC_DEFINE(HAVE_IBASE,1,[ ])
PHP_NEW_EXTENSION(interbase, interbase.c ibase_query.c ibase_service.c ibase_events.c ibase_blobs.c, $ext_shared,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1)
PHP_SUBST(INTERBASE_SHARED_LIBADD)
diff --git a/ext/interbase/ibase_service.c b/ext/interbase/ibase_service.c
index d8c638607b..0dbe1a07f3 100644
--- a/ext/interbase/ibase_service.c
+++ b/ext/interbase/ibase_service.c
@@ -226,7 +226,7 @@ PHP_FUNCTION(ibase_service_attach)
user, isc_spb_password, (char)plen, pass, host);
if (spb_len > sizeof(buf) || spb_len == -1) {
- _php_ibase_module_error("Internal error: insufficient buffer space for SPB (%d)", spb_len);
+ _php_ibase_module_error("Internal error: insufficient buffer space for SPB (%zd)", spb_len);
RETURN_FALSE;
}
@@ -450,7 +450,7 @@ static void _php_ibase_backup_restore(INTERNAL_FUNCTION_PARAMETERS, char operati
}
if (spb_len > sizeof(buf) || spb_len <= 0) {
- _php_ibase_module_error("Internal error: insufficient buffer space for SPB (%d)", spb_len);
+ _php_ibase_module_error("Internal error: insufficient buffer space for SPB (%zd)", spb_len);
RETURN_FALSE;
}
diff --git a/ext/interbase/php_ibase_includes.h b/ext/interbase/php_ibase_includes.h
index 7170f00ae2..26b0bc641a 100644
--- a/ext/interbase/php_ibase_includes.h
+++ b/ext/interbase/php_ibase_includes.h
@@ -151,7 +151,7 @@ typedef void (*info_func_t)(char*);
void _php_ibase_error(void);
void _php_ibase_module_error(char *, ...)
- PHP_ATTRIBUTE_FORMAT(printf,1,PHP_ATTR_FMT_OFFSET +2);
+ PHP_ATTRIBUTE_FORMAT(printf,1,2);
/* determine if a resource is a link or transaction handle */
#define PHP_IBASE_LINK_TRANS(zv, lh, th) \
diff --git a/ext/interbase/tests/interbase.inc b/ext/interbase/tests/interbase.inc
index fcf053213d..7e58ecca74 100644
--- a/ext/interbase/tests/interbase.inc
+++ b/ext/interbase/tests/interbase.inc
@@ -8,7 +8,7 @@ ini_set('ibase.default_user',$user);
ini_set('ibase.default_password',$password);
/* we need just the generated name, not the file itself */
-unlink($test_base = tempnam('/tmp',"php_ibase_test"));
+unlink($test_base = tempnam(sys_get_temp_dir(),"php_ibase_test"));
function init_db()
{
diff --git a/ext/intl/ERROR.CONVENTIONS b/ext/intl/ERROR.CONVENTIONS
index e053c8fbc7..a7ef53665e 100644
--- a/ext/intl/ERROR.CONVENTIONS
+++ b/ext/intl/ERROR.CONVENTIONS
@@ -80,7 +80,7 @@ ICU operates, where functions return immediately if an error is set.
Error resetting can be done with:
void intl_error_reset(NULL); /* reset global error */
-void intl_errors_reset(intl_error* err ); /* reset global and object error */
+void intl_errors_reset(intl_error* err); /* reset global and object error */
In practice, intl_errors_reset() is not used because most classes have also
plain functions mapped to the same internal functions as their instance methods.
diff --git a/ext/intl/breakiterator/breakiterator_class.cpp b/ext/intl/breakiterator/breakiterator_class.cpp
index db99ad09cf..ae9e258608 100644
--- a/ext/intl/breakiterator/breakiterator_class.cpp
+++ b/ext/intl/breakiterator/breakiterator_class.cpp
@@ -184,13 +184,6 @@ static void breakiterator_object_init(BreakIterator_object *bio)
}
/* }}} */
-/* {{{ BreakIterator_objects_dtor */
-static void BreakIterator_objects_dtor(zend_object *object)
-{
- zend_objects_destroy_object(object);
-}
-/* }}} */
-
/* {{{ BreakIterator_objects_free */
static void BreakIterator_objects_free(zend_object *object)
{
@@ -332,7 +325,6 @@ U_CFUNC void breakiterator_register_BreakIterator_class(void)
BreakIterator_handlers.compare_objects = BreakIterator_compare_objects;
BreakIterator_handlers.clone_obj = BreakIterator_clone_obj;
BreakIterator_handlers.get_debug_info = BreakIterator_get_debug_info;
- BreakIterator_handlers.dtor_obj = BreakIterator_objects_dtor;
BreakIterator_handlers.free_obj = BreakIterator_objects_free;
zend_class_implements(BreakIterator_ce_ptr, 1,
diff --git a/ext/intl/calendar/calendar_class.cpp b/ext/intl/calendar/calendar_class.cpp
index 4414a7a092..a2024c0712 100644
--- a/ext/intl/calendar/calendar_class.cpp
+++ b/ext/intl/calendar/calendar_class.cpp
@@ -231,13 +231,6 @@ static void calendar_object_init(Calendar_object *co)
}
/* }}} */
-/* {{{ Calendar_objects_dtor */
-static void Calendar_objects_dtor(zend_object *object)
-{
- zend_objects_destroy_object(object);
-}
-/* }}} */
-
/* {{{ Calendar_objects_free */
static void Calendar_objects_free(zend_object *object)
{
@@ -474,7 +467,6 @@ void calendar_register_IntlCalendar_class(void)
Calendar_handlers.offset = XtOffsetOf(Calendar_object, zo);
Calendar_handlers.clone_obj = Calendar_clone_obj;
Calendar_handlers.get_debug_info = Calendar_get_debug_info;
- Calendar_handlers.dtor_obj = Calendar_objects_dtor;
Calendar_handlers.free_obj = Calendar_objects_free;
/* Create and register 'IntlGregorianCalendar' class. */
@@ -505,8 +497,6 @@ void calendar_register_IntlCalendar_class(void)
CALENDAR_DECL_LONG_CONST("FIELD_AM_PM", UCAL_AM_PM);
CALENDAR_DECL_LONG_CONST("FIELD_HOUR", UCAL_HOUR);
CALENDAR_DECL_LONG_CONST("FIELD_HOUR_OF_DAY", UCAL_HOUR_OF_DAY);
- CALENDAR_DECL_LONG_CONST("FIELD_HOUR", UCAL_HOUR);
- CALENDAR_DECL_LONG_CONST("FIELD_HOUR_OF_DAY", UCAL_HOUR_OF_DAY);
CALENDAR_DECL_LONG_CONST("FIELD_MINUTE", UCAL_MINUTE);
CALENDAR_DECL_LONG_CONST("FIELD_SECOND", UCAL_SECOND);
CALENDAR_DECL_LONG_CONST("FIELD_MILLISECOND", UCAL_MILLISECOND);
diff --git a/ext/intl/calendar/calendar_methods.cpp b/ext/intl/calendar/calendar_methods.cpp
index 155ed5f7f4..590917d272 100644
--- a/ext/intl/calendar/calendar_methods.cpp
+++ b/ext/intl/calendar/calendar_methods.cpp
@@ -118,7 +118,7 @@ public:
}
if (resultLength) {
//the bug is that uenum_next doesn't set the length
- *resultLength = (length == -1) ? strlen(str) : length;
+ *resultLength = (length == -1) ? (int32_t)strlen(str) : length;
}
return str;
diff --git a/ext/intl/collator/collator_class.c b/ext/intl/collator/collator_class.c
index 26917fd5d7..d77a3432b8 100644
--- a/ext/intl/collator/collator_class.c
+++ b/ext/intl/collator/collator_class.c
@@ -35,13 +35,6 @@ static zend_object_handlers Collator_handlers;
* Auxiliary functions needed by objects of 'Collator' class
*/
-/* {{{ Collator_objects_dtor */
-static void Collator_objects_dtor(zend_object *object )
-{
- zend_objects_destroy_object(object );
-}
-/* }}} */
-
/* {{{ Collator_objects_free */
void Collator_objects_free(zend_object *object )
{
@@ -142,7 +135,6 @@ void collator_register_Collator_class( void )
for which we don't have the place to keep */
Collator_handlers.offset = XtOffsetOf(Collator_object, zo);
Collator_handlers.clone_obj = NULL;
- Collator_handlers.dtor_obj = Collator_objects_dtor;
Collator_handlers.free_obj = Collator_objects_free;
/* Declare 'Collator' class properties. */
diff --git a/ext/intl/collator/collator_compare.c b/ext/intl/collator/collator_compare.c
index a7bc7f6383..983b9d7f2c 100644
--- a/ext/intl/collator/collator_compare.c
+++ b/ext/intl/collator/collator_compare.c
@@ -62,7 +62,7 @@ PHP_FUNCTION( collator_compare )
intl_error_set_code( NULL, COLLATOR_ERROR_CODE( co ) );
intl_errors_set_custom_msg( COLLATOR_ERROR_P( co ),
"Object not initialized", 0 );
- php_error_docref(NULL, E_RECOVERABLE_ERROR, "Object not initialized");
+ zend_throw_error(NULL, "Object not initialized");
RETURN_FALSE;
}
diff --git a/ext/intl/collator/collator_convert.c b/ext/intl/collator/collator_convert.c
index 8f06c8f1ca..2d431a19d6 100644
--- a/ext/intl/collator/collator_convert.c
+++ b/ext/intl/collator/collator_convert.c
@@ -28,12 +28,6 @@
#include <unicode/ustring.h>
#include <php.h>
-#if PHP_VERSION_ID <= 50100
-#define CAST_OBJECT_SHOULD_FREE ,0
-#else
-#define CAST_OBJECT_SHOULD_FREE
-#endif
-
#define COLLATOR_CONVERT_RETURN_FAILED(retval) { \
Z_TRY_ADDREF_P(retval); \
return retval; \
@@ -258,7 +252,7 @@ zval* collator_convert_object_to_string( zval* obj, zval *rv )
{
zstr = rv;
- if( Z_OBJ_HT_P(obj)->cast_object( obj, zstr, IS_STRING CAST_OBJECT_SHOULD_FREE ) == FAILURE )
+ if( Z_OBJ_HT_P(obj)->cast_object( obj, zstr, IS_STRING ) == FAILURE )
{
/* cast_object failed => bail out. */
zval_ptr_dtor( zstr );
diff --git a/ext/intl/collator/collator_is_numeric.c b/ext/intl/collator/collator_is_numeric.c
index 6b0568dd64..e3535e7d4b 100644
--- a/ext/intl/collator/collator_is_numeric.c
+++ b/ext/intl/collator/collator_is_numeric.c
@@ -17,17 +17,6 @@
#include "collator_is_numeric.h"
-#if ZEND_MODULE_API_NO < 20071006
-/* not 5.3 */
-#ifndef ALLOCA_FLAG
-#define ALLOCA_FLAG(use_heap)
-#endif
-#define _do_alloca(x, y) do_alloca((x))
-#define _free_alloca(x, y) free_alloca((x))
-#else
-#define _do_alloca do_alloca
-#define _free_alloca free_alloca
-#endif
/* {{{ collator_u_strtod
* Taken from PHP6:zend_u_strtod()
*/
@@ -81,13 +70,13 @@ static double collator_u_strtod(const UChar *nptr, UChar **endptr) /* {{{ */
if (any) {
char buf[64], *numbuf, *bufpos;
- int length = u - nstart;
+ size_t length = u - nstart;
double value;
if (length < sizeof(buf)) {
numbuf = buf;
} else {
- numbuf = (char *) _do_alloca(length + 1, use_heap);
+ numbuf = (char *) do_alloca(length + 1, use_heap);
}
bufpos = numbuf;
@@ -100,7 +89,7 @@ static double collator_u_strtod(const UChar *nptr, UChar **endptr) /* {{{ */
value = zend_strtod(numbuf, NULL);
if (numbuf != buf) {
- _free_alloca(numbuf, use_heap);
+ free_alloca(numbuf, use_heap);
}
if (endptr != NULL) {
diff --git a/ext/intl/collator/collator_locale.c b/ext/intl/collator/collator_locale.c
index 8e0b32650a..b3ea572be1 100644
--- a/ext/intl/collator/collator_locale.c
+++ b/ext/intl/collator/collator_locale.c
@@ -55,7 +55,7 @@ PHP_FUNCTION( collator_get_locale )
intl_error_set_code( NULL, COLLATOR_ERROR_CODE( co ) );
intl_errors_set_custom_msg( COLLATOR_ERROR_P( co ),
"Object not initialized", 0 );
- php_error_docref(NULL, E_RECOVERABLE_ERROR, "Object not initialized");
+ zend_throw_error(NULL, "Object not initialized");
RETURN_FALSE;
}
diff --git a/ext/intl/collator/collator_sort.c b/ext/intl/collator/collator_sort.c
index 1ad42d3660..4fc5f6a8e8 100644
--- a/ext/intl/collator/collator_sort.c
+++ b/ext/intl/collator/collator_sort.c
@@ -75,8 +75,9 @@ static int collator_regular_compare_function(zval *result, zval *op1, zval *op2)
intl_error_set_code( NULL, COLLATOR_ERROR_CODE( co ) );
intl_errors_set_custom_msg( COLLATOR_ERROR_P( co ),
"Object not initialized", 0 );
- php_error_docref(NULL, E_RECOVERABLE_ERROR, "Object not initialized");
-
+ zend_throw_error(NULL, "Object not initialized");
+ rc = FAILURE;
+ goto cleanup;
}
/* Compare the strings using ICU. */
@@ -126,6 +127,7 @@ static int collator_regular_compare_function(zval *result, zval *op1, zval *op2)
zval_ptr_dtor( norm2_p );
}
+cleanup:
if( num1_p )
zval_ptr_dtor( num1_p );
@@ -370,7 +372,7 @@ PHP_FUNCTION( collator_sort_with_sort_keys )
char* sortKeyBuf = NULL; /* buffer to store sort keys */
uint32_t sortKeyBufSize = DEF_SORT_KEYS_BUF_SIZE; /* buffer size */
ptrdiff_t sortKeyBufOffset = 0; /* pos in buffer to store sort key */
- int32_t sortKeyLen = 0; /* the length of currently processing key */
+ uint32_t sortKeyLen = 0; /* the length of currently processing key */
uint32_t bufLeft = 0;
uint32_t bufIncrement = 0;
@@ -404,7 +406,7 @@ PHP_FUNCTION( collator_sort_with_sort_keys )
intl_error_set_code( NULL, COLLATOR_ERROR_CODE( co ) );
intl_errors_set_custom_msg( COLLATOR_ERROR_P( co ),
"Object not initialized", 0 );
- php_error_docref(NULL, E_RECOVERABLE_ERROR, "Object not initialized");
+ zend_throw_error(NULL, "Object not initialized");
RETURN_FALSE;
}
@@ -570,7 +572,7 @@ PHP_FUNCTION( collator_get_sort_key )
intl_error_set_code( NULL, COLLATOR_ERROR_CODE( co ) );
intl_errors_set_custom_msg( COLLATOR_ERROR_P( co ),
"Object not initialized", 0 );
- php_error_docref(NULL, E_RECOVERABLE_ERROR, "Object not initialized");
+ zend_throw_error(NULL, "Object not initialized");
RETURN_FALSE;
}
diff --git a/ext/intl/common/common_date.cpp b/ext/intl/common/common_date.cpp
index f1bf75ab0f..26c198bc42 100644
--- a/ext/intl/common/common_date.cpp
+++ b/ext/intl/common/common_date.cpp
@@ -125,6 +125,8 @@ U_CFUNC int intl_datetime_decompose(zval *z, double *millis, TimeZone **tz,
}
if (millis) {
+ php_date_obj *datetime;
+
ZVAL_STRING(&zfuncname, "getTimestamp");
if (call_user_function(NULL, z, &zfuncname, &retval, 0, NULL)
!= SUCCESS || Z_TYPE(retval) != IS_LONG) {
@@ -137,7 +139,8 @@ U_CFUNC int intl_datetime_decompose(zval *z, double *millis, TimeZone **tz,
return FAILURE;
}
- *millis = U_MILLIS_PER_SECOND * (double)Z_LVAL(retval);
+ datetime = Z_PHPDATE_P(z);
+ *millis = U_MILLIS_PER_SECOND * ((double)Z_LVAL(retval) + datetime->time->f);
zval_ptr_dtor(&zfuncname);
}
@@ -145,8 +148,8 @@ U_CFUNC int intl_datetime_decompose(zval *z, double *millis, TimeZone **tz,
php_date_obj *datetime;
datetime = Z_PHPDATE_P(z);
if (!datetime->time) {
- spprintf(&message, 0, "%s: the DateTime object is not properly "
- "initialized", func);
+ spprintf(&message, 0, "%s: the %s object is not properly "
+ "initialized", func, ZSTR_VAL(Z_OBJCE_P(z)->name));
intl_errors_set(err, U_ILLEGAL_ARGUMENT_ERROR,
message, 1);
efree(message);
@@ -205,7 +208,7 @@ U_CFUNC double intl_zval_to_millis(zval *z, intl_error *err, const char *func)
rv = U_MILLIS_PER_SECOND * Z_DVAL_P(z);
break;
case IS_OBJECT:
- if (instanceof_function(Z_OBJCE_P(z), php_date_get_date_ce())) {
+ if (instanceof_function(Z_OBJCE_P(z), php_date_get_interface_ce())) {
intl_datetime_decompose(z, &rv, NULL, err, func);
} else if (instanceof_function(Z_OBJCE_P(z), Calendar_ce_ptr)) {
Calendar_object *co = Z_INTL_CALENDAR_P(z);
@@ -228,7 +231,7 @@ U_CFUNC double intl_zval_to_millis(zval *z, intl_error *err, const char *func)
} else {
/* TODO: try with cast(), get() to obtain a number */
spprintf(&message, 0, "%s: invalid object type for date/time "
- "(only IntlCalendar and DateTime permitted)", func);
+ "(only IntlCalendar and DateTimeInterface permitted)", func);
intl_errors_set(err, U_ILLEGAL_ARGUMENT_ERROR,
message, 1);
efree(message);
diff --git a/ext/intl/converter/converter.c b/ext/intl/converter/converter.c
index 5653b46365..ede61fc80a 100644
--- a/ext/intl/converter/converter.c
+++ b/ext/intl/converter/converter.c
@@ -152,7 +152,7 @@ static PHP_METHOD(UConverter, fromUCallback) {
/* {{{ php_converter_check_limits */
static inline zend_bool php_converter_check_limits(php_converter_object *objval, zend_long available, zend_long needed) {
if (available < needed) {
- php_converter_throw_failure(objval, U_BUFFER_OVERFLOW_ERROR, "Buffer overrun %pd bytes needed, %pd available", needed, available);
+ php_converter_throw_failure(objval, U_BUFFER_OVERFLOW_ERROR, "Buffer overrun " ZEND_LONG_FMT " bytes needed, " ZEND_LONG_FMT " available", needed, available);
return 0;
}
return 1;
@@ -732,7 +732,7 @@ static PHP_METHOD(UConverter, reasonText) {
UCNV_REASON_CASE(CLOSE)
UCNV_REASON_CASE(CLONE)
default:
- php_error_docref(NULL, E_WARNING, "Unknown UConverterCallbackReason: %pd", reason);
+ php_error_docref(NULL, E_WARNING, "Unknown UConverterCallbackReason: " ZEND_LONG_FMT, reason);
RETURN_FALSE;
}
}
@@ -997,7 +997,7 @@ static zend_function_entry php_converter_methods[] = {
PHP_ME(UConverter, getAvailable, php_converter_getavailable_arginfo, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
PHP_ME(UConverter, getAliases, php_converter_getaliases_arginfo, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
PHP_ME(UConverter, getStandards, php_converter_getstandards_arginfo, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
- { NULL, NULL, NULL }
+ PHP_FE_END
};
/* {{{ Converter create/clone/destroy */
diff --git a/ext/intl/dateformat/dateformat_attr.c b/ext/intl/dateformat/dateformat_attr.c
index dffb1b4523..d44b3d1789 100644
--- a/ext/intl/dateformat/dateformat_attr.c
+++ b/ext/intl/dateformat/dateformat_attr.c
@@ -86,7 +86,7 @@ PHP_FUNCTION( datefmt_get_timetype )
PHP_FUNCTION( datefmt_get_pattern )
{
UChar value_buf[64];
- int length = USIZE( value_buf );
+ uint32_t length = USIZE( value_buf );
UChar* value = value_buf;
zend_bool is_pattern_localized =FALSE;
diff --git a/ext/intl/dateformat/dateformat_format.c b/ext/intl/dateformat/dateformat_format.c
index dda1dd4fd7..2d1e78b512 100644
--- a/ext/intl/dateformat/dateformat_format.c
+++ b/ext/intl/dateformat/dateformat_format.c
@@ -18,10 +18,11 @@
#include "config.h"
#endif
+#include "../php_intl.h"
+
#include <unicode/ustring.h>
#include <unicode/ucal.h>
-#include "../php_intl.h"
#include "../intl_convert.h"
#include "../common/common_date.h"
#include "dateformat.h"
@@ -79,7 +80,7 @@ static int32_t internal_get_arr_ele(IntlDateFormatter_object *dfo,
} else {
if (Z_LVAL_P(ele_value) > INT32_MAX ||
Z_LVAL_P(ele_value) < INT32_MIN) {
- spprintf(&message, 0, "datefmt_format: value %pd is out of "
+ spprintf(&message, 0, "datefmt_format: value " ZEND_LONG_FMT " is out of "
"bounds for a 32-bit integer in key '%s'",
Z_LVAL_P(ele_value), key_name);
intl_errors_set(err, U_ILLEGAL_ARGUMENT_ERROR, message, 1);
diff --git a/ext/intl/dateformat/dateformat_format_object.cpp b/ext/intl/dateformat/dateformat_format_object.cpp
index 3be76332a8..e96ebe8243 100644
--- a/ext/intl/dateformat/dateformat_format_object.cpp
+++ b/ext/intl/dateformat/dateformat_format_object.cpp
@@ -146,7 +146,9 @@ U_CFUNC PHP_FUNCTION(datefmt_format_object)
}
//there's no support for relative time in ICU yet
- timeStyle = (DateFormat::EStyle)(timeStyle & ~DateFormat::kRelative);
+ if (timeStyle != DateFormat::NONE) {
+ timeStyle = (DateFormat::EStyle)(timeStyle & ~DateFormat::kRelative);
+ }
zend_class_entry *instance_ce = Z_OBJCE_P(object);
if (instanceof_function(instance_ce, Calendar_ce_ptr)) {
@@ -188,11 +190,11 @@ U_CFUNC PHP_FUNCTION(datefmt_format_object)
}
if (pattern) {
- df = new SimpleDateFormat(
- UnicodeString(Z_STRVAL_P(format), Z_STRLEN_P(format),
- UnicodeString::kInvariant),
- Locale::createFromName(locale_str),
- status);
+ StringPiece sp(Z_STRVAL_P(format));
+ df = new SimpleDateFormat(
+ UnicodeString::fromUTF8(sp),
+ Locale::createFromName(locale_str),
+ status);
if (U_FAILURE(status)) {
intl_error_set(NULL, status,
diff --git a/ext/intl/dateformat/dateformat_parse.c b/ext/intl/dateformat/dateformat_parse.c
index 378ddf641a..a8331777bb 100644
--- a/ext/intl/dateformat/dateformat_parse.c
+++ b/ext/intl/dateformat/dateformat_parse.c
@@ -130,7 +130,7 @@ PHP_FUNCTION(datefmt_parse)
char* text_to_parse = NULL;
size_t text_len =0;
zval* z_parse_pos = NULL;
- int32_t parse_pos = -1;
+ int32_t parse_pos = -1;
DATE_FORMAT_METHOD_INIT_VARS;
@@ -147,8 +147,13 @@ PHP_FUNCTION(datefmt_parse)
if (z_parse_pos) {
ZVAL_DEREF(z_parse_pos);
convert_to_long(z_parse_pos);
+ if (ZEND_LONG_INT_OVFL(Z_LVAL_P(z_parse_pos))) {
+ intl_error_set_code(NULL, U_ILLEGAL_ARGUMENT_ERROR);
+ intl_error_set_custom_msg(NULL, "String index is out of valid range.", 0);
+ RETURN_FALSE;
+ }
parse_pos = (int32_t)Z_LVAL_P(z_parse_pos);
- if(parse_pos > text_len) {
+ if((size_t)parse_pos > text_len) {
RETURN_FALSE;
}
}
@@ -186,8 +191,13 @@ PHP_FUNCTION(datefmt_localtime)
if (z_parse_pos) {
ZVAL_DEREF(z_parse_pos);
convert_to_long(z_parse_pos);
+ if (ZEND_LONG_INT_OVFL(Z_LVAL_P(z_parse_pos))) {
+ intl_error_set_code(NULL, U_ILLEGAL_ARGUMENT_ERROR);
+ intl_error_set_custom_msg(NULL, "String index is out of valid range.", 0);
+ RETURN_FALSE;
+ }
parse_pos = (int32_t)Z_LVAL_P(z_parse_pos);
- if(parse_pos > text_len) {
+ if((size_t)parse_pos > text_len) {
RETURN_FALSE;
}
}
diff --git a/ext/intl/formatter/formatter_attr.c b/ext/intl/formatter/formatter_attr.c
index 8e4a18be41..f5efda6119 100644
--- a/ext/intl/formatter/formatter_attr.c
+++ b/ext/intl/formatter/formatter_attr.c
@@ -252,7 +252,7 @@ PHP_FUNCTION( numfmt_get_symbol )
zend_long symbol;
UChar value_buf[4];
UChar *value = value_buf;
- int32_t length = USIZE(value_buf);
+ uint32_t length = USIZE(value_buf);
FORMATTER_METHOD_INIT_VARS;
/* Parse parameters. */
@@ -345,7 +345,7 @@ PHP_FUNCTION( numfmt_set_symbol )
PHP_FUNCTION( numfmt_get_pattern )
{
UChar value_buf[64];
- int32_t length = USIZE( value_buf );
+ uint32_t length = USIZE( value_buf );
UChar* value = value_buf;
FORMATTER_METHOD_INIT_VARS;
diff --git a/ext/intl/formatter/formatter_class.c b/ext/intl/formatter/formatter_class.c
index 287d19aaa9..bf4387b3d8 100644
--- a/ext/intl/formatter/formatter_class.c
+++ b/ext/intl/formatter/formatter_class.c
@@ -33,13 +33,6 @@ static zend_object_handlers NumberFormatter_handlers;
* Auxiliary functions needed by objects of 'NumberFormatter' class
*/
-/* {{{ NumberFormatter_objects_dtor */
-static void NumberFormatter_object_dtor(zend_object *object)
-{
- zend_objects_destroy_object( object );
-}
-/* }}} */
-
/* {{{ NumberFormatter_objects_free */
void NumberFormatter_object_free( zend_object *object )
{
@@ -195,7 +188,6 @@ void formatter_register_class( void )
sizeof(NumberFormatter_handlers));
NumberFormatter_handlers.offset = XtOffsetOf(NumberFormatter_object, zo);
NumberFormatter_handlers.clone_obj = NumberFormatter_object_clone;
- NumberFormatter_handlers.dtor_obj = NumberFormatter_object_dtor;
NumberFormatter_handlers.free_obj = NumberFormatter_object_free;
/* Declare 'NumberFormatter' class properties. */
diff --git a/ext/intl/formatter/formatter_format.c b/ext/intl/formatter/formatter_format.c
index 369756ebdb..f3253dcdb2 100644
--- a/ext/intl/formatter/formatter_format.c
+++ b/ext/intl/formatter/formatter_format.c
@@ -18,9 +18,10 @@
#include "config.h"
#endif
+#include "php_intl.h"
+
#include <unicode/ustring.h>
-#include "php_intl.h"
#include "formatter_class.h"
#include "formatter_format.h"
#include "intl_convert.h"
@@ -119,7 +120,7 @@ PHP_FUNCTION( numfmt_format )
break;
default:
- php_error_docref(NULL, E_WARNING, "Unsupported format type %pd", type);
+ php_error_docref(NULL, E_WARNING, "Unsupported format type " ZEND_LONG_FMT, type);
RETURN_FALSE;
break;
}
diff --git a/ext/intl/formatter/formatter_parse.c b/ext/intl/formatter/formatter_parse.c
index f0d42cfabe..37b28ae558 100644
--- a/ext/intl/formatter/formatter_parse.c
+++ b/ext/intl/formatter/formatter_parse.c
@@ -18,10 +18,11 @@
#include "config.h"
#endif
+#include "php_intl.h"
+
#include <unicode/ustring.h>
#include <locale.h>
-#include "php_intl.h"
#include "formatter_class.h"
#include "formatter_format.h"
#include "formatter_parse.h"
@@ -97,7 +98,7 @@ PHP_FUNCTION( numfmt_parse )
RETVAL_DOUBLE(val_double);
break;
default:
- php_error_docref(NULL, E_WARNING, "Unsupported format type %pd", type);
+ php_error_docref(NULL, E_WARNING, "Unsupported format type " ZEND_LONG_FMT, type);
RETVAL_FALSE;
break;
}
diff --git a/ext/intl/grapheme/grapheme_string.c b/ext/intl/grapheme/grapheme_string.c
index ad75b3580b..91d5741ea1 100644
--- a/ext/intl/grapheme/grapheme_string.c
+++ b/ext/intl/grapheme/grapheme_string.c
@@ -127,7 +127,7 @@ PHP_FUNCTION(grapheme_strpos)
/* we checked that it will fit: */
offset = (int32_t) loffset;
- noffset = offset >= 0 ? offset : haystack_len + offset;
+ noffset = offset >= 0 ? offset : (int32_t)haystack_len + offset;
/* the offset is 'grapheme count offset' so it still might be invalid - we'll check it later */
@@ -136,20 +136,21 @@ PHP_FUNCTION(grapheme_strpos)
RETURN_FALSE;
}
+ if (offset >= 0) {
+ /* quick check to see if the string might be there
+ * I realize that 'offset' is 'grapheme count offset' but will work in spite of that
+ */
+ found = php_memnstr(haystack + noffset, needle, needle_len, haystack + haystack_len);
- /* quick check to see if the string might be there
- * I realize that 'offset' is 'grapheme count offset' but will work in spite of that
- */
- found = php_memnstr(haystack + noffset, needle, needle_len, haystack + haystack_len);
-
- /* if it isn't there the we are done */
- if (!found) {
- RETURN_FALSE;
- }
+ /* if it isn't there the we are done */
+ if (!found) {
+ RETURN_FALSE;
+ }
- /* if it is there, and if the haystack is ascii, we are all done */
- if ( grapheme_ascii_check((unsigned char *)haystack, haystack_len) >= 0 ) {
- RETURN_LONG(found - haystack);
+ /* if it is there, and if the haystack is ascii, we are all done */
+ if ( grapheme_ascii_check((unsigned char *)haystack, haystack_len) >= 0 ) {
+ RETURN_LONG(found - haystack);
+ }
}
/* do utf16 part of the strpos */
@@ -197,11 +198,10 @@ PHP_FUNCTION(grapheme_stripos)
RETURN_FALSE;
}
-
is_ascii = ( grapheme_ascii_check((unsigned char*)haystack, haystack_len) >= 0 );
if ( is_ascii ) {
- int32_t noffset = offset >= 0 ? offset : haystack_len + offset;
+ int32_t noffset = offset >= 0 ? offset : (int32_t)haystack_len + offset;
needle_dup = estrndup(needle, needle_len);
php_strtolower(needle_dup, needle_len);
haystack_dup = estrndup(haystack, haystack_len);
@@ -792,6 +792,10 @@ PHP_FUNCTION(grapheme_extract)
RETURN_FALSE;
}
+ if (lstart < 0) {
+ lstart += str_len;
+ }
+
if ( NULL != next ) {
if ( !Z_ISREF_P(next) ) {
intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
@@ -812,7 +816,7 @@ PHP_FUNCTION(grapheme_extract)
RETURN_FALSE;
}
- if ( lstart > INT32_MAX || lstart < 0 || lstart >= str_len ) {
+ if ( lstart > INT32_MAX || lstart < 0 || (size_t)lstart >= str_len ) {
intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "grapheme_extract: start not contained in string", 0 );
RETURN_FALSE;
}
diff --git a/ext/intl/grapheme/grapheme_util.c b/ext/intl/grapheme/grapheme_util.c
index 020d0d6ef9..d81060b5a1 100644
--- a/ext/intl/grapheme/grapheme_util.c
+++ b/ext/intl/grapheme/grapheme_util.c
@@ -345,7 +345,7 @@ grapheme_strrpos_ascii(char *haystack, size_t haystack_len, char *needle, size_t
e = haystack + haystack_len - needle_len;
} else {
p = haystack;
- if (needle_len > -offset) {
+ if (needle_len > (size_t)-offset) {
e = haystack + haystack_len - needle_len;
} else {
e = haystack + haystack_len + offset;
diff --git a/ext/intl/grapheme/grapheme_util.h b/ext/intl/grapheme/grapheme_util.h
index 492b19bb7f..5b942d030e 100644
--- a/ext/intl/grapheme/grapheme_util.h
+++ b/ext/intl/grapheme/grapheme_util.h
@@ -34,8 +34,6 @@ int32_t grapheme_split_string(const UChar *text, int32_t text_length, int bounda
int32_t grapheme_count_graphemes(UBreakIterator *bi, UChar *string, int32_t string_len);
-inline void *grapheme_memrchr_grapheme(const void *s, int c, int32_t n);
-
int32_t grapheme_get_haystack_offset(UBreakIterator* bi, int32_t offset);
UBreakIterator* grapheme_get_break_iterator(void *stack_buffer, UErrorCode *status );
diff --git a/ext/intl/locale/locale_methods.c b/ext/intl/locale/locale_methods.c
index 247262ad19..dba93f3fbc 100644
--- a/ext/intl/locale/locale_methods.c
+++ b/ext/intl/locale/locale_methods.c
@@ -124,7 +124,7 @@ static int16_t findOffset(const char* const* list, const char* key)
static char* getPreferredTag(const char* gf_tag)
{
char* result = NULL;
- int grOffset = 0;
+ zend_off_t grOffset = 0;
grOffset = findOffset( LOC_GRANDFATHERED ,gf_tag);
if(grOffset < 0) {
@@ -145,10 +145,10 @@ static char* getPreferredTag(const char* gf_tag)
* or -1 if no token
* strtokr equivalent search for token in reverse direction
*/
-static int getStrrtokenPos(char* str, int savedPos)
+static zend_off_t getStrrtokenPos(char* str, zend_off_t savedPos)
{
- int result =-1;
- int i;
+ zend_off_t result =-1;
+ zend_off_t i;
for(i=savedPos-1; i>=0; i--) {
if(isIDSeparator(*(str+i)) ){
@@ -175,14 +175,14 @@ static int getStrrtokenPos(char* str, int savedPos)
* returns -1 if no singleton
* strtok equivalent search for singleton
*/
-static int getSingletonPos(const char* str)
+static zend_off_t getSingletonPos(const char* str)
{
- int result =-1;
- int i=0;
- int len = 0;
+ zend_off_t result =-1;
+ zend_off_t i=0;
+ size_t len = 0;
if( str && ((len=strlen(str))>0) ){
- for( i=0; i<len ; i++){
+ for( i=0; (size_t)i < len ; i++){
if( isIDSeparator(*(str+i)) ){
if( i==1){
/* string is of the form x-avy or a-prv1 */
@@ -258,15 +258,15 @@ PHP_NAMED_FUNCTION(zif_locale_set_default)
*/
static zend_string* get_icu_value_internal( const char* loc_name , char* tag_name, int* result , int fromParseLocale)
{
- zend_string* tag_value = NULL;
- int32_t tag_value_len = 512;
+ zend_string* tag_value = NULL;
+ int32_t tag_value_len = 512;
- int singletonPos = 0;
- char* mod_loc_name = NULL;
- int grOffset = 0;
+ zend_off_t singletonPos = 0;
+ char* mod_loc_name = NULL;
+ zend_off_t grOffset = 0;
- int32_t buflen = 512;
- UErrorCode status = U_ZERO_ERROR;
+ int32_t buflen = 512;
+ UErrorCode status = U_ZERO_ERROR;
if (strlen(loc_name) > INTL_MAX_LOCALE_LEN) {
return NULL;
@@ -439,7 +439,7 @@ static void get_icu_value_src_php( char* tag_name, INTERNAL_FUNCTION_PARAMETERS)
}
}
-/* }}} */
+/* }}} */
/* {{{ proto static string Locale::getScript($locale)
* gets the script for the $locale
@@ -1000,40 +1000,36 @@ PHP_FUNCTION(locale_compose)
*/
static zend_string* get_private_subtags(const char* loc_name)
{
- zend_string* result =NULL;
- int singletonPos = 0;
- int len =0;
- const char* mod_loc_name =NULL;
+ zend_string* result = NULL;
+ zend_off_t singletonPos = 0;
+ size_t len = 0;
+ const char* mod_loc_name =NULL;
if( loc_name && (len = strlen(loc_name)>0 ) ){
mod_loc_name = loc_name ;
len = strlen(mod_loc_name);
- while( (singletonPos = getSingletonPos(mod_loc_name))!= -1){
-
- if( singletonPos!=-1){
- if( (*(mod_loc_name+singletonPos)=='x') || (*(mod_loc_name+singletonPos)=='X') ){
- /* private subtag start found */
- if( singletonPos + 2 == len){
- /* loc_name ends with '-x-' ; return NULL */
- }
- else{
- /* result = mod_loc_name + singletonPos +2; */
- result = zend_string_init(mod_loc_name + singletonPos+2 , (len -( singletonPos +2) ), 0);
- }
- break;
+ while( (singletonPos = getSingletonPos(mod_loc_name)) > -1){
+ if( (*(mod_loc_name+singletonPos)=='x') || (*(mod_loc_name+singletonPos)=='X') ){
+ /* private subtag start found */
+ if( singletonPos + 2 == len){
+ /* loc_name ends with '-x-' ; return NULL */
}
else{
- if( singletonPos + 1 >= len){
- /* String end */
- break;
- } else {
- /* singleton found but not a private subtag , hence check further in the string for the private subtag */
- mod_loc_name = mod_loc_name + singletonPos +1;
- len = strlen(mod_loc_name);
- }
+ /* result = mod_loc_name + singletonPos +2; */
+ result = zend_string_init(mod_loc_name + singletonPos+2 , (len -( singletonPos +2) ), 0);
+ }
+ break;
+ }
+ else{
+ if((size_t)(singletonPos + 1) >= len){
+ /* String end */
+ break;
+ } else {
+ /* singleton found but not a private subtag , hence check further in the string for the private subtag */
+ mod_loc_name = mod_loc_name + singletonPos +1;
+ len = strlen(mod_loc_name);
}
}
-
} /* end of while */
}
@@ -1445,7 +1441,7 @@ static zend_string* lookup_loc_range(const char* loc_range, HashTable* hash_arr,
char* cur_loc_range = NULL;
zend_string* can_loc_range = NULL;
- int saved_pos = 0;
+ zend_off_t saved_pos = 0;
zend_string* return_value = NULL;
diff --git a/ext/intl/msgformat/msgformat_class.c b/ext/intl/msgformat/msgformat_class.c
index 8d464c6ca4..90dac81252 100644
--- a/ext/intl/msgformat/msgformat_class.c
+++ b/ext/intl/msgformat/msgformat_class.c
@@ -33,13 +33,6 @@ static zend_object_handlers MessageFormatter_handlers;
* Auxiliary functions needed by objects of 'MessageFormatter' class
*/
-/* {{{ MessageFormatter_objects_dtor */
-static void MessageFormatter_object_dtor(zend_object *object )
-{
- zend_objects_destroy_object( object );
-}
-/* }}} */
-
/* {{{ MessageFormatter_objects_free */
void MessageFormatter_object_free( zend_object *object )
{
@@ -163,7 +156,6 @@ void msgformat_register_class( void )
sizeof MessageFormatter_handlers);
MessageFormatter_handlers.offset = XtOffsetOf(MessageFormatter_object, zo);
MessageFormatter_handlers.clone_obj = MessageFormatter_object_clone;
- MessageFormatter_handlers.dtor_obj = MessageFormatter_object_dtor;
MessageFormatter_handlers.free_obj = MessageFormatter_object_free;
diff --git a/ext/intl/php_intl.c b/ext/intl/php_intl.c
index b05e746ac5..77cb2bac3c 100644
--- a/ext/intl/php_intl.c
+++ b/ext/intl/php_intl.c
@@ -133,11 +133,6 @@ ZEND_BEGIN_ARG_INFO_EX(collator_static_1_arg, 0, 0, 1)
ZEND_ARG_INFO(0, arg1)
ZEND_END_ARG_INFO()
-ZEND_BEGIN_ARG_INFO_EX(collator_static_2_args, 0, 0, 2)
- ZEND_ARG_INFO(0, arg1)
- ZEND_ARG_INFO(0, arg2)
-ZEND_END_ARG_INFO()
-
ZEND_BEGIN_ARG_INFO_EX(collator_0_args, 0, 0, 1)
ZEND_ARG_OBJ_INFO(0, object, Collator, 0)
ZEND_END_ARG_INFO()
@@ -383,13 +378,6 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_idn_to_ascii, 0, 0, 1)
ZEND_ARG_INFO(1, idn_info)
ZEND_END_ARG_INFO()
-ZEND_BEGIN_ARG_INFO_EX(arginfo_idn_to_utf8, 0, 0, 1)
- ZEND_ARG_INFO(0, domain)
- ZEND_ARG_INFO(0, option)
- ZEND_ARG_INFO(0, variant)
- ZEND_ARG_INFO(1, idn_info)
-ZEND_END_ARG_INFO()
-
ZEND_BEGIN_ARG_INFO_EX( arginfo_resourcebundle_create_proc, 0, 0, 2 )
ZEND_ARG_INFO( 0, locale )
ZEND_ARG_INFO( 0, bundlename )
@@ -458,10 +446,6 @@ ZEND_BEGIN_ARG_INFO_EX( arginfo_tz_create_enumeration, 0, 0, 0 )
ZEND_ARG_INFO( 0, countryOrRawOffset )
ZEND_END_ARG_INFO()
-ZEND_BEGIN_ARG_INFO_EX( arginfo_tz_count_equivalent_ids, 0, 0, 1 )
- ZEND_ARG_INFO( 0, zoneId )
-ZEND_END_ARG_INFO()
-
ZEND_BEGIN_ARG_INFO_EX( arginfo_tz_create_time_zone_id_enumeration, 0, 0, 1 )
ZEND_ARG_INFO( 0, zoneType )
ZEND_ARG_INFO( 0, region )
diff --git a/ext/intl/spoofchecker/spoofchecker_class.c b/ext/intl/spoofchecker/spoofchecker_class.c
index d93c709bd7..5cfc27dad1 100644
--- a/ext/intl/spoofchecker/spoofchecker_class.c
+++ b/ext/intl/spoofchecker/spoofchecker_class.c
@@ -29,13 +29,6 @@ static zend_object_handlers Spoofchecker_handlers;
* Auxiliary functions needed by objects of 'Spoofchecker' class
*/
-/* {{{ Spoofchecker_objects_dtor */
-static void Spoofchecker_objects_dtor(zend_object *object)
-{
- zend_objects_destroy_object(object);
-}
-/* }}} */
-
/* {{{ Spoofchecker_objects_free */
void Spoofchecker_objects_free(zend_object *object)
{
@@ -124,7 +117,7 @@ static zend_object *spoofchecker_clone_obj(zval *object) /* {{{ */
if(U_FAILURE(SPOOFCHECKER_ERROR_CODE(new_sfo))) {
/* set up error in case error handler is interested */
intl_error_set( NULL, SPOOFCHECKER_ERROR_CODE(new_sfo), "Failed to clone SpoofChecker object", 0 );
- Spoofchecker_objects_dtor(&new_sfo->zo); /* free new object */
+ Spoofchecker_objects_free(&new_sfo->zo); /* free new object */
zend_error(E_ERROR, "Failed to clone SpoofChecker object");
}
return new_obj_val;
@@ -147,7 +140,6 @@ void spoofchecker_register_Spoofchecker_class(void)
sizeof Spoofchecker_handlers);
Spoofchecker_handlers.offset = XtOffsetOf(Spoofchecker_object, zo);
Spoofchecker_handlers.clone_obj = spoofchecker_clone_obj;
- Spoofchecker_handlers.dtor_obj = Spoofchecker_objects_dtor;
Spoofchecker_handlers.free_obj = Spoofchecker_objects_free;
if (!Spoofchecker_ce_ptr) {
diff --git a/ext/intl/tests/bug60192-compare.phpt b/ext/intl/tests/bug60192-compare.phpt
index 12f3273538..ce9728023a 100644
--- a/ext/intl/tests/bug60192-compare.phpt
+++ b/ext/intl/tests/bug60192-compare.phpt
@@ -16,4 +16,8 @@ $c = new Collator2();
$a = $c->compare('h', 'H');
--EXPECTF--
-Catchable fatal error: Collator::compare(): Object not initialized in %s on line %d
+Fatal error: Uncaught Error: Object not initialized in %s:%d
+Stack trace:
+#0 %s(%d): Collator->compare('h', 'H')
+#1 {main}
+ thrown in %s on line %d
diff --git a/ext/intl/tests/bug60192-getlocale.phpt b/ext/intl/tests/bug60192-getlocale.phpt
index 9f340c5f67..c4155e9ab5 100644
--- a/ext/intl/tests/bug60192-getlocale.phpt
+++ b/ext/intl/tests/bug60192-getlocale.phpt
@@ -17,4 +17,8 @@ $c = new Collator2();
$c->getLocale(Locale::ACTUAL_LOCALE);
--EXPECTF--
-Catchable fatal error: Collator::getLocale(): Object not initialized in %s on line %d
+Fatal error: Uncaught Error: Object not initialized in %s:%d
+Stack trace:
+#0 %s(%d): Collator->getLocale(0)
+#1 {main}
+ thrown in %s on line %d
diff --git a/ext/intl/tests/bug60192-getsortkey.phpt b/ext/intl/tests/bug60192-getsortkey.phpt
index f3e68f9c61..0d0f07e768 100644
--- a/ext/intl/tests/bug60192-getsortkey.phpt
+++ b/ext/intl/tests/bug60192-getsortkey.phpt
@@ -17,4 +17,8 @@ $c = new Collator2();
$c->getSortKey('h');
--EXPECTF--
-Catchable fatal error: Collator::getSortKey(): Object not initialized in %s on line %d
+Fatal error: Uncaught Error: Object not initialized in %s:%d
+Stack trace:
+#0 %s(%d): Collator->getSortKey('h')
+#1 {main}
+ thrown in %s on line %d
diff --git a/ext/intl/tests/bug60192-sort.phpt b/ext/intl/tests/bug60192-sort.phpt
index ee506d3a5a..c452d0de6d 100644
--- a/ext/intl/tests/bug60192-sort.phpt
+++ b/ext/intl/tests/bug60192-sort.phpt
@@ -18,4 +18,9 @@ $a = array('a', 'b');
$c->sort($a);
--EXPECTF--
-Catchable fatal error: Collator::sort(): Object not initialized in %s on line %d
+Fatal error: Uncaught Error: Object not initialized in %s:%d
+Stack trace:
+#0 %s(%d): Collator->sort(Array)
+#1 {main}
+ thrown in %s on line %d
+
diff --git a/ext/intl/tests/bug60192-sortwithsortkeys.phpt b/ext/intl/tests/bug60192-sortwithsortkeys.phpt
index c26b2daf85..e7d7c1dc1d 100644
--- a/ext/intl/tests/bug60192-sortwithsortkeys.phpt
+++ b/ext/intl/tests/bug60192-sortwithsortkeys.phpt
@@ -18,4 +18,9 @@ $a = array('a', 'b');
$c->sortWithSortKeys($a);
--EXPECTF--
-Catchable fatal error: Collator::sortWithSortKeys(): Object not initialized in %s on line %d
+Fatal error: Uncaught Error: Object not initialized in %s:%d
+Stack trace:
+#0 %s(%d): Collator->sortWithSortKeys(Array)
+#1 {main}
+ thrown in %s on line %d
+
diff --git a/ext/intl/tests/bug69374.phpt b/ext/intl/tests/bug69374.phpt
new file mode 100644
index 0000000000..4d9fffab7a
--- /dev/null
+++ b/ext/intl/tests/bug69374.phpt
@@ -0,0 +1,24 @@
+--TEST--
+IntlDateFormatter::formatObject(): returns wrong utf8 value when $format param is utf8 string pattern.
+--SKIPIF--
+<?php
+if (!extension_loaded('intl')) die('skip intl extension not enabled'); ?>
+<?php if (version_compare(INTL_ICU_VERSION, '50.1.2') < 0) die('skip for ICU >= 51.1.2'); ?>
+--FILE--
+<?php
+$millitimestamp = 1428133423941.0; // 14:43:43 April 04 2015
+$pattern1 = '\'tháng\' MM, y'; // yMM format for Vietnamese
+$pattern2 = 'y년 MMM'; // yMM format for Korean
+$date = IntlCalendar::createInstance('Asia/Ho_Chi_Minh');
+$date->setTime($millitimestamp);
+echo IntlDateFormatter::formatObject($date, $pattern1, 'vi_VN'), "\n";
+echo IntlDateFormatter::formatObject ($date, $pattern2, 'ko_KR'), "\n";
+?>
+==DONE==
+
+--EXPECTF--
+tháng 04, 2015
+2015년 4월
+==DONE==
+
+
diff --git a/ext/intl/tests/bug69398.phpt b/ext/intl/tests/bug69398.phpt
new file mode 100644
index 0000000000..ea7dbd5098
--- /dev/null
+++ b/ext/intl/tests/bug69398.phpt
@@ -0,0 +1,22 @@
+--TEST--
+IntlDateFormatter::formatObject(): returns wrong value when time style is NONE.
+--SKIPIF--
+<?php
+if (!extension_loaded('intl')) die('skip intl extension not enabled'); ?>
+<?php if (version_compare(INTL_ICU_VERSION, '50.1.2') < 0) die('skip for ICU >= 51.1.2'); ?>
+--FILE--
+<?php
+$millitimestamp = 1428133423941.0; // 14:43:43 April 04 2015
+$date = IntlCalendar::createInstance('Asia/Ho_Chi_Minh');
+$date->setTime($millitimestamp);
+echo IntlDateFormatter::formatObject($date, array(IntlDateFormatter::SHORT, IntlDateFormatter::NONE), 'vi_VN'), "\n";
+echo IntlDateFormatter::formatObject ($date, array(IntlDateFormatter::SHORT, IntlDateFormatter::NONE), 'ko_KR'), "\n";
+?>
+==DONE==
+
+--EXPECTF--
+04/04/2015
+15. 4. 4.
+==DONE==
+
+
diff --git a/ext/intl/tests/bug74298.phpt b/ext/intl/tests/bug74298.phpt
new file mode 100644
index 0000000000..0bfb59b48e
--- /dev/null
+++ b/ext/intl/tests/bug74298.phpt
@@ -0,0 +1,30 @@
+--TEST--
+Bug #74298 (IntlDateFormatter->format() doesn't return microseconds/fractions)
+--SKIPIF--
+<?php if (!extension_loaded('intl')) print 'skip'; ?>
+--FILE--
+<?php
+var_dump((new \DateTime('2017-01-01 01:02:03.123456'))->format('Y-m-d\TH:i:s.u'));
+
+var_dump((new \IntlDateFormatter(
+ 'en-US',
+ \IntlDateFormatter::FULL,
+ \IntlDateFormatter::FULL,
+ 'UTC',
+ \IntlDateFormatter::GREGORIAN,
+ 'yyyy-MM-dd HH:mm:ss.SSSSSS'
+))->format(new \DateTime('2017-01-01 01:02:03.123456', new \DateTimeZone('UTC'))));
+
+var_dump(datefmt_create(
+ 'en-US',
+ \IntlDateFormatter::FULL,
+ \IntlDateFormatter::FULL,
+ 'UTC',
+ \IntlDateFormatter::GREGORIAN,
+ 'yyyy-MM-dd HH:mm:ss.SSSSSS'
+)->format(new \DateTime('2017-01-01 01:02:03.123456', new \DateTimeZone('UTC'))));
+?>
+--EXPECTF--
+string(26) "2017-01-01T01:02:03.123456"
+string(26) "2017-01-01 01:02:03.123000"
+string(26) "2017-01-01 01:02:03.123000"
diff --git a/ext/intl/tests/dateformat_bug65683.phpt b/ext/intl/tests/dateformat_bug65683.phpt
new file mode 100644
index 0000000000..d18cb4c3da
--- /dev/null
+++ b/ext/intl/tests/dateformat_bug65683.phpt
@@ -0,0 +1,16 @@
+--TEST--
+Bug #65683 IntlDateFormatter accepts DateTimeImmutable
+--SKIPIF--
+<?php
+if (!extension_loaded('intl')) die('skip intl extension not enabled'); ?>
+--FILE--
+<?php
+
+$formatter = new IntlDateFormatter('en-US', IntlDateFormatter::FULL, IntlDateFormatter::NONE, new DateTimeZone("UTC"));
+var_dump($formatter->format(new DateTimeImmutable('2017-03-27 00:00:00 UTC'))) . "\n";
+
+?>
+==DONE==
+--EXPECTF--
+string(%s) "Monday, March %d, 2017"
+==DONE==
diff --git a/ext/intl/tests/dateformat_format.phpt b/ext/intl/tests/dateformat_format.phpt
index 241c5f975a..a0135a809c 100644
--- a/ext/intl/tests/dateformat_format.phpt
+++ b/ext/intl/tests/dateformat_format.phpt
@@ -400,24 +400,24 @@ Formatted DateTime is : 20001230 05:04 PM
Date is: stdClass::__set_state(array(
))
------------
-Error while formatting as: 'datefmt_format: invalid object type for date/time (only IntlCalendar and DateTime permitted): U_ILLEGAL_ARGUMENT_ERROR'
+Error while formatting as: 'datefmt_format: invalid object type for date/time (only IntlCalendar and DateTimeInterface permitted): U_ILLEGAL_ARGUMENT_ERROR'
------------
Date is: stdClass::__set_state(array(
))
------------
-Error while formatting as: 'datefmt_format: invalid object type for date/time (only IntlCalendar and DateTime permitted): U_ILLEGAL_ARGUMENT_ERROR'
+Error while formatting as: 'datefmt_format: invalid object type for date/time (only IntlCalendar and DateTimeInterface permitted): U_ILLEGAL_ARGUMENT_ERROR'
------------
Date is: stdClass::__set_state(array(
))
------------
-Error while formatting as: 'datefmt_format: invalid object type for date/time (only IntlCalendar and DateTime permitted): U_ILLEGAL_ARGUMENT_ERROR'
+Error while formatting as: 'datefmt_format: invalid object type for date/time (only IntlCalendar and DateTimeInterface permitted): U_ILLEGAL_ARGUMENT_ERROR'
------------
Date is: stdClass::__set_state(array(
))
------------
-Error while formatting as: 'datefmt_format: invalid object type for date/time (only IntlCalendar and DateTime permitted): U_ILLEGAL_ARGUMENT_ERROR'
+Error while formatting as: 'datefmt_format: invalid object type for date/time (only IntlCalendar and DateTimeInterface permitted): U_ILLEGAL_ARGUMENT_ERROR'
------------
Date is: stdClass::__set_state(array(
))
------------
-Error while formatting as: 'datefmt_format: invalid object type for date/time (only IntlCalendar and DateTime permitted): U_ILLEGAL_ARGUMENT_ERROR'
+Error while formatting as: 'datefmt_format: invalid object type for date/time (only IntlCalendar and DateTimeInterface permitted): U_ILLEGAL_ARGUMENT_ERROR'
diff --git a/ext/intl/tests/dateformat_format_variant2.phpt b/ext/intl/tests/dateformat_format_variant2.phpt
index 07c67e9322..a3df39aa95 100644
--- a/ext/intl/tests/dateformat_format_variant2.phpt
+++ b/ext/intl/tests/dateformat_format_variant2.phpt
@@ -400,24 +400,24 @@ Formatted DateTime is : 20001230 05:04 PM
Date is: stdClass::__set_state(array(
))
------------
-Error while formatting as: 'datefmt_format: invalid object type for date/time (only IntlCalendar and DateTime permitted): U_ILLEGAL_ARGUMENT_ERROR'
+Error while formatting as: 'datefmt_format: invalid object type for date/time (only IntlCalendar and DateTimeInterface permitted): U_ILLEGAL_ARGUMENT_ERROR'
------------
Date is: stdClass::__set_state(array(
))
------------
-Error while formatting as: 'datefmt_format: invalid object type for date/time (only IntlCalendar and DateTime permitted): U_ILLEGAL_ARGUMENT_ERROR'
+Error while formatting as: 'datefmt_format: invalid object type for date/time (only IntlCalendar and DateTimeInterface permitted): U_ILLEGAL_ARGUMENT_ERROR'
------------
Date is: stdClass::__set_state(array(
))
------------
-Error while formatting as: 'datefmt_format: invalid object type for date/time (only IntlCalendar and DateTime permitted): U_ILLEGAL_ARGUMENT_ERROR'
+Error while formatting as: 'datefmt_format: invalid object type for date/time (only IntlCalendar and DateTimeInterface permitted): U_ILLEGAL_ARGUMENT_ERROR'
------------
Date is: stdClass::__set_state(array(
))
------------
-Error while formatting as: 'datefmt_format: invalid object type for date/time (only IntlCalendar and DateTime permitted): U_ILLEGAL_ARGUMENT_ERROR'
+Error while formatting as: 'datefmt_format: invalid object type for date/time (only IntlCalendar and DateTimeInterface permitted): U_ILLEGAL_ARGUMENT_ERROR'
------------
Date is: stdClass::__set_state(array(
))
------------
-Error while formatting as: 'datefmt_format: invalid object type for date/time (only IntlCalendar and DateTime permitted): U_ILLEGAL_ARGUMENT_ERROR'
+Error while formatting as: 'datefmt_format: invalid object type for date/time (only IntlCalendar and DateTimeInterface permitted): U_ILLEGAL_ARGUMENT_ERROR'
diff --git a/ext/intl/tests/dateformat_format_variant3.phpt b/ext/intl/tests/dateformat_format_variant3.phpt
index d770473f44..da0d662ca7 100644
--- a/ext/intl/tests/dateformat_format_variant3.phpt
+++ b/ext/intl/tests/dateformat_format_variant3.phpt
@@ -400,24 +400,24 @@ Formatted DateTime is : 20001230 05:04 PM
Date is: stdClass::__set_state(array(
))
------------
-Error while formatting as: 'datefmt_format: invalid object type for date/time (only IntlCalendar and DateTime permitted): U_ILLEGAL_ARGUMENT_ERROR'
+Error while formatting as: 'datefmt_format: invalid object type for date/time (only IntlCalendar and DateTimeInterface permitted): U_ILLEGAL_ARGUMENT_ERROR'
------------
Date is: stdClass::__set_state(array(
))
------------
-Error while formatting as: 'datefmt_format: invalid object type for date/time (only IntlCalendar and DateTime permitted): U_ILLEGAL_ARGUMENT_ERROR'
+Error while formatting as: 'datefmt_format: invalid object type for date/time (only IntlCalendar and DateTimeInterface permitted): U_ILLEGAL_ARGUMENT_ERROR'
------------
Date is: stdClass::__set_state(array(
))
------------
-Error while formatting as: 'datefmt_format: invalid object type for date/time (only IntlCalendar and DateTime permitted): U_ILLEGAL_ARGUMENT_ERROR'
+Error while formatting as: 'datefmt_format: invalid object type for date/time (only IntlCalendar and DateTimeInterface permitted): U_ILLEGAL_ARGUMENT_ERROR'
------------
Date is: stdClass::__set_state(array(
))
------------
-Error while formatting as: 'datefmt_format: invalid object type for date/time (only IntlCalendar and DateTime permitted): U_ILLEGAL_ARGUMENT_ERROR'
+Error while formatting as: 'datefmt_format: invalid object type for date/time (only IntlCalendar and DateTimeInterface permitted): U_ILLEGAL_ARGUMENT_ERROR'
------------
Date is: stdClass::__set_state(array(
))
------------
-Error while formatting as: 'datefmt_format: invalid object type for date/time (only IntlCalendar and DateTime permitted): U_ILLEGAL_ARGUMENT_ERROR'
+Error while formatting as: 'datefmt_format: invalid object type for date/time (only IntlCalendar and DateTimeInterface permitted): U_ILLEGAL_ARGUMENT_ERROR'
diff --git a/ext/intl/tests/formatter_fail.phpt b/ext/intl/tests/formatter_fail.phpt
index 72335e2022..dcc5cb24fa 100644
--- a/ext/intl/tests/formatter_fail.phpt
+++ b/ext/intl/tests/formatter_fail.phpt
@@ -66,7 +66,7 @@ foreach($args as $arg) {
?>
--EXPECTF--
-TypeError: NumberFormatter::__construct() expects at least 2 parameters, 0 given in %s on line %d
+ArgumentCountError: NumberFormatter::__construct() expects at least 2 parameters, 0 given in %s on line %d
'numfmt_create: unable to parse input parameters: U_ILLEGAL_ARGUMENT_ERROR'
Warning: numfmt_create() expects at least 2 parameters, 0 given in %s on line %d
diff --git a/ext/intl/tests/grapheme.phpt b/ext/intl/tests/grapheme.phpt
index def9110d0d..251b1d142f 100644
--- a/ext/intl/tests/grapheme.phpt
+++ b/ext/intl/tests/grapheme.phpt
@@ -68,9 +68,13 @@ function ut_main()
array( "abc", "a", 0 ),
array( "abc", "a", 0, 0 ),
array( "abc", "a", 1, "false" ),
+ array( "abc", "a", -1, "false" ),
array( "ababc", "a", 1, 2 ),
array( "ao" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "o", "o", 2, 6 ),
+ array( "ao" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "o", "o", -1, 6 ),
+ array( "ao" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "o", "o", -5, 6 ),
array( $char_o_diaeresis_nfd . $char_a_ring_nfd . "a" . $char_a_ring_nfd . "bc", $char_a_ring_nfd, 2, 3 ),
+ array( $char_o_diaeresis_nfd . $char_a_ring_nfd . "a" . $char_a_ring_nfd . "bc", $char_a_ring_nfd, -4, 3 ),
array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "opq", "op", 5 ),
array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "opq", "opq", 5 ),
@@ -91,6 +95,7 @@ function ut_main()
array( "ababc", "ab", 1, 2 ),
array( "ababc", "abc", 1, 2 ),
array( "ao" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "o" . $char_a_ring_nfd . "bc", "o" . $char_a_ring_nfd . "bc", 2, 6 ),
+ array( "ao" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "o" . $char_a_ring_nfd . "bc", "o" . $char_a_ring_nfd . "bc", -8, 6 ),
array( $char_o_diaeresis_nfd . $char_a_ring_nfd . "a" . $char_a_ring_nfd . "bc" . $char_a_ring_nfd . "def", $char_a_ring_nfd . "bc" . $char_a_ring_nfd, 2, 3 ),
);
@@ -120,10 +125,12 @@ function ut_main()
$tests = array(
array( "ao" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "O", "o", 2, 6 ),
+ array( "ao" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Oo", "o", -6, 6 ),
array( $char_o_diaeresis_nfd . $char_a_ring_nfd . "a" . $char_A_ring_nfd . "bc", $char_a_ring_nfd, 2, 3 ),
array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "O", "o", 5 ),
array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd, "O", "false" ),
array( "a" . $char_a_ring_nfd . "bc" . $char_O_diaeresis_nfd, $char_o_diaeresis_nfd, 4 ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_O_diaeresis_nfd, $char_o_diaeresis_nfd, -1, 4 ),
array( $char_o_diaeresis_nfd . "a" . $char_a_ring_nfd . "bc", $char_A_ring_nfd, 2 ),
array( "a" . $char_A_ring_nfd . "bc", $char_a_ring_nfd, 1 ),
array( "Abc", $char_a_ring_nfd, "false" ),
@@ -153,6 +160,7 @@ function ut_main()
array( "aBc", "abC", 0, 0 ),
array( "abc", "aBc", 1, "false" ),
array( "ABabc", "AB", 1, 2 ),
+ array( "ABabc", "AB", -4, 2 ),
array( "abaBc", "aBc", 1, 2 ),
array( "ao" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "o" . $char_A_ring_nfd . "bC", "O" . $char_a_ring_nfd . "bC", 2, 6 ),
array( $char_o_diaeresis_nfd . $char_a_ring_nfd . "a" . $char_A_ring_nfd . "bC" . $char_a_ring_nfd . "def", $char_a_ring_nfd . "Bc" . $char_a_ring_nfd, 2, 3 ),
@@ -559,6 +567,7 @@ function ut_main()
array( "abc", 1, 0, 1, "a" ),
array( "abc", 1, 1, 2, "b" ),
array( "abc", 1, 2, 3, "c" ),
+ array( "abc", 1, -2, 2, "b" ),
array( "abc", 0, 2, 2, "" ),
array( "http://news.bbc.co.uk/2/hi/middle_east/7831588.stm", 48, 48 , 50 , "tm" ),
@@ -569,8 +578,11 @@ function ut_main()
array( $char_a_ring_nfd . "bc", 2, 0, 4, $char_a_ring_nfd . "b" ),
array( $char_a_ring_nfd . "bc", 1, 0, 3, $char_a_ring_nfd . "" ),
array( $char_a_ring_nfd . "bcde", 2, 3, 5, "bc" ),
+ array( $char_a_ring_nfd . "bcde", 2, -4, 5, "bc" ),
array( $char_a_ring_nfd . "bcde", 2, 4, 6, "cd" ),
+ array( $char_a_ring_nfd . "bcde", 2, -7, 4, $char_a_ring_nfd . "b" ),
array( $char_a_ring_nfd . "bcde" . $char_a_ring_nfd . "f", 4, 5, 11, "de" . $char_a_ring_nfd . "f" ),
+ array( $char_a_ring_nfd . "bcde" . $char_a_ring_nfd . "f", 4, -6, 11, "de" . $char_a_ring_nfd . "f" ),
array( $char_a_ring_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd, 3, $char_a_ring_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd ),
array( $char_a_ring_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd, 2, $char_a_ring_nfd . $char_o_diaeresis_nfd ),
@@ -794,9 +806,13 @@ find "b" in "abc" - grapheme_strpos = 1 == 1
find "a" in "abc" - grapheme_strpos = 0 == 0
find "a" in "abc" - grapheme_strpos from 0 = 0 == 0
find "a" in "abc" - grapheme_strpos from 1 = false == false
+find "a" in "abc" - grapheme_strpos from -1 = false == false
find "a" in "ababc" - grapheme_strpos from 1 = 2 == 2
find "o" in "aoa%CC%8Abco%CC%88o" - grapheme_strpos from 2 = 6 == 6
+find "o" in "aoa%CC%8Abco%CC%88o" - grapheme_strpos from -1 = 6 == 6
+find "o" in "aoa%CC%8Abco%CC%88o" - grapheme_strpos from -5 = 6 == 6
find "a%CC%8A" in "o%CC%88a%CC%8Aaa%CC%8Abc" - grapheme_strpos from 2 = 3 == 3
+find "a%CC%8A" in "o%CC%88a%CC%8Aaa%CC%8Abc" - grapheme_strpos from -4 = 3 == 3
find "op" in "aa%CC%8Abco%CC%88opq" - grapheme_strpos = 5 == 5
find "opq" in "aa%CC%8Abco%CC%88opq" - grapheme_strpos = 5 == 5
find "abc" in "aa%CC%8Abco%CC%88" - grapheme_strpos = false == false
@@ -816,15 +832,18 @@ find "abc" in "abc" - grapheme_strpos from 1 = false == false
find "ab" in "ababc" - grapheme_strpos from 1 = 2 == 2
find "abc" in "ababc" - grapheme_strpos from 1 = 2 == 2
find "oa%CC%8Abc" in "aoa%CC%8Abco%CC%88oa%CC%8Abc" - grapheme_strpos from 2 = 6 == 6
+find "oa%CC%8Abc" in "aoa%CC%8Abco%CC%88oa%CC%8Abc" - grapheme_strpos from -8 = 6 == 6
find "a%CC%8Abca%CC%8A" in "o%CC%88a%CC%8Aaa%CC%8Abca%CC%8Adef" - grapheme_strpos from 2 = 3 == 3
function grapheme_stripos($haystack, $needle, $offset = 0) {}
find "o" in "aoa%CC%8Abco%CC%88O" - grapheme_stripos from 2 = 6 == 6
+find "o" in "aoa%CC%8Abco%CC%88Oo" - grapheme_stripos from -6 = 6 == 6
find "a%CC%8A" in "o%CC%88a%CC%8AaA%CC%8Abc" - grapheme_stripos from 2 = 3 == 3
find "o" in "aa%CC%8Abco%CC%88O" - grapheme_stripos = 5 == 5
find "O" in "aa%CC%8Abco%CC%88" - grapheme_stripos = false == false
find "o%CC%88" in "aa%CC%8AbcO%CC%88" - grapheme_stripos = 4 == 4
+find "o%CC%88" in "aa%CC%8AbcO%CC%88" - grapheme_stripos from -1 = 4 == 4
find "A%CC%8A" in "o%CC%88aa%CC%8Abc" - grapheme_stripos = 2 == 2
find "a%CC%8A" in "aA%CC%8Abc" - grapheme_stripos = 1 == 1
find "a%CC%8A" in "Abc" - grapheme_stripos = false == false
@@ -853,6 +872,7 @@ find "ab" in "ABc" - grapheme_stripos from 0 = 0 == 0
find "abC" in "aBc" - grapheme_stripos from 0 = 0 == 0
find "aBc" in "abc" - grapheme_stripos from 1 = false == false
find "AB" in "ABabc" - grapheme_stripos from 1 = 2 == 2
+find "AB" in "ABabc" - grapheme_stripos from -4 = 2 == 2
find "aBc" in "abaBc" - grapheme_stripos from 1 = 2 == 2
find "Oa%CC%8AbC" in "aoa%CC%8Abco%CC%88oA%CC%8AbC" - grapheme_stripos from 2 = 6 == 6
find "a%CC%8ABca%CC%8A" in "o%CC%88a%CC%8AaA%CC%8AbCa%CC%8Adef" - grapheme_stripos from 2 = 3 == 3
@@ -1094,6 +1114,7 @@ extract from "abc" "0" graphemes - grapheme_extract starting at byte position 0
extract from "abc" "1" graphemes - grapheme_extract starting at byte position 0 with $next = a == a $next=1 == 1
extract from "abc" "1" graphemes - grapheme_extract starting at byte position 1 with $next = b == b $next=2 == 2
extract from "abc" "1" graphemes - grapheme_extract starting at byte position 2 with $next = c == c $next=3 == 3
+extract from "abc" "1" graphemes - grapheme_extract starting at byte position -2 with $next = b == b $next=2 == 2
extract from "abc" "0" graphemes - grapheme_extract starting at byte position 2 with $next = == $next=2 == 2
extract from "http%3A%2F%2Fnews.bbc.co.uk%2F2%2Fhi%2Fmiddle_east%2F7831588.stm" "48" graphemes - grapheme_extract starting at byte position 48 with $next = tm == tm $next=50 == 50
extract from "a%CC%8Abc" "3" graphemes - grapheme_extract = a%CC%8Abc == a%CC%8Abc
@@ -1103,8 +1124,11 @@ extract from "a%CC%8Abc" "3" graphemes - grapheme_extract starting at byte posit
extract from "a%CC%8Abc" "2" graphemes - grapheme_extract starting at byte position 0 with $next = a%CC%8Ab == a%CC%8Ab $next=4 == 4
extract from "a%CC%8Abc" "1" graphemes - grapheme_extract starting at byte position 0 with $next = a%CC%8A == a%CC%8A $next=3 == 3
extract from "a%CC%8Abcde" "2" graphemes - grapheme_extract starting at byte position 3 with $next = bc == bc $next=5 == 5
+extract from "a%CC%8Abcde" "2" graphemes - grapheme_extract starting at byte position -4 with $next = bc == bc $next=5 == 5
extract from "a%CC%8Abcde" "2" graphemes - grapheme_extract starting at byte position 4 with $next = cd == cd $next=6 == 6
+extract from "a%CC%8Abcde" "2" graphemes - grapheme_extract starting at byte position -7 with $next = a%CC%8Ab == a%CC%8Ab $next=4 == 4
extract from "a%CC%8Abcdea%CC%8Af" "4" graphemes - grapheme_extract starting at byte position 5 with $next = dea%CC%8Af == dea%CC%8Af $next=11 == 11
+extract from "a%CC%8Abcdea%CC%8Af" "4" graphemes - grapheme_extract starting at byte position -6 with $next = dea%CC%8Af == dea%CC%8Af $next=11 == 11
extract from "a%CC%8Ao%CC%88o%CC%88" "3" graphemes - grapheme_extract = a%CC%8Ao%CC%88o%CC%88 == a%CC%8Ao%CC%88o%CC%88
extract from "a%CC%8Ao%CC%88o%CC%88" "2" graphemes - grapheme_extract = a%CC%8Ao%CC%88 == a%CC%8Ao%CC%88
extract from "a%CC%8Ao%CC%88c" "1" graphemes - grapheme_extract = a%CC%8A == a%CC%8A
diff --git a/ext/intl/tests/locale_bug72658.phpt b/ext/intl/tests/locale_bug72658.phpt
new file mode 100644
index 0000000000..877f0b25f4
--- /dev/null
+++ b/ext/intl/tests/locale_bug72658.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Bug #72658 Locale::lookup() / locale_lookup() hangs if no match found
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+var_dump(
+ Locale::lookup(['en-Latn-US', 'sl', 'sl-IT'], 'en-US', true, 'de-DE'),
+ Locale::lookup(['en-Latn-US', 'sl', 'sl-IT'], 'en-US', false, 'de-DE')
+);
+
+?>
+==DONE==
+--EXPECT--
+string(5) "de-DE"
+string(5) "de-DE"
+==DONE==
diff --git a/ext/intl/tests/msgfmt_fail2.phpt b/ext/intl/tests/msgfmt_fail2.phpt
index 5dcd09ccc8..698d19afce 100644
--- a/ext/intl/tests/msgfmt_fail2.phpt
+++ b/ext/intl/tests/msgfmt_fail2.phpt
@@ -79,7 +79,7 @@ foreach($args as $arg) {
?>
--EXPECTF--
-TypeError: MessageFormatter::__construct() expects exactly 2 parameters, 0 given in %s on line %d
+ArgumentCountError: MessageFormatter::__construct() expects exactly 2 parameters, 0 given in %s on line %d
'msgfmt_create: unable to parse input parameters: U_ILLEGAL_ARGUMENT_ERROR'
Warning: msgfmt_create() expects exactly 2 parameters, 0 given in %s on line %d
@@ -88,7 +88,7 @@ Warning: msgfmt_create() expects exactly 2 parameters, 0 given in %s on line %d
Warning: MessageFormatter::create() expects exactly 2 parameters, 0 given in %s on line %d
'msgfmt_create: unable to parse input parameters: U_ILLEGAL_ARGUMENT_ERROR'
-TypeError: MessageFormatter::__construct() expects exactly 2 parameters, 1 given in %s on line %d
+ArgumentCountError: MessageFormatter::__construct() expects exactly 2 parameters, 1 given in %s on line %d
'msgfmt_create: unable to parse input parameters: U_ILLEGAL_ARGUMENT_ERROR'
Warning: msgfmt_create() expects exactly 2 parameters, 1 given in %s on line %d
diff --git a/ext/intl/tests/msgfmt_format_datetime.phpt b/ext/intl/tests/msgfmt_format_datetime.phpt
index 07e7d68f14..bf0d24ba6a 100644
--- a/ext/intl/tests/msgfmt_format_datetime.phpt
+++ b/ext/intl/tests/msgfmt_format_datetime.phpt
@@ -16,13 +16,16 @@ $fmt = <<<EOD
EOD;
$dt = new DateTime("2012-05-06 18:00:42", new DateTimeZone("Europe/Lisbon"));
+$dti = new DateTimeImmutable("2012-05-06 18:00:42", new DateTimeZone("Europe/Lisbon"));
$mf = new MessageFormatter('en_US', $fmt);
var_dump($mf->format(array($dt)));
+var_dump($mf->format(array($dti)));
?>
==DONE==
--EXPECTF--
string(%s) "May %d, 2012 %d:%d:42 %s"
+string(%s) "May %d, 2012 %d:%d:42 %s"
==DONE==
diff --git a/ext/intl/tests/msgfmt_format_error5.phpt b/ext/intl/tests/msgfmt_format_error5.phpt
index ebbd4550e8..d5f62f9baf 100644
--- a/ext/intl/tests/msgfmt_format_error5.phpt
+++ b/ext/intl/tests/msgfmt_format_error5.phpt
@@ -20,7 +20,7 @@ $mf = new MessageFormatter('en_US', $fmt);
var_dump($mf->format(array("foo" => new stdclass())));
--EXPECTF--
-Warning: MessageFormatter::format(): msgfmt_format: invalid object type for date/time (only IntlCalendar and DateTime permitted) in %s on line %d
+Warning: MessageFormatter::format(): msgfmt_format: invalid object type for date/time (only IntlCalendar and DateTimeInterface permitted) in %s on line %d
Warning: MessageFormatter::format(): The argument for key 'foo' cannot be used as a date or time in %s on line %d
bool(false)
diff --git a/ext/intl/tests/timezone_IDforWindowsID_basic.phpt b/ext/intl/tests/timezone_IDforWindowsID_basic.phpt
new file mode 100644
index 0000000000..4127d8e31c
--- /dev/null
+++ b/ext/intl/tests/timezone_IDforWindowsID_basic.phpt
@@ -0,0 +1,46 @@
+--TEST--
+IntlTimeZone::getIDForWindowsID basic test
+--SKIPIF--
+<?php
+if (!extension_loaded('intl'))
+ die('skip intl extension not enabled');
+if (version_compare(INTL_ICU_VERSION, '52') < 0)
+ die('skip for ICU >= 52');
+--FILE--
+<?php
+
+$tzs = array(
+ 'Gnomeregan' => array(NULL),
+ 'India Standard Time' => array(NULL),
+ 'Pacific Standard Time' => array('001', 'CA', 'MX', 'US', 'ZZ'),
+ 'Romance Standard Time' => array('001', 'BE', 'DK', 'ES', 'FR'),
+);
+
+foreach ($tzs as $tz => $regions) {
+ echo "** $tz\n";
+ foreach ($regions as $region) {
+ var_dump(IntlTimeZone::getIDForWindowsID($tz, $region));
+ if (intl_get_error_code() != U_ZERO_ERROR) {
+ echo "Error: ", intl_get_error_message(), "\n";
+ }
+ }
+}
+
+--EXPECT--
+** Gnomeregan
+bool(false)
+Error: intltz_get_windows_id: Unknown windows timezone: U_ILLEGAL_ARGUMENT_ERROR
+** India Standard Time
+string(13) "Asia/Calcutta"
+** Pacific Standard Time
+string(19) "America/Los_Angeles"
+string(17) "America/Vancouver"
+string(15) "America/Tijuana"
+string(19) "America/Los_Angeles"
+string(7) "PST8PDT"
+** Romance Standard Time
+string(12) "Europe/Paris"
+string(15) "Europe/Brussels"
+string(17) "Europe/Copenhagen"
+string(13) "Europe/Madrid"
+string(12) "Europe/Paris"
diff --git a/ext/intl/tests/timezone_getCanonicalID_error.phpt b/ext/intl/tests/timezone_getCanonicalID_error.phpt
index e268e216a8..b29ca67701 100644
--- a/ext/intl/tests/timezone_getCanonicalID_error.phpt
+++ b/ext/intl/tests/timezone_getCanonicalID_error.phpt
@@ -11,7 +11,6 @@ ini_set("intl.error_level", E_WARNING);
var_dump(IntlTimeZone::getCanonicalID());
var_dump(IntlTimeZone::getCanonicalID(array()));
var_dump(IntlTimeZone::getCanonicalID("foo\x81"));
-var_dump(IntlTimeZone::getCanonicalID('foobar', null));
--EXPECTF--
@@ -28,8 +27,3 @@ bool(false)
Warning: IntlTimeZone::getCanonicalID(): intltz_get_canonical_id: could not convert time zone id to UTF-16 in %s on line %d
bool(false)
-
-Fatal error: Uncaught Error: Cannot pass parameter 2 by reference in %s:%d
-Stack trace:
-#0 {main}
- thrown in %s on line %d
diff --git a/ext/intl/tests/timezone_windowsID_basic.phpt b/ext/intl/tests/timezone_windowsID_basic.phpt
new file mode 100644
index 0000000000..dd48f016e2
--- /dev/null
+++ b/ext/intl/tests/timezone_windowsID_basic.phpt
@@ -0,0 +1,43 @@
+--TEST--
+IntlTimeZone::getWindowsID basic test
+--SKIPIF--
+<?php
+if (!extension_loaded('intl'))
+ die('skip intl extension not enabled');
+if (version_compare(INTL_ICU_VERSION, '52') < 0)
+ die('skip for ICU >= 52');
+--FILE--
+<?php
+
+$tzs = array(
+ 'America/Bogota',
+ 'America/Havana',
+ 'America/Indiana/Knox',
+ 'America/Los_Angeles',
+ 'Azeroth/Kalimdor/Durotar',
+ 'Africa/Casablanca',
+ 'Asia/Singapore',
+ 'Australia/Perth',
+ 'Europe/London',
+ 'Europe/Istanbul',
+);
+
+foreach ($tzs as $tz) {
+ var_dump(IntlTimeZone::getWindowsID($tz));
+ if (intl_get_error_code() != U_ZERO_ERROR) {
+ echo "Error: ", intl_get_error_message(), "\n";
+ }
+}
+
+--EXPECT--
+string(24) "SA Pacific Standard Time"
+string(21) "Eastern Standard Time"
+string(21) "Central Standard Time"
+string(21) "Pacific Standard Time"
+bool(false)
+Error: intltz_get_windows_id: Unknown system timezone: U_ILLEGAL_ARGUMENT_ERROR
+string(21) "Morocco Standard Time"
+string(23) "Singapore Standard Time"
+string(26) "W. Australia Standard Time"
+string(17) "GMT Standard Time"
+string(20) "Turkey Standard Time"
diff --git a/ext/intl/timezone/timezone_class.cpp b/ext/intl/timezone/timezone_class.cpp
index d1e8e2e0a6..f67e55ae4e 100644
--- a/ext/intl/timezone/timezone_class.cpp
+++ b/ext/intl/timezone/timezone_class.cpp
@@ -439,6 +439,17 @@ ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(ainfo_tz_void, 0, 0, 0)
ZEND_END_ARG_INFO()
+#if U_ICU_VERSION_MAJOR_NUM >= 52
+ZEND_BEGIN_ARG_INFO_EX(ainfo_tz_getWindowsID, 0, ZEND_RETURN_VALUE, 1)
+ ZEND_ARG_INFO(0, timezone)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(ainfo_tz_getIDForWindowsID, 0, ZEND_RETURN_VALUE, 1)
+ ZEND_ARG_INFO(0, timezone)
+ ZEND_ARG_INFO(0, region)
+ZEND_END_ARG_INFO()
+#endif
+
/* }}} */
/* {{{ TimeZone_class_functions
@@ -475,6 +486,10 @@ static zend_function_entry TimeZone_class_functions[] = {
PHP_ME_MAPPING(toDateTimeZone, intltz_to_date_time_zone, ainfo_tz_void, ZEND_ACC_PUBLIC)
PHP_ME_MAPPING(getErrorCode, intltz_get_error_code, ainfo_tz_void, ZEND_ACC_PUBLIC)
PHP_ME_MAPPING(getErrorMessage, intltz_get_error_message, ainfo_tz_void, ZEND_ACC_PUBLIC)
+#if U_ICU_VERSION_MAJOR_NUM >= 52
+ PHP_ME_MAPPING(getWindowsID, intltz_get_windows_id, ainfo_tz_getWindowsID, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
+ PHP_ME_MAPPING(getIDForWindowsID, intltz_get_id_for_windows_id, ainfo_tz_getIDForWindowsID, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
+#endif
PHP_FE_END
};
/* }}} */
diff --git a/ext/intl/timezone/timezone_methods.cpp b/ext/intl/timezone/timezone_methods.cpp
index a35174d3da..20c0e02480 100644
--- a/ext/intl/timezone/timezone_methods.cpp
+++ b/ext/intl/timezone/timezone_methods.cpp
@@ -647,3 +647,81 @@ U_CFUNC PHP_FUNCTION(intltz_get_error_message)
message = intl_error_get_message(TIMEZONE_ERROR_P(to));
RETURN_STR(message);
}
+
+#if U_ICU_VERSION_MAJOR_NUM >= 52
+/* {{{ proto string IntlTimeZone::getWindowsID(string $timezone)
+ proto string intltz_get_windows_id(string $timezone)
+Translate a system timezone (e.g. "America/Los_Angeles" into a
+Windows Timezone (e.g. "Pacific Standard Time")
+ */
+U_CFUNC PHP_FUNCTION(intltz_get_windows_id)
+{
+ zend_string *id, *winID;
+ UnicodeString uID, uWinID;
+ UErrorCode error;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &id) == FAILURE) {
+ return;
+ }
+
+ error = U_ZERO_ERROR;
+ if (intl_stringFromChar(uID, id->val, id->len, &error) == FAILURE) {
+ intl_error_set(NULL, error,
+ "intltz_get_windows_id: could not convert time zone id to UTF-16", 0);
+ RETURN_FALSE;
+ }
+
+ error = U_ZERO_ERROR;
+ TimeZone::getWindowsID(uID, uWinID, error);
+ INTL_CHECK_STATUS(error, "intltz_get_windows_id: Unable to get timezone from windows ID");
+ if (uWinID.length() == 0) {
+ intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "intltz_get_windows_id: Unknown system timezone", 0);
+ RETURN_FALSE;
+ }
+
+ error = U_ZERO_ERROR;
+ winID = intl_convert_utf16_to_utf8(uWinID.getBuffer(), uWinID.length(), &error);
+ INTL_CHECK_STATUS(error, "intltz_get_windows_id: could not convert time zone id to UTF-8");
+ RETURN_STR(winID);
+}
+/* }}} */
+
+/* {{{ proto string IntlTimeZone::getIDForWindowsID(string $timezone[, string $region = NULL])
+ proto string intltz_get_id_for_windows_id(string $timezone[, string $region = NULL])
+Translate a windows timezone (e.g. "Pacific Time Zone" into a
+System Timezone (e.g. "America/Los_Angeles")
+ */
+U_CFUNC PHP_FUNCTION(intltz_get_id_for_windows_id)
+{
+ zend_string *winID, *region = NULL, *id;
+ UnicodeString uWinID, uID;
+ UErrorCode error;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|S", &winID, &region) == FAILURE) {
+ return;
+ }
+
+ error = U_ZERO_ERROR;
+ if (intl_stringFromChar(uWinID, winID->val, winID->len, &error) == FAILURE) {
+ intl_error_set(NULL, error,
+ "intltz_get_id_for_windows_id: could not convert time zone id to UTF-16", 0);
+ RETURN_FALSE;
+ }
+
+ error = U_ZERO_ERROR;
+ TimeZone::getIDForWindowsID(uWinID, region ? region->val : NULL, uID, error);
+ INTL_CHECK_STATUS(error, "intltz_get_id_for_windows_id: Unable to get windows ID for timezone");
+ if (uID.length() == 0) {
+ intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "intltz_get_windows_id: Unknown windows timezone", 0);
+ RETURN_FALSE;
+ }
+
+ error = U_ZERO_ERROR;
+ id = intl_convert_utf16_to_utf8(uID.getBuffer(), uID.length(), &error);
+ INTL_CHECK_STATUS(error, "intltz_get_id_for_windows_id: could not convert time zone id to UTF-8");
+ RETURN_STR(id);
+}
+/* }}} */
+#endif
diff --git a/ext/intl/timezone/timezone_methods.h b/ext/intl/timezone/timezone_methods.h
index 29d72913fd..6e6fa3f472 100644
--- a/ext/intl/timezone/timezone_methods.h
+++ b/ext/intl/timezone/timezone_methods.h
@@ -65,4 +65,9 @@ PHP_FUNCTION(intltz_get_error_code);
PHP_FUNCTION(intltz_get_error_message);
+#if U_ICU_VERSION_MAJOR_NUM >= 52
+PHP_FUNCTION(intltz_get_windows_id);
+PHP_FUNCTION(intltz_get_id_for_windows_id);
+#endif
+
#endif /* #ifndef TIMEZONE_METHODS_H */
diff --git a/ext/intl/transliterator/transliterator_class.c b/ext/intl/transliterator/transliterator_class.c
index ce8c7e6291..4325a45d06 100644
--- a/ext/intl/transliterator/transliterator_class.c
+++ b/ext/intl/transliterator/transliterator_class.c
@@ -97,14 +97,6 @@ static void transliterator_object_destroy( Transliterator_object* to )
}
/* }}} */
-/* {{{ Transliterator_objects_dtor */
-static void Transliterator_objects_dtor(
- zend_object *object )
-{
- zend_objects_destroy_object( object );
-}
-/* }}} */
-
/* {{{ Transliterator_objects_free */
static void Transliterator_objects_free( zend_object *object )
{
@@ -183,7 +175,7 @@ err:
"Could not clone transliterator", 0 );
err_msg = intl_error_get_message( TRANSLITERATOR_ERROR_P( to_orig ) );
- php_error_docref( NULL, E_ERROR, "%s", ZSTR_VAL(err_msg) );
+ zend_throw_error( NULL, "%s", ZSTR_VAL(err_msg) );
zend_string_free( err_msg ); /* if it's changed into a warning */
/* do not destroy tempz; we need to return something */
}
@@ -269,9 +261,15 @@ static zval *Transliterator_read_property( zval *object, zval *member, int type,
static void Transliterator_write_property( zval *object, zval *member, zval *value,
void **cache_slot )
{
+ zend_class_entry *scope;
TRANSLITERATOR_PROPERTY_HANDLER_PROLOG;
- if( ( EG( scope ) != Transliterator_ce_ptr ) &&
+ if (EG(fake_scope)) {
+ scope = EG(fake_scope);
+ } else {
+ scope = zend_get_executed_scope();
+ }
+ if( ( scope != Transliterator_ce_ptr ) &&
( zend_binary_strcmp( "id", sizeof( "id" ) - 1,
Z_STRVAL_P( member ), Z_STRLEN_P( member ) ) == 0 ) )
{
@@ -305,20 +303,11 @@ ZEND_BEGIN_ARG_INFO_EX( ainfo_trans_create_from_rules, 0, 0, 1 )
ZEND_ARG_INFO( 0, direction )
ZEND_END_ARG_INFO()
-ZEND_BEGIN_ARG_INFO_EX( ainfo_trans_create_inverse, 0, 0, 1 )
- ZEND_ARG_OBJ_INFO( 0, orig_trans, Transliterator, 0 )
-ZEND_END_ARG_INFO()
-
ZEND_BEGIN_ARG_INFO_EX( ainfo_trans_me_transliterate, 0, 0, 1 )
ZEND_ARG_INFO( 0, subject )
ZEND_ARG_INFO( 0, start )
ZEND_ARG_INFO( 0, end )
ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX( ainfo_trans_error, 0, 0, 1 )
- ZEND_ARG_OBJ_INFO( 0, trans, Transliterator, 0 )
-ZEND_END_ARG_INFO()
-
/* }}} */
/* {{{ Transliterator_class_functions
@@ -351,7 +340,6 @@ void transliterator_register_Transliterator_class( void )
memcpy( &Transliterator_handlers, zend_get_std_object_handlers(),
sizeof Transliterator_handlers );
Transliterator_handlers.offset = XtOffsetOf(Transliterator_object, zo);
- Transliterator_handlers.dtor_obj = Transliterator_objects_dtor;
Transliterator_handlers.free_obj = Transliterator_objects_free;
Transliterator_handlers.clone_obj = Transliterator_clone_obj;
Transliterator_handlers.get_property_ptr_ptr = Transliterator_get_property_ptr_ptr;
diff --git a/ext/intl/uchar/uchar.c b/ext/intl/uchar/uchar.c
index 7abc2a3623..1e0041f03c 100644
--- a/ext/intl/uchar/uchar.c
+++ b/ext/intl/uchar/uchar.c
@@ -8,12 +8,21 @@
static inline int convert_cp(UChar32* pcp, zval *zcp) {
zend_long cp = -1;
+
if (Z_TYPE_P(zcp) == IS_LONG) {
cp = Z_LVAL_P(zcp);
} else if (Z_TYPE_P(zcp) == IS_STRING) {
- int i = 0;
- U8_NEXT(Z_STRVAL_P(zcp), i, Z_STRLEN_P(zcp), cp);
- if (i != Z_STRLEN_P(zcp)) {
+ int32_t i = 0;
+ size_t zcp_len = Z_STRLEN_P(zcp);
+
+ if (ZEND_SIZE_T_INT_OVFL(zcp_len)) {
+ intl_error_set_code(NULL, U_ILLEGAL_ARGUMENT_ERROR);
+ intl_error_set_custom_msg(NULL, "Input string is too long.", 0);
+ return FAILURE;
+ }
+
+ U8_NEXT(Z_STRVAL_P(zcp), i, zcp_len, cp);
+ if ((size_t)i != zcp_len) {
intl_error_set_code(NULL, U_ILLEGAL_ARGUMENT_ERROR);
intl_error_set_custom_msg(NULL, "Passing a UTF-8 character for codepoint requires a string which is exactly one UTF-8 codepoint long.", 0);
return FAILURE;
diff --git a/ext/json/config.m4 b/ext/json/config.m4
index fb87a93992..1411e83faa 100644
--- a/ext/json/config.m4
+++ b/ext/json/config.m4
@@ -15,7 +15,7 @@ PHP_NEW_EXTENSION(json,
json_parser.tab.c \
json_scanner.c,
$ext_shared,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1)
- PHP_INSTALL_HEADERS([ext/json], [php_json.h])
+ PHP_INSTALL_HEADERS([ext/json], [php_json.h php_json_parser.h php_json_scanner.h])
PHP_ADD_MAKEFILE_FRAGMENT()
PHP_SUBST(JSON_SHARED_LIBADD)
fi
diff --git a/ext/json/json.c b/ext/json/json.c
index 24065341d2..13e3ece349 100644
--- a/ext/json/json.c
+++ b/ext/json/json.c
@@ -33,14 +33,6 @@
#include "php_json_parser.h"
#include <zend_exceptions.h>
-#include <float.h>
-#if defined(DBL_MANT_DIG) && defined(DBL_MIN_EXP)
-#define NUM_BUF_SIZE (3 + DBL_MANT_DIG - DBL_MIN_EXP)
-#else
-#define NUM_BUF_SIZE 1080
-#endif
-
-
static PHP_MINFO_FUNCTION(json);
static PHP_FUNCTION(json_encode);
static PHP_FUNCTION(json_decode);
@@ -117,6 +109,7 @@ static PHP_MINIT_FUNCTION(json)
PHP_JSON_REGISTER_CONSTANT("JSON_UNESCAPED_UNICODE", PHP_JSON_UNESCAPED_UNICODE);
PHP_JSON_REGISTER_CONSTANT("JSON_PARTIAL_OUTPUT_ON_ERROR", PHP_JSON_PARTIAL_OUTPUT_ON_ERROR);
PHP_JSON_REGISTER_CONSTANT("JSON_PRESERVE_ZERO_FRACTION", PHP_JSON_PRESERVE_ZERO_FRACTION);
+ PHP_JSON_REGISTER_CONSTANT("JSON_UNESCAPED_LINE_TERMINATORS", PHP_JSON_UNESCAPED_LINE_TERMINATORS);
/* options for json_decode */
PHP_JSON_REGISTER_CONSTANT("JSON_OBJECT_AS_ARRAY", PHP_JSON_OBJECT_AS_ARRAY);
@@ -191,13 +184,23 @@ static PHP_MINFO_FUNCTION(json)
}
/* }}} */
-PHP_JSON_API void php_json_encode(smart_str *buf, zval *val, int options) /* {{{ */
+PHP_JSON_API int php_json_encode(smart_str *buf, zval *val, int options) /* {{{ */
{
- php_json_encode_zval(buf, val, options);
+ php_json_encoder encoder;
+ int return_code;
+
+ php_json_encode_init(&encoder);
+ encoder.max_depth = JSON_G(encode_max_depth);
+ encoder.error_code = PHP_JSON_ERROR_NONE;
+
+ return_code = php_json_encode_zval(buf, val, options, &encoder);
+ JSON_G(error_code) = encoder.error_code;
+
+ return return_code;
}
/* }}} */
-PHP_JSON_API void php_json_decode_ex(zval *return_value, char *str, size_t str_len, zend_long options, zend_long depth) /* {{{ */
+PHP_JSON_API int php_json_decode_ex(zval *return_value, char *str, size_t str_len, zend_long options, zend_long depth) /* {{{ */
{
php_json_parser parser;
@@ -205,8 +208,11 @@ PHP_JSON_API void php_json_decode_ex(zval *return_value, char *str, size_t str_l
if (php_json_yyparse(&parser)) {
JSON_G(error_code) = php_json_parser_error_code(&parser);
- RETURN_NULL();
+ RETVAL_NULL();
+ return FAILURE;
}
+
+ return SUCCESS;
}
/* }}} */
@@ -215,6 +221,7 @@ PHP_JSON_API void php_json_decode_ex(zval *return_value, char *str, size_t str_l
static PHP_FUNCTION(json_encode)
{
zval *parameter;
+ php_json_encoder encoder;
smart_str buf = {0};
zend_long options = 0;
zend_long depth = PHP_JSON_PARSER_DEFAULT_DEPTH;
@@ -223,19 +230,22 @@ static PHP_FUNCTION(json_encode)
return;
}
- JSON_G(error_code) = PHP_JSON_ERROR_NONE;
-
- JSON_G(encode_max_depth) = (int)depth;
-
- php_json_encode(&buf, parameter, (int)options);
+ php_json_encode_init(&encoder);
+ encoder.max_depth = (int)depth;
+ encoder.error_code = PHP_JSON_ERROR_NONE;
+ php_json_encode_zval(&buf, parameter, (int)options, &encoder);
+ JSON_G(error_code) = encoder.error_code;
- if (JSON_G(error_code) != PHP_JSON_ERROR_NONE && !(options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR)) {
+ if (encoder.error_code != PHP_JSON_ERROR_NONE && !(options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR)) {
smart_str_free(&buf);
- ZVAL_FALSE(return_value);
- } else {
- smart_str_0(&buf); /* copy? */
- ZVAL_NEW_STR(return_value, buf.s);
+ RETURN_FALSE;
+ }
+
+ smart_str_0(&buf); /* copy? */
+ if (buf.s) {
+ RETURN_NEW_STR(buf.s);
}
+ RETURN_EMPTY_STRING();
}
/* }}} */
diff --git a/ext/json/json_encoder.c b/ext/json/json_encoder.c
index 43f78cb17c..c5f92e1919 100644
--- a/ext/json/json_encoder.c
+++ b/ext/json/json_encoder.c
@@ -29,19 +29,14 @@
#include "ext/standard/html.h"
#include "zend_smart_str.h"
#include "php_json.h"
+#include "php_json_encoder.h"
#include <zend_exceptions.h>
-/* double limits */
-#include <float.h>
-#if defined(DBL_MANT_DIG) && defined(DBL_MIN_EXP)
-#define PHP_JSON_DOUBLE_MAX_LENGTH (3 + DBL_MANT_DIG - DBL_MIN_EXP)
-#else
-#define PHP_JSON_DOUBLE_MAX_LENGTH 1080
-#endif
-
static const char digits[] = "0123456789abcdef";
-static void php_json_escape_string(smart_str *buf, char *s, size_t len, int options);
+static int php_json_escape_string(
+ smart_str *buf, char *s, size_t len,
+ int options, php_json_encoder *encoder);
static int php_json_determine_array_type(zval *val) /* {{{ */
{
@@ -53,6 +48,10 @@ static int php_json_determine_array_type(zval *val) /* {{{ */
zend_string *key;
zend_ulong index, idx;
+ if (HT_IS_PACKED(myht) && HT_IS_WITHOUT_HOLES(myht)) {
+ return PHP_JSON_OUTPUT_ARRAY;
+ }
+
idx = 0;
ZEND_HASH_FOREACH_KEY(myht, index, key) {
if (key) {
@@ -80,12 +79,12 @@ static inline void php_json_pretty_print_char(smart_str *buf, int options, char
}
/* }}} */
-static inline void php_json_pretty_print_indent(smart_str *buf, int options) /* {{{ */
+static inline void php_json_pretty_print_indent(smart_str *buf, int options, php_json_encoder *encoder) /* {{{ */
{
int i;
if (options & PHP_JSON_PRETTY_PRINT) {
- for (i = 0; i < JSON_G(encoder_depth); ++i) {
+ for (i = 0; i < encoder->depth; ++i) {
smart_str_appendl(buf, " ", 4);
}
}
@@ -103,10 +102,11 @@ static inline int php_json_is_valid_double(double d) /* {{{ */
static inline void php_json_encode_double(smart_str *buf, double d, int options) /* {{{ */
{
size_t len;
- char num[PHP_JSON_DOUBLE_MAX_LENGTH];
- php_gcvt(d, (int)EG(precision), '.', 'e', &num[0]);
+ char num[PHP_DOUBLE_MAX_LENGTH];
+
+ php_gcvt(d, (int)PG(serialize_precision), '.', 'e', num);
len = strlen(num);
- if (options & PHP_JSON_PRESERVE_ZERO_FRACTION && strchr(num, '.') == NULL && len < PHP_JSON_DOUBLE_MAX_LENGTH - 2) {
+ if (options & PHP_JSON_PRESERVE_ZERO_FRACTION && strchr(num, '.') == NULL && len < PHP_DOUBLE_MAX_LENGTH - 2) {
num[len++] = '.';
num[len++] = '0';
num[len] = '\0';
@@ -115,7 +115,21 @@ static inline void php_json_encode_double(smart_str *buf, double d, int options)
}
/* }}} */
-static void php_json_encode_array(smart_str *buf, zval *val, int options) /* {{{ */
+#define PHP_JSON_HASH_APPLY_PROTECTION_INC(_tmp_ht) \
+ do { \
+ if (tmp_ht && ZEND_HASH_APPLY_PROTECTION(_tmp_ht)) { \
+ ZEND_HASH_INC_APPLY_COUNT(_tmp_ht); \
+ } \
+ } while (0)
+
+#define PHP_JSON_HASH_APPLY_PROTECTION_DEC(_tmp_ht) \
+ do { \
+ if (tmp_ht && ZEND_HASH_APPLY_PROTECTION(_tmp_ht)) { \
+ ZEND_HASH_DEC_APPLY_COUNT(_tmp_ht); \
+ } \
+ } while (0)
+
+static int php_json_encode_array(smart_str *buf, zval *val, int options, php_json_encoder *encoder) /* {{{ */
{
int i, r, need_comma = 0;
HashTable *myht;
@@ -129,9 +143,9 @@ static void php_json_encode_array(smart_str *buf, zval *val, int options) /* {{{
}
if (myht && ZEND_HASH_GET_APPLY_COUNT(myht) > 1) {
- JSON_G(error_code) = PHP_JSON_ERROR_RECURSION;
+ encoder->error_code = PHP_JSON_ERROR_RECURSION;
smart_str_appendl(buf, "null", 4);
- return;
+ return FAILURE;
}
if (r == PHP_JSON_OUTPUT_ARRAY) {
@@ -140,7 +154,7 @@ static void php_json_encode_array(smart_str *buf, zval *val, int options) /* {{{
smart_str_appendc(buf, '{');
}
- ++JSON_G(encoder_depth);
+ ++encoder->depth;
i = myht ? zend_hash_num_elements(myht) : 0;
@@ -153,9 +167,7 @@ static void php_json_encode_array(smart_str *buf, zval *val, int options) /* {{{
ZEND_HASH_FOREACH_KEY_VAL_IND(myht, index, key, data) {
ZVAL_DEREF(data);
tmp_ht = HASH_OF(data);
- if (tmp_ht && ZEND_HASH_APPLY_PROTECTION(tmp_ht)) {
- ZEND_HASH_INC_APPLY_COUNT(tmp_ht);
- }
+ PHP_JSON_HASH_APPLY_PROTECTION_INC(tmp_ht);
if (r == PHP_JSON_OUTPUT_ARRAY) {
if (need_comma) {
@@ -165,15 +177,12 @@ static void php_json_encode_array(smart_str *buf, zval *val, int options) /* {{{
}
php_json_pretty_print_char(buf, options, '\n');
- php_json_pretty_print_indent(buf, options);
- php_json_encode(buf, data, options);
+ php_json_pretty_print_indent(buf, options, encoder);
} else if (r == PHP_JSON_OUTPUT_OBJECT) {
if (key) {
- if (ZSTR_VAL(key)[0] == '\0' && Z_TYPE_P(val) == IS_OBJECT) {
+ if (ZSTR_VAL(key)[0] == '\0' && ZSTR_LEN(key) > 0 && Z_TYPE_P(val) == IS_OBJECT) {
/* Skip protected and private members. */
- if (tmp_ht && ZEND_HASH_APPLY_PROTECTION(tmp_ht)) {
- ZEND_HASH_DEC_APPLY_COUNT(tmp_ht);
- }
+ PHP_JSON_HASH_APPLY_PROTECTION_DEC(tmp_ht);
continue;
}
@@ -184,14 +193,10 @@ static void php_json_encode_array(smart_str *buf, zval *val, int options) /* {{{
}
php_json_pretty_print_char(buf, options, '\n');
- php_json_pretty_print_indent(buf, options);
-
- php_json_escape_string(buf, ZSTR_VAL(key), ZSTR_LEN(key), options & ~PHP_JSON_NUMERIC_CHECK);
- smart_str_appendc(buf, ':');
-
- php_json_pretty_print_char(buf, options, ' ');
+ php_json_pretty_print_indent(buf, options, encoder);
- php_json_encode(buf, data, options);
+ php_json_escape_string(buf, ZSTR_VAL(key), ZSTR_LEN(key),
+ options & ~PHP_JSON_NUMERIC_CHECK, encoder);
} else {
if (need_comma) {
smart_str_appendc(buf, ',');
@@ -200,34 +205,39 @@ static void php_json_encode_array(smart_str *buf, zval *val, int options) /* {{{
}
php_json_pretty_print_char(buf, options, '\n');
- php_json_pretty_print_indent(buf, options);
+ php_json_pretty_print_indent(buf, options, encoder);
smart_str_appendc(buf, '"');
smart_str_append_long(buf, (zend_long) index);
smart_str_appendc(buf, '"');
- smart_str_appendc(buf, ':');
-
- php_json_pretty_print_char(buf, options, ' ');
-
- php_json_encode(buf, data, options);
}
+
+ smart_str_appendc(buf, ':');
+ php_json_pretty_print_char(buf, options, ' ');
}
- if (tmp_ht && ZEND_HASH_APPLY_PROTECTION(tmp_ht)) {
- ZEND_HASH_DEC_APPLY_COUNT(tmp_ht);
+ if (php_json_encode_zval(buf, data, options, encoder) == FAILURE &&
+ !(options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR)) {
+ PHP_JSON_HASH_APPLY_PROTECTION_DEC(tmp_ht);
+ return FAILURE;
}
+
+ PHP_JSON_HASH_APPLY_PROTECTION_DEC(tmp_ht);
} ZEND_HASH_FOREACH_END();
}
- if (JSON_G(encoder_depth) > JSON_G(encode_max_depth)) {
- JSON_G(error_code) = PHP_JSON_ERROR_DEPTH;
+ if (encoder->depth > encoder->max_depth) {
+ encoder->error_code = PHP_JSON_ERROR_DEPTH;
+ if (!(options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR)) {
+ return FAILURE;
+ }
}
- --JSON_G(encoder_depth);
+ --encoder->depth;
/* Only keep closing bracket on same line for empty arrays/objects */
if (need_comma) {
php_json_pretty_print_char(buf, options, '\n');
- php_json_pretty_print_indent(buf, options);
+ php_json_pretty_print_indent(buf, options, encoder);
}
if (r == PHP_JSON_OUTPUT_ARRAY) {
@@ -235,6 +245,8 @@ static void php_json_encode_array(smart_str *buf, zval *val, int options) /* {{{
} else {
smart_str_appendc(buf, '}');
}
+
+ return SUCCESS;
}
/* }}} */
@@ -275,7 +287,9 @@ static int php_json_utf8_to_utf16(unsigned short *utf16, char utf8[], size_t len
}
/* }}} */
-static void php_json_escape_string(smart_str *buf, char *s, size_t len, int options) /* {{{ */
+static int php_json_escape_string(
+ smart_str *buf, char *s, size_t len,
+ int options, php_json_encoder *encoder) /* {{{ */
{
int status;
unsigned int us;
@@ -283,7 +297,7 @@ static void php_json_escape_string(smart_str *buf, char *s, size_t len, int opti
if (len == 0) {
smart_str_appendl(buf, "\"\"", 2);
- return;
+ return SUCCESS;
}
if (options & PHP_JSON_NUMERIC_CHECK) {
@@ -294,10 +308,10 @@ static void php_json_escape_string(smart_str *buf, char *s, size_t len, int opti
if ((type = is_numeric_string(s, len, &p, &d, 0)) != 0) {
if (type == IS_LONG) {
smart_str_append_long(buf, p);
- return;
+ return SUCCESS;
} else if (type == IS_DOUBLE && php_json_is_valid_double(d)) {
php_json_encode_double(buf, d, options);
- return;
+ return SUCCESS;
}
}
@@ -306,9 +320,11 @@ static void php_json_escape_string(smart_str *buf, char *s, size_t len, int opti
if (options & PHP_JSON_UNESCAPED_UNICODE) {
/* validate UTF-8 string first */
if (php_json_utf8_to_utf16(NULL, s, len) < 0) {
- JSON_G(error_code) = PHP_JSON_ERROR_UTF8;
- smart_str_appendl(buf, "null", 4);
- return;
+ encoder->error_code = PHP_JSON_ERROR_UTF8;
+ if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) {
+ smart_str_appendl(buf, "null", 4);
+ }
+ return FAILURE;
}
}
@@ -321,16 +337,27 @@ static void php_json_escape_string(smart_str *buf, char *s, size_t len, int opti
do {
us = (unsigned char)s[pos];
- if (us >= 0x80 && !(options & PHP_JSON_UNESCAPED_UNICODE)) {
+ if (us >= 0x80 && (!(options & PHP_JSON_UNESCAPED_UNICODE) || us == 0xE2)) {
/* UTF-8 character */
us = php_next_utf8_char((const unsigned char *)s, len, &pos, &status);
if (status != SUCCESS) {
if (buf->s) {
ZSTR_LEN(buf->s) = checkpoint;
}
- JSON_G(error_code) = PHP_JSON_ERROR_UTF8;
- smart_str_appendl(buf, "null", 4);
- return;
+ encoder->error_code = PHP_JSON_ERROR_UTF8;
+ if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) {
+ smart_str_appendl(buf, "null", 4);
+ }
+ return FAILURE;
+ }
+ /* Escape U+2028/U+2029 line terminators, UNLESS both
+ JSON_UNESCAPED_UNICODE and
+ JSON_UNESCAPED_LINE_TERMINATORS were provided */
+ if ((options & PHP_JSON_UNESCAPED_UNICODE)
+ && ((options & PHP_JSON_UNESCAPED_LINE_TERMINATORS)
+ || us < 0x2028 || us > 0x2029)) {
+ smart_str_appendl(buf, &s[pos - 3], 3);
+ continue;
}
/* From http://en.wikipedia.org/wiki/UTF16 */
if (us >= 0x10000) {
@@ -440,15 +467,17 @@ static void php_json_escape_string(smart_str *buf, char *s, size_t len, int opti
} while (pos < len);
smart_str_appendc(buf, '"');
+
+ return SUCCESS;
}
/* }}} */
-static void php_json_encode_serializable_object(smart_str *buf, zval *val, int options) /* {{{ */
+static int php_json_encode_serializable_object(smart_str *buf, zval *val, int options, php_json_encoder *encoder) /* {{{ */
{
zend_class_entry *ce = Z_OBJCE_P(val);
zval retval, fname;
HashTable* myht;
- int origin_error_code;
+ int return_code;
if (Z_TYPE_P(val) == IS_ARRAY) {
myht = Z_ARRVAL_P(val);
@@ -457,48 +486,56 @@ static void php_json_encode_serializable_object(smart_str *buf, zval *val, int o
}
if (myht && ZEND_HASH_GET_APPLY_COUNT(myht) > 1) {
- JSON_G(error_code) = PHP_JSON_ERROR_RECURSION;
- smart_str_appendl(buf, "null", 4);
- return;
+ encoder->error_code = PHP_JSON_ERROR_RECURSION;
+ if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) {
+ smart_str_appendl(buf, "null", 4);
+ }
+ return FAILURE;
}
ZVAL_STRING(&fname, "jsonSerialize");
- origin_error_code = JSON_G(error_code);
if (FAILURE == call_user_function_ex(EG(function_table), val, &fname, &retval, 0, NULL, 1, NULL) || Z_TYPE(retval) == IS_UNDEF) {
if (!EG(exception)) {
zend_throw_exception_ex(NULL, 0, "Failed calling %s::jsonSerialize()", ZSTR_VAL(ce->name));
}
- smart_str_appendl(buf, "null", sizeof("null") - 1);
zval_ptr_dtor(&fname);
- return;
+
+ if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) {
+ smart_str_appendl(buf, "null", 4);
+ }
+ return FAILURE;
}
- JSON_G(error_code) = origin_error_code;
if (EG(exception)) {
/* Error already raised */
zval_ptr_dtor(&retval);
zval_ptr_dtor(&fname);
- smart_str_appendl(buf, "null", sizeof("null") - 1);
- return;
+
+ if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) {
+ smart_str_appendl(buf, "null", 4);
+ }
+ return FAILURE;
}
if ((Z_TYPE(retval) == IS_OBJECT) &&
(Z_OBJ(retval) == Z_OBJ_P(val))) {
/* Handle the case where jsonSerialize does: return $this; by going straight to encode array */
- php_json_encode_array(buf, &retval, options);
+ return_code = php_json_encode_array(buf, &retval, options, encoder);
} else {
/* All other types, encode as normal */
- php_json_encode(buf, &retval, options);
+ return_code = php_json_encode_zval(buf, &retval, options, encoder);
}
zval_ptr_dtor(&retval);
zval_ptr_dtor(&fname);
+
+ return return_code;
}
/* }}} */
-void php_json_encode_zval(smart_str *buf, zval *val, int options) /* {{{ */
+int php_json_encode_zval(smart_str *buf, zval *val, int options, php_json_encoder *encoder) /* {{{ */
{
again:
switch (Z_TYPE_P(val))
@@ -522,36 +559,35 @@ again:
if (php_json_is_valid_double(Z_DVAL_P(val))) {
php_json_encode_double(buf, Z_DVAL_P(val), options);
} else {
- JSON_G(error_code) = PHP_JSON_ERROR_INF_OR_NAN;
+ encoder->error_code = PHP_JSON_ERROR_INF_OR_NAN;
smart_str_appendc(buf, '0');
}
break;
case IS_STRING:
- php_json_escape_string(buf, Z_STRVAL_P(val), Z_STRLEN_P(val), options);
- break;
+ return php_json_escape_string(buf, Z_STRVAL_P(val), Z_STRLEN_P(val), options, encoder);
case IS_OBJECT:
if (instanceof_function(Z_OBJCE_P(val), php_json_serializable_ce)) {
- php_json_encode_serializable_object(buf, val, options);
- break;
+ return php_json_encode_serializable_object(buf, val, options, encoder);
}
/* fallthrough -- Non-serializable object */
case IS_ARRAY:
- php_json_encode_array(buf, val, options);
- break;
+ return php_json_encode_array(buf, val, options, encoder);
case IS_REFERENCE:
val = Z_REFVAL_P(val);
goto again;
default:
- JSON_G(error_code) = PHP_JSON_ERROR_UNSUPPORTED_TYPE;
- smart_str_appendl(buf, "null", 4);
- break;
+ encoder->error_code = PHP_JSON_ERROR_UNSUPPORTED_TYPE;
+ if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) {
+ smart_str_appendl(buf, "null", 4);
+ }
+ return FAILURE;
}
- return;
+ return SUCCESS;
}
/* }}} */
diff --git a/ext/json/json_parser.tab.c b/ext/json/json_parser.tab.c
index c429a85361..c5247e5f04 100644
--- a/ext/json/json_parser.tab.c
+++ b/ext/json/json_parser.tab.c
@@ -98,6 +98,14 @@ int json_yydebug = 1;
#define PHP_JSON_USE_1(uvr, uv1) PHP_JSON_USE(uvr); PHP_JSON_USE(uv1)
#define PHP_JSON_USE_2(uvr, uv1, uv2) PHP_JSON_USE(uvr); PHP_JSON_USE(uv1); PHP_JSON_USE(uv2)
+#define PHP_JSON_DEPTH_DEC --parser->depth
+#define PHP_JSON_DEPTH_INC \
+ if (parser->max_depth && parser->depth >= parser->max_depth) { \
+ parser->scanner.errcode = PHP_JSON_ERROR_DEPTH; \
+ YYERROR; \
+ } \
+ ++parser->depth
+
@@ -131,8 +139,8 @@ int json_yydebug = 1;
/* In a future release of Bison, this section will be replaced
by #include "json_parser.tab.h". */
-#ifndef YY_PHP_JSON_YY_HOME_JAKUB_PROG_PHP_MASTER_EXT_JSON_JSON_PARSER_TAB_H_INCLUDED
-# define YY_PHP_JSON_YY_HOME_JAKUB_PROG_PHP_MASTER_EXT_JSON_JSON_PARSER_TAB_H_INCLUDED
+#ifndef YY_PHP_JSON_YY_HOME_DMITRY_PHP_PHP_MASTER_EXT_JSON_JSON_PARSER_TAB_H_INCLUDED
+# define YY_PHP_JSON_YY_HOME_DMITRY_PHP_PHP_MASTER_EXT_JSON_JSON_PARSER_TAB_H_INCLUDED
/* Debug traces. */
#ifndef YYDEBUG
# define YYDEBUG 0
@@ -193,7 +201,7 @@ typedef union YYSTYPE YYSTYPE;
int php_json_yyparse (php_json_parser *parser);
-#endif /* !YY_PHP_JSON_YY_HOME_JAKUB_PROG_PHP_MASTER_EXT_JSON_JSON_PARSER_TAB_H_INCLUDED */
+#endif /* !YY_PHP_JSON_YY_HOME_DMITRY_PHP_PHP_MASTER_EXT_JSON_JSON_PARSER_TAB_H_INCLUDED */
/* Copy the second part of user declarations. */
@@ -201,20 +209,9 @@ int php_json_yyparse (php_json_parser *parser);
/* Unqualified %code blocks. */
-int php_json_yylex(union YYSTYPE *value, php_json_parser *parser);
-void php_json_yyerror(php_json_parser *parser, char const *msg);
-void php_json_parser_object_init(php_json_parser *parser, zval *object);
-int php_json_parser_object_update(php_json_parser *parser, zval *object, zend_string *key, zval *zvalue);
-void php_json_parser_array_init(zval *object);
-void php_json_parser_array_append(zval *array, zval *zvalue);
+static int php_json_yylex(union YYSTYPE *value, php_json_parser *parser);
+static void php_json_yyerror(php_json_parser *parser, char const *msg);
-#define PHP_JSON_DEPTH_DEC --parser->depth
-#define PHP_JSON_DEPTH_INC \
- if (parser->max_depth && parser->depth >= parser->max_depth) { \
- parser->scanner.errcode = PHP_JSON_ERROR_DEPTH; \
- YYERROR; \
- } \
- ++parser->depth
@@ -514,10 +511,10 @@ static const yytype_uint8 yytranslate[] =
/* YYRLINE[YYN] -- Source line where rule number YYN was defined. */
static const yytype_uint8 yyrline[] =
{
- 0, 92, 92, 98, 105, 105, 113, 114, 123, 126,
- 130, 136, 142, 149, 154, 161, 161, 169, 170, 179,
- 182, 186, 191, 196, 203, 204, 208, 209, 210, 211,
- 212, 213, 214, 215, 216, 217, 221
+ 0, 89, 89, 95, 103, 102, 120, 121, 130, 133,
+ 137, 144, 151, 158, 163, 171, 170, 188, 189, 198,
+ 201, 205, 210, 215, 222, 223, 227, 228, 229, 230,
+ 231, 232, 233, 234, 235, 236, 240
};
#endif
@@ -1465,15 +1462,23 @@ yyreduce:
case 4:
- { PHP_JSON_DEPTH_INC; }
+ {
+ PHP_JSON_DEPTH_INC;
+ if (parser->methods.object_start && FAILURE == parser->methods.object_start(parser)) {
+ YYERROR;
+ }
+ }
break;
case 5:
{
+ ZVAL_COPY_VALUE(&(yyval.value), &(yyvsp[-1].value));
PHP_JSON_DEPTH_DEC;
- (yyval.value) = (yyvsp[-1].value);
+ if (parser->methods.object_end && FAILURE == parser->methods.object_end(parser, &(yyval.value))) {
+ YYERROR;
+ }
}
break;
@@ -1490,7 +1495,7 @@ yyreduce:
case 8:
{
- php_json_parser_object_init(parser, &(yyval.value));
+ parser->methods.object_create(parser, &(yyval.value));
}
break;
@@ -1498,9 +1503,10 @@ yyreduce:
case 10:
{
- php_json_parser_object_init(parser, &(yyval.value));
- if (php_json_parser_object_update(parser, &(yyval.value), (yyvsp[0].pair).key, &(yyvsp[0].pair).val) == FAILURE)
+ parser->methods.object_create(parser, &(yyval.value));
+ if (parser->methods.object_update(parser, &(yyval.value), (yyvsp[0].pair).key, &(yyvsp[0].pair).val) == FAILURE) {
YYERROR;
+ }
}
break;
@@ -1508,8 +1514,9 @@ yyreduce:
case 11:
{
- if (php_json_parser_object_update(parser, &(yyvsp[-2].value), (yyvsp[0].pair).key, &(yyvsp[0].pair).val) == FAILURE)
+ if (parser->methods.object_update(parser, &(yyvsp[-2].value), (yyvsp[0].pair).key, &(yyvsp[0].pair).val) == FAILURE) {
YYERROR;
+ }
ZVAL_COPY_VALUE(&(yyval.value), &(yyvsp[-2].value));
}
@@ -1542,15 +1549,23 @@ yyreduce:
case 15:
- { PHP_JSON_DEPTH_INC; }
+ {
+ PHP_JSON_DEPTH_INC;
+ if (parser->methods.array_start && FAILURE == parser->methods.array_start(parser)) {
+ YYERROR;
+ }
+ }
break;
case 16:
{
- PHP_JSON_DEPTH_DEC;
ZVAL_COPY_VALUE(&(yyval.value), &(yyvsp[-1].value));
+ PHP_JSON_DEPTH_DEC;
+ if (parser->methods.array_end && FAILURE == parser->methods.array_end(parser, &(yyval.value))) {
+ YYERROR;
+ }
}
break;
@@ -1567,7 +1582,7 @@ yyreduce:
case 19:
{
- php_json_parser_array_init(&(yyval.value));
+ parser->methods.array_create(parser, &(yyval.value));
}
break;
@@ -1575,8 +1590,8 @@ yyreduce:
case 21:
{
- php_json_parser_array_init(&(yyval.value));
- php_json_parser_array_append(&(yyval.value), &(yyvsp[0].value));
+ parser->methods.array_create(parser, &(yyval.value));
+ parser->methods.array_append(parser, &(yyval.value), &(yyvsp[0].value));
}
break;
@@ -1584,7 +1599,7 @@ yyreduce:
case 22:
{
- php_json_parser_array_append(&(yyvsp[-2].value), &(yyvsp[0].value));
+ parser->methods.array_append(parser, &(yyvsp[-2].value), &(yyvsp[0].value));
ZVAL_COPY_VALUE(&(yyval.value), &(yyvsp[-2].value));
}
@@ -1839,40 +1854,34 @@ yyreturn:
/* Functions */
-void php_json_parser_init(php_json_parser *parser, zval *return_value, char *str, size_t str_len, int options, int max_depth)
+static int php_json_parser_array_create(php_json_parser *parser, zval *array)
{
- memset(parser, 0, sizeof(php_json_parser));
- php_json_scanner_init(&parser->scanner, str, str_len, options);
- parser->depth = 1;
- parser->max_depth = max_depth;
- parser->return_value = return_value;
+ return array_init(array);
}
-php_json_error_code php_json_parser_error_code(php_json_parser *parser)
+static int php_json_parser_array_append(php_json_parser *parser, zval *array, zval *zvalue)
{
- return parser->scanner.errcode;
+ zend_hash_next_index_insert(Z_ARRVAL_P(array), zvalue);
+ return SUCCESS;
}
-void php_json_parser_object_init(php_json_parser *parser, zval *object)
+static int php_json_parser_object_create(php_json_parser *parser, zval *object)
{
if (parser->scanner.options & PHP_JSON_OBJECT_AS_ARRAY) {
- array_init(object);
+ return array_init(object);
} else {
- object_init(object);
+ return object_init(object);
}
}
-int php_json_parser_object_update(php_json_parser *parser, zval *object, zend_string *key, zval *zvalue)
+static int php_json_parser_object_update(php_json_parser *parser, zval *object, zend_string *key, zval *zvalue)
{
/* if JSON_OBJECT_AS_ARRAY is set */
if (Z_TYPE_P(object) == IS_ARRAY) {
zend_symtable_update(Z_ARRVAL_P(object), key, zvalue);
} else {
zval zkey;
- if (ZSTR_LEN(key) == 0) {
- zend_string_release(key);
- key = zend_string_init("_empty_", sizeof("_empty_") - 1, 0);
- } else if (ZSTR_VAL(key)[0] == '\0') {
+ if (ZSTR_LEN(key) > 0 && ZSTR_VAL(key)[0] == '\0') {
parser->scanner.errcode = PHP_JSON_ERROR_INVALID_PROPERTY_NAME;
zend_string_release(key);
zval_dtor(zvalue);
@@ -1880,7 +1889,7 @@ int php_json_parser_object_update(php_json_parser *parser, zval *object, zend_st
return FAILURE;
}
ZVAL_NEW_STR(&zkey, key);
- zend_std_write_property(object, &zkey, zvalue, NULL);
+ zend_std_write_property(object, &zkey, zvalue, NULL);
if (Z_REFCOUNTED_P(zvalue)) {
Z_DELREF_P(zvalue);
@@ -1891,26 +1900,71 @@ int php_json_parser_object_update(php_json_parser *parser, zval *object, zend_st
return SUCCESS;
}
-void php_json_parser_array_init(zval *array)
-{
- array_init(array);
-}
-
-void php_json_parser_array_append(zval *array, zval *zvalue)
-{
- zend_hash_next_index_insert(Z_ARRVAL_P(array), zvalue);
-}
-
-int php_json_yylex(union YYSTYPE *value, php_json_parser *parser)
+static int php_json_yylex(union YYSTYPE *value, php_json_parser *parser)
{
int token = php_json_scan(&parser->scanner);
value->value = parser->scanner.value;
return token;
}
-void php_json_yyerror(php_json_parser *parser, char const *msg)
+static void php_json_yyerror(php_json_parser *parser, char const *msg)
{
if (!parser->scanner.errcode) {
parser->scanner.errcode = PHP_JSON_ERROR_SYNTAX;
}
}
+
+PHP_JSON_API php_json_error_code php_json_parser_error_code(const php_json_parser *parser)
+{
+ return parser->scanner.errcode;
+}
+
+static const php_json_parser_methods default_parser_methods =
+{
+ php_json_parser_array_create,
+ php_json_parser_array_append,
+ NULL,
+ NULL,
+ php_json_parser_object_create,
+ php_json_parser_object_update,
+ NULL,
+ NULL,
+};
+
+PHP_JSON_API void php_json_parser_init_ex(php_json_parser *parser,
+ zval *return_value,
+ char *str,
+ size_t str_len,
+ int options,
+ int max_depth,
+ const php_json_parser_methods *parser_methods)
+{
+ memset(parser, 0, sizeof(php_json_parser));
+ php_json_scanner_init(&parser->scanner, str, str_len, options);
+ parser->depth = 1;
+ parser->max_depth = max_depth;
+ parser->return_value = return_value;
+ memcpy(&parser->methods, parser_methods, sizeof(php_json_parser_methods));
+}
+
+PHP_JSON_API void php_json_parser_init(php_json_parser *parser,
+ zval *return_value,
+ char *str,
+ size_t str_len,
+ int options,
+ int max_depth)
+{
+ php_json_parser_init_ex(
+ parser,
+ return_value,
+ str,
+ str_len,
+ options,
+ max_depth,
+ &default_parser_methods);
+}
+
+PHP_JSON_API int php_json_parse(php_json_parser *parser)
+{
+ return php_json_yyparse(parser);
+}
diff --git a/ext/json/json_parser.tab.h b/ext/json/json_parser.tab.h
index 56bc2c40c9..4349b70406 100644
--- a/ext/json/json_parser.tab.h
+++ b/ext/json/json_parser.tab.h
@@ -30,8 +30,8 @@
This special exception was added by the Free Software Foundation in
version 2.2 of Bison. */
-#ifndef YY_PHP_JSON_YY_HOME_JAKUB_PROG_PHP_MASTER_EXT_JSON_JSON_PARSER_TAB_H_INCLUDED
-# define YY_PHP_JSON_YY_HOME_JAKUB_PROG_PHP_MASTER_EXT_JSON_JSON_PARSER_TAB_H_INCLUDED
+#ifndef YY_PHP_JSON_YY_HOME_DMITRY_PHP_PHP_MASTER_EXT_JSON_JSON_PARSER_TAB_H_INCLUDED
+# define YY_PHP_JSON_YY_HOME_DMITRY_PHP_PHP_MASTER_EXT_JSON_JSON_PARSER_TAB_H_INCLUDED
/* Debug traces. */
#ifndef YYDEBUG
# define YYDEBUG 0
@@ -92,4 +92,4 @@ typedef union YYSTYPE YYSTYPE;
int php_json_yyparse (php_json_parser *parser);
-#endif /* !YY_PHP_JSON_YY_HOME_JAKUB_PROG_PHP_MASTER_EXT_JSON_JSON_PARSER_TAB_H_INCLUDED */
+#endif /* !YY_PHP_JSON_YY_HOME_DMITRY_PHP_PHP_MASTER_EXT_JSON_JSON_PARSER_TAB_H_INCLUDED */
diff --git a/ext/json/json_parser.y b/ext/json/json_parser.y
index 25dc672222..e24d42c831 100644
--- a/ext/json/json_parser.y
+++ b/ext/json/json_parser.y
@@ -36,6 +36,14 @@ int json_yydebug = 1;
#define PHP_JSON_USE_1(uvr, uv1) PHP_JSON_USE(uvr); PHP_JSON_USE(uv1)
#define PHP_JSON_USE_2(uvr, uv1, uv2) PHP_JSON_USE(uvr); PHP_JSON_USE(uv1); PHP_JSON_USE(uv2)
+#define PHP_JSON_DEPTH_DEC --parser->depth
+#define PHP_JSON_DEPTH_INC \
+ if (parser->max_depth && parser->depth >= parser->max_depth) { \
+ parser->scanner.errcode = PHP_JSON_ERROR_DEPTH; \
+ YYERROR; \
+ } \
+ ++parser->depth
+
}
%pure-parser
@@ -70,20 +78,9 @@ int json_yydebug = 1;
%destructor { zend_string_release($$.key); zval_dtor(&$$.val); } <pair>
%code {
-int php_json_yylex(union YYSTYPE *value, php_json_parser *parser);
-void php_json_yyerror(php_json_parser *parser, char const *msg);
-void php_json_parser_object_init(php_json_parser *parser, zval *object);
-int php_json_parser_object_update(php_json_parser *parser, zval *object, zend_string *key, zval *zvalue);
-void php_json_parser_array_init(zval *object);
-void php_json_parser_array_append(zval *array, zval *zvalue);
+static int php_json_yylex(union YYSTYPE *value, php_json_parser *parser);
+static void php_json_yyerror(php_json_parser *parser, char const *msg);
-#define PHP_JSON_DEPTH_DEC --parser->depth
-#define PHP_JSON_DEPTH_INC \
- if (parser->max_depth && parser->depth >= parser->max_depth) { \
- parser->scanner.errcode = PHP_JSON_ERROR_DEPTH; \
- YYERROR; \
- } \
- ++parser->depth
}
%% /* Rules */
@@ -102,10 +99,20 @@ start:
;
object:
- '{' { PHP_JSON_DEPTH_INC; } members object_end
+ '{'
{
+ PHP_JSON_DEPTH_INC;
+ if (parser->methods.object_start && FAILURE == parser->methods.object_start(parser)) {
+ YYERROR;
+ }
+ }
+ members object_end
+ {
+ ZVAL_COPY_VALUE(&$$, &$3);
PHP_JSON_DEPTH_DEC;
- $$ = $3;
+ if (parser->methods.object_end && FAILURE == parser->methods.object_end(parser, &$$)) {
+ YYERROR;
+ }
}
;
@@ -121,7 +128,7 @@ object_end:
members:
/* empty */
{
- php_json_parser_object_init(parser, &$$);
+ parser->methods.object_create(parser, &$$);
}
| member
;
@@ -129,14 +136,16 @@ members:
member:
pair
{
- php_json_parser_object_init(parser, &$$);
- if (php_json_parser_object_update(parser, &$$, $1.key, &$1.val) == FAILURE)
+ parser->methods.object_create(parser, &$$);
+ if (parser->methods.object_update(parser, &$$, $1.key, &$1.val) == FAILURE) {
YYERROR;
+ }
}
| member ',' pair
{
- if (php_json_parser_object_update(parser, &$1, $3.key, &$3.val) == FAILURE)
+ if (parser->methods.object_update(parser, &$1, $3.key, &$3.val) == FAILURE) {
YYERROR;
+ }
ZVAL_COPY_VALUE(&$$, &$1);
}
| member errlex
@@ -158,10 +167,20 @@ pair:
;
array:
- '[' { PHP_JSON_DEPTH_INC; } elements array_end
+ '['
+ {
+ PHP_JSON_DEPTH_INC;
+ if (parser->methods.array_start && FAILURE == parser->methods.array_start(parser)) {
+ YYERROR;
+ }
+ }
+ elements array_end
{
- PHP_JSON_DEPTH_DEC;
ZVAL_COPY_VALUE(&$$, &$3);
+ PHP_JSON_DEPTH_DEC;
+ if (parser->methods.array_end && FAILURE == parser->methods.array_end(parser, &$$)) {
+ YYERROR;
+ }
}
;
@@ -177,7 +196,7 @@ array_end:
elements:
/* empty */
{
- php_json_parser_array_init(&$$);
+ parser->methods.array_create(parser, &$$);
}
| element
;
@@ -185,12 +204,12 @@ elements:
element:
value
{
- php_json_parser_array_init(&$$);
- php_json_parser_array_append(&$$, &$1);
+ parser->methods.array_create(parser, &$$);
+ parser->methods.array_append(parser, &$$, &$1);
}
| element ',' value
{
- php_json_parser_array_append(&$1, &$3);
+ parser->methods.array_append(parser, &$1, &$3);
ZVAL_COPY_VALUE(&$$, &$1);
}
| element errlex
@@ -224,43 +243,37 @@ errlex:
YYERROR;
}
;
-
+
%% /* Functions */
-void php_json_parser_init(php_json_parser *parser, zval *return_value, char *str, size_t str_len, int options, int max_depth)
+static int php_json_parser_array_create(php_json_parser *parser, zval *array)
{
- memset(parser, 0, sizeof(php_json_parser));
- php_json_scanner_init(&parser->scanner, str, str_len, options);
- parser->depth = 1;
- parser->max_depth = max_depth;
- parser->return_value = return_value;
+ return array_init(array);
}
-php_json_error_code php_json_parser_error_code(php_json_parser *parser)
+static int php_json_parser_array_append(php_json_parser *parser, zval *array, zval *zvalue)
{
- return parser->scanner.errcode;
+ zend_hash_next_index_insert(Z_ARRVAL_P(array), zvalue);
+ return SUCCESS;
}
-void php_json_parser_object_init(php_json_parser *parser, zval *object)
+static int php_json_parser_object_create(php_json_parser *parser, zval *object)
{
if (parser->scanner.options & PHP_JSON_OBJECT_AS_ARRAY) {
- array_init(object);
+ return array_init(object);
} else {
- object_init(object);
+ return object_init(object);
}
}
-int php_json_parser_object_update(php_json_parser *parser, zval *object, zend_string *key, zval *zvalue)
+static int php_json_parser_object_update(php_json_parser *parser, zval *object, zend_string *key, zval *zvalue)
{
/* if JSON_OBJECT_AS_ARRAY is set */
if (Z_TYPE_P(object) == IS_ARRAY) {
zend_symtable_update(Z_ARRVAL_P(object), key, zvalue);
} else {
zval zkey;
- if (ZSTR_LEN(key) == 0) {
- zend_string_release(key);
- key = zend_string_init("_empty_", sizeof("_empty_") - 1, 0);
- } else if (ZSTR_VAL(key)[0] == '\0') {
+ if (ZSTR_LEN(key) > 0 && ZSTR_VAL(key)[0] == '\0') {
parser->scanner.errcode = PHP_JSON_ERROR_INVALID_PROPERTY_NAME;
zend_string_release(key);
zval_dtor(zvalue);
@@ -268,7 +281,7 @@ int php_json_parser_object_update(php_json_parser *parser, zval *object, zend_st
return FAILURE;
}
ZVAL_NEW_STR(&zkey, key);
- zend_std_write_property(object, &zkey, zvalue, NULL);
+ zend_std_write_property(object, &zkey, zvalue, NULL);
if (Z_REFCOUNTED_P(zvalue)) {
Z_DELREF_P(zvalue);
@@ -279,26 +292,71 @@ int php_json_parser_object_update(php_json_parser *parser, zval *object, zend_st
return SUCCESS;
}
-void php_json_parser_array_init(zval *array)
-{
- array_init(array);
-}
-
-void php_json_parser_array_append(zval *array, zval *zvalue)
-{
- zend_hash_next_index_insert(Z_ARRVAL_P(array), zvalue);
-}
-
-int php_json_yylex(union YYSTYPE *value, php_json_parser *parser)
+static int php_json_yylex(union YYSTYPE *value, php_json_parser *parser)
{
int token = php_json_scan(&parser->scanner);
value->value = parser->scanner.value;
return token;
}
-void php_json_yyerror(php_json_parser *parser, char const *msg)
+static void php_json_yyerror(php_json_parser *parser, char const *msg)
{
if (!parser->scanner.errcode) {
parser->scanner.errcode = PHP_JSON_ERROR_SYNTAX;
}
}
+
+PHP_JSON_API php_json_error_code php_json_parser_error_code(const php_json_parser *parser)
+{
+ return parser->scanner.errcode;
+}
+
+static const php_json_parser_methods default_parser_methods =
+{
+ php_json_parser_array_create,
+ php_json_parser_array_append,
+ NULL,
+ NULL,
+ php_json_parser_object_create,
+ php_json_parser_object_update,
+ NULL,
+ NULL,
+};
+
+PHP_JSON_API void php_json_parser_init_ex(php_json_parser *parser,
+ zval *return_value,
+ char *str,
+ size_t str_len,
+ int options,
+ int max_depth,
+ const php_json_parser_methods *parser_methods)
+{
+ memset(parser, 0, sizeof(php_json_parser));
+ php_json_scanner_init(&parser->scanner, str, str_len, options);
+ parser->depth = 1;
+ parser->max_depth = max_depth;
+ parser->return_value = return_value;
+ memcpy(&parser->methods, parser_methods, sizeof(php_json_parser_methods));
+}
+
+PHP_JSON_API void php_json_parser_init(php_json_parser *parser,
+ zval *return_value,
+ char *str,
+ size_t str_len,
+ int options,
+ int max_depth)
+{
+ php_json_parser_init_ex(
+ parser,
+ return_value,
+ str,
+ str_len,
+ options,
+ max_depth,
+ &default_parser_methods);
+}
+
+PHP_JSON_API int php_json_parse(php_json_parser *parser)
+{
+ return php_json_yyparse(parser);
+}
diff --git a/ext/json/php_json.h b/ext/json/php_json.h
index 4e61ae45e9..992f42c087 100644
--- a/ext/json/php_json.h
+++ b/ext/json/php_json.h
@@ -22,7 +22,7 @@
#ifndef PHP_JSON_H
#define PHP_JSON_H
-#define PHP_JSON_VERSION "1.4.0"
+#define PHP_JSON_VERSION "1.5.0"
#include "zend_smart_str_public.h"
extern zend_module_entry json_module_entry;
@@ -67,6 +67,7 @@ typedef enum {
#define PHP_JSON_UNESCAPED_UNICODE (1<<8)
#define PHP_JSON_PARTIAL_OUTPUT_ON_ERROR (1<<9)
#define PHP_JSON_PRESERVE_ZERO_FRACTION (1<<10)
+#define PHP_JSON_UNESCAPED_LINE_TERMINATORS (1<<11)
/* json_decode() options */
#define PHP_JSON_OBJECT_AS_ARRAY (1<<0)
@@ -92,12 +93,12 @@ PHP_JSON_API ZEND_EXTERN_MODULE_GLOBALS(json)
ZEND_TSRMLS_CACHE_EXTERN()
#endif
-PHP_JSON_API void php_json_encode(smart_str *buf, zval *val, int options);
-PHP_JSON_API void php_json_decode_ex(zval *return_value, char *str, size_t str_len, zend_long options, zend_long depth);
+PHP_JSON_API int php_json_encode(smart_str *buf, zval *val, int options);
+PHP_JSON_API int php_json_decode_ex(zval *return_value, char *str, size_t str_len, zend_long options, zend_long depth);
-static inline void php_json_decode(zval *return_value, char *str, int str_len, zend_bool assoc, zend_long depth)
+static inline int php_json_decode(zval *return_value, char *str, int str_len, zend_bool assoc, zend_long depth)
{
- php_json_decode_ex(return_value, str, str_len, assoc ? PHP_JSON_OBJECT_AS_ARRAY : 0, depth);
+ return php_json_decode_ex(return_value, str, str_len, assoc ? PHP_JSON_OBJECT_AS_ARRAY : 0, depth);
}
diff --git a/ext/json/php_json_encoder.h b/ext/json/php_json_encoder.h
index 01c2ae0b20..52c024fa9f 100644
--- a/ext/json/php_json_encoder.h
+++ b/ext/json/php_json_encoder.h
@@ -22,6 +22,19 @@
#include "php.h"
#include "zend_smart_str.h"
-void php_json_encode_zval(smart_str *buf, zval *val, int options);
+typedef struct _php_json_encoder php_json_encoder;
+
+struct _php_json_encoder {
+ int depth;
+ int max_depth;
+ php_json_error_code error_code;
+};
+
+static inline void php_json_encode_init(php_json_encoder *encoder)
+{
+ memset(encoder, 0, sizeof(php_json_encoder));
+}
+
+int php_json_encode_zval(smart_str *buf, zval *val, int options, php_json_encoder *encoder);
#endif /* PHP_JSON_ENCODER_H */
diff --git a/ext/json/php_json_parser.h b/ext/json/php_json_parser.h
index 0dd0f88007..571326775c 100644
--- a/ext/json/php_json_parser.h
+++ b/ext/json/php_json_parser.h
@@ -22,16 +22,64 @@
#include "php.h"
#include "php_json_scanner.h"
-typedef struct _php_json_parser {
+typedef struct _php_json_parser php_json_parser;
+
+typedef int (*php_json_parser_func_array_create_t)(
+ php_json_parser *parser, zval *array);
+typedef int (*php_json_parser_func_array_append_t)(
+ php_json_parser *parser, zval *array, zval *zvalue);
+typedef int (*php_json_parser_func_array_start_t)(
+ php_json_parser *parser);
+typedef int (*php_json_parser_func_array_end_t)(
+ php_json_parser *parser, zval *object);
+typedef int (*php_json_parser_func_object_create_t)(
+ php_json_parser *parser, zval *object);
+typedef int (*php_json_parser_func_object_update_t)(
+ php_json_parser *parser, zval *object, zend_string *key, zval *zvalue);
+typedef int (*php_json_parser_func_object_start_t)(
+ php_json_parser *parser);
+typedef int (*php_json_parser_func_object_end_t)(
+ php_json_parser *parser, zval *object);
+
+typedef struct _php_json_parser_methods {
+ php_json_parser_func_array_create_t array_create;
+ php_json_parser_func_array_append_t array_append;
+ php_json_parser_func_array_start_t array_start;
+ php_json_parser_func_array_end_t array_end;
+ php_json_parser_func_object_create_t object_create;
+ php_json_parser_func_object_update_t object_update;
+ php_json_parser_func_object_start_t object_start;
+ php_json_parser_func_object_end_t object_end;
+} php_json_parser_methods;
+
+struct _php_json_parser {
php_json_scanner scanner;
zval *return_value;
int depth;
int max_depth;
-} php_json_parser;
+ php_json_parser_methods methods;
+};
+
+PHP_JSON_API void php_json_parser_init_ex(
+ php_json_parser *parser,
+ zval *return_value,
+ char *str,
+ size_t str_len,
+ int options,
+ int max_depth,
+ const php_json_parser_methods *methods);
+
+PHP_JSON_API void php_json_parser_init(
+ php_json_parser *parser,
+ zval *return_value,
+ char *str,
+ size_t str_len,
+ int options,
+ int max_depth);
-void php_json_parser_init(php_json_parser *parser, zval *return_value, char *str, size_t str_len, int options, int max_depth);
+PHP_JSON_API php_json_error_code php_json_parser_error_code(const php_json_parser *parser);
-php_json_error_code php_json_parser_error_code(php_json_parser *parser);
+PHP_JSON_API int php_json_parse(php_json_parser *parser);
int php_json_yyparse(php_json_parser *parser);
diff --git a/ext/json/tests/001.phpt b/ext/json/tests/001.phpt
index 02d43c4243..e908b44349 100644
--- a/ext/json/tests/001.phpt
+++ b/ext/json/tests/001.phpt
@@ -53,16 +53,16 @@ object(stdClass)#%d (1) {
}
}
object(stdClass)#%d (1) {
- ["_empty_"]=>
+ [""]=>
object(stdClass)#%d (1) {
["foo"]=>
string(0) ""
}
}
object(stdClass)#%d (1) {
- ["_empty_"]=>
+ [""]=>
object(stdClass)#%d (1) {
- ["_empty_"]=>
+ [""]=>
string(0) ""
}
}
diff --git a/ext/json/tests/bug66025.phpt b/ext/json/tests/bug66025.phpt
new file mode 100644
index 0000000000..9322d39b66
--- /dev/null
+++ b/ext/json/tests/bug66025.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Bug #66025 (Indent wrong when json_encode() called from jsonSerialize function)
+--SKIPIF--
+<?php
+if (!extension_loaded('json')) die('skip');
+?>
+--FILE--
+<?php
+
+class Foo implements JsonSerializable {
+ public function jsonSerialize() {
+ return json_encode([1], JSON_PRETTY_PRINT);
+ }
+}
+
+echo json_encode([new Foo]), "\n";
+?>
+--EXPECT--
+["[\n 1\n]"]
diff --git a/ext/json/tests/bug68992.phpt b/ext/json/tests/bug68992.phpt
new file mode 100644
index 0000000000..06448bbb38
--- /dev/null
+++ b/ext/json/tests/bug68992.phpt
@@ -0,0 +1,29 @@
+--TEST--
+Bug #68992 (json_encode stacks exceptions thrown by JsonSerializable classes)
+--SKIPIF--
+<?php
+if (!extension_loaded('json')) die('skip');
+?>
+--FILE--
+<?php
+
+class MyClass implements JsonSerializable {
+ public function jsonSerialize() {
+ throw new Exception('Not implemented!');
+ }
+}
+$classes = [];
+for($i = 0; $i < 5; $i++) {
+ $classes[] = new MyClass();
+}
+
+try {
+ json_encode($classes);
+} catch(Exception $e) {
+ do {
+ printf("%s (%d) [%s]\n", $e->getMessage(), $e->getCode(), get_class($e));
+ } while ($e = $e->getPrevious());
+}
+?>
+--EXPECT--
+Not implemented! (0) [Exception]
diff --git a/ext/json/tests/bug73254.phpt b/ext/json/tests/bug73254.phpt
new file mode 100644
index 0000000000..b043330cb7
--- /dev/null
+++ b/ext/json/tests/bug73254.phpt
@@ -0,0 +1,21 @@
+--TEST--
+Bug #73254 (Incorrect indentation generated by json_encode() with JSON_PRETTY_PRINT)
+--SKIPIF--
+<?php
+if (!extension_loaded('json')) die('skip');
+?>
+--FILE--
+<?php
+
+echo json_encode([json_encode([1], JSON_PRETTY_PRINT)]), "\n";
+
+$fp = fopen('php://temp', 'r');
+$data = ['a' => $fp];
+echo json_encode($data), "\n";
+echo json_encode([json_encode([1], JSON_PRETTY_PRINT)]), "\n";
+
+?>
+--EXPECT--
+["[\n 1\n]"]
+
+["[\n 1\n]"]
diff --git a/ext/json/tests/json_encode_u2028_u2029.phpt b/ext/json/tests/json_encode_u2028_u2029.phpt
new file mode 100644
index 0000000000..4b87e9b307
--- /dev/null
+++ b/ext/json/tests/json_encode_u2028_u2029.phpt
@@ -0,0 +1,36 @@
+--TEST--
+json_encode() tests for U+2028, U+2029
+--SKIPIF--
+<?php if (!extension_loaded("json")) print "skip"; ?>
+--FILE--
+<?php
+var_dump(json_encode(array("a\xC3\xA1b")));
+var_dump(json_encode(array("a\xC3\xA1b"), JSON_UNESCAPED_UNICODE));
+var_dump(json_encode("a\xE2\x80\xA7b"));
+var_dump(json_encode("a\xE2\x80\xA7b", JSON_UNESCAPED_UNICODE));
+var_dump(json_encode("a\xE2\x80\xA8b"));
+var_dump(json_encode("a\xE2\x80\xA8b", JSON_UNESCAPED_UNICODE));
+var_dump(json_encode("a\xE2\x80\xA8b", JSON_UNESCAPED_LINE_TERMINATORS));
+var_dump(json_encode("a\xE2\x80\xA8b", JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_LINE_TERMINATORS));
+var_dump(json_encode("a\xE2\x80\xA9b"));
+var_dump(json_encode("a\xE2\x80\xA9b", JSON_UNESCAPED_UNICODE));
+var_dump(json_encode("a\xE2\x80\xA9b", JSON_UNESCAPED_LINE_TERMINATORS));
+var_dump(json_encode("a\xE2\x80\xA9b", JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_LINE_TERMINATORS));
+var_dump(json_encode("a\xE2\x80\xAAb"));
+var_dump(json_encode("a\xE2\x80\xAAb", JSON_UNESCAPED_UNICODE));
+?>
+--EXPECT--
+string(12) "["a\u00e1b"]"
+string(8) "["aáb"]"
+string(10) ""a\u2027b""
+string(7) ""a‧b""
+string(10) ""a\u2028b""
+string(10) ""a\u2028b""
+string(10) ""a\u2028b""
+string(7) ""a
b""
+string(10) ""a\u2029b""
+string(10) ""a\u2029b""
+string(10) ""a\u2029b""
+string(7) ""a
b""
+string(10) ""a\u202ab""
+string(7) ""a‪b""
diff --git a/ext/json/tests/pass001.1.phpt b/ext/json/tests/pass001.1.phpt
index 611c40c4a4..d952e5811f 100644
--- a/ext/json/tests/pass001.1.phpt
+++ b/ext/json/tests/pass001.1.phpt
@@ -204,7 +204,7 @@ array(14) {
float(1.23456789E-13)
["E"]=>
float(1.23456789E+34)
- ["_empty_"]=>
+ [""]=>
float(INF)
["E no ."]=>
float(4000000000000)
@@ -527,7 +527,7 @@ array(14) {
string(7) "rosebud"
}
ENCODE: FROM OBJECT
-["JSON Test Pattern pass1",{"object with 1 member":["array with 1 element"]},{},[],-42,true,false,null,{"integer":1234567890,"real":-9876.54321,"e":1.23456789e-13,"E":1.23456789e+34,"_empty_":0,"E no .":4000000000000,"zero":0,"one":1,"space":" ","quote":"\"","backslash":"\\","controls":"\b\f\n\r\t","slash":"\/ & \/","alpha":"abcdefghijklmnopqrstuvwyz","ALPHA":"ABCDEFGHIJKLMNOPQRSTUVWYZ","digit":"0123456789","special":"`1~!@#$%^&*()_+-={':[,]}|;.<\/>?","hex":"\u0123\u4567\u89ab\ucdef\uabcd\uef4a","unicode":"\u30d7\u30ec\u30b9\u30ad\u30c3\u30c8","\u30d7\u30ec\u30b9\u30ad\u30c3\u30c8":"\u30d7\u30ec\u30b9\u30ad\u30c3\u30c8","empty_string":"","true":true,"false":false,"null":null,"array":[],"object":{},"123":{"456":{"abc":{"789":"def","012":[1,2,"5",500],"ghi":[1,2,"five",50,"sixty"]}}},"address":"50 St. James Street","url":"http:\/\/www.JSON.org\/","comment":"\/\/ \/* <!-- --","# -- --> *\/":" "," s p a c e d ":[1,2,3,4,5,6,7],"compact":[1,2,3,4,5,6,7],"jsontext":"{\"object with 1 member\":[\"array with 1 element\"]}","quotes":"&#34; \" %22 0x22 034 &#x22;","\/\\\"\ucafe\ubabe\uab98\ufcde\ubcda\uef4a\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',.\/<>?":"A key can be any string"},0.5,98.6,99.44,1066,"rosebud"]
+["JSON Test Pattern pass1",{"object with 1 member":["array with 1 element"]},{},[],-42,true,false,null,{"integer":1234567890,"real":-9876.54321,"e":1.23456789e-13,"E":1.23456789e+34,"":0,"E no .":4000000000000,"zero":0,"one":1,"space":" ","quote":"\"","backslash":"\\","controls":"\b\f\n\r\t","slash":"\/ & \/","alpha":"abcdefghijklmnopqrstuvwyz","ALPHA":"ABCDEFGHIJKLMNOPQRSTUVWYZ","digit":"0123456789","special":"`1~!@#$%^&*()_+-={':[,]}|;.<\/>?","hex":"\u0123\u4567\u89ab\ucdef\uabcd\uef4a","unicode":"\u30d7\u30ec\u30b9\u30ad\u30c3\u30c8","\u30d7\u30ec\u30b9\u30ad\u30c3\u30c8":"\u30d7\u30ec\u30b9\u30ad\u30c3\u30c8","empty_string":"","true":true,"false":false,"null":null,"array":[],"object":{},"123":{"456":{"abc":{"789":"def","012":[1,2,"5",500],"ghi":[1,2,"five",50,"sixty"]}}},"address":"50 St. James Street","url":"http:\/\/www.JSON.org\/","comment":"\/\/ \/* <!-- --","# -- --> *\/":" "," s p a c e d ":[1,2,3,4,5,6,7],"compact":[1,2,3,4,5,6,7],"jsontext":"{\"object with 1 member\":[\"array with 1 element\"]}","quotes":"&#34; \" %22 0x22 034 &#x22;","\/\\\"\ucafe\ubabe\uab98\ufcde\ubcda\uef4a\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',.\/<>?":"A key can be any string"},0.5,98.6,99.44,1066,"rosebud"]
ENCODE: FROM ARRAY
["JSON Test Pattern pass1",{"object with 1 member":["array with 1 element"]},[],[],-42,true,false,null,{"integer":1234567890,"real":-9876.54321,"e":1.23456789e-13,"E":1.23456789e+34,"":0,"E no .":4000000000000,"zero":0,"one":1,"space":" ","quote":"\"","backslash":"\\","controls":"\b\f\n\r\t","slash":"\/ & \/","alpha":"abcdefghijklmnopqrstuvwyz","ALPHA":"ABCDEFGHIJKLMNOPQRSTUVWYZ","digit":"0123456789","special":"`1~!@#$%^&*()_+-={':[,]}|;.<\/>?","hex":"\u0123\u4567\u89ab\ucdef\uabcd\uef4a","unicode":"\u30d7\u30ec\u30b9\u30ad\u30c3\u30c8","\u30d7\u30ec\u30b9\u30ad\u30c3\u30c8":"\u30d7\u30ec\u30b9\u30ad\u30c3\u30c8","empty_string":"","true":true,"false":false,"null":null,"array":[],"object":[],"123":{"456":{"abc":{"789":"def","012":[1,2,"5",500],"ghi":[1,2,"five",50,"sixty"]}}},"address":"50 St. James Street","url":"http:\/\/www.JSON.org\/","comment":"\/\/ \/* <!-- --","# -- --> *\/":" "," s p a c e d ":[1,2,3,4,5,6,7],"compact":[1,2,3,4,5,6,7],"jsontext":"{\"object with 1 member\":[\"array with 1 element\"]}","quotes":"&#34; \" %22 0x22 034 &#x22;","\/\\\"\ucafe\ubabe\uab98\ufcde\ubcda\uef4a\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',.\/<>?":"A key can be any string"},0.5,98.6,99.44,1066,"rosebud"]
DECODE AGAIN: AS OBJECT
@@ -566,7 +566,7 @@ array(14) {
float(1.23456789E-13)
["E"]=>
float(1.23456789E+34)
- ["_empty_"]=>
+ [""]=>
int(0)
["E no ."]=>
%s(4000000000000)
diff --git a/ext/json/tests/pass001.1_64bit.phpt b/ext/json/tests/pass001.1_64bit.phpt
index e6666d1599..3970fa434e 100644
--- a/ext/json/tests/pass001.1_64bit.phpt
+++ b/ext/json/tests/pass001.1_64bit.phpt
@@ -204,7 +204,7 @@ array(14) {
float(1.23456789E-13)
["E"]=>
float(1.23456789E+34)
- ["_empty_"]=>
+ [""]=>
float(INF)
["E no ."]=>
float(4000000000000)
@@ -527,7 +527,7 @@ array(14) {
string(7) "rosebud"
}
ENCODE: FROM OBJECT
-["JSON Test Pattern pass1",{"object with 1 member":["array with 1 element"]},{},[],-42,true,false,null,{"integer":1234567890,"real":-9876.54321,"e":1.23456789e-13,"E":1.23456789e+34,"_empty_":0,"E no .":4000000000000,"zero":0,"one":1,"space":" ","quote":"\"","backslash":"\\","controls":"\b\f\n\r\t","slash":"\/ & \/","alpha":"abcdefghijklmnopqrstuvwyz","ALPHA":"ABCDEFGHIJKLMNOPQRSTUVWYZ","digit":"0123456789","special":"`1~!@#$%^&*()_+-={':[,]}|;.<\/>?","hex":"\u0123\u4567\u89ab\ucdef\uabcd\uef4a","unicode":"\u30d7\u30ec\u30b9\u30ad\u30c3\u30c8","\u30d7\u30ec\u30b9\u30ad\u30c3\u30c8":"\u30d7\u30ec\u30b9\u30ad\u30c3\u30c8","empty_string":"","true":true,"false":false,"null":null,"array":[],"object":{},"123":{"456":{"abc":{"789":"def","012":[1,2,"5",500],"ghi":[1,2,"five",50,"sixty"]}}},"address":"50 St. James Street","url":"http:\/\/www.JSON.org\/","comment":"\/\/ \/* <!-- --","# -- --> *\/":" "," s p a c e d ":[1,2,3,4,5,6,7],"compact":[1,2,3,4,5,6,7],"jsontext":"{\"object with 1 member\":[\"array with 1 element\"]}","quotes":"&#34; \" %22 0x22 034 &#x22;","\/\\\"\ucafe\ubabe\uab98\ufcde\ubcda\uef4a\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',.\/<>?":"A key can be any string"},0.5,98.6,99.44,1066,"rosebud"]
+["JSON Test Pattern pass1",{"object with 1 member":["array with 1 element"]},{},[],-42,true,false,null,{"integer":1234567890,"real":-9876.54321,"e":1.23456789e-13,"E":1.23456789e+34,"":0,"E no .":4000000000000,"zero":0,"one":1,"space":" ","quote":"\"","backslash":"\\","controls":"\b\f\n\r\t","slash":"\/ & \/","alpha":"abcdefghijklmnopqrstuvwyz","ALPHA":"ABCDEFGHIJKLMNOPQRSTUVWYZ","digit":"0123456789","special":"`1~!@#$%^&*()_+-={':[,]}|;.<\/>?","hex":"\u0123\u4567\u89ab\ucdef\uabcd\uef4a","unicode":"\u30d7\u30ec\u30b9\u30ad\u30c3\u30c8","\u30d7\u30ec\u30b9\u30ad\u30c3\u30c8":"\u30d7\u30ec\u30b9\u30ad\u30c3\u30c8","empty_string":"","true":true,"false":false,"null":null,"array":[],"object":{},"123":{"456":{"abc":{"789":"def","012":[1,2,"5",500],"ghi":[1,2,"five",50,"sixty"]}}},"address":"50 St. James Street","url":"http:\/\/www.JSON.org\/","comment":"\/\/ \/* <!-- --","# -- --> *\/":" "," s p a c e d ":[1,2,3,4,5,6,7],"compact":[1,2,3,4,5,6,7],"jsontext":"{\"object with 1 member\":[\"array with 1 element\"]}","quotes":"&#34; \" %22 0x22 034 &#x22;","\/\\\"\ucafe\ubabe\uab98\ufcde\ubcda\uef4a\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',.\/<>?":"A key can be any string"},0.5,98.6,99.44,1066,"rosebud"]
ENCODE: FROM ARRAY
["JSON Test Pattern pass1",{"object with 1 member":["array with 1 element"]},[],[],-42,true,false,null,{"integer":1234567890,"real":-9876.54321,"e":1.23456789e-13,"E":1.23456789e+34,"":0,"E no .":4000000000000,"zero":0,"one":1,"space":" ","quote":"\"","backslash":"\\","controls":"\b\f\n\r\t","slash":"\/ & \/","alpha":"abcdefghijklmnopqrstuvwyz","ALPHA":"ABCDEFGHIJKLMNOPQRSTUVWYZ","digit":"0123456789","special":"`1~!@#$%^&*()_+-={':[,]}|;.<\/>?","hex":"\u0123\u4567\u89ab\ucdef\uabcd\uef4a","unicode":"\u30d7\u30ec\u30b9\u30ad\u30c3\u30c8","\u30d7\u30ec\u30b9\u30ad\u30c3\u30c8":"\u30d7\u30ec\u30b9\u30ad\u30c3\u30c8","empty_string":"","true":true,"false":false,"null":null,"array":[],"object":[],"123":{"456":{"abc":{"789":"def","012":[1,2,"5",500],"ghi":[1,2,"five",50,"sixty"]}}},"address":"50 St. James Street","url":"http:\/\/www.JSON.org\/","comment":"\/\/ \/* <!-- --","# -- --> *\/":" "," s p a c e d ":[1,2,3,4,5,6,7],"compact":[1,2,3,4,5,6,7],"jsontext":"{\"object with 1 member\":[\"array with 1 element\"]}","quotes":"&#34; \" %22 0x22 034 &#x22;","\/\\\"\ucafe\ubabe\uab98\ufcde\ubcda\uef4a\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',.\/<>?":"A key can be any string"},0.5,98.6,99.44,1066,"rosebud"]
DECODE AGAIN: AS OBJECT
@@ -566,7 +566,7 @@ array(14) {
float(1.23456789E-13)
["E"]=>
float(1.23456789E+34)
- ["_empty_"]=>
+ [""]=>
int(0)
["E no ."]=>
int(4000000000000)
diff --git a/ext/json/tests/pass001.phpt b/ext/json/tests/pass001.phpt
index ffe7c61f01..948929d5a5 100644
--- a/ext/json/tests/pass001.phpt
+++ b/ext/json/tests/pass001.phpt
@@ -188,7 +188,7 @@ array(14) {
float(1.23456789E-13)
["E"]=>
float(1.23456789E+34)
- ["_empty_"]=>
+ [""]=>
float(INF)
["zero"]=>
int(0)
@@ -425,7 +425,7 @@ array(14) {
string(7) "rosebud"
}
ENCODE: FROM OBJECT
-["JSON Test Pattern pass1",{"object with 1 member":["array with 1 element"]},{},[],-42,true,false,null,{"integer":1234567890,"real":-9876.54321,"e":1.23456789e-13,"E":1.23456789e+34,"_empty_":0,"zero":0,"one":1,"space":" ","quote":"\"","backslash":"\\","controls":"\b\f\n\r\t","slash":"\/ & \/","alpha":"abcdefghijklmnopqrstuvwyz","ALPHA":"ABCDEFGHIJKLMNOPQRSTUVWYZ","digit":"0123456789","special":"`1~!@#$%^&*()_+-={':[,]}|;.<\/>?","hex":"\u0123\u4567\u89ab\ucdef\uabcd\uef4a","true":true,"false":false,"null":null,"array":[],"object":{},"address":"50 St. James Street","url":"http:\/\/www.JSON.org\/","comment":"\/\/ \/* <!-- --","# -- --> *\/":" "," s p a c e d ":[1,2,3,4,5,6,7],"compact":[1,2,3,4,5,6,7],"jsontext":"{\"object with 1 member\":[\"array with 1 element\"]}","quotes":"&#34; \" %22 0x22 034 &#x22;","\/\\\"\ucafe\ubabe\uab98\ufcde\ubcda\uef4a\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',.\/<>?":"A key can be any string"},0.5,98.6,99.44,1066,"rosebud"]
+["JSON Test Pattern pass1",{"object with 1 member":["array with 1 element"]},{},[],-42,true,false,null,{"integer":1234567890,"real":-9876.54321,"e":1.23456789e-13,"E":1.23456789e+34,"":0,"zero":0,"one":1,"space":" ","quote":"\"","backslash":"\\","controls":"\b\f\n\r\t","slash":"\/ & \/","alpha":"abcdefghijklmnopqrstuvwyz","ALPHA":"ABCDEFGHIJKLMNOPQRSTUVWYZ","digit":"0123456789","special":"`1~!@#$%^&*()_+-={':[,]}|;.<\/>?","hex":"\u0123\u4567\u89ab\ucdef\uabcd\uef4a","true":true,"false":false,"null":null,"array":[],"object":{},"address":"50 St. James Street","url":"http:\/\/www.JSON.org\/","comment":"\/\/ \/* <!-- --","# -- --> *\/":" "," s p a c e d ":[1,2,3,4,5,6,7],"compact":[1,2,3,4,5,6,7],"jsontext":"{\"object with 1 member\":[\"array with 1 element\"]}","quotes":"&#34; \" %22 0x22 034 &#x22;","\/\\\"\ucafe\ubabe\uab98\ufcde\ubcda\uef4a\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',.\/<>?":"A key can be any string"},0.5,98.6,99.44,1066,"rosebud"]
ENCODE: FROM ARRAY
["JSON Test Pattern pass1",{"object with 1 member":["array with 1 element"]},[],[],-42,true,false,null,{"integer":1234567890,"real":-9876.54321,"e":1.23456789e-13,"E":1.23456789e+34,"":0,"zero":0,"one":1,"space":" ","quote":"\"","backslash":"\\","controls":"\b\f\n\r\t","slash":"\/ & \/","alpha":"abcdefghijklmnopqrstuvwyz","ALPHA":"ABCDEFGHIJKLMNOPQRSTUVWYZ","digit":"0123456789","special":"`1~!@#$%^&*()_+-={':[,]}|;.<\/>?","hex":"\u0123\u4567\u89ab\ucdef\uabcd\uef4a","true":true,"false":false,"null":null,"array":[],"object":[],"address":"50 St. James Street","url":"http:\/\/www.JSON.org\/","comment":"\/\/ \/* <!-- --","# -- --> *\/":" "," s p a c e d ":[1,2,3,4,5,6,7],"compact":[1,2,3,4,5,6,7],"jsontext":"{\"object with 1 member\":[\"array with 1 element\"]}","quotes":"&#34; \" %22 0x22 034 &#x22;","\/\\\"\ucafe\ubabe\uab98\ufcde\ubcda\uef4a\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',.\/<>?":"A key can be any string"},0.5,98.6,99.44,1066,"rosebud"]
DECODE AGAIN: AS OBJECT
@@ -464,7 +464,7 @@ array(14) {
float(1.23456789E-13)
["E"]=>
float(1.23456789E+34)
- ["_empty_"]=>
+ [""]=>
int(0)
["zero"]=>
int(0)
diff --git a/ext/ldap/config.w32 b/ext/ldap/config.w32
index 9102c6c952..11aa5cb452 100644
--- a/ext/ldap/config.w32
+++ b/ext/ldap/config.w32
@@ -6,10 +6,8 @@ ARG_WITH("ldap", "LDAP support", "no");
if (PHP_LDAP != "no") {
if (CHECK_HEADER_ADD_INCLUDE("ldap.h", "CFLAGS_LDAP", PHP_PHP_BUILD + "\\include\\openldap;" + PHP_PHP_BUILD + "\\openldap\\include;" + PHP_LDAP) &&
- CHECK_HEADER_ADD_INCLUDE("lber.h", "CFLAGS_LDAP", PHP_PHP_BUILD + "\\include\\openldap;" + PHP_PHP_BUILD + "\\openldap\\include;" + PHP_LDAP)
- &&
- CHECK_LIB("ssleay32.lib", "ldap", PHP_LDAP) &&
- CHECK_LIB("libeay32.lib", "ldap", PHP_LDAP) &&
+ CHECK_HEADER_ADD_INCLUDE("lber.h", "CFLAGS_LDAP", PHP_PHP_BUILD + "\\include\\openldap;" + PHP_PHP_BUILD + "\\openldap\\include;" + PHP_LDAP) &&
+ SETUP_OPENSSL("ldap", PHP_LDAP) > 0 &&
CHECK_LIB("oldap32_a.lib", "ldap", PHP_LDAP) &&
CHECK_LIB("olber32_a.lib", "ldap", PHP_LDAP)&&
CHECK_LIB("libsasl.lib", "ldap", PHP_LDAP)) {
diff --git a/ext/ldap/ldap.c b/ext/ldap/ldap.c
index cec20da3cc..0779387e5f 100644
--- a/ext/ldap/ldap.c
+++ b/ext/ldap/ldap.c
@@ -220,6 +220,12 @@ PHP_MINIT_FUNCTION(ldap)
REGISTER_LONG_CONSTANT("LDAP_OPT_X_SASL_AUTHCID", LDAP_OPT_X_SASL_AUTHCID, CONST_PERSISTENT | CONST_CS);
REGISTER_LONG_CONSTANT("LDAP_OPT_X_SASL_AUTHZID", LDAP_OPT_X_SASL_AUTHZID, CONST_PERSISTENT | CONST_CS);
#endif
+#ifdef LDAP_OPT_X_SASL_NOCANON
+ REGISTER_LONG_CONSTANT("LDAP_OPT_X_SASL_NOCANON", LDAP_OPT_X_SASL_NOCANON, CONST_PERSISTENT | CONST_CS);
+#endif
+#ifdef LDAP_OPT_X_SASL_USERNAME
+ REGISTER_LONG_CONSTANT("LDAP_OPT_X_SASL_USERNAME", LDAP_OPT_X_SASL_USERNAME, CONST_PERSISTENT | CONST_CS);
+#endif
#ifdef ORALDAP
REGISTER_LONG_CONSTANT("GSLC_SSL_NO_AUTH", GSLC_SSL_NO_AUTH, CONST_PERSISTENT | CONST_CS);
@@ -235,6 +241,49 @@ PHP_MINIT_FUNCTION(ldap)
REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_DEMAND", LDAP_OPT_X_TLS_DEMAND, CONST_PERSISTENT | CONST_CS);
REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_ALLOW", LDAP_OPT_X_TLS_ALLOW, CONST_PERSISTENT | CONST_CS);
REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_TRY", LDAP_OPT_X_TLS_TRY, CONST_PERSISTENT | CONST_CS);
+
+ REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_CACERTDIR", LDAP_OPT_X_TLS_CACERTDIR, CONST_PERSISTENT | CONST_CS);
+ REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_CACERTFILE", LDAP_OPT_X_TLS_CACERTFILE, CONST_PERSISTENT | CONST_CS);
+ REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_CERTFILE", LDAP_OPT_X_TLS_CERTFILE, CONST_PERSISTENT | CONST_CS);
+ REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_CIPHER_SUITE", LDAP_OPT_X_TLS_CIPHER_SUITE, CONST_PERSISTENT | CONST_CS);
+ REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_KEYFILE", LDAP_OPT_X_TLS_KEYFILE, CONST_PERSISTENT | CONST_CS);
+ REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_RANDOM_FILE", LDAP_OPT_X_TLS_RANDOM_FILE, CONST_PERSISTENT | CONST_CS);
+#endif
+
+#ifdef LDAP_OPT_X_TLS_CRLCHECK
+ REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_CRLCHECK", LDAP_OPT_X_TLS_CRLCHECK, CONST_PERSISTENT | CONST_CS);
+
+ REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_CRL_NONE", LDAP_OPT_X_TLS_CRL_NONE, CONST_PERSISTENT | CONST_CS);
+ REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_CRL_PEER", LDAP_OPT_X_TLS_CRL_PEER, CONST_PERSISTENT | CONST_CS);
+ REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_CRL_ALL", LDAP_OPT_X_TLS_CRL_ALL, CONST_PERSISTENT | CONST_CS);
+#endif
+
+#ifdef LDAP_OPT_X_TLS_DHFILE
+ REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_DHFILE", LDAP_OPT_X_TLS_DHFILE, CONST_PERSISTENT | CONST_CS);
+#endif
+
+#ifdef LDAP_OPT_X_TLS_CRLFILE
+ REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_CRLFILE", LDAP_OPT_X_TLS_CRLFILE, CONST_PERSISTENT | CONST_CS);
+#endif
+
+#ifdef LDAP_OPT_X_TLS_PROTOCOL_MIN
+ REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_PROTOCOL_MIN", LDAP_OPT_X_TLS_PROTOCOL_MIN, CONST_PERSISTENT | CONST_CS);
+
+ REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_PROTOCOL_SSL2", LDAP_OPT_X_TLS_PROTOCOL_SSL2, CONST_PERSISTENT | CONST_CS);
+ REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_PROTOCOL_SSL3", LDAP_OPT_X_TLS_PROTOCOL_SSL3, CONST_PERSISTENT | CONST_CS);
+ REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_PROTOCOL_TLS1_0", LDAP_OPT_X_TLS_PROTOCOL_TLS1_0, CONST_PERSISTENT | CONST_CS);
+ REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_PROTOCOL_TLS1_1", LDAP_OPT_X_TLS_PROTOCOL_TLS1_1, CONST_PERSISTENT | CONST_CS);
+ REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_PROTOCOL_TLS1_2", LDAP_OPT_X_TLS_PROTOCOL_TLS1_2, CONST_PERSISTENT | CONST_CS);
+#endif
+
+#ifdef LDAP_OPT_X_TLS_PACKAGE
+ REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_PACKAGE", LDAP_OPT_X_TLS_PACKAGE, CONST_PERSISTENT | CONST_CS);
+#endif
+
+#ifdef LDAP_OPT_X_KEEPALIVE_IDLE
+ REGISTER_LONG_CONSTANT("LDAP_OPT_X_KEEPALIVE_IDLE", LDAP_OPT_X_KEEPALIVE_IDLE, CONST_PERSISTENT | CONST_CS);
+ REGISTER_LONG_CONSTANT("LDAP_OPT_X_KEEPALIVE_PROBES", LDAP_OPT_X_KEEPALIVE_PROBES, CONST_PERSISTENT | CONST_CS);
+ REGISTER_LONG_CONSTANT("LDAP_OPT_X_KEEPALIVE_INTERVAL", LDAP_OPT_X_KEEPALIVE_INTERVAL, CONST_PERSISTENT | CONST_CS);
#endif
REGISTER_LONG_CONSTANT("LDAP_ESCAPE_FILTER", PHP_LDAP_ESCAPE_FILTER, CONST_PERSISTENT | CONST_CS);
@@ -357,7 +406,7 @@ PHP_FUNCTION(ldap_connect)
#endif
if (LDAPG(max_links) != -1 && LDAPG(num_links) >= LDAPG(max_links)) {
- php_error_docref(NULL, E_WARNING, "Too many open links (%pd)", LDAPG(num_links));
+ php_error_docref(NULL, E_WARNING, "Too many open links (" ZEND_LONG_FMT ")", LDAPG(num_links));
RETURN_FALSE;
}
@@ -371,12 +420,12 @@ PHP_FUNCTION(ldap_connect)
if (port <= 0 || port > 65535) {
efree(ld);
- php_error_docref(NULL, E_WARNING, "invalid port number: %ld", port);
+ php_error_docref(NULL, E_WARNING, "invalid port number: " ZEND_LONG_FMT, port);
RETURN_FALSE;
}
url = emalloc(urllen);
- snprintf( url, urllen, "ldap://%s:%ld", host, port );
+ snprintf( url, urllen, "ldap://%s:" ZEND_LONG_FMT, host, port );
}
#ifdef LDAP_API_FEATURE_X_OPENLDAP
@@ -1603,7 +1652,7 @@ PHP_FUNCTION(ldap_delete)
*/
static int _ldap_str_equal_to_const(const char *str, uint str_len, const char *cstr)
{
- int i;
+ uint i;
if (strlen(cstr) != str_len)
return 0;
@@ -1622,7 +1671,7 @@ static int _ldap_str_equal_to_const(const char *str, uint str_len, const char *c
*/
static int _ldap_strlen_max(const char *str, uint max_len)
{
- int i;
+ uint i;
for (i = 0; i < max_len; ++i) {
if (str[i] == '\0') {
@@ -1698,7 +1747,7 @@ PHP_FUNCTION(ldap_modify_batch)
zend_ulong tmpUlong;
/* make sure the DN contains no NUL bytes */
- if (_ldap_strlen_max(dn, dn_len) != dn_len) {
+ if ((size_t)_ldap_strlen_max(dn, dn_len) != dn_len) {
php_error_docref(NULL, E_WARNING, "DN must not contain NUL bytes");
RETURN_FALSE;
}
@@ -1758,7 +1807,7 @@ PHP_FUNCTION(ldap_modify_batch)
RETURN_FALSE;
}
- if (Z_STRLEN_P(modinfo) != _ldap_strlen_max(Z_STRVAL_P(modinfo), Z_STRLEN_P(modinfo))) {
+ if (Z_STRLEN_P(modinfo) != (size_t)_ldap_strlen_max(Z_STRVAL_P(modinfo), Z_STRLEN_P(modinfo))) {
php_error_docref(NULL, E_WARNING, "A '" LDAP_MODIFY_BATCH_ATTRIB "' value must not contain NUL bytes");
RETURN_FALSE;
}
@@ -1867,8 +1916,11 @@ PHP_FUNCTION(ldap_modify_batch)
oper = LDAP_MOD_REPLACE;
break;
default:
- php_error_docref(NULL, E_ERROR, "Unknown and uncaught modification type.");
- RETURN_FALSE;
+ zend_throw_error(NULL, "Unknown and uncaught modification type.");
+ RETVAL_FALSE;
+ efree(ldap_mods[i]);
+ num_mods = i;
+ goto cleanup;
}
/* fill in the basic info */
@@ -1913,7 +1965,7 @@ PHP_FUNCTION(ldap_modify_batch)
} else RETVAL_TRUE;
/* clean up */
- {
+ cleanup: {
for (i = 0; i < num_mods; i++) {
/* attribute */
efree(ldap_mods[i]->mod_type);
@@ -2097,9 +2149,23 @@ PHP_FUNCTION(ldap_get_option)
#ifdef LDAP_OPT_RESTART
case LDAP_OPT_RESTART:
#endif
+#ifdef LDAP_OPT_X_SASL_NOCANON
+ case LDAP_OPT_X_SASL_NOCANON:
+#endif
#ifdef LDAP_OPT_X_TLS_REQUIRE_CERT
case LDAP_OPT_X_TLS_REQUIRE_CERT:
#endif
+#ifdef LDAP_OPT_X_TLS_CRLCHECK
+ case LDAP_OPT_X_TLS_CRLCHECK:
+#endif
+#ifdef LDAP_OPT_X_TLS_PROTOCOL_MIN
+ case LDAP_OPT_X_TLS_PROTOCOL_MIN:
+#endif
+#ifdef LDAP_OPT_X_KEEPALIVE_IDLE
+ case LDAP_OPT_X_KEEPALIVE_IDLE:
+ case LDAP_OPT_X_KEEPALIVE_PROBES:
+ case LDAP_OPT_X_KEEPALIVE_INTERVAL:
+#endif
{
int val;
@@ -2169,6 +2235,26 @@ PHP_FUNCTION(ldap_get_option)
case LDAP_OPT_X_SASL_AUTHCID:
case LDAP_OPT_X_SASL_AUTHZID:
#endif
+#ifdef LDAP_OPT_X_SASL_USERNAME
+ case LDAP_OPT_X_SASL_USERNAME:
+#endif
+#if (LDAP_API_VERSION > 2000)
+ case LDAP_OPT_X_TLS_CACERTDIR:
+ case LDAP_OPT_X_TLS_CACERTFILE:
+ case LDAP_OPT_X_TLS_CERTFILE:
+ case LDAP_OPT_X_TLS_CIPHER_SUITE:
+ case LDAP_OPT_X_TLS_KEYFILE:
+ case LDAP_OPT_X_TLS_RANDOM_FILE:
+#endif
+#ifdef LDAP_OPT_X_TLS_PACKAGE
+ case LDAP_OPT_X_TLS_PACKAGE:
+#endif
+#ifdef LDAP_OPT_X_TLS_CRLFILE
+ case LDAP_OPT_X_TLS_CRLFILE:
+#endif
+#ifdef LDAP_OPT_X_TLS_DHFILE
+ case LDAP_OPT_X_TLS_DHFILE:
+#endif
#ifdef LDAP_OPT_MATCHED_DN
case LDAP_OPT_MATCHED_DN:
#endif
@@ -2233,6 +2319,17 @@ PHP_FUNCTION(ldap_set_option)
#ifdef LDAP_OPT_X_TLS_REQUIRE_CERT
case LDAP_OPT_X_TLS_REQUIRE_CERT:
#endif
+#ifdef LDAP_OPT_X_TLS_CRLCHECK
+ case LDAP_OPT_X_TLS_CRLCHECK:
+#endif
+#ifdef LDAP_OPT_X_TLS_PROTOCOL_MIN
+ case LDAP_OPT_X_TLS_PROTOCOL_MIN:
+#endif
+#ifdef LDAP_OPT_X_KEEPALIVE_IDLE
+ case LDAP_OPT_X_KEEPALIVE_IDLE:
+ case LDAP_OPT_X_KEEPALIVE_PROBES:
+ case LDAP_OPT_X_KEEPALIVE_INTERVAL:
+#endif
{
int val;
@@ -2290,6 +2387,20 @@ PHP_FUNCTION(ldap_set_option)
case LDAP_OPT_X_SASL_AUTHCID:
case LDAP_OPT_X_SASL_AUTHZID:
#endif
+#if (LDAP_API_VERSION > 2000)
+ case LDAP_OPT_X_TLS_CACERTDIR:
+ case LDAP_OPT_X_TLS_CACERTFILE:
+ case LDAP_OPT_X_TLS_CERTFILE:
+ case LDAP_OPT_X_TLS_CIPHER_SUITE:
+ case LDAP_OPT_X_TLS_KEYFILE:
+ case LDAP_OPT_X_TLS_RANDOM_FILE:
+#endif
+#ifdef LDAP_OPT_X_TLS_CRLFILE
+ case LDAP_OPT_X_TLS_CRLFILE:
+#endif
+#ifdef LDAP_OPT_X_TLS_DHFILE
+ case LDAP_OPT_X_TLS_DHFILE:
+#endif
#ifdef LDAP_OPT_MATCHED_DN
case LDAP_OPT_MATCHED_DN:
#endif
@@ -2306,6 +2417,9 @@ PHP_FUNCTION(ldap_set_option)
#ifdef LDAP_OPT_RESTART
case LDAP_OPT_RESTART:
#endif
+#ifdef LDAP_OPT_X_SASL_NOCANON
+ case LDAP_OPT_X_SASL_NOCANON:
+#endif
{
void *val;
convert_to_boolean_ex(newval);
@@ -2716,23 +2830,30 @@ PHP_FUNCTION(ldap_set_rebind_proc)
/* }}} */
#endif
-static zend_string* php_ldap_do_escape(const zend_bool *map, const char *value, size_t valuelen)
+static zend_string* php_ldap_do_escape(const zend_bool *map, const char *value, size_t valuelen, zend_long flags)
{
char hex[] = "0123456789abcdef";
- int i, p = 0;
+ size_t i, p = 0;
size_t len = 0;
zend_string *ret;
for (i = 0; i < valuelen; i++) {
len += (map[(unsigned char) value[i]]) ? 3 : 1;
}
+ /* Per RFC 4514, a leading and trailing space must be escaped */
+ if ((flags & PHP_LDAP_ESCAPE_DN) && (value[0] == ' ')) {
+ len += 2;
+ }
+ if ((flags & PHP_LDAP_ESCAPE_DN) && ((valuelen > 1) && (value[valuelen - 1] == ' '))) {
+ len += 2;
+ }
ret = zend_string_alloc(len, 0);
for (i = 0; i < valuelen; i++) {
unsigned char v = (unsigned char) value[i];
- if (map[v]) {
+ if (map[v] || ((flags & PHP_LDAP_ESCAPE_DN) && ((i == 0) || (i + 1 == valuelen)) && (v == ' '))) {
ZSTR_VAL(ret)[p++] = '\\';
ZSTR_VAL(ret)[p++] = hex[v >> 4];
ZSTR_VAL(ret)[p++] = hex[v & 0x0f];
@@ -2777,7 +2898,7 @@ PHP_FUNCTION(ldap_escape)
if (flags & PHP_LDAP_ESCAPE_DN) {
havecharlist = 1;
- php_ldap_escape_map_set_chars(map, "\\,=+<>;\"#", sizeof("\\,=+<>;\"#") - 1, 1);
+ php_ldap_escape_map_set_chars(map, "\\,=+<>;\"#\r", sizeof("\\,=+<>;\"#\r") - 1, 1);
}
if (!havecharlist) {
@@ -2790,7 +2911,7 @@ PHP_FUNCTION(ldap_escape)
php_ldap_escape_map_set_chars(map, ignores, ignoreslen, 0);
}
- RETURN_NEW_STR(php_ldap_do_escape(map, value, valuelen));
+ RETURN_NEW_STR(php_ldap_do_escape(map, value, valuelen, flags));
}
#ifdef STR_TRANSLATION
diff --git a/ext/ldap/tests/bug72021.phpt b/ext/ldap/tests/bug72021.phpt
new file mode 100644
index 0000000000..6dfcf44680
--- /dev/null
+++ b/ext/ldap/tests/bug72021.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Bug #72021 (ldap_escape() with DN flag is not RFC compliant)
+--CREDITS--
+Chad Sikorra <Chad.Sikorra@gmail.com>
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--FILE--
+<?php
+$subject = " Joe,= \rSmith ";
+
+var_dump(ldap_escape($subject, null, LDAP_ESCAPE_DN));
+?>
+--EXPECT--
+string(24) "\20Joe\2c\3d \0dSmith\20"
diff --git a/ext/ldap/tests/ldap_get_option_package_basic.phpt b/ext/ldap/tests/ldap_get_option_package_basic.phpt
new file mode 100644
index 0000000000..af8146dc9c
--- /dev/null
+++ b/ext/ldap/tests/ldap_get_option_package_basic.phpt
@@ -0,0 +1,21 @@
+--TEST--
+ldap_get_option() - Basic test for getting the TLS package ldap option
+--CREDITS--
+Chad Sikorra <Chad.Sikorra@gmail.com>
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--FILE--
+<?php
+require "connect.inc";
+$link = ldap_connect($host, $port);
+
+$result = ldap_get_option($link, LDAP_OPT_X_TLS_PACKAGE, $optionval);
+var_dump(in_array($optionval, ['GnuTLS', 'OpenSSL', 'MozNSS']));
+// This is a read-only option.
+var_dump(ldap_set_option($link, LDAP_OPT_X_TLS_PACKAGE, 'foo'));
+?>
+===DONE===
+--EXPECT--
+bool(true)
+bool(false)
+===DONE===
diff --git a/ext/ldap/tests/ldap_set_option_cafiles_basic.phpt b/ext/ldap/tests/ldap_set_option_cafiles_basic.phpt
new file mode 100644
index 0000000000..ff93c9de00
--- /dev/null
+++ b/ext/ldap/tests/ldap_set_option_cafiles_basic.phpt
@@ -0,0 +1,41 @@
+--TEST--
+ldap_set_option() - Basic test for TLS CA/Cert/CRL/DH/Key file ldap options
+--CREDITS--
+Chad Sikorra <Chad.Sikorra@gmail.com>
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--FILE--
+<?php
+require "connect.inc";
+$link = ldap_connect($host, $port);
+
+foreach([
+ LDAP_OPT_X_TLS_CACERTDIR,
+ LDAP_OPT_X_TLS_CACERTFILE,
+ LDAP_OPT_X_TLS_CERTFILE,
+ LDAP_OPT_X_TLS_KEYFILE,
+ LDAP_OPT_X_TLS_CRLFILE,
+ LDAP_OPT_X_TLS_DHFILE,
+] as $option) {
+ $result = ldap_set_option($link, $option, '/foo/bar');
+ var_dump($result);
+
+ ldap_get_option($link, $option, $optionval);
+ var_dump($optionval);
+}
+?>
+===DONE===
+--EXPECT--
+bool(true)
+string(8) "/foo/bar"
+bool(true)
+string(8) "/foo/bar"
+bool(true)
+string(8) "/foo/bar"
+bool(true)
+string(8) "/foo/bar"
+bool(true)
+string(8) "/foo/bar"
+bool(true)
+string(8) "/foo/bar"
+===DONE===
diff --git a/ext/ldap/tests/ldap_set_option_ciphersuite_basic.phpt b/ext/ldap/tests/ldap_set_option_ciphersuite_basic.phpt
new file mode 100644
index 0000000000..9b47a826e5
--- /dev/null
+++ b/ext/ldap/tests/ldap_set_option_ciphersuite_basic.phpt
@@ -0,0 +1,22 @@
+--TEST--
+ldap_set_option() - Basic test for TLS cipher suite ldap option
+--CREDITS--
+Chad Sikorra <Chad.Sikorra@gmail.com>
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--FILE--
+<?php
+require "connect.inc";
+$link = ldap_connect($host, $port);
+
+$result = ldap_set_option($link, LDAP_OPT_X_TLS_CIPHER_SUITE, '3DES');
+var_dump($result);
+
+ldap_get_option($link, LDAP_OPT_X_TLS_CIPHER_SUITE, $optionval);
+var_dump($optionval);
+?>
+===DONE===
+--EXPECT--
+bool(true)
+string(4) "3DES"
+===DONE===
diff --git a/ext/ldap/tests/ldap_set_option_crlcheck_basic.phpt b/ext/ldap/tests/ldap_set_option_crlcheck_basic.phpt
new file mode 100644
index 0000000000..a9aeec0a7b
--- /dev/null
+++ b/ext/ldap/tests/ldap_set_option_crlcheck_basic.phpt
@@ -0,0 +1,40 @@
+--TEST--
+ldap_set_option() - Basic test for TLS CRL check ldap option
+--CREDITS--
+Chad Sikorra <Chad.Sikorra@gmail.com>
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+<?php
+ require "connect.inc";
+ $link = ldap_connect($host, $port);
+ ldap_get_option($link, LDAP_OPT_X_TLS_PACKAGE, $package);
+ if ($package != 'OpenSSL') {
+ die("skip OpenSSL required for CRL check options, got: $package");
+ }
+?>
+--FILE--
+<?php
+require "connect.inc";
+$link = ldap_connect($host, $port);
+
+foreach([
+ LDAP_OPT_X_TLS_CRL_NONE,
+ LDAP_OPT_X_TLS_CRL_PEER,
+ LDAP_OPT_X_TLS_CRL_ALL,
+] as $option) {
+ $result = ldap_set_option($link, LDAP_OPT_X_TLS_CRLCHECK, $option);
+ var_dump($result);
+
+ ldap_get_option($link, LDAP_OPT_X_TLS_CRLCHECK, $optionval);
+ var_dump($optionval);
+}
+?>
+===DONE===
+--EXPECT--
+bool(true)
+int(0)
+bool(true)
+int(1)
+bool(true)
+int(2)
+===DONE===
diff --git a/ext/ldap/tests/ldap_set_option_crlcheck_error.phpt b/ext/ldap/tests/ldap_set_option_crlcheck_error.phpt
new file mode 100644
index 0000000000..ea5318344a
--- /dev/null
+++ b/ext/ldap/tests/ldap_set_option_crlcheck_error.phpt
@@ -0,0 +1,17 @@
+--TEST--
+ldap_set_option() - Error test for TLS CRL check ldap option
+--CREDITS--
+Chad Sikorra <Chad.Sikorra@gmail.com>
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--FILE--
+<?php
+require "connect.inc";
+$link = ldap_connect($host, $port);
+$result = ldap_set_option($link, LDAP_OPT_X_TLS_CRLCHECK, 9001);
+var_dump($result);
+?>
+===DONE===
+--EXPECT--
+bool(false)
+===DONE===
diff --git a/ext/ldap/tests/ldap_set_option_keepalive_basic.phpt b/ext/ldap/tests/ldap_set_option_keepalive_basic.phpt
new file mode 100644
index 0000000000..211644b444
--- /dev/null
+++ b/ext/ldap/tests/ldap_set_option_keepalive_basic.phpt
@@ -0,0 +1,32 @@
+--TEST--
+ldap_set_option() - Basic test for TCP keepalive ldap options
+--CREDITS--
+Chad Sikorra <Chad.Sikorra@gmail.com>
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--FILE--
+<?php
+require "connect.inc";
+$link = ldap_connect($host, $port);
+
+foreach([
+ LDAP_OPT_X_KEEPALIVE_IDLE,
+ LDAP_OPT_X_KEEPALIVE_PROBES,
+ LDAP_OPT_X_KEEPALIVE_INTERVAL,
+] as $option) {
+ $result = ldap_set_option($link, $option, 5);
+ var_dump($result);
+
+ ldap_get_option($link, $option, $optionval);
+ var_dump($optionval);
+}
+?>
+===DONE===
+--EXPECT--
+bool(true)
+int(5)
+bool(true)
+int(5)
+bool(true)
+int(5)
+===DONE===
diff --git a/ext/ldap/tests/ldap_set_option_tls_protocol_min_basic.phpt b/ext/ldap/tests/ldap_set_option_tls_protocol_min_basic.phpt
new file mode 100644
index 0000000000..81360d0759
--- /dev/null
+++ b/ext/ldap/tests/ldap_set_option_tls_protocol_min_basic.phpt
@@ -0,0 +1,38 @@
+--TEST--
+ldap_set_option() - Basic test for TLS protocol min ldap option
+--CREDITS--
+Chad Sikorra <Chad.Sikorra@gmail.com>
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--FILE--
+<?php
+require "connect.inc";
+$link = ldap_connect($host, $port);
+
+foreach([
+ LDAP_OPT_X_TLS_PROTOCOL_SSL2,
+ LDAP_OPT_X_TLS_PROTOCOL_SSL3,
+ LDAP_OPT_X_TLS_PROTOCOL_TLS1_0,
+ LDAP_OPT_X_TLS_PROTOCOL_TLS1_1,
+ LDAP_OPT_X_TLS_PROTOCOL_TLS1_2,
+] as $option) {
+ $result = ldap_set_option($link, LDAP_OPT_X_TLS_PROTOCOL_MIN, $option);
+ var_dump($result);
+
+ ldap_get_option($link, LDAP_OPT_X_TLS_PROTOCOL_MIN, $optionval);
+ var_dump($optionval);
+}
+?>
+===DONE===
+--EXPECT--
+bool(true)
+int(512)
+bool(true)
+int(768)
+bool(true)
+int(769)
+bool(true)
+int(770)
+bool(true)
+int(771)
+===DONE===
diff --git a/ext/libxml/tests/bug69753-mb.phpt b/ext/libxml/tests/bug69753-mb.phpt
new file mode 100644
index 0000000000..eec993ddd2
--- /dev/null
+++ b/ext/libxml/tests/bug69753-mb.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Bug #69753 - libXMLError::file contains invalid URI
+--XFAIL--
+Awaiting upstream fix: https://bugzilla.gnome.org/show_bug.cgi?id=750365
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') die("skip this test is for Windows platforms only");
+if (!extension_loaded('dom')) die('skip dom extension not available');
+?>
+--FILE--
+<?php
+libxml_use_internal_errors(true);
+$doc = new DomDocument();
+$doc->load(__DIR__ . DIRECTORY_SEPARATOR . 'bug69753私はガラスを食べられます.xml');
+$error = libxml_get_last_error();
+var_dump($error->file);
+?>
+--EXPECTF--
+string(%d) "file:///%s/ext/libxml/tests/bug69753.xml"
diff --git a/ext/libxml/tests/bug69753私はガラスを食べられます.xml b/ext/libxml/tests/bug69753私はガラスを食べられます.xml
new file mode 100644
index 0000000000..63b18d5c6d
--- /dev/null
+++ b/ext/libxml/tests/bug69753私はガラスを食べられます.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<root>
+ <sub>
+</root>
diff --git a/ext/libxml/tests/libxml_set_external_entity_loader_error1.phpt b/ext/libxml/tests/libxml_set_external_entity_loader_error1.phpt
index 5ed079d8dd..7f1adde613 100644
--- a/ext/libxml/tests/libxml_set_external_entity_loader_error1.phpt
+++ b/ext/libxml/tests/libxml_set_external_entity_loader_error1.phpt
@@ -17,7 +17,11 @@ var_dump(libxml_set_external_entity_loader());
var_dump(libxml_set_external_entity_loader(function() {}, 2));
var_dump(libxml_set_external_entity_loader(function($a, $b, $c, $d) {}));
-var_dump($dd->validate());
+try {
+ var_dump($dd->validate());
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
echo "Done.\n";
@@ -32,8 +36,6 @@ Warning: libxml_set_external_entity_loader() expects exactly 1 parameter, 2 give
NULL
bool(true)
-Warning: Missing argument 4 for {closure}() in %s on line %d
-
Warning: DOMDocument::validate(): Could not load the external subset "http://example.com/foobar" in %s on line %d
-bool(false)
+Exception: Too few arguments to function {closure}(), 3 passed and exactly 4 expected
Done.
diff --git a/ext/mbstring/mb_gpc.c b/ext/mbstring/mb_gpc.c
index ef2035f1ff..dfe96ccf13 100644
--- a/ext/mbstring/mb_gpc.c
+++ b/ext/mbstring/mb_gpc.c
@@ -254,7 +254,7 @@ const mbfl_encoding *_php_mb_encoding_handler_ex(const php_mb_encoding_handler_i
}
if (n > (PG(max_input_vars) * 2)) {
- php_error_docref(NULL, E_WARNING, "Input variables exceeded %pd. To increase the limit change max_input_vars in php.ini.", PG(max_input_vars));
+ php_error_docref(NULL, E_WARNING, "Input variables exceeded " ZEND_LONG_FMT ". To increase the limit change max_input_vars in php.ini.", PG(max_input_vars));
goto out;
}
diff --git a/ext/mbstring/mbstring.c b/ext/mbstring/mbstring.c
index 72e285d34a..c47cf5431f 100644
--- a/ext/mbstring/mbstring.c
+++ b/ext/mbstring/mbstring.c
@@ -687,8 +687,8 @@ static sapi_post_entry mbstr_post_entries[] = {
static int
php_mb_parse_encoding_list(const char *value, size_t value_length, const mbfl_encoding ***return_list, size_t *return_size, int persistent)
{
- int size, bauto, ret = SUCCESS;
- size_t n;
+ int bauto, ret = SUCCESS;
+ size_t n, size;
char *p, *p1, *p2, *endp, *tmpstr;
const mbfl_encoding **entry, **list;
@@ -1594,6 +1594,8 @@ PHP_MSHUTDOWN_FUNCTION(mbstring)
{
UNREGISTER_INI_ENTRIES();
+ zend_multibyte_restore_functions();
+
#if HAVE_MBREGEX
PHP_MSHUTDOWN(mb_regex) (INIT_FUNC_ARGS_PASSTHRU);
#endif
@@ -2097,8 +2099,13 @@ PHP_FUNCTION(mb_parse_str)
detected = _php_mb_encoding_handler_ex(&info, track_vars_array, encstr);
} else {
zval tmp;
- zend_array *symbol_table = zend_rebuild_symbol_table();
+ zend_array *symbol_table;
+ if (zend_forbid_dynamic_call("mb_parse_str() with a single argument") == FAILURE) {
+ efree(encstr);
+ return;
+ }
+ symbol_table = zend_rebuild_symbol_table();
ZVAL_ARR(&tmp, symbol_table);
detected = _php_mb_encoding_handler_ex(&info, &tmp, encstr);
}
@@ -2262,7 +2269,7 @@ PHP_FUNCTION(mb_strlen)
PHP_FUNCTION(mb_strpos)
{
int n, reverse = 0;
- zend_long offset = 0;
+ zend_long offset = 0, slen;
mbfl_string haystack, needle;
char *enc_name = NULL;
size_t enc_name_len, haystack_len, needle_len;
@@ -2297,7 +2304,11 @@ PHP_FUNCTION(mb_strpos)
}
}
- if (offset < 0 || offset > mbfl_strlen(&haystack)) {
+ slen = mbfl_strlen(&haystack);
+ if (offset < 0) {
+ offset += slen;
+ }
+ if (offset < 0 || offset > slen) {
php_error_docref(NULL, E_WARNING, "Offset not contained in string");
RETURN_FALSE;
}
@@ -3060,7 +3071,7 @@ PHP_FUNCTION(mb_strwidth)
PHP_FUNCTION(mb_strimwidth)
{
char *str, *trimmarker = NULL, *encoding = NULL;
- zend_long from, width;
+ zend_long from, width, swidth;
size_t str_len, trimmarker_len, encoding_len;
mbfl_string string, result, marker, *ret;
@@ -3088,13 +3099,25 @@ PHP_FUNCTION(mb_strimwidth)
string.val = (unsigned char *)str;
string.len = str_len;
+ if ((from < 0) || (width < 0)) {
+ swidth = mbfl_strwidth(&string);
+ }
+
+ if (from < 0) {
+ from += swidth;
+ }
+
if (from < 0 || (size_t)from > str_len) {
php_error_docref(NULL, E_WARNING, "Start position is out of range");
RETURN_FALSE;
}
if (width < 0) {
- php_error_docref(NULL, E_WARNING, "Width is negative value");
+ width = swidth + width - from;
+ }
+
+ if (width < 0) {
+ php_error_docref(NULL, E_WARNING, "Width is out of range");
RETURN_FALSE;
}
@@ -3434,6 +3457,10 @@ PHP_FUNCTION(mb_list_encodings)
const mbfl_encoding *encoding;
int i;
+ if (zend_parse_parameters_none() == FAILURE) {
+ return;
+ }
+
array_init(return_value);
i = 0;
encodings = mbfl_get_supported_encodings();
@@ -4696,40 +4723,32 @@ PHP_FUNCTION(mb_get_info)
}
/* }}} */
-/* {{{ proto bool mb_check_encoding([string var[, string encoding]])
- Check if the string is valid for the specified encoding */
-PHP_FUNCTION(mb_check_encoding)
+MBSTRING_API int php_mb_check_encoding(const char *input, size_t length, const char *enc)
{
- char *var = NULL;
- size_t var_len;
- char *enc = NULL;
- size_t enc_len;
- mbfl_buffer_converter *convd;
const mbfl_encoding *encoding = MBSTRG(current_internal_encoding);
+ mbfl_buffer_converter *convd;
mbfl_string string, result, *ret = NULL;
long illegalchars = 0;
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "|ss", &var, &var_len, &enc, &enc_len) == FAILURE) {
- return;
- }
-
- if (var == NULL) {
- RETURN_BOOL(MBSTRG(illegalchars) == 0);
+ if (input == NULL) {
+ return MBSTRG(illegalchars) == 0;
}
if (enc != NULL) {
encoding = mbfl_name2encoding(enc);
if (!encoding || encoding == &mbfl_encoding_pass) {
php_error_docref(NULL, E_WARNING, "Invalid encoding \"%s\"", enc);
- RETURN_FALSE;
+ return 0;
}
}
convd = mbfl_buffer_converter_new2(encoding, encoding, 0);
+
if (convd == NULL) {
php_error_docref(NULL, E_WARNING, "Unable to create converter");
- RETURN_FALSE;
+ return 0;
}
+
mbfl_buffer_converter_illegal_mode(convd, MBFL_OUTPUTFILTER_ILLEGAL_MODE_NONE);
mbfl_buffer_converter_illegal_substchar(convd, 0);
@@ -4737,19 +4756,43 @@ PHP_FUNCTION(mb_check_encoding)
mbfl_string_init_set(&string, mbfl_no_language_neutral, encoding->no_encoding);
mbfl_string_init(&result);
- string.val = (unsigned char *)var;
- string.len = var_len;
+ string.val = (unsigned char *) input;
+ string.len = length;
+
ret = mbfl_buffer_converter_feed_result(convd, &string, &result);
illegalchars = mbfl_buffer_illegalchars(convd);
mbfl_buffer_converter_delete(convd);
- RETVAL_FALSE;
if (ret != NULL) {
if (illegalchars == 0 && string.len == result.len && memcmp(string.val, result.val, string.len) == 0) {
- RETVAL_TRUE;
+ mbfl_string_clear(&result);
+ return 1;
}
+
mbfl_string_clear(&result);
}
+
+ return 0;
+}
+
+/* {{{ proto bool mb_check_encoding([string var[, string encoding]])
+ Check if the string is valid for the specified encoding */
+PHP_FUNCTION(mb_check_encoding)
+{
+ char *var = NULL;
+ size_t var_len;
+ char *enc = NULL;
+ size_t enc_len;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "|ss", &var, &var_len, &enc, &enc_len) == FAILURE) {
+ return;
+ }
+
+ RETVAL_FALSE;
+
+ if (php_mb_check_encoding(var, var_len, enc)) {
+ RETVAL_TRUE;
+ }
}
/* }}} */
@@ -4916,6 +4959,9 @@ MBSTRING_API int php_mb_stripos(int mode, const char *old_haystack, unsigned int
break;
}
} else {
+ if (offset < 0) {
+ offset += (long)haystack_char_len;
+ }
if (offset < 0 || offset > haystack_char_len) {
php_error_docref(NULL, E_WARNING, "Offset not contained in string");
break;
diff --git a/ext/mbstring/mbstring.h b/ext/mbstring/mbstring.h
index 255ac56634..c757d7255e 100644
--- a/ext/mbstring/mbstring.h
+++ b/ext/mbstring/mbstring.h
@@ -152,6 +152,7 @@ MBSTRING_API int php_mb_encoding_detector_ex(const char *arg_string, int arg_len
MBSTRING_API int php_mb_encoding_converter_ex(char **str, int *len, const char *encoding_to,
const char *encoding_from);
MBSTRING_API int php_mb_stripos(int mode, const char *old_haystack, unsigned int old_haystack_len, const char *old_needle, unsigned int old_needle_len, long offset, const char *from_encoding);
+MBSTRING_API int php_mb_check_encoding(const char *input, size_t length, const char *enc);
/* internal use only */
int _php_mb_ini_mbstring_internal_encoding_set(const char *new_value, uint new_value_length);
diff --git a/ext/mbstring/php_mbregex.c b/ext/mbstring/php_mbregex.c
index 747c98a682..998d9a7072 100644
--- a/ext/mbstring/php_mbregex.c
+++ b/ext/mbstring/php_mbregex.c
@@ -703,6 +703,23 @@ static void _php_mb_regex_ereg_exec(INTERNAL_FUNCTION_PARAMETERS, int icase)
RETURN_FALSE;
}
+ if (!php_mb_check_encoding(
+ string,
+ string_len,
+ _php_mb_regex_mbctype2name(MBREX(current_mbctype))
+ )) {
+ if (array != NULL) {
+ zval_dtor(array);
+ array_init(array);
+ }
+ RETURN_FALSE;
+ }
+
+ if (array != NULL) {
+ zval_dtor(array);
+ array_init(array);
+ }
+
options = MBREX(regex_default_options);
if (icase) {
options |= ONIG_OPTION_IGNORECASE;
@@ -741,14 +758,12 @@ static void _php_mb_regex_ereg_exec(INTERNAL_FUNCTION_PARAMETERS, int icase)
match_len = 1;
str = string;
if (array != NULL) {
- zval_dtor(array);
- array_init(array);
match_len = regs->end[0] - regs->beg[0];
for (i = 0; i < regs->num_regs; i++) {
beg = regs->beg[i];
end = regs->end[i];
- if (beg >= 0 && beg < end && end <= string_len) {
+ if (beg >= 0 && beg < end && (size_t)end <= string_len) {
add_index_stringl(array, i, (char *)&str[beg], end - beg);
} else {
add_index_bool(array, i, 0);
@@ -807,7 +822,8 @@ static void _php_mb_regex_ereg_replace_exec(INTERNAL_FUNCTION_PARAMETERS, OnigOp
smart_str out_buf = {0};
smart_str eval_buf = {0};
smart_str *pbuf;
- int i, err, eval, n;
+ size_t i;
+ int err, eval, n;
OnigUChar *pos;
OnigUChar *string_lim;
char *description = NULL;
@@ -847,6 +863,14 @@ static void _php_mb_regex_ereg_replace_exec(INTERNAL_FUNCTION_PARAMETERS, OnigOp
}
}
+ if (!php_mb_check_encoding(
+ string,
+ string_len,
+ _php_mb_regex_mbctype2name(MBREX(current_mbctype))
+ )) {
+ RETURN_NULL();
+ }
+
if (option_str != NULL) {
_php_mb_regex_init_options(option_str, option_str_len, &options, &syntax, &eval);
} else {
@@ -854,6 +878,9 @@ static void _php_mb_regex_ereg_replace_exec(INTERNAL_FUNCTION_PARAMETERS, OnigOp
syntax = MBREX(regex_default_syntax);
}
}
+ if (eval && !is_callable) {
+ php_error_docref(NULL, E_DEPRECATED, "The 'e' option is deprecated, use mb_ereg_replace_callback instead");
+ }
if (Z_TYPE_P(arg_pattern_zval) == IS_STRING) {
arg_pattern = Z_STRVAL_P(arg_pattern_zval);
arg_pattern_len = Z_STRLEN_P(arg_pattern_zval);
@@ -926,7 +953,7 @@ static void _php_mb_regex_ereg_replace_exec(INTERNAL_FUNCTION_PARAMETERS, OnigOp
n = p[1] - '0';
}
if (n >= 0 && n < regs->num_regs) {
- if (regs->beg[n] >= 0 && regs->beg[n] < regs->end[n] && regs->end[n] <= string_len) {
+ if (regs->beg[n] >= 0 && regs->beg[n] < regs->end[n] && (size_t)regs->end[n] <= string_len) {
smart_str_appendl(pbuf, string + regs->beg[n], regs->end[n] - regs->beg[n]);
}
p += 2;
@@ -954,8 +981,11 @@ static void _php_mb_regex_ereg_replace_exec(INTERNAL_FUNCTION_PARAMETERS, OnigOp
/* do eval */
if (zend_eval_stringl(ZSTR_VAL(eval_str), ZSTR_LEN(eval_str), &v, description) == FAILURE) {
efree(description);
- php_error_docref(NULL,E_ERROR, "Failed evaluating code: %s%s", PHP_EOL, ZSTR_VAL(eval_str));
- /* zend_error() does not return in this case */
+ zend_throw_error(NULL, "Failed evaluating code: %s%s", PHP_EOL, ZSTR_VAL(eval_str));
+ onig_region_free(regs, 0);
+ smart_str_free(&out_buf);
+ smart_str_free(&eval_buf);
+ RETURN_FALSE;
}
/* result of eval */
@@ -1100,7 +1130,7 @@ PHP_FUNCTION(mb_split)
beg = regs->beg[0], end = regs->end[0];
/* add it to the array */
if ((pos - (OnigUChar *)string) < end) {
- if (beg < string_len && beg >= (chunk_pos - (OnigUChar *)string)) {
+ if ((size_t)beg < string_len && beg >= (chunk_pos - (OnigUChar *)string)) {
add_next_index_stringl(return_value, (char *)chunk_pos, ((OnigUChar *)(string + beg) - chunk_pos));
--count;
} else {
@@ -1315,13 +1345,13 @@ PHP_FUNCTION(mb_ereg_search_regs)
PHP_FUNCTION(mb_ereg_search_init)
{
size_t argc = ZEND_NUM_ARGS();
- zval *arg_str;
+ zend_string *arg_str;
char *arg_pattern = NULL, *arg_options = NULL;
size_t arg_pattern_len = 0, arg_options_len = 0;
OnigSyntaxType *syntax = NULL;
OnigOptionType option;
- if (zend_parse_parameters(argc, "z|ss", &arg_str, &arg_pattern, &arg_pattern_len, &arg_options, &arg_options_len) == FAILURE) {
+ if (zend_parse_parameters(argc, "S|ss", &arg_str, &arg_pattern, &arg_pattern_len, &arg_options, &arg_options_len) == FAILURE) {
return;
}
@@ -1349,16 +1379,24 @@ PHP_FUNCTION(mb_ereg_search_init)
zval_ptr_dtor(&MBREX(search_str));
}
- ZVAL_DUP(&MBREX(search_str), arg_str);
+ ZVAL_STR_COPY(&MBREX(search_str), arg_str);
- MBREX(search_pos) = 0;
+ if (php_mb_check_encoding(
+ ZSTR_VAL(arg_str),
+ ZSTR_LEN(arg_str),
+ _php_mb_regex_mbctype2name(MBREX(current_mbctype))
+ )) {
+ MBREX(search_pos) = 0;
+ RETVAL_TRUE;
+ } else {
+ MBREX(search_pos) = ZSTR_LEN(arg_str);
+ RETVAL_FALSE;
+ }
if (MBREX(search_regs) != NULL) {
onig_region_free(MBREX(search_regs), 1);
MBREX(search_regs) = NULL;
}
-
- RETURN_TRUE;
}
/* }}} */
@@ -1408,6 +1446,11 @@ PHP_FUNCTION(mb_ereg_search_setpos)
return;
}
+ /* Accept negative position if length of search string can be determined */
+ if ((position < 0) && (!Z_ISUNDEF(MBREX(search_str))) && (Z_TYPE(MBREX(search_str)) == IS_STRING)) {
+ position += Z_STRLEN(MBREX(search_str));
+ }
+
if (position < 0 || (!Z_ISUNDEF(MBREX(search_str)) && Z_TYPE(MBREX(search_str)) == IS_STRING && (size_t)position > Z_STRLEN(MBREX(search_str)))) {
php_error_docref(NULL, E_WARNING, "Position is out of range");
MBREX(search_pos) = 0;
diff --git a/ext/mbstring/tests/bug43301.phpt b/ext/mbstring/tests/bug43301.phpt
index 2a5f748c5b..f209bd7554 100644
--- a/ext/mbstring/tests/bug43301.phpt
+++ b/ext/mbstring/tests/bug43301.phpt
@@ -15,7 +15,16 @@ echo mb_ereg_replace($ptr,'$1',$txt,'e');
?>
--EXPECTF--
-Parse error: syntax error, unexpected %s, expecting %s or '$' in %sbug43301.php(%d) : mbregex replace on line %d
+Deprecated: mb_ereg_replace(): The 'e' option is deprecated, use mb_ereg_replace_callback instead in %s%ebug43301.php on line %d
-Fatal error: mb_ereg_replace(): Failed evaluating code:
-$1 in %sbug43301.php on line %d
+Fatal error: Uncaught ParseError: syntax error, unexpected '1' (T_LNUMBER), expecting variable (T_VARIABLE) or '{' or '$' in %sbug43301.php(%d) : mbregex replace:1
+Stack trace:
+#0 %sbug43301.php(%d): mb_ereg_replace('hello', '$1', 'hello, I have g...', 'e')
+#1 {main}
+
+Next Error: Failed evaluating code:
+$1 in %sbug43301.php:%d
+Stack trace:
+#0 %sbug43301.php(%d): mb_ereg_replace('hello', '$1', 'hello, I have g...', 'e')
+#1 {main}
+ thrown in %sbug43301.php on line %d
diff --git a/ext/mbstring/tests/bug43994.phpt b/ext/mbstring/tests/bug43994.phpt
index 8fdb904a7c..b2fd867da9 100644
--- a/ext/mbstring/tests/bug43994.phpt
+++ b/ext/mbstring/tests/bug43994.phpt
@@ -49,7 +49,8 @@ With $regs arg:
Warning: mb_ereg(): empty pattern in %s on line %d
bool(false)
-NULL
+array(0) {
+}
-- Iteration 2 --
Without $regs arg:
@@ -60,7 +61,8 @@ With $regs arg:
Warning: mb_ereg(): empty pattern in %s on line %d
bool(false)
-NULL
+array(0) {
+}
-- Iteration 3 --
Without $regs arg:
@@ -71,7 +73,8 @@ With $regs arg:
Warning: mb_ereg(): empty pattern in %s on line %d
bool(false)
-NULL
+array(0) {
+}
-- Iteration 4 --
Without $regs arg:
@@ -82,7 +85,8 @@ With $regs arg:
Warning: mb_ereg(): empty pattern in %s on line %d
bool(false)
-NULL
+array(0) {
+}
-- Iteration 5 --
Without $regs arg:
@@ -93,7 +97,8 @@ With $regs arg:
Warning: mb_ereg(): empty pattern in %s on line %d
bool(false)
-NULL
+array(0) {
+}
-- Iteration 6 --
Without $regs arg:
@@ -104,7 +109,8 @@ With $regs arg:
Warning: mb_ereg(): empty pattern in %s on line %d
bool(false)
-NULL
+array(0) {
+}
-- Iteration 7 --
Without $regs arg:
@@ -115,7 +121,8 @@ With $regs arg:
Warning: mb_ereg(): empty pattern in %s on line %d
bool(false)
-NULL
+array(0) {
+}
-- Iteration 8 --
Without $regs arg:
@@ -126,4 +133,5 @@ With $regs arg:
Warning: mb_ereg(): empty pattern in %s on line %d
bool(false)
-NULL
+array(0) {
+}
diff --git a/ext/mbstring/tests/bug45923.phpt b/ext/mbstring/tests/bug45923.phpt
index 2d184ab019..41ffd70924 100644
--- a/ext/mbstring/tests/bug45923.phpt
+++ b/ext/mbstring/tests/bug45923.phpt
@@ -6,197 +6,241 @@ Bug #45923 (mb_st[r]ripos() offset not handled correctly)
mbstring.internal_encoding=UTF-8
--FILE--
<?php
-var_dump(strpos("abc abc abc", "abc", 0));
-var_dump(strpos("abc abc abc", "abc", 3));
-var_dump(strpos("abc abc abc", "abc", 6));
-var_dump(strpos("abc abc abc", "abc", 9));
-var_dump(strpos("abc abc abc", "abc", 11));
-var_dump(strpos("abc abc abc", "abc", 12));
-var_dump(strpos("abc abc abc", "abc", -1));
-var_dump(strpos("abc abc abc", "abc", -3));
-var_dump(strpos("abc abc abc", "abc", -6));
-
-var_dump(mb_strpos("●○◆ ●○◆ ●○◆", "●○◆", 0));
-var_dump(mb_strpos("●○◆ ●○◆ ●○◆", "●○◆", 3));
-var_dump(mb_strpos("●○◆ ●○◆ ●○◆", "●○◆", 6));
-var_dump(mb_strpos("●○◆ ●○◆ ●○◆", "●○◆", 9));
-var_dump(mb_strpos("●○◆ ●○◆ ●○◆", "●○◆", 11));
-var_dump(mb_strpos("●○◆ ●○◆ ●○◆", "●○◆", 12));
-var_dump(mb_strpos("●○◆ ●○◆ ●○◆", "●○◆", -1));
-var_dump(mb_strpos("●○◆ ●○◆ ●○◆", "●○◆", -3));
-var_dump(mb_strpos("●○◆ ●○◆ ●○◆", "●○◆", -6));
-
-var_dump(stripos("abc abc abc", "abc", 0));
-var_dump(stripos("abc abc abc", "abc", 3));
-var_dump(stripos("abc abc abc", "abc", 6));
-var_dump(stripos("abc abc abc", "abc", 9));
-var_dump(stripos("abc abc abc", "abc", 11));
-var_dump(stripos("abc abc abc", "abc", 12));
-var_dump(stripos("abc abc abc", "abc", -1));
-var_dump(stripos("abc abc abc", "abc", -3));
-var_dump(stripos("abc abc abc", "abc", -6));
-
-var_dump(mb_stripos("●○◆ ●○◆ ●○◆", "●○◆", 0));
-var_dump(mb_stripos("●○◆ ●○◆ ●○◆", "●○◆", 3));
-var_dump(mb_stripos("●○◆ ●○◆ ●○◆", "●○◆", 6));
-var_dump(mb_stripos("●○◆ ●○◆ ●○◆", "●○◆", 9));
-var_dump(mb_stripos("●○◆ ●○◆ ●○◆", "●○◆", 11));
-var_dump(mb_stripos("●○◆ ●○◆ ●○◆", "●○◆", 12));
-var_dump(mb_stripos("●○◆ ●○◆ ●○◆", "●○◆", -1));
-var_dump(mb_stripos("●○◆ ●○◆ ●○◆", "●○◆", -3));
-var_dump(mb_stripos("●○◆ ●○◆ ●○◆", "●○◆", -6));
-
-var_dump(strrpos("abc abc abc", "abc", 0));
-var_dump(strrpos("abc abc abc", "abc", 3));
-var_dump(strrpos("abc abc abc", "abc", 6));
-var_dump(strrpos("abc abc abc", "abc", 9));
-var_dump(strrpos("abc abc abc", "abc", 11));
-var_dump(strrpos("abc abc abc", "abc", 12));
-var_dump(strrpos("abc abc abc", "abc", -1));
-var_dump(strrpos("abc abc abc", "abc", -3));
-var_dump(strrpos("abc abc abc", "abc", -6));
-
-var_dump(mb_strrpos("●○◆ ●○◆ ●○◆", "●○◆", 0));
-var_dump(mb_strrpos("●○◆ ●○◆ ●○◆", "●○◆", 3));
-var_dump(mb_strrpos("●○◆ ●○◆ ●○◆", "●○◆", 6));
-var_dump(mb_strrpos("●○◆ ●○◆ ●○◆", "●○◆", 9));
-var_dump(mb_strrpos("●○◆ ●○◆ ●○◆", "●○◆", 11));
-var_dump(mb_strrpos("●○◆ ●○◆ ●○◆", "●○◆", 12));
-var_dump(mb_strrpos("●○◆ ●○◆ ●○◆", "●○◆", -1));
-var_dump(mb_strrpos("●○◆ ●○◆ ●○◆", "●○◆", -3));
-var_dump(mb_strrpos("●○◆ ●○◆ ●○◆", "●○◆", -6));
-
-var_dump(strripos("abc abc abc", "abc", 0));
-var_dump(strripos("abc abc abc", "abc", 3));
-var_dump(strripos("abc abc abc", "abc", 6));
-var_dump(strripos("abc abc abc", "abc", 9));
-var_dump(strripos("abc abc abc", "abc", 11));
-var_dump(strripos("abc abc abc", "abc", 12));
-var_dump(strripos("abc abc abc", "abc", -1));
-var_dump(strripos("abc abc abc", "abc", -3));
-var_dump(strripos("abc abc abc", "abc", -6));
-
-var_dump(mb_strripos("●○◆ ●○◆ ●○◆", "●○◆", 0));
-var_dump(mb_strripos("●○◆ ●○◆ ●○◆", "●○◆", 3));
-var_dump(mb_strripos("●○◆ ●○◆ ●○◆", "●○◆", 6));
-var_dump(mb_strripos("●○◆ ●○◆ ●○◆", "●○◆", 9));
-var_dump(mb_strripos("●○◆ ●○◆ ●○◆", "●○◆", 11));
-var_dump(mb_strripos("●○◆ ●○◆ ●○◆", "●○◆", 12));
-var_dump(mb_strripos("●○◆ ●○◆ ●○◆", "●○◆", -1));
-var_dump(mb_strripos("●○◆ ●○◆ ●○◆", "●○◆", -3));
-var_dump(mb_strripos("●○◆ ●○◆ ●○◆", "●○◆", -6));
+function section($func, $haystack, $needle)
+{
+ echo "\n------- $func -----------\n\n";
+ foreach(array(0, 3, 6, 9, 11, 12, -1, -3, -6, -20) as $offset) {
+ echo "> Offset: $offset\n";
+ var_dump($func($haystack,$needle,$offset));
+ }
+}
+
+section('strpos' , "abc abc abc" , "abc");
+section('mb_strpos' , "●○◆ ●○◆ ●○◆", "●○◆");
+
+section('stripos' , "abc abc abc" , "abc");
+section('mb_stripos' , "●○◆ ●○◆ ●○◆", "●○◆");
+
+section('strrpos' , "abc abc abc" , "abc");
+section('mb_strrpos' , "●○◆ ●○◆ ●○◆", "●○◆");
+
+section('strripos' , "abc abc abc" , "abc");
+section('mb_strripos', "●○◆ ●○◆ ●○◆", "●○◆");
?>
--EXPECTF--
+------- strpos -----------
+
+> Offset: 0
int(0)
+> Offset: 3
int(4)
+> Offset: 6
int(8)
+> Offset: 9
bool(false)
+> Offset: 11
bool(false)
+> Offset: 12
Warning: strpos(): Offset not contained in string in %s on line %d
bool(false)
-
-Warning: strpos(): Offset not contained in string in %s on line %d
+> Offset: -1
bool(false)
+> Offset: -3
+int(8)
+> Offset: -6
+int(8)
+> Offset: -20
Warning: strpos(): Offset not contained in string in %s on line %d
bool(false)
-Warning: strpos(): Offset not contained in string in %s on line %d
-bool(false)
+------- mb_strpos -----------
+
+> Offset: 0
int(0)
+> Offset: 3
int(4)
+> Offset: 6
int(8)
+> Offset: 9
bool(false)
+> Offset: 11
bool(false)
+> Offset: 12
Warning: mb_strpos(): Offset not contained in string in %s on line %d
bool(false)
-
-Warning: mb_strpos(): Offset not contained in string in %s on line %d
+> Offset: -1
bool(false)
+> Offset: -3
+int(8)
+> Offset: -6
+int(8)
+> Offset: -20
Warning: mb_strpos(): Offset not contained in string in %s on line %d
bool(false)
-Warning: mb_strpos(): Offset not contained in string in %s on line %d
-bool(false)
+------- stripos -----------
+
+> Offset: 0
int(0)
+> Offset: 3
int(4)
+> Offset: 6
int(8)
+> Offset: 9
bool(false)
+> Offset: 11
bool(false)
+> Offset: 12
Warning: stripos(): Offset not contained in string in %s on line %d
bool(false)
-
-Warning: stripos(): Offset not contained in string in %s on line %d
+> Offset: -1
bool(false)
+> Offset: -3
+int(8)
+> Offset: -6
+int(8)
+> Offset: -20
Warning: stripos(): Offset not contained in string in %s on line %d
bool(false)
-Warning: stripos(): Offset not contained in string in %s on line %d
-bool(false)
+------- mb_stripos -----------
+
+> Offset: 0
int(0)
+> Offset: 3
int(4)
+> Offset: 6
int(8)
+> Offset: 9
bool(false)
+> Offset: 11
bool(false)
+> Offset: 12
Warning: mb_stripos(): Offset not contained in string in %s on line %d
bool(false)
-
-Warning: mb_stripos(): Offset not contained in string in %s on line %d
+> Offset: -1
bool(false)
+> Offset: -3
+int(8)
+> Offset: -6
+int(8)
+> Offset: -20
Warning: mb_stripos(): Offset not contained in string in %s on line %d
bool(false)
-Warning: mb_stripos(): Offset not contained in string in %s on line %d
-bool(false)
+------- strrpos -----------
+
+> Offset: 0
int(8)
+> Offset: 3
int(8)
+> Offset: 6
int(8)
+> Offset: 9
bool(false)
+> Offset: 11
bool(false)
+> Offset: 12
Warning: strrpos(): Offset is greater than the length of haystack string in %s on line %d
bool(false)
+> Offset: -1
int(8)
+> Offset: -3
int(8)
+> Offset: -6
int(4)
+> Offset: -20
+
+Warning: strrpos(): Offset is greater than the length of haystack string in %s on line %d
+bool(false)
+
+------- mb_strrpos -----------
+
+> Offset: 0
int(8)
+> Offset: 3
int(8)
+> Offset: 6
int(8)
+> Offset: 9
bool(false)
+> Offset: 11
bool(false)
+> Offset: 12
Warning: mb_strrpos(): Offset is greater than the length of haystack string in %s on line %d
bool(false)
+> Offset: -1
int(8)
+> Offset: -3
int(8)
+> Offset: -6
int(4)
+> Offset: -20
+
+Warning: mb_strrpos(): Offset is greater than the length of haystack string in %s on line %d
+bool(false)
+
+------- strripos -----------
+
+> Offset: 0
int(8)
+> Offset: 3
int(8)
+> Offset: 6
int(8)
+> Offset: 9
bool(false)
+> Offset: 11
bool(false)
+> Offset: 12
Warning: strripos(): Offset is greater than the length of haystack string in %s on line %d
bool(false)
+> Offset: -1
int(8)
+> Offset: -3
int(8)
+> Offset: -6
int(4)
+> Offset: -20
+
+Warning: strripos(): Offset is greater than the length of haystack string in %s on line %d
+bool(false)
+
+------- mb_strripos -----------
+
+> Offset: 0
int(8)
+> Offset: 3
int(8)
+> Offset: 6
int(8)
+> Offset: 9
bool(false)
+> Offset: 11
bool(false)
+> Offset: 12
Warning: mb_strripos(): Offset is greater than the length of haystack string in %s on line %d
bool(false)
+> Offset: -1
int(8)
+> Offset: -3
int(8)
+> Offset: -6
int(4)
+> Offset: -20
+
+Warning: mb_strripos(): Offset is greater than the length of haystack string in %s on line %d
+bool(false) \ No newline at end of file
diff --git a/ext/mbstring/tests/bug69151.phpt b/ext/mbstring/tests/bug69151.phpt
new file mode 100644
index 0000000000..801f27e4a7
--- /dev/null
+++ b/ext/mbstring/tests/bug69151.phpt
@@ -0,0 +1,21 @@
+--TEST--
+Bug #69151 (mb_ereg should reject ill-formed byte sequence)
+--SKIPIF--
+<?php extension_loaded('mbstring') or die('skip mbstring not available'); ?>
+--FILE--
+<?php
+$str = "\x80";
+var_dump(
+ false === mb_eregi('.', $str, $matches),
+ [] === $matches,
+ NULL === mb_ereg_replace('.', "\\0", $str),
+ false === mb_ereg_search_init("\x80", '.'),
+ false === mb_ereg_search()
+);
+?>
+--EXPECT--
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
diff --git a/ext/mbstring/tests/bug72164.phpt b/ext/mbstring/tests/bug72164.phpt
index bd58f7e5a5..8666447e7a 100644
--- a/ext/mbstring/tests/bug72164.phpt
+++ b/ext/mbstring/tests/bug72164.phpt
@@ -10,5 +10,6 @@ $var3 = NULL;
$var8 = mbereg_replace($var2,$var3,$var3,$var0);
var_dump($var8);
?>
---EXPECT--
+--EXPECTF--
+Deprecated: mbereg_replace(): The 'e' option is deprecated, use mb_ereg_replace_callback instead in %s%ebug72164.php on line %d
string(0) ""
diff --git a/ext/mbstring/tests/bug73532.phpt b/ext/mbstring/tests/bug73532.phpt
new file mode 100644
index 0000000000..0bc838b075
--- /dev/null
+++ b/ext/mbstring/tests/bug73532.phpt
@@ -0,0 +1,8 @@
+--TEST--
+Bug #73532 (Null pointer dereference in mb_eregi)
+--FILE--
+<?php
+var_dump(mb_eregi("a", "\xf5"));
+?>
+--EXPECTF--
+bool(false)
diff --git a/ext/mbstring/tests/bug73646.phpt b/ext/mbstring/tests/bug73646.phpt
new file mode 100644
index 0000000000..a6aefb22d4
--- /dev/null
+++ b/ext/mbstring/tests/bug73646.phpt
@@ -0,0 +1,11 @@
+--TEST--
+Bug #73646 (mb_ereg_search_init null pointer dereference)
+--FILE--
+<?php
+
+$v1=str_repeat("#", -1);
+var_dump(mb_ereg_search_init($v1));
+?>
+--EXPECTF--
+Warning: str_repeat(): Second argument has to be greater than or equal to 0 in %sbug73646.php on line %d
+bool(true)
diff --git a/ext/mbstring/tests/common.inc b/ext/mbstring/tests/common.inc
index a40dde0399..4205cce7bc 100644
--- a/ext/mbstring/tests/common.inc
+++ b/ext/mbstring/tests/common.inc
@@ -20,7 +20,7 @@ function test_error_handler($err_no, $err_msg, $filename, $linenum, $vars) {
512 => "User Warning", // E_USER_WARMING
1024=> "User Notice", // E_USER_NOTICE
2048=> "Strict Notice", // E_STRICT
- 4096=> "Catchable fatal error", // E_RECOVERABLE_ERROR
+ 4096=> "Recoverable fatal error", // E_RECOVERABLE_ERROR
);
if (!empty($debug)) {
diff --git a/ext/mbstring/tests/mb_ereg1.phpt b/ext/mbstring/tests/mb_ereg1.phpt
index 57884c0e02..c61cdb6da2 100644
--- a/ext/mbstring/tests/mb_ereg1.phpt
+++ b/ext/mbstring/tests/mb_ereg1.phpt
@@ -27,7 +27,8 @@ array(3) {
[1]=>
int(2)
[2]=>
- int(3)
+ array(0) {
+ }
}
Warning: mb_ereg(): empty pattern in %s on line %d
@@ -38,7 +39,8 @@ array(3) {
[1]=>
string(0) ""
[2]=>
- string(0) ""
+ array(0) {
+ }
}
Notice: Array to string conversion in %s on line %d
@@ -50,7 +52,8 @@ array(3) {
[1]=>
int(1)
[2]=>
- string(0) ""
+ array(0) {
+ }
}
Warning: mb_ereg() expects parameter 2 to be string, array given in %s on line %d
diff --git a/ext/mbstring/tests/mb_ereg_basic.phpt b/ext/mbstring/tests/mb_ereg_basic.phpt
index db28223393..6ab15f4a5b 100644
--- a/ext/mbstring/tests/mb_ereg_basic.phpt
+++ b/ext/mbstring/tests/mb_ereg_basic.phpt
@@ -113,5 +113,6 @@ array(3) {
string(8) "MTIzNA=="
}
bool(false)
-NULL
-Done \ No newline at end of file
+array(0) {
+}
+Done
diff --git a/ext/mbstring/tests/mb_ereg_search_setpos.phpt b/ext/mbstring/tests/mb_ereg_search_setpos.phpt
new file mode 100644
index 0000000000..0a4c18473b
--- /dev/null
+++ b/ext/mbstring/tests/mb_ereg_search_setpos.phpt
@@ -0,0 +1,70 @@
+--TEST--
+mb_ereg_search_setpos() function
+--SKIPIF--
+<?php
+if (!extension_loaded('mbstring')) die('skip mbstring not enabled');
+?>
+--FILE--
+<?php
+mb_regex_encoding('iso-8859-1');
+$test_str = 'Itrntinliztin'; // Length = 20
+
+var_dump(mb_ereg_search_setpos(50)); // OK
+var_dump(mb_ereg_search_setpos(-1)); // Error
+
+mb_ereg_search_init($test_str);
+
+$positions = array( 5, 20, 21, 25, 0, -5, -20, -30);
+foreach($positions as $pos) {
+ echo("\n* Position: $pos :\n");
+ var_dump(mb_ereg_search_setpos($pos));
+ var_dump(mb_ereg_search_getpos());
+}
+?>
+==DONE==
+--EXPECTF--
+bool(true)
+
+Warning: mb_ereg_search_setpos(): Position is out of range in %s on line %d
+bool(false)
+
+* Position: 5 :
+bool(true)
+int(5)
+
+* Position: 20 :
+bool(true)
+int(20)
+
+* Position: 21 :
+
+Warning: mb_ereg_search_setpos(): Position is out of range in %s on line %d
+bool(false)
+int(0)
+
+* Position: 25 :
+
+Warning: mb_ereg_search_setpos(): Position is out of range in %s on line %d
+bool(false)
+int(0)
+
+* Position: 0 :
+bool(true)
+int(0)
+
+* Position: -5 :
+bool(true)
+int(15)
+
+* Position: -20 :
+bool(true)
+int(0)
+
+* Position: -30 :
+
+Warning: mb_ereg_search_setpos(): Position is out of range in %s on line %d
+bool(false)
+int(0)
+==DONE==
+
+
diff --git a/ext/mbstring/tests/mb_ereg_variation1.phpt b/ext/mbstring/tests/mb_ereg_variation1.phpt
index 1f4419ddfe..3077bcbd5d 100644
--- a/ext/mbstring/tests/mb_ereg_variation1.phpt
+++ b/ext/mbstring/tests/mb_ereg_variation1.phpt
@@ -95,47 +95,58 @@ echo "Done";
-- Iteration 1 --
bool(false)
-NULL
+array(0) {
+}
-- Iteration 2 --
bool(false)
-NULL
+array(0) {
+}
-- Iteration 3 --
bool(false)
-NULL
+array(0) {
+}
-- Iteration 4 --
bool(false)
-NULL
+array(0) {
+}
-- Iteration 5 --
bool(false)
-NULL
+array(0) {
+}
-- Iteration 6 --
bool(false)
-NULL
+array(0) {
+}
-- Iteration 7 --
bool(false)
-NULL
+array(0) {
+}
-- Iteration 8 --
bool(false)
-NULL
+array(0) {
+}
-- Iteration 9 --
bool(false)
-NULL
+array(0) {
+}
-- Iteration 10 --
bool(false)
-NULL
+array(0) {
+}
-- Iteration 11 --
bool(false)
-NULL
+array(0) {
+}
-- Iteration 12 --
int(6)
@@ -153,13 +164,16 @@ array(1) {
-- Iteration 14 --
bool(false)
-NULL
+array(0) {
+}
-- Iteration 15 --
bool(false)
-NULL
+array(0) {
+}
-- Iteration 16 --
bool(false)
-NULL
+array(0) {
+}
Done
diff --git a/ext/mbstring/tests/mb_ereg_variation2.phpt b/ext/mbstring/tests/mb_ereg_variation2.phpt
index d85c8bd560..291c1c1ec4 100644
--- a/ext/mbstring/tests/mb_ereg_variation2.phpt
+++ b/ext/mbstring/tests/mb_ereg_variation2.phpt
@@ -112,71 +112,88 @@ echo "Done";
-- Iteration 1 --
bool(false)
-NULL
+array(0) {
+}
-- Iteration 2 --
bool(false)
-NULL
+array(0) {
+}
-- Iteration 3 --
bool(false)
-NULL
+array(0) {
+}
-- Iteration 4 --
bool(false)
-NULL
+array(0) {
+}
-- Iteration 5 --
bool(false)
-NULL
+array(0) {
+}
-- Iteration 6 --
bool(false)
-NULL
+array(0) {
+}
-- Iteration 7 --
bool(false)
-NULL
+array(0) {
+}
-- Iteration 8 --
bool(false)
-NULL
+array(0) {
+}
-- Iteration 9 --
bool(false)
-NULL
+array(0) {
+}
-- Iteration 10 --
bool(false)
-NULL
+array(0) {
+}
-- Iteration 11 --
bool(false)
-NULL
+array(0) {
+}
-- Iteration 12 --
bool(false)
-NULL
+array(0) {
+}
-- Iteration 13 --
bool(false)
-NULL
+array(0) {
+}
-- Iteration 14 --
bool(false)
-NULL
+array(0) {
+}
-- Iteration 15 --
bool(false)
-NULL
+array(0) {
+}
-- Iteration 16 --
bool(false)
-NULL
+array(0) {
+}
-- Iteration 17 --
bool(false)
-NULL
+array(0) {
+}
-- Iteration 18 --
int(3)
@@ -194,19 +211,23 @@ array(1) {
-- Iteration 20 --
bool(false)
-NULL
+array(0) {
+}
-- Iteration 21 --
bool(false)
-NULL
+array(0) {
+}
-- Iteration 22 --
bool(false)
-NULL
+array(0) {
+}
-- Iteration 23 --
bool(false)
-NULL
+array(0) {
+}
-- Iteration 24 --
diff --git a/ext/mbstring/tests/mb_strimwidth.phpt b/ext/mbstring/tests/mb_strimwidth.phpt
index 82780d6756..f257604c6b 100644
--- a/ext/mbstring/tests/mb_strimwidth.phpt
+++ b/ext/mbstring/tests/mb_strimwidth.phpt
@@ -14,30 +14,44 @@ include_once('common.inc');
// EUC-JP
$euc_jp = '0123ʸܸǤEUC-JPȤäƤޤܸݽ';
+print "String width: ".mb_strwidth($euc_jp,'EUC-JP')."\n";
+
print "1: ". mb_strimwidth($euc_jp, 0, 15,'...','EUC-JP') . "\n";
print "2: ". mb_strimwidth($euc_jp, 0, 100,'...','EUC-JP') . "\n";
print "3: ". mb_strimwidth($euc_jp, 15, 100,'...','EUC-JP') . "\n";
-// Note: Did not start form -22 offset. Staring from 0.
-$str = mb_strimwidth($euc_jp,-22, 100,'...','EUC-JP');
-($str === FALSE) ? print "4 OK\n" : print "NG: $str\n";
+print "4: ". mb_strimwidth($euc_jp, -30, 5,'...','EUC-JP') . "\n";
+print "5: ". mb_strimwidth($euc_jp, 38, 5,'...','EUC-JP') . "\n";
+print "6: ". mb_strimwidth($euc_jp, 38, -25,'...','EUC-JP') . "\n";
+print "7: ". mb_strimwidth($euc_jp, -30, -25,'...','EUC-JP') . "\n";
+
+$str = mb_strimwidth($euc_jp, 0, -100,'...','EUC-JP');
+($str === FALSE) ? print "10 OK\n" : print "NG: $str\n";
-$str = mb_strimwidth($euc_jp, 100, -10,'...','EUC-JP');
-($str === FALSE) ? print "5 OK\n" : print "NG: $str\n";
+$str = mb_strimwidth($euc_jp, 100, 10,'...','EUC-JP');
+($str === FALSE) ? print "11 OK\n" : print "NG: $str\n";
$str = mb_strimwidth($euc_jp, -100, 10,'...','EUC-JP');
-($str === FALSE) ? print "6 OK\n" : print "NG: $str\n";
+($str === FALSE) ? print "12 OK\n" : print "NG: $str\n";
+
+$str = mb_strimwidth($euc_jp, -10, -12,'...','EUC-JP');
+($str === FALSE) ? print "13 OK\n" : print "NG: $str\n";
?>
--EXPECT--
+String width: 68
1: 0123ʸ...
2: 0123ʸܸǤEUC-JPȤäƤޤܸݽ
3: EUC-JPȤäƤޤܸݽ
+4:
+5:
+6:
+7:
ERR: Warning
-4 OK
+10 OK
ERR: Warning
-5 OK
+11 OK
ERR: Warning
-6 OK
-
-
+12 OK
+ERR: Warning
+13 OK
diff --git a/ext/mbstring/tests/mb_stripos.phpt b/ext/mbstring/tests/mb_stripos.phpt
index 4ea8cfa6d6..8268872223 100644
--- a/ext/mbstring/tests/mb_stripos.phpt
+++ b/ext/mbstring/tests/mb_stripos.phpt
@@ -1,10 +1,7 @@
--TEST--
mb_stripos()
--SKIPIF--
-<?php
-extension_loaded('mbstring') or die('skip');
-function_exists('mb_stripos') or die("skip mb_stripos() is not available in this build");
-?>
+<?php extension_loaded('mbstring') or die('skip mbstring not available'); ?>
--FILE--
<?php
// TODO: Add more encodings
@@ -17,43 +14,62 @@ include_once('common.inc');
// Test string
$euc_jp = b'0123ʸܸǤEUC-JPȤäƤޤ0123ܸݽ';
+$slen = mb_strlen($euc_jp, 'EUC-JP');
+echo "String len: $slen\n";
+
// EUC-JP - With encoding parameter
mb_internal_encoding('UTF-8') or print("mb_internal_encoding() failed\n");
echo "== POSITIVE OFFSET ==\n";
-print mb_stripos($euc_jp,b'ܸ', 0, 'EUC-JP') . "\n";
+
+print mb_stripos($euc_jp, b'ܸ', 0, 'EUC-JP') . "\n";
print mb_stripos($euc_jp, b'0', 0, 'EUC-JP') . "\n";
print mb_stripos($euc_jp, 3, 0, 'EUC-JP') . "\n";
print mb_stripos($euc_jp, 0, 0, 'EUC-JP') . "\n";
-print mb_stripos($euc_jp,b'ܸ', 15, 'EUC-JP') . "\n";
+print mb_stripos($euc_jp, b'ܸ', 15, 'EUC-JP') . "\n";
print mb_stripos($euc_jp, b'0', 15, 'EUC-JP') . "\n";
print mb_stripos($euc_jp, 3, 15, 'EUC-JP') . "\n";
print mb_stripos($euc_jp, 0, 15, 'EUC-JP') . "\n";
+
// Negative offset
-// Note: PHP Warning - offset is negative.
-// Note: For offset(-15). It does not return position of latter string. (ie the same result as -50)
echo "== NEGATIVE OFFSET ==\n";
-$r = mb_stripos($euc_jp,b'ܸ', -15, 'EUC-JP');
-($r === FALSE) ? print "OK_NEGATIVE_OFFSET\n" : print "NG_NEGATIVE_OFFSET\n";
-$r = mb_stripos($euc_jp, b'0', -15, 'EUC-JP');
-($r === FALSE) ? print "OK_NEGATIVE_OFFSET\n" : print "NG_NEGATIVE_OFFSET\n";
-$r = mb_stripos($euc_jp, 3, -15, 'EUC-JP');
-($r === FALSE) ? print "OK_NEGATIVE_OFFSET\n" : print "NG_NEGATIVE_OFFSET\n";
-$r = mb_stripos($euc_jp, 0, -15, 'EUC-JP');
-($r === FALSE) ? print "OK_NEGATIVE_OFFSET\n" : print "NG_NEGATIVE_OFFSET\n";
-$r = mb_stripos($euc_jp,b'ܸ', -50, 'EUC-JP');
-($r === FALSE) ? print "OK_NEGATIVE_OFFSET\n" : print "NG_NEGATIVE_OFFSET\n";
+
+print mb_stripos($euc_jp, b'ܸ', -15, 'EUC-JP') . "\n";
+print mb_stripos($euc_jp, b'0', -15, 'EUC-JP') . "\n";
+print mb_stripos($euc_jp, 3, -15, 'EUC-JP') . "\n";
+print mb_stripos($euc_jp, 0, -15, 'EUC-JP') . "\n";
+print mb_stripos($euc_jp, 0, -43, 'EUC-JP') . "\n";
+
+
+// Invalid offset - should return false with warning
+print ("== INVALID OFFSET ==\n");
+
+$r = mb_stripos($euc_jp, b'ܸ', 44, 'EUC-JP');
+($r === FALSE) ? print "OK_INVALID_OFFSET\n" : print "NG_INVALID_OFFSET\n";
+$r = mb_stripos($euc_jp, b'ܸ', 50, 'EUC-JP');
+($r === FALSE) ? print "OK_INVALID_OFFSET\n" : print "NG_INVALID_OFFSET\n";
+$r = mb_stripos($euc_jp, b'0', 50, 'EUC-JP');
+($r === FALSE) ? print "OK_INVALID_OFFSET\n" : print "NG_INVALID_OFFSET\n";
+$r = mb_stripos($euc_jp, 3, 50, 'EUC-JP');
+($r === FALSE) ? print "OK_INVALID_OFFSET\n" : print "NG_INVALID_OFFSET\n";
+$r = mb_stripos($euc_jp, 0, 50, 'EUC-JP');
+($r === FALSE) ? print "OK_INVALID_OFFSET\n" : print "NG_INVALID_OFFSET\n";
+$r = mb_stripos($euc_jp, b'ܸ', -50, 'EUC-JP');
+($r === FALSE) ? print "OK_INVALID_OFFSET\n" : print "NG_INVALID_OFFSET\n";
$r = mb_stripos($euc_jp, b'0', -50, 'EUC-JP');
-($r === FALSE) ? print "OK_NEGATIVE_OFFSET\n" : print "NG_NEGATIVE_OFFSET\n";
+($r === FALSE) ? print "OK_INVALID_OFFSET\n" : print "NG_INVALID_OFFSET\n";
$r = mb_stripos($euc_jp, 3, -50, 'EUC-JP');
-($r === FALSE) ? print "OK_NEGATIVE_OFFSET\n" : print "NG_NEGATIVE_OFFSET\n";
+($r === FALSE) ? print "OK_INVALID_OFFSET\n" : print "NG_INVALID_OFFSET\n";
$r = mb_stripos($euc_jp, 0, -50, 'EUC-JP');
-($r === FALSE) ? print "OK_NEGATIVE_OFFSET\n" : print "NG_NEGATIVE_OFFSET\n";
+($r === FALSE) ? print "OK_INVALID_OFFSET\n" : print "NG_INVALID_OFFSET\n";
+$r = mb_stripos($euc_jp, 0, -44, 'EUC-JP');
+($r === FALSE) ? print "OK_INVALID_OFFSET\n" : print "NG_INVALID_OFFSET\n";
// Out of range - should return false
print ("== OUT OF RANGE ==\n");
-$r = mb_stripos($euc_jp,b'ܸ', 40, 'EUC-JP');
+
+$r = mb_stripos($euc_jp, b'ܸ', 40, 'EUC-JP');
($r === FALSE) ? print "OK_OUT_RANGE\n" : print "NG_OUT_RANGE\n";
$r = mb_stripos($euc_jp, b'0', 40, 'EUC-JP');
($r === FALSE) ? print "OK_OUT_RANGE\n" : print "NG_OUT_RANGE\n";
@@ -61,12 +77,19 @@ $r = mb_stripos($euc_jp, 3, 40, 'EUC-JP');
($r === FALSE) ? print "OK_OUT_RANGE\n" : print "NG_OUT_RANGE\n";
$r = mb_stripos($euc_jp, 0, 40, 'EUC-JP');
($r === FALSE) ? print "OK_OUT_RANGE\n" : print "NG_OUT_RANGE\n";
-// Note: Returned NULL string
-// echo gettype($r). ' val '. $r ."\n";
+$r = mb_stripos($euc_jp, b'ܸ', -3, 'EUC-JP');
+($r === FALSE) ? print "OK_OUT_RANGE\n" : print "NG_OUT_RANGE\n";
+$r = mb_stripos($euc_jp, b'0', -3, 'EUC-JP');
+($r === FALSE) ? print "OK_OUT_RANGE\n" : print "NG_OUT_RANGE\n";
+$r = mb_stripos($euc_jp, 3, -3, 'EUC-JP');
+($r === FALSE) ? print "OK_OUT_RANGE\n" : print "NG_OUT_RANGE\n";
+$r = mb_stripos($euc_jp, 0, -3, 'EUC-JP');
+($r === FALSE) ? print "OK_OUT_RANGE\n" : print "NG_OUT_RANGE\n";
// Non-existent
echo "== NON-EXISTENT ==\n";
+
$r = mb_stripos($euc_jp, b'ڹ', 0, 'EUC-JP');
($r === FALSE) ? print "OK_STR\n" : print "NG_STR\n";
$r = mb_stripos($euc_jp, b"\n", 0, 'EUC-JP');
@@ -75,30 +98,32 @@ $r = mb_stripos($euc_jp, b"\n", 0, 'EUC-JP');
// EUC-JP - No encoding parameter
echo "== NO ENCODING PARAMETER ==\n";
+
mb_internal_encoding('EUC-JP') or print("mb_internal_encoding() failed\n");
-print mb_stripos($euc_jp,b'ܸ', 0) . "\n";
+print mb_stripos($euc_jp, b'ܸ', 0) . "\n";
print mb_stripos($euc_jp, b'0', 0) . "\n";
print mb_stripos($euc_jp, 3, 0) . "\n";
print mb_stripos($euc_jp, 0, 0) . "\n";
-$r = mb_stripos($euc_jp,b'ڹ', 0);
+$r = mb_stripos($euc_jp, b'ڹ', 0);
($r === FALSE) ? print "OK_STR\n" : print "NG_STR\n";
-$r = mb_stripos($euc_jp,b"\n", 0);
+$r = mb_stripos($euc_jp, b"\n", 0);
($r === FALSE) ? print "OK_NEWLINE\n" : print "NG_NEWLINE\n";
// EUC-JP - No offset and encoding parameter
echo "== NO OFFSET AND ENCODING PARAMETER ==\n";
+
mb_internal_encoding('EUC-JP') or print("mb_internal_encoding() failed\n");
-print mb_stripos($euc_jp,b'ܸ') . "\n";
+print mb_stripos($euc_jp, b'ܸ') . "\n";
print mb_stripos($euc_jp, b'0') . "\n";
print mb_stripos($euc_jp, 3) . "\n";
print mb_stripos($euc_jp, 0) . "\n";
-$r = mb_stripos($euc_jp,b'ڹ');
+$r = mb_stripos($euc_jp, b'ڹ');
($r === FALSE) ? print "OK_STR\n" : print "NG_STR\n";
-$r = mb_stripos($euc_jp,b"\n");
+$r = mb_stripos($euc_jp, b"\n");
($r === FALSE) ? print "OK_NEWLINE\n" : print "NG_NEWLINE\n";
@@ -113,11 +138,10 @@ $r = mb_stripos($euc_jp, $t_obj, 'EUC-JP');
($r === NULL) ? print("OK_OBJECT\n") : print("NG_OBJECT\n");
$r = mb_stripos($euc_jp, $t_obj, 'BAD_ENCODING');
($r === NULL) ? print("OK_BAD_ENCODING\n") : print("NG_BAD_ENCODING\n");
-
-
?>
-
+==DONE==
--EXPECT--
+String len: 43
== POSITIVE OFFSET ==
10
0
@@ -128,27 +152,41 @@ $r = mb_stripos($euc_jp, $t_obj, 'BAD_ENCODING');
33
30
== NEGATIVE OFFSET ==
+34
+30
+33
+30
+0
+== INVALID OFFSET ==
ERR: Warning
-OK_NEGATIVE_OFFSET
+OK_INVALID_OFFSET
ERR: Warning
-OK_NEGATIVE_OFFSET
+OK_INVALID_OFFSET
ERR: Warning
-OK_NEGATIVE_OFFSET
+OK_INVALID_OFFSET
ERR: Warning
-OK_NEGATIVE_OFFSET
+OK_INVALID_OFFSET
ERR: Warning
-OK_NEGATIVE_OFFSET
+OK_INVALID_OFFSET
ERR: Warning
-OK_NEGATIVE_OFFSET
+OK_INVALID_OFFSET
ERR: Warning
-OK_NEGATIVE_OFFSET
+OK_INVALID_OFFSET
ERR: Warning
-OK_NEGATIVE_OFFSET
+OK_INVALID_OFFSET
+ERR: Warning
+OK_INVALID_OFFSET
+ERR: Warning
+OK_INVALID_OFFSET
== OUT OF RANGE ==
OK_OUT_RANGE
OK_OUT_RANGE
OK_OUT_RANGE
OK_OUT_RANGE
+OK_OUT_RANGE
+OK_OUT_RANGE
+OK_OUT_RANGE
+OK_OUT_RANGE
== NON-EXISTENT ==
OK_STR
OK_NEWLINE
@@ -175,4 +213,4 @@ ERR: Warning
OK_OBJECT
ERR: Warning
OK_BAD_ENCODING
-
+==DONE==
diff --git a/ext/mbstring/tests/mb_stripos_variation3.phpt b/ext/mbstring/tests/mb_stripos_variation3.phpt
index 21a1293786..69c4a9e53b 100644
--- a/ext/mbstring/tests/mb_stripos_variation3.phpt
+++ b/ext/mbstring/tests/mb_stripos_variation3.phpt
@@ -8,10 +8,9 @@ if (PHP_INT_SIZE != 8) die('skip 64-bit only');
?>
--FILE--
<?php
-/* Prototype : int mb_stripos(string haystack, string needle [, int offset [, string encoding]])
- * Description: Finds position of first occurrence of a string within another, case insensitive
+/* Prototype : int mb_stripos(string $haystack, string $needle [, int $offset [, string $encoding]])
+ * Description: Find position of first occurrence of a string within another, case insensitive
* Source code: ext/mbstring/mbstring.c
- * Alias to functions:
*/
/*
@@ -52,45 +51,47 @@ $inputs = array(
/*1*/ 0,
1,
12345,
+ -5,
-2345,
// float data
-/*5*/ 10.5,
- -10.5,
+/*6*/ 10.5,
+ -5.5,
+ -100.5,
12.3456789000e10,
12.3456789000E-10,
.5,
// null data
-/*10*/ NULL,
+/*12*/ NULL,
null,
// boolean data
-/*12*/ true,
+/*14*/ true,
false,
TRUE,
FALSE,
// empty data
-/*16*/ "",
+/*18*/ "",
'',
// string data
-/*18*/ "string",
+/*20*/ "string",
'string',
$heredoc,
// object data
-/*21*/ new classA(),
+/*23*/ new classA(),
// undefined data
-/*22*/ @$undefined_var,
+/*24*/ @$undefined_var,
// unset data
-/*23*/ @$unset_var,
+/*25*/ @$unset_var,
// resource variable
-/*24*/ $fp
+/*26*/ $fp
);
// loop through each element of $inputs to check the behavior of mb_stripos()
@@ -120,28 +121,28 @@ Warning: mb_stripos(): Offset not contained in string in %s on line %d
bool(false)
-- Iteration 4 --
-
-Warning: mb_stripos(): Offset not contained in string in %s on line %d
-bool(false)
+int(8)
-- Iteration 5 --
+
+Warning: mb_stripos(): Offset not contained in string in %s on line %d
bool(false)
-- Iteration 6 --
-
-Warning: mb_stripos(): Offset not contained in string in %s on line %d
bool(false)
-- Iteration 7 --
+int(8)
+
+-- Iteration 8 --
Warning: mb_stripos(): Offset not contained in string in %s on line %d
bool(false)
--- Iteration 8 --
-int(8)
-
-- Iteration 9 --
-int(8)
+
+Warning: mb_stripos(): Offset not contained in string in %s on line %d
+bool(false)
-- Iteration 10 --
int(8)
@@ -162,42 +163,48 @@ int(8)
int(8)
-- Iteration 16 --
+int(8)
+
+-- Iteration 17 --
+int(8)
+
+-- Iteration 18 --
Warning: mb_stripos() expects parameter 3 to be integer, string given in %s on line %d
NULL
--- Iteration 17 --
+-- Iteration 19 --
Warning: mb_stripos() expects parameter 3 to be integer, string given in %s on line %d
NULL
--- Iteration 18 --
+-- Iteration 20 --
Warning: mb_stripos() expects parameter 3 to be integer, string given in %s on line %d
NULL
--- Iteration 19 --
+-- Iteration 21 --
Warning: mb_stripos() expects parameter 3 to be integer, string given in %s on line %d
NULL
--- Iteration 20 --
+-- Iteration 22 --
Warning: mb_stripos() expects parameter 3 to be integer, string given in %s on line %d
NULL
--- Iteration 21 --
+-- Iteration 23 --
Warning: mb_stripos() expects parameter 3 to be integer, object given in %s on line %d
NULL
--- Iteration 22 --
+-- Iteration 24 --
int(8)
--- Iteration 23 --
+-- Iteration 25 --
int(8)
--- Iteration 24 --
+-- Iteration 26 --
Warning: mb_stripos() expects parameter 3 to be integer, resource given in %s on line %d
NULL
diff --git a/ext/mbstring/tests/mb_stripos_variation5_Bug45923.phpt b/ext/mbstring/tests/mb_stripos_variation5_Bug45923.phpt
index fbe4937ac2..8ffcae5c0f 100644
--- a/ext/mbstring/tests/mb_stripos_variation5_Bug45923.phpt
+++ b/ext/mbstring/tests/mb_stripos_variation5_Bug45923.phpt
@@ -7,10 +7,9 @@ function_exists('mb_stripos') or die("skip mb_stripos() is not available in this
?>
--FILE--
<?php
-/* Prototype : int mb_stripos(string haystack, string needle [, int offset [, string encoding]])
- * Description: Finds position of first occurrence of a string within another, case insensitive
+/* Prototype : int mb_stripos(string $haystack, string $needle [, int $offset [, string $encoding]])
+ * Description: Find position of first occurrence of a string within another, case insensitive
* Source code: ext/mbstring/mbstring.c
- * Alias to functions:
*/
/*
@@ -34,7 +33,7 @@ $needle_mb = base64_decode('44CC');
* mb_stripos should not be able to accept negative values as $offset.
* 60 is larger than *BYTE* count for $string_mb
*/
-for ($i = -10; $i <= 60; $i += 10) {
+for ($i = -30; $i <= 60; $i += 10) {
echo "\n**-- Offset is: $i --**\n";
echo "-- ASCII String --\n";
var_dump(mb_stripos($string_ascii, $needle_ascii, $i));
@@ -48,7 +47,7 @@ echo "Done";
--EXPECTF--
*** Testing mb_stripos() : usage variations ***
-**-- Offset is: -10 --**
+**-- Offset is: -30 --**
-- ASCII String --
Warning: mb_stripos(): Offset not contained in string in %s on line %d
@@ -58,6 +57,18 @@ bool(false)
Warning: mb_stripos(): Offset not contained in string in %s on line %d
bool(false)
+**-- Offset is: -20 --**
+-- ASCII String --
+int(9)
+--Multibyte String --
+int(9)
+
+**-- Offset is: -10 --**
+-- ASCII String --
+int(20)
+--Multibyte String --
+int(20)
+
**-- Offset is: 0 --**
-- ASCII String --
int(9)
@@ -116,4 +127,3 @@ bool(false)
Warning: mb_stripos(): Offset not contained in string in %s on line %d
bool(false)
Done
-
diff --git a/ext/mbstring/tests/mb_strpos.phpt b/ext/mbstring/tests/mb_strpos.phpt
index e1222ca6dd..364bc7cc1a 100644
--- a/ext/mbstring/tests/mb_strpos.phpt
+++ b/ext/mbstring/tests/mb_strpos.phpt
@@ -14,10 +14,14 @@ include_once('common.inc');
// Test string
$euc_jp = b'0123ʸܸǤEUC-JPȤäƤޤ0123ܸݽ';
+$slen = mb_strlen($euc_jp, 'EUC-JP');
+echo "String len: $slen\n";
+
// EUC-JP - With encoding parameter
mb_internal_encoding('UTF-8') or print("mb_internal_encoding() failed\n");
echo "== POSITIVE OFFSET ==\n";
+
print mb_strpos($euc_jp, b'ܸ', 0, 'EUC-JP') . "\n";
print mb_strpos($euc_jp, b'0', 0, 'EUC-JP') . "\n";
print mb_strpos($euc_jp, 3, 0, 'EUC-JP') . "\n";
@@ -27,29 +31,44 @@ print mb_strpos($euc_jp, b'0', 15, 'EUC-JP') . "\n";
print mb_strpos($euc_jp, 3, 15, 'EUC-JP') . "\n";
print mb_strpos($euc_jp, 0, 15, 'EUC-JP') . "\n";
+
// Negative offset
-// Note: PHP Warning - offset is negative.
-// Note: For offset(-15). It does not return position of latter string. (ie the same result as -50)
echo "== NEGATIVE OFFSET ==\n";
-$r = mb_strpos($euc_jp, b'ܸ', -15, 'EUC-JP');
-($r === FALSE) ? print "OK_NEGATIVE_OFFSET\n" : print "NG_NEGATIVE_OFFSET\n";
-$r = mb_strpos($euc_jp, b'0', -15, 'EUC-JP');
-($r === FALSE) ? print "OK_NEGATIVE_OFFSET\n" : print "NG_NEGATIVE_OFFSET\n";
-$r = mb_strpos($euc_jp, 3, -15, 'EUC-JP');
-($r === FALSE) ? print "OK_NEGATIVE_OFFSET\n" : print "NG_NEGATIVE_OFFSET\n";
-$r = mb_strpos($euc_jp, 0, -15, 'EUC-JP');
-($r === FALSE) ? print "OK_NEGATIVE_OFFSET\n" : print "NG_NEGATIVE_OFFSET\n";
+
+print mb_strpos($euc_jp, b'ܸ', -15, 'EUC-JP') . "\n";
+print mb_strpos($euc_jp, b'0', -15, 'EUC-JP') . "\n";
+print mb_strpos($euc_jp, 3, -15, 'EUC-JP') . "\n";
+print mb_strpos($euc_jp, 0, -15, 'EUC-JP') . "\n";
+print mb_strpos($euc_jp, 0, -43, 'EUC-JP') . "\n";
+
+
+// Invalid offset - should return false with warning
+print ("== INVALID OFFSET ==\n");
+
+$r = mb_strpos($euc_jp, b'ܸ', 44, 'EUC-JP');
+($r === FALSE) ? print "OK_INVALID_OFFSET\n" : print "NG_INVALID_OFFSET\n";
+$r = mb_strpos($euc_jp, b'ܸ', 50, 'EUC-JP');
+($r === FALSE) ? print "OK_INVALID_OFFSET\n" : print "NG_INVALID_OFFSET\n";
+$r = mb_strpos($euc_jp, b'0', 50, 'EUC-JP');
+($r === FALSE) ? print "OK_INVALID_OFFSET\n" : print "NG_INVALID_OFFSET\n";
+$r = mb_strpos($euc_jp, 3, 50, 'EUC-JP');
+($r === FALSE) ? print "OK_INVALID_OFFSET\n" : print "NG_INVALID_OFFSET\n";
+$r = mb_strpos($euc_jp, 0, 50, 'EUC-JP');
+($r === FALSE) ? print "OK_INVALID_OFFSET\n" : print "NG_INVALID_OFFSET\n";
$r = mb_strpos($euc_jp, b'ܸ', -50, 'EUC-JP');
-($r === FALSE) ? print "OK_NEGATIVE_OFFSET\n" : print "NG_NEGATIVE_OFFSET\n";
+($r === FALSE) ? print "OK_INVALID_OFFSET\n" : print "NG_INVALID_OFFSET\n";
$r = mb_strpos($euc_jp, b'0', -50, 'EUC-JP');
-($r === FALSE) ? print "OK_NEGATIVE_OFFSET\n" : print "NG_NEGATIVE_OFFSET\n";
+($r === FALSE) ? print "OK_INVALID_OFFSET\n" : print "NG_INVALID_OFFSET\n";
$r = mb_strpos($euc_jp, 3, -50, 'EUC-JP');
-($r === FALSE) ? print "OK_NEGATIVE_OFFSET\n" : print "NG_NEGATIVE_OFFSET\n";
+($r === FALSE) ? print "OK_INVALID_OFFSET\n" : print "NG_INVALID_OFFSET\n";
$r = mb_strpos($euc_jp, 0, -50, 'EUC-JP');
-($r === FALSE) ? print "OK_NEGATIVE_OFFSET\n" : print "NG_NEGATIVE_OFFSET\n";
+($r === FALSE) ? print "OK_INVALID_OFFSET\n" : print "NG_INVALID_OFFSET\n";
+$r = mb_strpos($euc_jp, 0, -44, 'EUC-JP');
+($r === FALSE) ? print "OK_INVALID_OFFSET\n" : print "NG_INVALID_OFFSET\n";
// Out of range - should return false
print ("== OUT OF RANGE ==\n");
+
$r = mb_strpos($euc_jp, b'ܸ', 40, 'EUC-JP');
($r === FALSE) ? print "OK_OUT_RANGE\n" : print "NG_OUT_RANGE\n";
$r = mb_strpos($euc_jp, b'0', 40, 'EUC-JP');
@@ -58,12 +77,19 @@ $r = mb_strpos($euc_jp, 3, 40, 'EUC-JP');
($r === FALSE) ? print "OK_OUT_RANGE\n" : print "NG_OUT_RANGE\n";
$r = mb_strpos($euc_jp, 0, 40, 'EUC-JP');
($r === FALSE) ? print "OK_OUT_RANGE\n" : print "NG_OUT_RANGE\n";
-// Note: Returned NULL string
-// echo gettype($r). ' val '. $r ."\n";
+$r = mb_strpos($euc_jp, b'ܸ', -3, 'EUC-JP');
+($r === FALSE) ? print "OK_OUT_RANGE\n" : print "NG_OUT_RANGE\n";
+$r = mb_strpos($euc_jp, b'0', -3, 'EUC-JP');
+($r === FALSE) ? print "OK_OUT_RANGE\n" : print "NG_OUT_RANGE\n";
+$r = mb_strpos($euc_jp, 3, -3, 'EUC-JP');
+($r === FALSE) ? print "OK_OUT_RANGE\n" : print "NG_OUT_RANGE\n";
+$r = mb_strpos($euc_jp, 0, -3, 'EUC-JP');
+($r === FALSE) ? print "OK_OUT_RANGE\n" : print "NG_OUT_RANGE\n";
// Non-existent
echo "== NON-EXISTENT ==\n";
+
$r = mb_strpos($euc_jp, b'ڹ', 0, 'EUC-JP');
($r === FALSE) ? print "OK_STR\n" : print "NG_STR\n";
$r = mb_strpos($euc_jp, b"\n", 0, 'EUC-JP');
@@ -72,6 +98,7 @@ $r = mb_strpos($euc_jp, b"\n", 0, 'EUC-JP');
// EUC-JP - No encoding parameter
echo "== NO ENCODING PARAMETER ==\n";
+
mb_internal_encoding('EUC-JP') or print("mb_internal_encoding() failed\n");
print mb_strpos($euc_jp, b'ܸ', 0) . "\n";
@@ -86,6 +113,7 @@ $r = mb_strpos($euc_jp, b"\n", 0);
// EUC-JP - No offset and encoding parameter
echo "== NO OFFSET AND ENCODING PARAMETER ==\n";
+
mb_internal_encoding('EUC-JP') or print("mb_internal_encoding() failed\n");
print mb_strpos($euc_jp, b'ܸ') . "\n";
@@ -110,11 +138,10 @@ $r = mb_strpos($euc_jp, $t_obj, 'EUC-JP');
($r === NULL) ? print("OK_OBJECT\n") : print("NG_OBJECT\n");
$r = mb_strpos($euc_jp, $t_obj, 'BAD_ENCODING');
($r === NULL) ? print("OK_BAD_ENCODING\n") : print("NG_BAD_ENCODING\n");
-
-
?>
-
+==DONE==
--EXPECT--
+String len: 43
== POSITIVE OFFSET ==
10
0
@@ -125,27 +152,41 @@ $r = mb_strpos($euc_jp, $t_obj, 'BAD_ENCODING');
33
30
== NEGATIVE OFFSET ==
+34
+30
+33
+30
+0
+== INVALID OFFSET ==
+ERR: Warning
+OK_INVALID_OFFSET
ERR: Warning
-OK_NEGATIVE_OFFSET
+OK_INVALID_OFFSET
ERR: Warning
-OK_NEGATIVE_OFFSET
+OK_INVALID_OFFSET
ERR: Warning
-OK_NEGATIVE_OFFSET
+OK_INVALID_OFFSET
ERR: Warning
-OK_NEGATIVE_OFFSET
+OK_INVALID_OFFSET
ERR: Warning
-OK_NEGATIVE_OFFSET
+OK_INVALID_OFFSET
ERR: Warning
-OK_NEGATIVE_OFFSET
+OK_INVALID_OFFSET
ERR: Warning
-OK_NEGATIVE_OFFSET
+OK_INVALID_OFFSET
ERR: Warning
-OK_NEGATIVE_OFFSET
+OK_INVALID_OFFSET
+ERR: Warning
+OK_INVALID_OFFSET
== OUT OF RANGE ==
OK_OUT_RANGE
OK_OUT_RANGE
OK_OUT_RANGE
OK_OUT_RANGE
+OK_OUT_RANGE
+OK_OUT_RANGE
+OK_OUT_RANGE
+OK_OUT_RANGE
== NON-EXISTENT ==
OK_STR
OK_NEWLINE
@@ -172,4 +213,4 @@ ERR: Warning
OK_OBJECT
ERR: Warning
OK_BAD_ENCODING
-
+==DONE==
diff --git a/ext/mbstring/tests/mb_strpos_variation3.phpt b/ext/mbstring/tests/mb_strpos_variation3.phpt
index 8079a19021..f30b708183 100644
--- a/ext/mbstring/tests/mb_strpos_variation3.phpt
+++ b/ext/mbstring/tests/mb_strpos_variation3.phpt
@@ -51,45 +51,47 @@ $inputs = array(
/*1*/ 0,
1,
12345,
+ -5,
-2345,
// float data
-/*5*/ 10.5,
- -10.5,
+/*6*/ 10.5,
+ -5.5,
+ -100.5,
12.3456789000e10,
12.3456789000E-10,
.5,
// null data
-/*10*/ NULL,
+/*12*/ NULL,
null,
// boolean data
-/*12*/ true,
+/*14*/ true,
false,
TRUE,
FALSE,
// empty data
-/*16*/ "",
+/*18*/ "",
'',
// string data
-/*18*/ "string",
+/*20*/ "string",
'string',
$heredoc,
// object data
-/*21*/ new classA(),
+/*23*/ new classA(),
// undefined data
-/*22*/ @$undefined_var,
+/*24*/ @$undefined_var,
// unset data
-/*23*/ @$unset_var,
+/*25*/ @$unset_var,
// resource variable
-/*24*/ $fp
+/*26*/ $fp
);
// loop through each element of $inputs to check the behavior of mb_strpos()
@@ -119,28 +121,28 @@ Warning: mb_strpos(): Offset not contained in string in %s on line %d
bool(false)
-- Iteration 4 --
-
-Warning: mb_strpos(): Offset not contained in string in %s on line %d
-bool(false)
+int(8)
-- Iteration 5 --
+
+Warning: mb_strpos(): Offset not contained in string in %s on line %d
bool(false)
-- Iteration 6 --
-
-Warning: mb_strpos(): Offset not contained in string in %s on line %d
bool(false)
-- Iteration 7 --
+int(8)
+
+-- Iteration 8 --
Warning: mb_strpos(): Offset not contained in string in %s on line %d
bool(false)
--- Iteration 8 --
-int(8)
-
-- Iteration 9 --
-int(8)
+
+Warning: mb_strpos(): Offset not contained in string in %s on line %d
+bool(false)
-- Iteration 10 --
int(8)
@@ -161,42 +163,48 @@ int(8)
int(8)
-- Iteration 16 --
+int(8)
+
+-- Iteration 17 --
+int(8)
+
+-- Iteration 18 --
Warning: mb_strpos() expects parameter 3 to be integer, string given in %s on line %d
NULL
--- Iteration 17 --
+-- Iteration 19 --
Warning: mb_strpos() expects parameter 3 to be integer, string given in %s on line %d
NULL
--- Iteration 18 --
+-- Iteration 20 --
Warning: mb_strpos() expects parameter 3 to be integer, string given in %s on line %d
NULL
--- Iteration 19 --
+-- Iteration 21 --
Warning: mb_strpos() expects parameter 3 to be integer, string given in %s on line %d
NULL
--- Iteration 20 --
+-- Iteration 22 --
Warning: mb_strpos() expects parameter 3 to be integer, string given in %s on line %d
NULL
--- Iteration 21 --
+-- Iteration 23 --
Warning: mb_strpos() expects parameter 3 to be integer, object given in %s on line %d
NULL
--- Iteration 22 --
+-- Iteration 24 --
int(8)
--- Iteration 23 --
+-- Iteration 25 --
int(8)
--- Iteration 24 --
+-- Iteration 26 --
Warning: mb_strpos() expects parameter 3 to be integer, resource given in %s on line %d
NULL
diff --git a/ext/mbstring/tests/mb_strpos_variation5.phpt b/ext/mbstring/tests/mb_strpos_variation5.phpt
index 7a9604abef..23bfa22b61 100644
--- a/ext/mbstring/tests/mb_strpos_variation5.phpt
+++ b/ext/mbstring/tests/mb_strpos_variation5.phpt
@@ -33,7 +33,7 @@ $needle_mb = base64_decode('44CC');
* mb_strpos should not be able to accept negative values as $offset.
* 60 is larger than *BYTE* count for $string_mb
*/
-for ($i = -10; $i <= 60; $i += 10) {
+for ($i = -30; $i <= 60; $i += 10) {
echo "\n**-- Offset is: $i --**\n";
echo "-- ASCII String --\n";
var_dump(mb_strpos($string_ascii, $needle_ascii, $i));
@@ -47,7 +47,7 @@ echo "Done";
--EXPECTF--
*** Testing mb_strpos() : usage variations ***
-**-- Offset is: -10 --**
+**-- Offset is: -30 --**
-- ASCII String --
Warning: mb_strpos(): Offset not contained in string in %s on line %d
@@ -57,6 +57,18 @@ bool(false)
Warning: mb_strpos(): Offset not contained in string in %s on line %d
bool(false)
+**-- Offset is: -20 --**
+-- ASCII String --
+int(9)
+--Multibyte String --
+int(9)
+
+**-- Offset is: -10 --**
+-- ASCII String --
+int(20)
+--Multibyte String --
+int(20)
+
**-- Offset is: 0 --**
-- ASCII String --
int(9)
diff --git a/ext/mcrypt/mcrypt.c b/ext/mcrypt/mcrypt.c
index 5e32c3230a..ebe968cf24 100644
--- a/ext/mcrypt/mcrypt.c
+++ b/ext/mcrypt/mcrypt.c
@@ -207,43 +207,43 @@ ZEND_END_ARG_INFO()
/* }}} */
const zend_function_entry mcrypt_functions[] = { /* {{{ */
- PHP_FE(mcrypt_get_key_size, arginfo_mcrypt_get_key_size)
- PHP_FE(mcrypt_get_block_size, arginfo_mcrypt_get_block_size)
- PHP_FE(mcrypt_get_cipher_name, arginfo_mcrypt_get_cipher_name)
- PHP_FE(mcrypt_create_iv, arginfo_mcrypt_create_iv)
-
- PHP_FE(mcrypt_list_algorithms, arginfo_mcrypt_list_algorithms)
- PHP_FE(mcrypt_list_modes, arginfo_mcrypt_list_modes)
- PHP_FE(mcrypt_get_iv_size, arginfo_mcrypt_get_iv_size)
- PHP_FE(mcrypt_encrypt, arginfo_mcrypt_encrypt)
- PHP_FE(mcrypt_decrypt, arginfo_mcrypt_decrypt)
-
- PHP_FE(mcrypt_module_open, arginfo_mcrypt_module_open)
- PHP_FE(mcrypt_generic_init, arginfo_mcrypt_generic_init)
- PHP_FE(mcrypt_generic, arginfo_mcrypt_generic)
- PHP_FE(mdecrypt_generic, arginfo_mdecrypt_generic)
- PHP_FE(mcrypt_generic_deinit, arginfo_mcrypt_generic_deinit)
-
- PHP_FE(mcrypt_enc_self_test, arginfo_mcrypt_enc_self_test)
- PHP_FE(mcrypt_enc_is_block_algorithm_mode, arginfo_mcrypt_enc_is_block_algorithm_mode)
- PHP_FE(mcrypt_enc_is_block_algorithm, arginfo_mcrypt_enc_is_block_algorithm)
- PHP_FE(mcrypt_enc_is_block_mode, arginfo_mcrypt_enc_is_block_mode)
- PHP_FE(mcrypt_enc_get_block_size, arginfo_mcrypt_enc_get_block_size)
- PHP_FE(mcrypt_enc_get_key_size, arginfo_mcrypt_enc_get_key_size)
- PHP_FE(mcrypt_enc_get_supported_key_sizes, arginfo_mcrypt_enc_get_supported_key_sizes)
- PHP_FE(mcrypt_enc_get_iv_size, arginfo_mcrypt_enc_get_iv_size)
- PHP_FE(mcrypt_enc_get_algorithms_name, arginfo_mcrypt_enc_get_algorithms_name)
- PHP_FE(mcrypt_enc_get_modes_name, arginfo_mcrypt_enc_get_modes_name)
- PHP_FE(mcrypt_module_self_test, arginfo_mcrypt_module_self_test)
-
- PHP_FE(mcrypt_module_is_block_algorithm_mode, arginfo_mcrypt_module_is_block_algorithm_mode)
- PHP_FE(mcrypt_module_is_block_algorithm, arginfo_mcrypt_module_is_block_algorithm)
- PHP_FE(mcrypt_module_is_block_mode, arginfo_mcrypt_module_is_block_mode)
- PHP_FE(mcrypt_module_get_algo_block_size, arginfo_mcrypt_module_get_algo_block_size)
- PHP_FE(mcrypt_module_get_algo_key_size, arginfo_mcrypt_module_get_algo_key_size)
- PHP_FE(mcrypt_module_get_supported_key_sizes, arginfo_mcrypt_module_get_supported_key_sizes)
-
- PHP_FE(mcrypt_module_close, arginfo_mcrypt_module_close)
+ PHP_DEP_FE(mcrypt_get_key_size, arginfo_mcrypt_get_key_size)
+ PHP_DEP_FE(mcrypt_get_block_size, arginfo_mcrypt_get_block_size)
+ PHP_DEP_FE(mcrypt_get_cipher_name, arginfo_mcrypt_get_cipher_name)
+ PHP_DEP_FE(mcrypt_create_iv, arginfo_mcrypt_create_iv)
+
+ PHP_DEP_FE(mcrypt_list_algorithms, arginfo_mcrypt_list_algorithms)
+ PHP_DEP_FE(mcrypt_list_modes, arginfo_mcrypt_list_modes)
+ PHP_DEP_FE(mcrypt_get_iv_size, arginfo_mcrypt_get_iv_size)
+ PHP_DEP_FE(mcrypt_encrypt, arginfo_mcrypt_encrypt)
+ PHP_DEP_FE(mcrypt_decrypt, arginfo_mcrypt_decrypt)
+
+ PHP_DEP_FE(mcrypt_module_open, arginfo_mcrypt_module_open)
+ PHP_DEP_FE(mcrypt_generic_init, arginfo_mcrypt_generic_init)
+ PHP_DEP_FE(mcrypt_generic, arginfo_mcrypt_generic)
+ PHP_DEP_FE(mdecrypt_generic, arginfo_mdecrypt_generic)
+ PHP_DEP_FE(mcrypt_generic_deinit, arginfo_mcrypt_generic_deinit)
+
+ PHP_DEP_FE(mcrypt_enc_self_test, arginfo_mcrypt_enc_self_test)
+ PHP_DEP_FE(mcrypt_enc_is_block_algorithm_mode, arginfo_mcrypt_enc_is_block_algorithm_mode)
+ PHP_DEP_FE(mcrypt_enc_is_block_algorithm, arginfo_mcrypt_enc_is_block_algorithm)
+ PHP_DEP_FE(mcrypt_enc_is_block_mode, arginfo_mcrypt_enc_is_block_mode)
+ PHP_DEP_FE(mcrypt_enc_get_block_size, arginfo_mcrypt_enc_get_block_size)
+ PHP_DEP_FE(mcrypt_enc_get_key_size, arginfo_mcrypt_enc_get_key_size)
+ PHP_DEP_FE(mcrypt_enc_get_supported_key_sizes, arginfo_mcrypt_enc_get_supported_key_sizes)
+ PHP_DEP_FE(mcrypt_enc_get_iv_size, arginfo_mcrypt_enc_get_iv_size)
+ PHP_DEP_FE(mcrypt_enc_get_algorithms_name, arginfo_mcrypt_enc_get_algorithms_name)
+ PHP_DEP_FE(mcrypt_enc_get_modes_name, arginfo_mcrypt_enc_get_modes_name)
+ PHP_DEP_FE(mcrypt_module_self_test, arginfo_mcrypt_module_self_test)
+
+ PHP_DEP_FE(mcrypt_module_is_block_algorithm_mode, arginfo_mcrypt_module_is_block_algorithm_mode)
+ PHP_DEP_FE(mcrypt_module_is_block_algorithm, arginfo_mcrypt_module_is_block_algorithm)
+ PHP_DEP_FE(mcrypt_module_is_block_mode, arginfo_mcrypt_module_is_block_mode)
+ PHP_DEP_FE(mcrypt_module_get_algo_block_size, arginfo_mcrypt_module_get_algo_block_size)
+ PHP_DEP_FE(mcrypt_module_get_algo_key_size, arginfo_mcrypt_module_get_algo_key_size)
+ PHP_DEP_FE(mcrypt_module_get_supported_key_sizes, arginfo_mcrypt_module_get_supported_key_sizes)
+
+ PHP_DEP_FE(mcrypt_module_close, arginfo_mcrypt_module_close)
PHP_FE_END
};
/* }}} */
@@ -563,7 +563,7 @@ PHP_FUNCTION(mcrypt_generic_init)
iv_s = emalloc(iv_size + 1);
memset(iv_s, 0, iv_size + 1);
- if (key_len > max_key_size) {
+ if (key_len > (size_t)max_key_size) {
php_error_docref(NULL, E_WARNING, "Key size too large; supplied length: %zd, max: %d", key_len, max_key_size);
key_size = max_key_size;
} else {
@@ -571,11 +571,11 @@ PHP_FUNCTION(mcrypt_generic_init)
}
memcpy(key_s, key, key_len);
- if (iv_len != iv_size) {
+ if (iv_len != (size_t)iv_size) {
if (mcrypt_enc_mode_has_iv(pm->td)) {
php_error_docref(NULL, E_WARNING, "Iv size incorrect; supplied length: %zd, needed: %d", iv_len, iv_size);
}
- if (iv_len > iv_size) {
+ if (iv_len > (size_t)iv_size) {
iv_len = iv_size;
}
}
@@ -1302,7 +1302,7 @@ static void php_mcrypt_do_crypt(char* cipher, const char *key, size_t key_len, c
if (mcrypt_generic_init(td, (void *) key, (int)key_len, (void *) iv) < 0) {
efree(data_s);
- php_error_docref(NULL, E_RECOVERABLE_ERROR, "Mcrypt initialisation failed");
+ zend_throw_error(NULL, "Mcrypt initialisation failed");
mcrypt_module_close(td);
RETURN_FALSE;
}
@@ -1398,7 +1398,7 @@ PHP_FUNCTION(mcrypt_create_iv)
}
}
- while (read_bytes < size) {
+ while ((zend_long)read_bytes < size) {
n = read(*fd, iv + read_bytes, size - read_bytes);
if (n <= 0) {
break;
diff --git a/ext/mcrypt/mcrypt_filter.c b/ext/mcrypt/mcrypt_filter.c
index 93be0511c7..9bd8c2a414 100644
--- a/ext/mcrypt/mcrypt_filter.c
+++ b/ext/mcrypt/mcrypt_filter.c
@@ -161,6 +161,8 @@ static php_stream_filter *php_mcrypt_filter_create(const char *filtername, zval
char *mode = "cbc";
php_mcrypt_filter_data *data;
+ php_error_docref(NULL, E_DEPRECATED, "mcrypt and mdecrypt stream filters have been deprecated");
+
if (strncasecmp(filtername, "mdecrypt.", sizeof("mdecrypt.") - 1) == 0) {
encrypt = 0;
cipher += sizeof("de") - 1;
@@ -226,7 +228,7 @@ static php_stream_filter *php_mcrypt_filter_create(const char *filtername, zval
}
iv = emalloc(iv_len + 1);
- if (iv_len <= Z_STRLEN_P(tmpzval)) {
+ if ((size_t)iv_len <= Z_STRLEN_P(tmpzval)) {
memcpy(iv, Z_STRVAL_P(tmpzval), iv_len);
} else {
memcpy(iv, Z_STRVAL_P(tmpzval), Z_STRLEN_P(tmpzval));
diff --git a/ext/mcrypt/tests/blowfish.phpt b/ext/mcrypt/tests/blowfish.phpt
index a133091d78..8b06d6ec7a 100644
--- a/ext/mcrypt/tests/blowfish.phpt
+++ b/ext/mcrypt/tests/blowfish.phpt
@@ -54,38 +54,178 @@ echo "\n", $guess, "\n";
?>
--EXPECTF--
key plain crypt guess stat
-0000000000000000 0000000000000000 4ef997456198dd78 4ef997456198dd78 OK
-FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF 51866fd5b85ecb8a 51866fd5b85ecb8a OK
-3000000000000000 1000000000000001 7d856f9a613063f2 7d856f9a613063f2 OK
-1111111111111111 1111111111111111 2466dd878b963c9d 2466dd878b963c9d OK
-0123456789ABCDEF 1111111111111111 61f9c3802281b096 61f9c3802281b096 OK
-1111111111111111 0123456789ABCDEF 7d0cc630afda1ec7 7d0cc630afda1ec7 OK
-FEDCBA9876543210 0123456789ABCDEF 0aceab0fc6a0a28d 0aceab0fc6a0a28d OK
-7CA110454A1A6E57 01A1D6D039776742 59c68245eb05282b 59c68245eb05282b OK
-0131D9619DC1376E 5CD54CA83DEF57DA b1b8cc0b250f09a0 b1b8cc0b250f09a0 OK
-07A1133E4A0B2686 0248D43806F67172 1730e5778bea1da4 1730e5778bea1da4 OK
-3849674C2602319E 51454B582DDF440A a25e7856cf2651eb a25e7856cf2651eb OK
-04B915BA43FEB5B6 42FD443059577FA2 353882b109ce8f1a 353882b109ce8f1a OK
-0113B970FD34F2CE 059B5E0851CF143A 48f4d0884c379918 48f4d0884c379918 OK
-0170F175468FB5E6 0756D8E0774761D2 432193b78951fc98 432193b78951fc98 OK
-43297FAD38E373FE 762514B829BF486A 13f04154d69d1ae5 13f04154d69d1ae5 OK
-07A7137045DA2A16 3BDD119049372802 2eedda93ffd39c79 2eedda93ffd39c79 OK
-04689104C2FD3B2F 26955F6835AF609A d887e0393c2da6e3 d887e0393c2da6e3 OK
-37D06BB516CB7546 164D5E404F275232 5f99d04f5b163969 5f99d04f5b163969 OK
-1F08260D1AC2465E 6B056E18759F5CCA 4a057a3b24d3977b 4a057a3b24d3977b OK
-584023641ABA6176 004BD6EF09176062 452031c1e4fada8e 452031c1e4fada8e OK
-025816164629B007 480D39006EE762F2 7555ae39f59b87bd 7555ae39f59b87bd OK
-49793EBC79B3258F 437540C8698F3CFA 53c55f9cb49fc019 53c55f9cb49fc019 OK
-4FB05E1515AB73A7 072D43A077075292 7a8e7bfa937e89a3 7a8e7bfa937e89a3 OK
-49E95D6D4CA229BF 02FE55778117F12A cf9c5d7a4986adb5 cf9c5d7a4986adb5 OK
-018310DC409B26D6 1D9D5C5018F728C2 d1abb290658bc778 d1abb290658bc778 OK
-1C587F1C13924FEF 305532286D6F295A 55cb3774d13ef201 55cb3774d13ef201 OK
-0101010101010101 0123456789ABCDEF fa34ec4847b268b2 fa34ec4847b268b2 OK
-1F1F1F1F0E0E0E0E 0123456789ABCDEF a790795108ea3cae a790795108ea3cae OK
-E0FEE0FEF1FEF1FE 0123456789ABCDEF c39e072d9fac631d c39e072d9fac631d OK
-0000000000000000 FFFFFFFFFFFFFFFF 014933e0cdaff6e4 014933e0cdaff6e4 OK
-FFFFFFFFFFFFFFFF 0000000000000000 f21e9a77b71c49bc f21e9a77b71c49bc OK
-0123456789ABCDEF 0000000000000000 245946885754369a 245946885754369a OK
-FEDCBA9876543210 FFFFFFFFFFFFFFFF 6b5c5a9c5d9e0a5a 6b5c5a9c5d9e0a5a OK
+
+Deprecated: Function mcrypt_module_open() is deprecated in %s%eblowfish.php on line %d
+0000000000000000 0000000000000000
+Deprecated: Function mcrypt_generic_init() is deprecated in %s%eblowfish.php on line %d
+
+Deprecated: Function mcrypt_generic() is deprecated in %s%eblowfish.php on line %d
+4ef997456198dd78 4ef997456198dd78 OK
+FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF
+Deprecated: Function mcrypt_generic_init() is deprecated in %s%eblowfish.php on line %d
+
+Deprecated: Function mcrypt_generic() is deprecated in %s%eblowfish.php on line %d
+51866fd5b85ecb8a 51866fd5b85ecb8a OK
+3000000000000000 1000000000000001
+Deprecated: Function mcrypt_generic_init() is deprecated in %s%eblowfish.php on line %d
+
+Deprecated: Function mcrypt_generic() is deprecated in %s%eblowfish.php on line %d
+7d856f9a613063f2 7d856f9a613063f2 OK
+1111111111111111 1111111111111111
+Deprecated: Function mcrypt_generic_init() is deprecated in %s%eblowfish.php on line %d
+
+Deprecated: Function mcrypt_generic() is deprecated in %s%eblowfish.php on line %d
+2466dd878b963c9d 2466dd878b963c9d OK
+0123456789ABCDEF 1111111111111111
+Deprecated: Function mcrypt_generic_init() is deprecated in %s%eblowfish.php on line %d
+
+Deprecated: Function mcrypt_generic() is deprecated in %s%eblowfish.php on line %d
+61f9c3802281b096 61f9c3802281b096 OK
+1111111111111111 0123456789ABCDEF
+Deprecated: Function mcrypt_generic_init() is deprecated in %s%eblowfish.php on line %d
+
+Deprecated: Function mcrypt_generic() is deprecated in %s%eblowfish.php on line %d
+7d0cc630afda1ec7 7d0cc630afda1ec7 OK
+FEDCBA9876543210 0123456789ABCDEF
+Deprecated: Function mcrypt_generic_init() is deprecated in %s%eblowfish.php on line %d
+
+Deprecated: Function mcrypt_generic() is deprecated in %s%eblowfish.php on line %d
+0aceab0fc6a0a28d 0aceab0fc6a0a28d OK
+7CA110454A1A6E57 01A1D6D039776742
+Deprecated: Function mcrypt_generic_init() is deprecated in %s%eblowfish.php on line %d
+
+Deprecated: Function mcrypt_generic() is deprecated in %s%eblowfish.php on line %d
+59c68245eb05282b 59c68245eb05282b OK
+0131D9619DC1376E 5CD54CA83DEF57DA
+Deprecated: Function mcrypt_generic_init() is deprecated in %s%eblowfish.php on line %d
+
+Deprecated: Function mcrypt_generic() is deprecated in %s%eblowfish.php on line %d
+b1b8cc0b250f09a0 b1b8cc0b250f09a0 OK
+07A1133E4A0B2686 0248D43806F67172
+Deprecated: Function mcrypt_generic_init() is deprecated in %s%eblowfish.php on line %d
+
+Deprecated: Function mcrypt_generic() is deprecated in %s%eblowfish.php on line %d
+1730e5778bea1da4 1730e5778bea1da4 OK
+3849674C2602319E 51454B582DDF440A
+Deprecated: Function mcrypt_generic_init() is deprecated in %s%eblowfish.php on line %d
+
+Deprecated: Function mcrypt_generic() is deprecated in %s%eblowfish.php on line %d
+a25e7856cf2651eb a25e7856cf2651eb OK
+04B915BA43FEB5B6 42FD443059577FA2
+Deprecated: Function mcrypt_generic_init() is deprecated in %s%eblowfish.php on line %d
+
+Deprecated: Function mcrypt_generic() is deprecated in %s%eblowfish.php on line %d
+353882b109ce8f1a 353882b109ce8f1a OK
+0113B970FD34F2CE 059B5E0851CF143A
+Deprecated: Function mcrypt_generic_init() is deprecated in %s%eblowfish.php on line %d
+
+Deprecated: Function mcrypt_generic() is deprecated in %s%eblowfish.php on line %d
+48f4d0884c379918 48f4d0884c379918 OK
+0170F175468FB5E6 0756D8E0774761D2
+Deprecated: Function mcrypt_generic_init() is deprecated in %s%eblowfish.php on line %d
+
+Deprecated: Function mcrypt_generic() is deprecated in %s%eblowfish.php on line %d
+432193b78951fc98 432193b78951fc98 OK
+43297FAD38E373FE 762514B829BF486A
+Deprecated: Function mcrypt_generic_init() is deprecated in %s%eblowfish.php on line %d
+
+Deprecated: Function mcrypt_generic() is deprecated in %s%eblowfish.php on line %d
+13f04154d69d1ae5 13f04154d69d1ae5 OK
+07A7137045DA2A16 3BDD119049372802
+Deprecated: Function mcrypt_generic_init() is deprecated in %s%eblowfish.php on line %d
+
+Deprecated: Function mcrypt_generic() is deprecated in %s%eblowfish.php on line %d
+2eedda93ffd39c79 2eedda93ffd39c79 OK
+04689104C2FD3B2F 26955F6835AF609A
+Deprecated: Function mcrypt_generic_init() is deprecated in %s%eblowfish.php on line %d
+
+Deprecated: Function mcrypt_generic() is deprecated in %s%eblowfish.php on line %d
+d887e0393c2da6e3 d887e0393c2da6e3 OK
+37D06BB516CB7546 164D5E404F275232
+Deprecated: Function mcrypt_generic_init() is deprecated in %s%eblowfish.php on line %d
+
+Deprecated: Function mcrypt_generic() is deprecated in %s%eblowfish.php on line %d
+5f99d04f5b163969 5f99d04f5b163969 OK
+1F08260D1AC2465E 6B056E18759F5CCA
+Deprecated: Function mcrypt_generic_init() is deprecated in %s%eblowfish.php on line %d
+
+Deprecated: Function mcrypt_generic() is deprecated in %s%eblowfish.php on line %d
+4a057a3b24d3977b 4a057a3b24d3977b OK
+584023641ABA6176 004BD6EF09176062
+Deprecated: Function mcrypt_generic_init() is deprecated in %s%eblowfish.php on line %d
+
+Deprecated: Function mcrypt_generic() is deprecated in %s%eblowfish.php on line %d
+452031c1e4fada8e 452031c1e4fada8e OK
+025816164629B007 480D39006EE762F2
+Deprecated: Function mcrypt_generic_init() is deprecated in %s%eblowfish.php on line %d
+
+Deprecated: Function mcrypt_generic() is deprecated in %s%eblowfish.php on line %d
+7555ae39f59b87bd 7555ae39f59b87bd OK
+49793EBC79B3258F 437540C8698F3CFA
+Deprecated: Function mcrypt_generic_init() is deprecated in %s%eblowfish.php on line %d
+
+Deprecated: Function mcrypt_generic() is deprecated in %s%eblowfish.php on line %d
+53c55f9cb49fc019 53c55f9cb49fc019 OK
+4FB05E1515AB73A7 072D43A077075292
+Deprecated: Function mcrypt_generic_init() is deprecated in %s%eblowfish.php on line %d
+
+Deprecated: Function mcrypt_generic() is deprecated in %s%eblowfish.php on line %d
+7a8e7bfa937e89a3 7a8e7bfa937e89a3 OK
+49E95D6D4CA229BF 02FE55778117F12A
+Deprecated: Function mcrypt_generic_init() is deprecated in %s%eblowfish.php on line %d
+
+Deprecated: Function mcrypt_generic() is deprecated in %s%eblowfish.php on line %d
+cf9c5d7a4986adb5 cf9c5d7a4986adb5 OK
+018310DC409B26D6 1D9D5C5018F728C2
+Deprecated: Function mcrypt_generic_init() is deprecated in %s%eblowfish.php on line %d
+
+Deprecated: Function mcrypt_generic() is deprecated in %s%eblowfish.php on line %d
+d1abb290658bc778 d1abb290658bc778 OK
+1C587F1C13924FEF 305532286D6F295A
+Deprecated: Function mcrypt_generic_init() is deprecated in %s%eblowfish.php on line %d
+
+Deprecated: Function mcrypt_generic() is deprecated in %s%eblowfish.php on line %d
+55cb3774d13ef201 55cb3774d13ef201 OK
+0101010101010101 0123456789ABCDEF
+Deprecated: Function mcrypt_generic_init() is deprecated in %s%eblowfish.php on line %d
+
+Deprecated: Function mcrypt_generic() is deprecated in %s%eblowfish.php on line %d
+fa34ec4847b268b2 fa34ec4847b268b2 OK
+1F1F1F1F0E0E0E0E 0123456789ABCDEF
+Deprecated: Function mcrypt_generic_init() is deprecated in %s%eblowfish.php on line %d
+
+Deprecated: Function mcrypt_generic() is deprecated in %s%eblowfish.php on line %d
+a790795108ea3cae a790795108ea3cae OK
+E0FEE0FEF1FEF1FE 0123456789ABCDEF
+Deprecated: Function mcrypt_generic_init() is deprecated in %s%eblowfish.php on line %d
+
+Deprecated: Function mcrypt_generic() is deprecated in %s%eblowfish.php on line %d
+c39e072d9fac631d c39e072d9fac631d OK
+0000000000000000 FFFFFFFFFFFFFFFF
+Deprecated: Function mcrypt_generic_init() is deprecated in %s%eblowfish.php on line %d
+
+Deprecated: Function mcrypt_generic() is deprecated in %s%eblowfish.php on line %d
+014933e0cdaff6e4 014933e0cdaff6e4 OK
+FFFFFFFFFFFFFFFF 0000000000000000
+Deprecated: Function mcrypt_generic_init() is deprecated in %s%eblowfish.php on line %d
+
+Deprecated: Function mcrypt_generic() is deprecated in %s%eblowfish.php on line %d
+f21e9a77b71c49bc f21e9a77b71c49bc OK
+0123456789ABCDEF 0000000000000000
+Deprecated: Function mcrypt_generic_init() is deprecated in %s%eblowfish.php on line %d
+
+Deprecated: Function mcrypt_generic() is deprecated in %s%eblowfish.php on line %d
+245946885754369a 245946885754369a OK
+FEDCBA9876543210 FFFFFFFFFFFFFFFF
+Deprecated: Function mcrypt_generic_init() is deprecated in %s%eblowfish.php on line %d
+
+Deprecated: Function mcrypt_generic() is deprecated in %s%eblowfish.php on line %d
+6b5c5a9c5d9e0a5a 6b5c5a9c5d9e0a5a OK
+
+Deprecated: Function mcrypt_module_open() is deprecated in %s%eblowfish.php on line %d
+
+Deprecated: Function mcrypt_generic_init() is deprecated in %s%eblowfish.php on line %d
+
+Deprecated: Function mcrypt_generic() is deprecated in %s%eblowfish.php on line %d
6b77b4d63006dee605b156e27403979358deb9e7154616d959f1652bd5ff92cc
diff --git a/ext/mcrypt/tests/bug35496.phpt b/ext/mcrypt/tests/bug35496.phpt
index 3add65e02d..b211b05151 100644
--- a/ext/mcrypt/tests/bug35496.phpt
+++ b/ext/mcrypt/tests/bug35496.phpt
@@ -9,6 +9,12 @@ mcrypt_generic($td, "foobar");
mdecrypt_generic($td, "baz");
?>
--EXPECTF--
+Deprecated: Function mcrypt_module_open() is deprecated in %s%ebug35496.php on line 2
+
+Deprecated: Function mcrypt_generic() is deprecated in %s%ebug35496.php on line 3
+
Warning: mcrypt_generic(): Operation disallowed prior to mcrypt_generic_init(). in %sbug35496.php on line 3
+Deprecated: Function mdecrypt_generic() is deprecated in %s%ebug35496.php on line 4
+
Warning: mdecrypt_generic(): Operation disallowed prior to mcrypt_generic_init(). in %sbug35496.php on line 4
diff --git a/ext/mcrypt/tests/bug37595.phpt b/ext/mcrypt/tests/bug37595.phpt
index 0530f3e813..a125629c03 100644
--- a/ext/mcrypt/tests/bug37595.phpt
+++ b/ext/mcrypt/tests/bug37595.phpt
Binary files differ
diff --git a/ext/mcrypt/tests/bug41252.phpt b/ext/mcrypt/tests/bug41252.phpt
index 481fc54984..3900a06fd4 100644
--- a/ext/mcrypt/tests/bug41252.phpt
+++ b/ext/mcrypt/tests/bug41252.phpt
@@ -9,5 +9,9 @@ echo mcrypt_generic($td,'aaaaaaaa');
print "I'm alive!\n";
?>
--EXPECTF--
+Deprecated: Function mcrypt_module_open() is deprecated in %s%ebug41252.php on line 2
+
+Deprecated: Function mcrypt_generic() is deprecated in %s%ebug41252.php on line 3
+
Warning: mcrypt_generic(): Operation disallowed prior to mcrypt_generic_init(). in %sbug41252.php on line 3
I'm alive!
diff --git a/ext/mcrypt/tests/bug43143.phpt b/ext/mcrypt/tests/bug43143.phpt
index 725840f9c5..904bfb7003 100644
--- a/ext/mcrypt/tests/bug43143.phpt
+++ b/ext/mcrypt/tests/bug43143.phpt
@@ -17,7 +17,11 @@ echo "END\n";
?>
--EXPECTF--
ECB
+
+Deprecated: Function mcrypt_encrypt() is deprecated in %s%ebug43143.php on line 5
CFB
+Deprecated: Function mcrypt_encrypt() is deprecated in %s%ebug43143.php on line 9
+
Warning: mcrypt_encrypt(): Encryption mode requires an initialization vector of size 32 in %sbug43143.php on line 9
END
diff --git a/ext/mcrypt/tests/bug46010.phpt b/ext/mcrypt/tests/bug46010.phpt
index 1f0fe40a3d..fd3b4b60e5 100644
--- a/ext/mcrypt/tests/bug46010.phpt
+++ b/ext/mcrypt/tests/bug46010.phpt
@@ -12,6 +12,11 @@ var_dump(bin2hex(mcrypt_encrypt(MCRYPT_TRIPLEDES, $key, "data", MCRYPT_MODE_ECB,
?>
--EXPECTF--
+Deprecated: Function mcrypt_encrypt() is deprecated in %s%ebug46010.php on line 4
string(16) "f7a2ce11d4002294"
+
+Deprecated: Function mcrypt_encrypt() is deprecated in %s%ebug46010.php on line 5
string(16) "f7a2ce11d4002294"
+
+Deprecated: Function mcrypt_encrypt() is deprecated in %s%ebug46010.php on line 6
string(16) "f7a2ce11d4002294"
diff --git a/ext/mcrypt/tests/bug49738.phpt b/ext/mcrypt/tests/bug49738.phpt
index 8f01bec496..713f6efa15 100644
--- a/ext/mcrypt/tests/bug49738.phpt
+++ b/ext/mcrypt/tests/bug49738.phpt
@@ -10,4 +10,12 @@ Bug #49738 (calling mcrypt after mcrypt_generic_deinit crashes)
echo mcrypt_generic($td, 'aaaaaaaa');
?>
--EXPECTF--
+Deprecated: Function mcrypt_module_open() is deprecated in %s%ebug49738.php on line 2
+
+Deprecated: Function mcrypt_generic_init() is deprecated in %s%ebug49738.php on line 3
+
+Deprecated: Function mcrypt_generic_deinit() is deprecated in %s%ebug49738.php on line 4
+
+Deprecated: Function mcrypt_generic() is deprecated in %s%ebug49738.php on line 5
+
Warning: mcrypt_generic(): Operation disallowed prior to mcrypt_generic_init(). in %sbug49738.php on line 5
diff --git a/ext/mcrypt/tests/bug55169.phpt b/ext/mcrypt/tests/bug55169.phpt
index bebcd675c8..25d5febe06 100644
--- a/ext/mcrypt/tests/bug55169.phpt
+++ b/ext/mcrypt/tests/bug55169.phpt
@@ -16,29 +16,43 @@ for( $i=1; $i<=64; $i = $i*2 ){
?>
--EXPECTF--
Input: 1
+
+Deprecated: Function mcrypt_create_iv() is deprecated in %s%ebug55169.php on line 4
Length: 1
Hex: %x
Input: 2
+
+Deprecated: Function mcrypt_create_iv() is deprecated in %s%ebug55169.php on line 4
Length: 2
Hex: %x
Input: 4
+
+Deprecated: Function mcrypt_create_iv() is deprecated in %s%ebug55169.php on line 4
Length: 4
Hex: %x
Input: 8
+
+Deprecated: Function mcrypt_create_iv() is deprecated in %s%ebug55169.php on line 4
Length: 8
Hex: %x
Input: 16
+
+Deprecated: Function mcrypt_create_iv() is deprecated in %s%ebug55169.php on line 4
Length: 16
Hex: %x
Input: 32
+
+Deprecated: Function mcrypt_create_iv() is deprecated in %s%ebug55169.php on line 4
Length: 32
Hex: %x
Input: 64
+
+Deprecated: Function mcrypt_create_iv() is deprecated in %s%ebug55169.php on line 4
Length: 64
Hex: %x
diff --git a/ext/mcrypt/tests/bug67707.phpt b/ext/mcrypt/tests/bug67707.phpt
index 9ba13ab0ac..928c542aee 100644
--- a/ext/mcrypt/tests/bug67707.phpt
+++ b/ext/mcrypt/tests/bug67707.phpt
@@ -8,3 +8,6 @@ $td = mcrypt_module_open('rijndael-256', '', 'ecb', '');
mcrypt_generic_init($td, 'secret key', NULL);
?>
--EXPECTF--
+Deprecated: Function mcrypt_module_open() is deprecated in %s on line %d
+
+Deprecated: Function mcrypt_generic_init() is deprecated in %s on line %d
diff --git a/ext/mcrypt/tests/bug70625.phpt b/ext/mcrypt/tests/bug70625.phpt
index e9c0de0be3..65ab8eb022 100644
--- a/ext/mcrypt/tests/bug70625.phpt
+++ b/ext/mcrypt/tests/bug70625.phpt
@@ -12,6 +12,9 @@ $plaintext = mcrypt_decrypt(MCRYPT_ARCFOUR, $key, $ciphertext, MCRYPT_MODE_STREA
var_dump($plaintext);
?>
---EXPECT--
+--EXPECTF--
+Deprecated: Function mcrypt_encrypt() is deprecated in %s%ebug70625.php on line 4
string(14) "d5c9a57023d0f1"
+
+Deprecated: Function mcrypt_decrypt() is deprecated in %s%ebug70625.php on line 6
string(7) "payload"
diff --git a/ext/mcrypt/tests/mcrypt_cbc.phpt b/ext/mcrypt/tests/mcrypt_cbc.phpt
index 310c4a88bd..0b60682cef 100644
--- a/ext/mcrypt/tests/mcrypt_cbc.phpt
+++ b/ext/mcrypt/tests/mcrypt_cbc.phpt
@@ -19,7 +19,16 @@ var_dump(mcrypt_decrypt($cipher, $key, $enc_data, MCRYPT_MODE_CBC));
?>
--EXPECTF--
+Deprecated: Function mcrypt_get_iv_size() is deprecated in %s%emcrypt_cbc.php on line 6
+
+Deprecated: Function mcrypt_create_iv() is deprecated in %s%emcrypt_cbc.php on line 6
+
+Deprecated: Function mcrypt_encrypt() is deprecated in %s%emcrypt_cbc.php on line 7
+
+Deprecated: Function mcrypt_decrypt() is deprecated in %s%emcrypt_cbc.php on line 10
PHP Testfest 2008
+Deprecated: Function mcrypt_decrypt() is deprecated in %s%emcrypt_cbc.php on line 13
+
Warning: mcrypt_decrypt(): Encryption mode requires an initialization vector of size 16 in %s on line %d
bool(false)
diff --git a/ext/mcrypt/tests/mcrypt_cbc_3des_decrypt.phpt b/ext/mcrypt/tests/mcrypt_cbc_3des_decrypt.phpt
index 939cc57196..b306a8a94e 100644
--- a/ext/mcrypt/tests/mcrypt_cbc_3des_decrypt.phpt
+++ b/ext/mcrypt/tests/mcrypt_cbc_3des_decrypt.phpt
@@ -67,19 +67,27 @@ function special_var_dump($str) {
key length=8
+Deprecated: Function mcrypt_decrypt() is deprecated in %s%emcrypt_cbc_3des_decrypt.php on line 41
+
Warning: mcrypt_decrypt(): Key of size 8 not supported by this algorithm. Only keys of size 24 supported in %s on line %d
string(0) ""
key length=20
+Deprecated: Function mcrypt_decrypt() is deprecated in %s%emcrypt_cbc_3des_decrypt.php on line 41
+
Warning: mcrypt_decrypt(): Key of size 20 not supported by this algorithm. Only keys of size 24 supported in %s on line %d
string(0) ""
key length=24
+
+Deprecated: Function mcrypt_decrypt() is deprecated in %s%emcrypt_cbc_3des_decrypt.php on line 41
string(32) "736563726574206d6573736167650000"
key length=26
+Deprecated: Function mcrypt_decrypt() is deprecated in %s%emcrypt_cbc_3des_decrypt.php on line 41
+
Warning: mcrypt_decrypt(): Key of size 26 not supported by this algorithm. Only keys of size 24 supported in %s on line %d
string(0) ""
@@ -87,14 +95,20 @@ string(0) ""
iv length=4
+Deprecated: Function mcrypt_decrypt() is deprecated in %s%emcrypt_cbc_3des_decrypt.php on line 48
+
Warning: mcrypt_decrypt(): Received initialization vector of size 4, but size 8 is required for this encryption mode in %s on line %d
string(0) ""
iv length=8
+
+Deprecated: Function mcrypt_decrypt() is deprecated in %s%emcrypt_cbc_3des_decrypt.php on line 48
string(32) "659ec947f4dc3a3b9c50de744598d3c8"
iv length=9
+Deprecated: Function mcrypt_decrypt() is deprecated in %s%emcrypt_cbc_3des_decrypt.php on line 48
+
Warning: mcrypt_decrypt(): Received initialization vector of size 9, but size 8 is required for this encryption mode in %s on line %d
string(0) ""
===DONE===
diff --git a/ext/mcrypt/tests/mcrypt_cbc_3des_encrypt.phpt b/ext/mcrypt/tests/mcrypt_cbc_3des_encrypt.phpt
index ef662cc383..0fea9063df 100644
--- a/ext/mcrypt/tests/mcrypt_cbc_3des_encrypt.phpt
+++ b/ext/mcrypt/tests/mcrypt_cbc_3des_encrypt.phpt
@@ -50,19 +50,27 @@ foreach ($ivs as $iv) {
key length=8
+Deprecated: Function mcrypt_encrypt() is deprecated in %s%emcrypt_cbc_3des_encrypt.php on line 28
+
Warning: mcrypt_encrypt(): Key of size 8 not supported by this algorithm. Only keys of size 24 supported in %s on line %d
string(0) ""
key length=20
+Deprecated: Function mcrypt_encrypt() is deprecated in %s%emcrypt_cbc_3des_encrypt.php on line 28
+
Warning: mcrypt_encrypt(): Key of size 20 not supported by this algorithm. Only keys of size 24 supported in %s on line %d
string(0) ""
key length=24
+
+Deprecated: Function mcrypt_encrypt() is deprecated in %s%emcrypt_cbc_3des_encrypt.php on line 28
string(112) "b85e21072239d60c63a80e7c9ae493cb741a1cd407e52f451c5f43a0d103f55a7b62617eb2e44213c2d44462d388bc0b8f119384b12c84ac"
key length=26
+Deprecated: Function mcrypt_encrypt() is deprecated in %s%emcrypt_cbc_3des_encrypt.php on line 28
+
Warning: mcrypt_encrypt(): Key of size 26 not supported by this algorithm. Only keys of size 24 supported in %s on line %d
string(0) ""
@@ -70,14 +78,20 @@ string(0) ""
iv length=4
+Deprecated: Function mcrypt_encrypt() is deprecated in %s%emcrypt_cbc_3des_encrypt.php on line 35
+
Warning: mcrypt_encrypt(): Received initialization vector of size 4, but size 8 is required for this encryption mode in %s on line %d
string(0) ""
iv length=8
+
+Deprecated: Function mcrypt_encrypt() is deprecated in %s%emcrypt_cbc_3des_encrypt.php on line 35
string(112) "b85e21072239d60c63a80e7c9ae493cb741a1cd407e52f451c5f43a0d103f55a7b62617eb2e44213c2d44462d388bc0b8f119384b12c84ac"
iv length=9
+Deprecated: Function mcrypt_encrypt() is deprecated in %s%emcrypt_cbc_3des_encrypt.php on line 35
+
Warning: mcrypt_encrypt(): Received initialization vector of size 9, but size 8 is required for this encryption mode in %s on line %d
string(0) ""
===DONE===
diff --git a/ext/mcrypt/tests/mcrypt_cfb.phpt b/ext/mcrypt/tests/mcrypt_cfb.phpt
index ebbbea7c93..1ff72ab2e0 100644
--- a/ext/mcrypt/tests/mcrypt_cfb.phpt
+++ b/ext/mcrypt/tests/mcrypt_cfb.phpt
@@ -18,7 +18,16 @@ echo trim(mcrypt_decrypt($cipher, $key, $enc_data, MCRYPT_MODE_CFB, $iv)) . "\n"
var_dump(mcrypt_decrypt($cipher, $key, $enc_data, MCRYPT_MODE_CFB));
--EXPECTF--
+Deprecated: Function mcrypt_get_iv_size() is deprecated in %s%emcrypt_cfb.php on line 6
+
+Deprecated: Function mcrypt_create_iv() is deprecated in %s%emcrypt_cfb.php on line 6
+
+Deprecated: Function mcrypt_encrypt() is deprecated in %s%emcrypt_cfb.php on line 7
+
+Deprecated: Function mcrypt_decrypt() is deprecated in %s%emcrypt_cfb.php on line 10
PHP Testfest 2008
+Deprecated: Function mcrypt_decrypt() is deprecated in %s%emcrypt_cfb.php on line 13
+
Warning: mcrypt_decrypt(): Encryption mode requires an initialization vector of size 16 in %s on line %d
bool(false)
diff --git a/ext/mcrypt/tests/mcrypt_create_iv.phpt b/ext/mcrypt/tests/mcrypt_create_iv.phpt
index 1aa48868b0..6faabbf831 100644
--- a/ext/mcrypt/tests/mcrypt_create_iv.phpt
+++ b/ext/mcrypt/tests/mcrypt_create_iv.phpt
@@ -11,7 +11,18 @@ $iv3 = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB)
echo strlen($iv1) . "\n";
echo strlen($iv2) . "\n";
echo strlen($iv3) . "\n";
---EXPECT--
+--EXPECTF--
+Deprecated: Function mcrypt_get_iv_size() is deprecated in %s%emcrypt_create_iv.php on line 2
+
+Deprecated: Function mcrypt_create_iv() is deprecated in %s%emcrypt_create_iv.php on line 2
+
+Deprecated: Function mcrypt_get_iv_size() is deprecated in %s%emcrypt_create_iv.php on line 3
+
+Deprecated: Function mcrypt_create_iv() is deprecated in %s%emcrypt_create_iv.php on line 3
+
+Deprecated: Function mcrypt_get_iv_size() is deprecated in %s%emcrypt_create_iv.php on line 4
+
+Deprecated: Function mcrypt_create_iv() is deprecated in %s%emcrypt_create_iv.php on line 4
16
16
16
diff --git a/ext/mcrypt/tests/mcrypt_decrypt.phpt b/ext/mcrypt/tests/mcrypt_decrypt.phpt
index f10757c45d..03098f0d8e 100644
--- a/ext/mcrypt/tests/mcrypt_decrypt.phpt
+++ b/ext/mcrypt/tests/mcrypt_decrypt.phpt
@@ -20,10 +20,21 @@ var_dump(mcrypt_decrypt($cipher, $key, $enc_data, MCRYPT_MODE_CBC));
var_dump(mcrypt_decrypt(MCRYPT_BLOWFISH, "FooBar", $enc_data, MCRYPT_MODE_CBC, $iv));
--EXPECTF--
+Deprecated: Function mcrypt_get_iv_size() is deprecated in %s%emcrypt_decrypt.php on line 7
+
+Deprecated: Function mcrypt_create_iv() is deprecated in %s%emcrypt_decrypt.php on line 7
+
+Deprecated: Function mcrypt_encrypt() is deprecated in %s%emcrypt_decrypt.php on line 8
+
+Deprecated: Function mcrypt_decrypt() is deprecated in %s%emcrypt_decrypt.php on line 11
PHP Testfest 2008
+Deprecated: Function mcrypt_decrypt() is deprecated in %s%emcrypt_decrypt.php on line 14
+
Warning: mcrypt_decrypt(): Encryption mode requires an initialization vector of size 16 in %s on line %d
bool(false)
+Deprecated: Function mcrypt_decrypt() is deprecated in %s%emcrypt_decrypt.php on line 16
+
Warning: mcrypt_decrypt(): Received initialization vector of size 16, but size 8 is required for this encryption mode in %s on line %d
bool(false)
diff --git a/ext/mcrypt/tests/mcrypt_decrypt_3des_cbc.phpt b/ext/mcrypt/tests/mcrypt_decrypt_3des_cbc.phpt
index 60af213911..cc5996d410 100644
--- a/ext/mcrypt/tests/mcrypt_decrypt_3des_cbc.phpt
+++ b/ext/mcrypt/tests/mcrypt_decrypt_3des_cbc.phpt
@@ -72,19 +72,27 @@ function special_var_dump($str) {
key length=8
+Deprecated: Function mcrypt_decrypt() is deprecated in %s%emcrypt_decrypt_3des_cbc.php on line 44
+
Warning: mcrypt_decrypt(): Key of size 8 not supported by this algorithm. Only keys of size 24 supported in %s on line %d
string(0) ""
key length=20
+Deprecated: Function mcrypt_decrypt() is deprecated in %s%emcrypt_decrypt_3des_cbc.php on line 44
+
Warning: mcrypt_decrypt(): Key of size 20 not supported by this algorithm. Only keys of size 24 supported in %s on line %d
string(0) ""
key length=24
+
+Deprecated: Function mcrypt_decrypt() is deprecated in %s%emcrypt_decrypt_3des_cbc.php on line 44
string(32) "736563726574206d6573736167650000"
key length=26
+Deprecated: Function mcrypt_decrypt() is deprecated in %s%emcrypt_decrypt_3des_cbc.php on line 44
+
Warning: mcrypt_decrypt(): Key of size 26 not supported by this algorithm. Only keys of size 24 supported in %s on line %d
string(0) ""
@@ -92,14 +100,20 @@ string(0) ""
iv length=4
+Deprecated: Function mcrypt_decrypt() is deprecated in %s%emcrypt_decrypt_3des_cbc.php on line 51
+
Warning: mcrypt_decrypt(): Received initialization vector of size 4, but size 8 is required for this encryption mode in %s on line %d
string(0) ""
iv length=8
+
+Deprecated: Function mcrypt_decrypt() is deprecated in %s%emcrypt_decrypt_3des_cbc.php on line 51
string(32) "659ec947f4dc3a3b9c50de744598d3c8"
iv length=9
+Deprecated: Function mcrypt_decrypt() is deprecated in %s%emcrypt_decrypt_3des_cbc.php on line 51
+
Warning: mcrypt_decrypt(): Received initialization vector of size 9, but size 8 is required for this encryption mode in %s on line %d
string(0) ""
===DONE===
diff --git a/ext/mcrypt/tests/mcrypt_decrypt_3des_ecb.phpt b/ext/mcrypt/tests/mcrypt_decrypt_3des_ecb.phpt
index c54e6e5098..e86a5650fe 100644
--- a/ext/mcrypt/tests/mcrypt_decrypt_3des_ecb.phpt
+++ b/ext/mcrypt/tests/mcrypt_decrypt_3des_ecb.phpt
@@ -71,30 +71,44 @@ function special_var_dump($str) {
key length=8
+Deprecated: Function mcrypt_decrypt() is deprecated in %s%emcrypt_decrypt_3des_ecb.php on line 43
+
Warning: mcrypt_decrypt(): Key of size 8 not supported by this algorithm. Only keys of size 24 supported in %s on line %d
string(0) ""
key length=20
+Deprecated: Function mcrypt_decrypt() is deprecated in %s%emcrypt_decrypt_3des_ecb.php on line 43
+
Warning: mcrypt_decrypt(): Key of size 20 not supported by this algorithm. Only keys of size 24 supported in %s on line %d
string(0) ""
key length=24
+
+Deprecated: Function mcrypt_decrypt() is deprecated in %s%emcrypt_decrypt_3des_ecb.php on line 43
string(32) "736563726574206d6573736167650000"
key length=26
+Deprecated: Function mcrypt_decrypt() is deprecated in %s%emcrypt_decrypt_3des_ecb.php on line 43
+
Warning: mcrypt_decrypt(): Key of size 26 not supported by this algorithm. Only keys of size 24 supported in %s on line %d
string(0) ""
--- testing different iv lengths
iv length=4
+
+Deprecated: Function mcrypt_decrypt() is deprecated in %s%emcrypt_decrypt_3des_ecb.php on line 50
string(32) "a9298896ed1b7335f8f10f7ff6d7a239"
iv length=8
+
+Deprecated: Function mcrypt_decrypt() is deprecated in %s%emcrypt_decrypt_3des_ecb.php on line 50
string(32) "a9298896ed1b7335f8f10f7ff6d7a239"
iv length=9
+
+Deprecated: Function mcrypt_decrypt() is deprecated in %s%emcrypt_decrypt_3des_ecb.php on line 50
string(32) "a9298896ed1b7335f8f10f7ff6d7a239"
===DONE===
diff --git a/ext/mcrypt/tests/mcrypt_decrypt_error.phpt b/ext/mcrypt/tests/mcrypt_decrypt_error.phpt
index aae42d8389..b99b51c96a 100644
--- a/ext/mcrypt/tests/mcrypt_decrypt_error.phpt
+++ b/ext/mcrypt/tests/mcrypt_decrypt_error.phpt
@@ -41,12 +41,15 @@ var_dump( mcrypt_decrypt($cipher, $key, $data) );
-- Testing mcrypt_decrypt() function with more than expected no. of arguments --
+Deprecated: Function mcrypt_decrypt() is deprecated in %s%emcrypt_decrypt_error.php on line 19
+
Warning: mcrypt_decrypt() expects at most 5 parameters, 6 given in %s on line %d
NULL
-- Testing mcrypt_decrypt() function with less than expected no. of arguments --
+Deprecated: Function mcrypt_decrypt() is deprecated in %s%emcrypt_decrypt_error.php on line 26
+
Warning: mcrypt_decrypt() expects at least 4 parameters, 3 given in %s on line %d
NULL
===DONE===
-
diff --git a/ext/mcrypt/tests/mcrypt_decrypt_variation1.phpt b/ext/mcrypt/tests/mcrypt_decrypt_variation1.phpt
index 21fda19732..37fffad4d7 100644
--- a/ext/mcrypt/tests/mcrypt_decrypt_variation1.phpt
+++ b/ext/mcrypt/tests/mcrypt_decrypt_variation1.phpt
@@ -124,107 +124,132 @@ fclose($fp);
*** Testing mcrypt_decrypt() : usage variation ***
--int 0--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation1.php(107)
Error: 2 - mcrypt_decrypt(): Module initialization failed, %s(%d)
bool(false)
--int 1--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation1.php(107)
Error: 2 - mcrypt_decrypt(): Module initialization failed, %s(%d)
bool(false)
--int 12345--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation1.php(107)
Error: 2 - mcrypt_decrypt(): Module initialization failed, %s(%d)
bool(false)
--int -12345--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation1.php(107)
Error: 2 - mcrypt_decrypt(): Module initialization failed, %s(%d)
bool(false)
--float 10.5--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation1.php(107)
Error: 2 - mcrypt_decrypt(): Module initialization failed, %s(%d)
bool(false)
--float -10.5--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation1.php(107)
Error: 2 - mcrypt_decrypt(): Module initialization failed, %s(%d)
bool(false)
--float 12.3456789000e10--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation1.php(107)
Error: 2 - mcrypt_decrypt(): Module initialization failed, %s(%d)
bool(false)
--float -12.3456789000e10--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation1.php(107)
Error: 2 - mcrypt_decrypt(): Module initialization failed, %s(%d)
bool(false)
--float .5--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation1.php(107)
Error: 2 - mcrypt_decrypt(): Module initialization failed, %s(%d)
bool(false)
--empty array--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation1.php(107)
Error: 2 - mcrypt_decrypt() expects parameter 1 to be string, array given, %s(%d)
NULL
--int indexed array--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation1.php(107)
Error: 2 - mcrypt_decrypt() expects parameter 1 to be string, array given, %s(%d)
NULL
--associative array--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation1.php(107)
Error: 2 - mcrypt_decrypt() expects parameter 1 to be string, array given, %s(%d)
NULL
--nested arrays--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation1.php(107)
Error: 2 - mcrypt_decrypt() expects parameter 1 to be string, array given, %s(%d)
NULL
--uppercase NULL--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation1.php(107)
Error: 2 - mcrypt_decrypt(): Module initialization failed, %s(%d)
bool(false)
--lowercase null--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation1.php(107)
Error: 2 - mcrypt_decrypt(): Module initialization failed, %s(%d)
bool(false)
--lowercase true--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation1.php(107)
Error: 2 - mcrypt_decrypt(): Module initialization failed, %s(%d)
bool(false)
--lowercase false--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation1.php(107)
Error: 2 - mcrypt_decrypt(): Module initialization failed, %s(%d)
bool(false)
--uppercase TRUE--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation1.php(107)
Error: 2 - mcrypt_decrypt(): Module initialization failed, %s(%d)
bool(false)
--uppercase FALSE--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation1.php(107)
Error: 2 - mcrypt_decrypt(): Module initialization failed, %s(%d)
bool(false)
--empty string DQ--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation1.php(107)
Error: 2 - mcrypt_decrypt(): Module initialization failed, %s(%d)
bool(false)
--empty string SQ--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation1.php(107)
Error: 2 - mcrypt_decrypt(): Module initialization failed, %s(%d)
bool(false)
--instance of classWithToString--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation1.php(107)
Error: 2 - mcrypt_decrypt(): Module initialization failed, %s(%d)
bool(false)
--instance of classWithoutToString--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation1.php(107)
Error: 2 - mcrypt_decrypt() expects parameter 1 to be string, object given, %s(%d)
NULL
--undefined var--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation1.php(107)
Error: 2 - mcrypt_decrypt(): Module initialization failed, %s(%d)
bool(false)
--unset var--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation1.php(107)
Error: 2 - mcrypt_decrypt(): Module initialization failed, %s(%d)
bool(false)
--resource--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation1.php(107)
Error: 2 - mcrypt_decrypt() expects parameter 1 to be string, resource given, %s(%d)
NULL
===DONE===
-
diff --git a/ext/mcrypt/tests/mcrypt_decrypt_variation2.phpt b/ext/mcrypt/tests/mcrypt_decrypt_variation2.phpt
index 985029c8ba..25cc371f75 100644
--- a/ext/mcrypt/tests/mcrypt_decrypt_variation2.phpt
+++ b/ext/mcrypt/tests/mcrypt_decrypt_variation2.phpt
@@ -124,107 +124,132 @@ fclose($fp);
*** Testing mcrypt_decrypt() : usage variation ***
--int 0--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation2.php(107)
Error: 2 - mcrypt_decrypt(): Key of size %d not supported by this algorithm. Only keys of size 24 supported, %s(%d)
string(0) ""
--int 1--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation2.php(107)
Error: 2 - mcrypt_decrypt(): Key of size %d not supported by this algorithm. Only keys of size 24 supported, %s(%d)
string(0) ""
--int 12345--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation2.php(107)
Error: 2 - mcrypt_decrypt(): Key of size %d not supported by this algorithm. Only keys of size 24 supported, %s(%d)
string(0) ""
--int -12345--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation2.php(107)
Error: 2 - mcrypt_decrypt(): Key of size %d not supported by this algorithm. Only keys of size 24 supported, %s(%d)
string(0) ""
--float 10.5--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation2.php(107)
Error: 2 - mcrypt_decrypt(): Key of size %d not supported by this algorithm. Only keys of size 24 supported, %s(%d)
string(0) ""
--float -10.5--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation2.php(107)
Error: 2 - mcrypt_decrypt(): Key of size %d not supported by this algorithm. Only keys of size 24 supported, %s(%d)
string(0) ""
--float 12.3456789000e10--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation2.php(107)
Error: 2 - mcrypt_decrypt(): Key of size %d not supported by this algorithm. Only keys of size 24 supported, %s(%d)
string(0) ""
--float -12.3456789000e10--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation2.php(107)
Error: 2 - mcrypt_decrypt(): Key of size %d not supported by this algorithm. Only keys of size 24 supported, %s(%d)
string(0) ""
--float .5--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation2.php(107)
Error: 2 - mcrypt_decrypt(): Key of size %d not supported by this algorithm. Only keys of size 24 supported, %s(%d)
string(0) ""
--empty array--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation2.php(107)
Error: 2 - mcrypt_decrypt() expects parameter 2 to be string, array given, %s(%d)
string(0) ""
--int indexed array--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation2.php(107)
Error: 2 - mcrypt_decrypt() expects parameter 2 to be string, array given, %s(%d)
string(0) ""
--associative array--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation2.php(107)
Error: 2 - mcrypt_decrypt() expects parameter 2 to be string, array given, %s(%d)
string(0) ""
--nested arrays--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation2.php(107)
Error: 2 - mcrypt_decrypt() expects parameter 2 to be string, array given, %s(%d)
string(0) ""
--uppercase NULL--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation2.php(107)
Error: 2 - mcrypt_decrypt(): Key of size %d not supported by this algorithm. Only keys of size 24 supported, %s(%d)
string(0) ""
--lowercase null--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation2.php(107)
Error: 2 - mcrypt_decrypt(): Key of size %d not supported by this algorithm. Only keys of size 24 supported, %s(%d)
string(0) ""
--lowercase true--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation2.php(107)
Error: 2 - mcrypt_decrypt(): Key of size %d not supported by this algorithm. Only keys of size 24 supported, %s(%d)
string(0) ""
--lowercase false--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation2.php(107)
Error: 2 - mcrypt_decrypt(): Key of size %d not supported by this algorithm. Only keys of size 24 supported, %s(%d)
string(0) ""
--uppercase TRUE--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation2.php(107)
Error: 2 - mcrypt_decrypt(): Key of size %d not supported by this algorithm. Only keys of size 24 supported, %s(%d)
string(0) ""
--uppercase FALSE--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation2.php(107)
Error: 2 - mcrypt_decrypt(): Key of size %d not supported by this algorithm. Only keys of size 24 supported, %s(%d)
string(0) ""
--empty string DQ--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation2.php(107)
Error: 2 - mcrypt_decrypt(): Key of size %d not supported by this algorithm. Only keys of size 24 supported, %s(%d)
string(0) ""
--empty string SQ--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation2.php(107)
Error: 2 - mcrypt_decrypt(): Key of size %d not supported by this algorithm. Only keys of size 24 supported, %s(%d)
string(0) ""
--instance of classWithToString--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation2.php(107)
Error: 2 - mcrypt_decrypt(): Key of size %d not supported by this algorithm. Only keys of size 24 supported, %s(%d)
string(0) ""
--instance of classWithoutToString--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation2.php(107)
Error: 2 - mcrypt_decrypt() expects parameter 2 to be string, object given, %s(%d)
string(0) ""
--undefined var--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation2.php(107)
Error: 2 - mcrypt_decrypt(): Key of size %d not supported by this algorithm. Only keys of size 24 supported, %s(%d)
string(0) ""
--unset var--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation2.php(107)
Error: 2 - mcrypt_decrypt(): Key of size %d not supported by this algorithm. Only keys of size 24 supported, %s(%d)
string(0) ""
--resource--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation2.php(107)
Error: 2 - mcrypt_decrypt() expects parameter 2 to be string, resource given, %s(%d)
string(0) ""
===DONE===
-
diff --git a/ext/mcrypt/tests/mcrypt_decrypt_variation3.phpt b/ext/mcrypt/tests/mcrypt_decrypt_variation3.phpt
index a36f2c7a83..caa3635986 100644
--- a/ext/mcrypt/tests/mcrypt_decrypt_variation3.phpt
+++ b/ext/mcrypt/tests/mcrypt_decrypt_variation3.phpt
@@ -124,87 +124,112 @@ fclose($fp);
*** Testing mcrypt_decrypt() : usage variation ***
--int 0--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation3.php(107)
string(16) "52833a00168e547f"
--int 1--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation3.php(107)
string(16) "82011a0a93098a13"
--int 12345--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation3.php(107)
string(16) "e8b71c21b6acc162"
--int -12345--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation3.php(107)
string(16) "db3c458e975563a8"
--float 10.5--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation3.php(107)
string(16) "6ee8764562f25913"
--float -10.5--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation3.php(107)
string(16) "d63b39fd5f65678e"
--float 12.3456789000e10--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation3.php(107)
string(32) "7712cc4828221be40672239d9c32e742"
--float -12.3456789000e10--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation3.php(107)
string(32) "caa892cb5d28b53c2b75b1e0799427c3"
--float .5--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation3.php(107)
string(16) "99880c86884385d9"
--empty array--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation3.php(107)
Error: 2 - mcrypt_decrypt() expects parameter 3 to be string, array given, %s(%d)
string(0) ""
--int indexed array--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation3.php(107)
Error: 2 - mcrypt_decrypt() expects parameter 3 to be string, array given, %s(%d)
string(0) ""
--associative array--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation3.php(107)
Error: 2 - mcrypt_decrypt() expects parameter 3 to be string, array given, %s(%d)
string(0) ""
--nested arrays--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation3.php(107)
Error: 2 - mcrypt_decrypt() expects parameter 3 to be string, array given, %s(%d)
string(0) ""
--uppercase NULL--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation3.php(107)
string(16) "d27689f6fd9700f4"
--lowercase null--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation3.php(107)
string(16) "d27689f6fd9700f4"
--lowercase true--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation3.php(107)
string(16) "82011a0a93098a13"
--lowercase false--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation3.php(107)
string(16) "d27689f6fd9700f4"
--uppercase TRUE--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation3.php(107)
string(16) "82011a0a93098a13"
--uppercase FALSE--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation3.php(107)
string(16) "d27689f6fd9700f4"
--empty string DQ--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation3.php(107)
string(16) "d27689f6fd9700f4"
--empty string SQ--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation3.php(107)
string(16) "d27689f6fd9700f4"
--instance of classWithToString--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation3.php(107)
string(32) "46677e368bc07ef375bd580e0c4b2594"
--instance of classWithoutToString--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation3.php(107)
Error: 2 - mcrypt_decrypt() expects parameter 3 to be string, object given, %s(%d)
string(0) ""
--undefined var--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation3.php(107)
string(16) "d27689f6fd9700f4"
--unset var--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation3.php(107)
string(16) "d27689f6fd9700f4"
--resource--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation3.php(107)
Error: 2 - mcrypt_decrypt() expects parameter 3 to be string, resource given, %s(%d)
string(0) ""
===DONE===
-
diff --git a/ext/mcrypt/tests/mcrypt_decrypt_variation4.phpt b/ext/mcrypt/tests/mcrypt_decrypt_variation4.phpt
index 1bb994dcc7..575ed8e9d7 100644
--- a/ext/mcrypt/tests/mcrypt_decrypt_variation4.phpt
+++ b/ext/mcrypt/tests/mcrypt_decrypt_variation4.phpt
@@ -124,106 +124,132 @@ fclose($fp);
*** Testing mcrypt_decrypt() : usage variation ***
--int 0--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation4.php(107)
Error: 2 - mcrypt_decrypt(): Module initialization failed, %s(%d)
bool(false)
--int 1--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation4.php(107)
Error: 2 - mcrypt_decrypt(): Module initialization failed, %s(%d)
bool(false)
--int 12345--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation4.php(107)
Error: 2 - mcrypt_decrypt(): Module initialization failed, %s(%d)
bool(false)
--int -12345--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation4.php(107)
Error: 2 - mcrypt_decrypt(): Module initialization failed, %s(%d)
bool(false)
--float 10.5--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation4.php(107)
Error: 2 - mcrypt_decrypt(): Module initialization failed, %s(%d)
bool(false)
--float -10.5--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation4.php(107)
Error: 2 - mcrypt_decrypt(): Module initialization failed, %s(%d)
bool(false)
--float 12.3456789000e10--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation4.php(107)
Error: 2 - mcrypt_decrypt(): Module initialization failed, %s(%d)
bool(false)
--float -12.3456789000e10--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation4.php(107)
Error: 2 - mcrypt_decrypt(): Module initialization failed, %s(%d)
bool(false)
--float .5--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation4.php(107)
Error: 2 - mcrypt_decrypt(): Module initialization failed, %s(%d)
bool(false)
--empty array--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation4.php(107)
Error: 2 - mcrypt_decrypt() expects parameter 4 to be string, array given, %s(%d)
NULL
--int indexed array--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation4.php(107)
Error: 2 - mcrypt_decrypt() expects parameter 4 to be string, array given, %s(%d)
NULL
--associative array--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation4.php(107)
Error: 2 - mcrypt_decrypt() expects parameter 4 to be string, array given, %s(%d)
NULL
--nested arrays--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation4.php(107)
Error: 2 - mcrypt_decrypt() expects parameter 4 to be string, array given, %s(%d)
NULL
--uppercase NULL--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation4.php(107)
Error: 2 - mcrypt_decrypt(): Module initialization failed, %s(%d)
bool(false)
--lowercase null--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation4.php(107)
Error: 2 - mcrypt_decrypt(): Module initialization failed, %s(%d)
bool(false)
--lowercase true--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation4.php(107)
Error: 2 - mcrypt_decrypt(): Module initialization failed, %s(%d)
bool(false)
--lowercase false--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation4.php(107)
Error: 2 - mcrypt_decrypt(): Module initialization failed, %s(%d)
bool(false)
--uppercase TRUE--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation4.php(107)
Error: 2 - mcrypt_decrypt(): Module initialization failed, %s(%d)
bool(false)
--uppercase FALSE--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation4.php(107)
Error: 2 - mcrypt_decrypt(): Module initialization failed, %s(%d)
bool(false)
--empty string DQ--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation4.php(107)
Error: 2 - mcrypt_decrypt(): Module initialization failed, %s(%d)
bool(false)
--empty string SQ--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation4.php(107)
Error: 2 - mcrypt_decrypt(): Module initialization failed, %s(%d)
bool(false)
--instance of classWithToString--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation4.php(107)
Error: 2 - mcrypt_decrypt(): Module initialization failed, %s(%d)
bool(false)
--instance of classWithoutToString--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation4.php(107)
Error: 2 - mcrypt_decrypt() expects parameter 4 to be string, object given, %s(%d)
NULL
--undefined var--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation4.php(107)
Error: 2 - mcrypt_decrypt(): Module initialization failed, %s(%d)
bool(false)
--unset var--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation4.php(107)
Error: 2 - mcrypt_decrypt(): Module initialization failed, %s(%d)
bool(false)
--resource--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation4.php(107)
Error: 2 - mcrypt_decrypt() expects parameter 4 to be string, resource given, %s(%d)
NULL
===DONE===
diff --git a/ext/mcrypt/tests/mcrypt_decrypt_variation5.phpt b/ext/mcrypt/tests/mcrypt_decrypt_variation5.phpt
index 0f5093ba88..9fd28498d1 100644
--- a/ext/mcrypt/tests/mcrypt_decrypt_variation5.phpt
+++ b/ext/mcrypt/tests/mcrypt_decrypt_variation5.phpt
@@ -124,107 +124,132 @@ fclose($fp);
*** Testing mcrypt_decrypt() : usage variation ***
--int 0--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation5.php(107)
Error: 2 - mcrypt_decrypt(): Received initialization vector of size %d, but size 8 is required for this encryption mode, %s(%d)
string(0) ""
--int 1--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation5.php(107)
Error: 2 - mcrypt_decrypt(): Received initialization vector of size %d, but size 8 is required for this encryption mode, %s(%d)
string(0) ""
--int 12345--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation5.php(107)
Error: 2 - mcrypt_decrypt(): Received initialization vector of size %d, but size 8 is required for this encryption mode, %s(%d)
string(0) ""
--int -12345--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation5.php(107)
Error: 2 - mcrypt_decrypt(): Received initialization vector of size %d, but size 8 is required for this encryption mode, %s(%d)
string(0) ""
--float 10.5--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation5.php(107)
Error: 2 - mcrypt_decrypt(): Received initialization vector of size %d, but size 8 is required for this encryption mode, %s(%d)
string(0) ""
--float -10.5--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation5.php(107)
Error: 2 - mcrypt_decrypt(): Received initialization vector of size %d, but size 8 is required for this encryption mode, %s(%d)
string(0) ""
--float 12.3456789000e10--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation5.php(107)
Error: 2 - mcrypt_decrypt(): Received initialization vector of size %d, but size 8 is required for this encryption mode, %s(%d)
string(0) ""
--float -12.3456789000e10--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation5.php(107)
Error: 2 - mcrypt_decrypt(): Received initialization vector of size %d, but size 8 is required for this encryption mode, %s(%d)
string(0) ""
--float .5--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation5.php(107)
Error: 2 - mcrypt_decrypt(): Received initialization vector of size %d, but size 8 is required for this encryption mode, %s(%d)
string(0) ""
--empty array--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation5.php(107)
Error: 2 - mcrypt_decrypt() expects parameter 5 to be string, array given, %s(%d)
string(0) ""
--int indexed array--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation5.php(107)
Error: 2 - mcrypt_decrypt() expects parameter 5 to be string, array given, %s(%d)
string(0) ""
--associative array--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation5.php(107)
Error: 2 - mcrypt_decrypt() expects parameter 5 to be string, array given, %s(%d)
string(0) ""
--nested arrays--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation5.php(107)
Error: 2 - mcrypt_decrypt() expects parameter 5 to be string, array given, %s(%d)
string(0) ""
--uppercase NULL--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation5.php(107)
Error: 2 - mcrypt_decrypt(): Received initialization vector of size %d, but size 8 is required for this encryption mode, %s(%d)
string(0) ""
--lowercase null--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation5.php(107)
Error: 2 - mcrypt_decrypt(): Received initialization vector of size %d, but size 8 is required for this encryption mode, %s(%d)
string(0) ""
--lowercase true--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation5.php(107)
Error: 2 - mcrypt_decrypt(): Received initialization vector of size %d, but size 8 is required for this encryption mode, %s(%d)
string(0) ""
--lowercase false--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation5.php(107)
Error: 2 - mcrypt_decrypt(): Received initialization vector of size %d, but size 8 is required for this encryption mode, %s(%d)
string(0) ""
--uppercase TRUE--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation5.php(107)
Error: 2 - mcrypt_decrypt(): Received initialization vector of size %d, but size 8 is required for this encryption mode, %s(%d)
string(0) ""
--uppercase FALSE--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation5.php(107)
Error: 2 - mcrypt_decrypt(): Received initialization vector of size %d, but size 8 is required for this encryption mode, %s(%d)
string(0) ""
--empty string DQ--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation5.php(107)
Error: 2 - mcrypt_decrypt(): Received initialization vector of size %d, but size 8 is required for this encryption mode, %s(%d)
string(0) ""
--empty string SQ--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation5.php(107)
Error: 2 - mcrypt_decrypt(): Received initialization vector of size %d, but size 8 is required for this encryption mode, %s(%d)
string(0) ""
--instance of classWithToString--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation5.php(107)
Error: 2 - mcrypt_decrypt(): Received initialization vector of size %d, but size 8 is required for this encryption mode, %s(%d)
string(0) ""
--instance of classWithoutToString--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation5.php(107)
Error: 2 - mcrypt_decrypt() expects parameter 5 to be string, object given, %s(%d)
string(0) ""
--undefined var--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation5.php(107)
Error: 2 - mcrypt_decrypt(): Received initialization vector of size %d, but size 8 is required for this encryption mode, %s(%d)
string(0) ""
--unset var--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation5.php(107)
Error: 2 - mcrypt_decrypt(): Received initialization vector of size %d, but size 8 is required for this encryption mode, %s(%d)
string(0) ""
--resource--
+Error: 8192 - Function mcrypt_decrypt() is deprecated, %s%emcrypt_decrypt_variation5.php(107)
Error: 2 - mcrypt_decrypt() expects parameter 5 to be string, resource given, %s(%d)
string(0) ""
===DONE===
-
diff --git a/ext/mcrypt/tests/mcrypt_ecb.phpt b/ext/mcrypt/tests/mcrypt_ecb.phpt
index 33c86bb45d..625d55880b 100644
--- a/ext/mcrypt/tests/mcrypt_ecb.phpt
+++ b/ext/mcrypt/tests/mcrypt_ecb.phpt
@@ -18,4 +18,13 @@ echo trim(mcrypt_decrypt($cipher, $key, $enc_data, MCRYPT_MODE_ECB, $iv)) . "\n"
mcrypt_decrypt($cipher, $key, $enc_data, MCRYPT_MODE_ECB);
--EXPECTF--
+Deprecated: Function mcrypt_get_iv_size() is deprecated in %s%emcrypt_ecb.php on line 6
+
+Deprecated: Function mcrypt_create_iv() is deprecated in %s%emcrypt_ecb.php on line 6
+
+Deprecated: Function mcrypt_encrypt() is deprecated in %s%emcrypt_ecb.php on line 7
+
+Deprecated: Function mcrypt_decrypt() is deprecated in %s%emcrypt_ecb.php on line 10
PHP Testfest 2008
+
+Deprecated: Function mcrypt_decrypt() is deprecated in %s%emcrypt_ecb.php on line 13
diff --git a/ext/mcrypt/tests/mcrypt_ecb_3des_decrypt.phpt b/ext/mcrypt/tests/mcrypt_ecb_3des_decrypt.phpt
index 6319fa15d0..635ca25a55 100644
--- a/ext/mcrypt/tests/mcrypt_ecb_3des_decrypt.phpt
+++ b/ext/mcrypt/tests/mcrypt_ecb_3des_decrypt.phpt
@@ -68,30 +68,44 @@ function special_var_dump($str) {
key length=8
+Deprecated: Function mcrypt_decrypt() is deprecated in %s%emcrypt_ecb_3des_decrypt.php on line 42
+
Warning: mcrypt_decrypt(): Key of size 8 not supported by this algorithm. Only keys of size 24 supported in %s on line %d
string(0) ""
key length=20
+Deprecated: Function mcrypt_decrypt() is deprecated in %s%emcrypt_ecb_3des_decrypt.php on line 42
+
Warning: mcrypt_decrypt(): Key of size 20 not supported by this algorithm. Only keys of size 24 supported in %s on line %d
string(0) ""
key length=24
+
+Deprecated: Function mcrypt_decrypt() is deprecated in %s%emcrypt_ecb_3des_decrypt.php on line 42
string(32) "736563726574206d6573736167650000"
key length=26
+Deprecated: Function mcrypt_decrypt() is deprecated in %s%emcrypt_ecb_3des_decrypt.php on line 42
+
Warning: mcrypt_decrypt(): Key of size 26 not supported by this algorithm. Only keys of size 24 supported in %s on line %d
string(0) ""
--- testing different iv lengths
iv length=4
+
+Deprecated: Function mcrypt_decrypt() is deprecated in %s%emcrypt_ecb_3des_decrypt.php on line 49
string(32) "a9298896ed1b7335f8f10f7ff6d7a239"
iv length=8
+
+Deprecated: Function mcrypt_decrypt() is deprecated in %s%emcrypt_ecb_3des_decrypt.php on line 49
string(32) "a9298896ed1b7335f8f10f7ff6d7a239"
iv length=9
+
+Deprecated: Function mcrypt_decrypt() is deprecated in %s%emcrypt_ecb_3des_decrypt.php on line 49
string(32) "a9298896ed1b7335f8f10f7ff6d7a239"
===DONE===
diff --git a/ext/mcrypt/tests/mcrypt_ecb_3des_encrypt.phpt b/ext/mcrypt/tests/mcrypt_ecb_3des_encrypt.phpt
index 0ccf4b70b3..68f056c510 100644
--- a/ext/mcrypt/tests/mcrypt_ecb_3des_encrypt.phpt
+++ b/ext/mcrypt/tests/mcrypt_ecb_3des_encrypt.phpt
@@ -53,30 +53,44 @@ foreach ($ivs as $iv) {
key length=8
+Deprecated: Function mcrypt_encrypt() is deprecated in %s%emcrypt_ecb_3des_encrypt.php on line 31
+
Warning: mcrypt_encrypt(): Key of size 8 not supported by this algorithm. Only keys of size 24 supported in %s on line %d
string(0) ""
key length=20
+Deprecated: Function mcrypt_encrypt() is deprecated in %s%emcrypt_ecb_3des_encrypt.php on line 31
+
Warning: mcrypt_encrypt(): Key of size 20 not supported by this algorithm. Only keys of size 24 supported in %s on line %d
string(0) ""
key length=24
+
+Deprecated: Function mcrypt_encrypt() is deprecated in %s%emcrypt_ecb_3des_encrypt.php on line 31
string(112) "923eedcb20e18e3efa466a6ca1b842b34e6ac46aa3690ef739d0d68a26eb64e1a6ad42e7d18312ae8a57ab927e1dc892e5ff56c061864f27"
key length=26
+Deprecated: Function mcrypt_encrypt() is deprecated in %s%emcrypt_ecb_3des_encrypt.php on line 31
+
Warning: mcrypt_encrypt(): Key of size 26 not supported by this algorithm. Only keys of size 24 supported in %s on line %d
string(0) ""
--- testing different iv lengths
iv length=4
+
+Deprecated: Function mcrypt_encrypt() is deprecated in %s%emcrypt_ecb_3des_encrypt.php on line 38
string(112) "440a6f54601969b15e81df09cd381ef585fede5f3620587fd1a949c520aed9f6d10ebbabf2cea3e1f04c9251c2878c0ca37d51c80d490165"
iv length=8
+
+Deprecated: Function mcrypt_encrypt() is deprecated in %s%emcrypt_ecb_3des_encrypt.php on line 38
string(112) "440a6f54601969b15e81df09cd381ef585fede5f3620587fd1a949c520aed9f6d10ebbabf2cea3e1f04c9251c2878c0ca37d51c80d490165"
iv length=9
+
+Deprecated: Function mcrypt_encrypt() is deprecated in %s%emcrypt_ecb_3des_encrypt.php on line 38
string(112) "440a6f54601969b15e81df09cd381ef585fede5f3620587fd1a949c520aed9f6d10ebbabf2cea3e1f04c9251c2878c0ca37d51c80d490165"
===DONE===
diff --git a/ext/mcrypt/tests/mcrypt_enc_get_algorithms_name.phpt b/ext/mcrypt/tests/mcrypt_enc_get_algorithms_name.phpt
index 5b0bb01b9f..f0ee269a35 100644
--- a/ext/mcrypt/tests/mcrypt_enc_get_algorithms_name.phpt
+++ b/ext/mcrypt/tests/mcrypt_enc_get_algorithms_name.phpt
@@ -14,9 +14,28 @@ $td = mcrypt_module_open(MCRYPT_BLOWFISH, '', MCRYPT_MODE_CBC, '');
echo mcrypt_enc_get_algorithms_name($td) . "\n";
$td = mcrypt_module_open('des', '', 'ecb', '');
echo mcrypt_enc_get_algorithms_name($td) . "\n";
---EXPECT--
+--EXPECTF--
+Deprecated: Function mcrypt_module_open() is deprecated in %s%emcrypt_enc_get_algorithms_name.php on line 2
+
+Deprecated: Function mcrypt_enc_get_algorithms_name() is deprecated in %s%emcrypt_enc_get_algorithms_name.php on line 3
Rijndael-128
+
+Deprecated: Function mcrypt_module_open() is deprecated in %s%emcrypt_enc_get_algorithms_name.php on line 4
+
+Deprecated: Function mcrypt_enc_get_algorithms_name() is deprecated in %s%emcrypt_enc_get_algorithms_name.php on line 5
Rijndael-128
+
+Deprecated: Function mcrypt_module_open() is deprecated in %s%emcrypt_enc_get_algorithms_name.php on line 6
+
+Deprecated: Function mcrypt_enc_get_algorithms_name() is deprecated in %s%emcrypt_enc_get_algorithms_name.php on line 7
RC2
+
+Deprecated: Function mcrypt_module_open() is deprecated in %s%emcrypt_enc_get_algorithms_name.php on line 8
+
+Deprecated: Function mcrypt_enc_get_algorithms_name() is deprecated in %s%emcrypt_enc_get_algorithms_name.php on line 9
Blowfish
+
+Deprecated: Function mcrypt_module_open() is deprecated in %s%emcrypt_enc_get_algorithms_name.php on line 10
+
+Deprecated: Function mcrypt_enc_get_algorithms_name() is deprecated in %s%emcrypt_enc_get_algorithms_name.php on line 11
DES \ No newline at end of file
diff --git a/ext/mcrypt/tests/mcrypt_enc_get_block_size.phpt b/ext/mcrypt/tests/mcrypt_enc_get_block_size.phpt
index 06f5adda7a..bbdd1de44b 100644
--- a/ext/mcrypt/tests/mcrypt_enc_get_block_size.phpt
+++ b/ext/mcrypt/tests/mcrypt_enc_get_block_size.phpt
@@ -10,7 +10,18 @@ $td = mcrypt_module_open(MCRYPT_3DES, '', MCRYPT_MODE_CBC, '');
var_dump(mcrypt_enc_get_block_size($td));
$td = mcrypt_module_open(MCRYPT_WAKE, '', MCRYPT_MODE_STREAM, '');
var_dump(mcrypt_enc_get_block_size($td));
---EXPECT--
+--EXPECTF--
+Deprecated: Function mcrypt_module_open() is deprecated in %s%emcrypt_enc_get_block_size.php on line 2
+
+Deprecated: Function mcrypt_enc_get_block_size() is deprecated in %s%emcrypt_enc_get_block_size.php on line 3
int(32)
+
+Deprecated: Function mcrypt_module_open() is deprecated in %s%emcrypt_enc_get_block_size.php on line 4
+
+Deprecated: Function mcrypt_enc_get_block_size() is deprecated in %s%emcrypt_enc_get_block_size.php on line 5
int(8)
+
+Deprecated: Function mcrypt_module_open() is deprecated in %s%emcrypt_enc_get_block_size.php on line 6
+
+Deprecated: Function mcrypt_enc_get_block_size() is deprecated in %s%emcrypt_enc_get_block_size.php on line 7
int(1)
diff --git a/ext/mcrypt/tests/mcrypt_enc_get_iv_size.phpt b/ext/mcrypt/tests/mcrypt_enc_get_iv_size.phpt
index b4f9d6e4b5..f48a870035 100644
--- a/ext/mcrypt/tests/mcrypt_enc_get_iv_size.phpt
+++ b/ext/mcrypt/tests/mcrypt_enc_get_iv_size.phpt
@@ -10,7 +10,18 @@ $td = mcrypt_module_open(MCRYPT_3DES, '', MCRYPT_MODE_CBC, '');
var_dump(mcrypt_enc_get_iv_size($td));
$td = mcrypt_module_open(MCRYPT_WAKE, '', MCRYPT_MODE_STREAM, '');
var_dump(mcrypt_enc_get_iv_size($td));
---EXPECT--
+--EXPECTF--
+Deprecated: Function mcrypt_module_open() is deprecated in %s%emcrypt_enc_get_iv_size.php on line 2
+
+Deprecated: Function mcrypt_enc_get_iv_size() is deprecated in %s%emcrypt_enc_get_iv_size.php on line 3
int(32)
+
+Deprecated: Function mcrypt_module_open() is deprecated in %s%emcrypt_enc_get_iv_size.php on line 4
+
+Deprecated: Function mcrypt_enc_get_iv_size() is deprecated in %s%emcrypt_enc_get_iv_size.php on line 5
int(8)
+
+Deprecated: Function mcrypt_module_open() is deprecated in %s%emcrypt_enc_get_iv_size.php on line 6
+
+Deprecated: Function mcrypt_enc_get_iv_size() is deprecated in %s%emcrypt_enc_get_iv_size.php on line 7
int(0)
diff --git a/ext/mcrypt/tests/mcrypt_enc_get_key_size.phpt b/ext/mcrypt/tests/mcrypt_enc_get_key_size.phpt
index 22328e14d6..08a8e0baf0 100644
--- a/ext/mcrypt/tests/mcrypt_enc_get_key_size.phpt
+++ b/ext/mcrypt/tests/mcrypt_enc_get_key_size.phpt
@@ -10,7 +10,18 @@ $td = mcrypt_module_open(MCRYPT_3DES, '', MCRYPT_MODE_CBC, '');
var_dump(mcrypt_enc_get_key_size($td));
$td = mcrypt_module_open(MCRYPT_WAKE, '', MCRYPT_MODE_STREAM, '');
var_dump(mcrypt_enc_get_key_size($td));
---EXPECT--
+--EXPECTF--
+Deprecated: Function mcrypt_module_open() is deprecated in %s%emcrypt_enc_get_key_size.php on line 2
+
+Deprecated: Function mcrypt_enc_get_key_size() is deprecated in %s%emcrypt_enc_get_key_size.php on line 3
int(32)
+
+Deprecated: Function mcrypt_module_open() is deprecated in %s%emcrypt_enc_get_key_size.php on line 4
+
+Deprecated: Function mcrypt_enc_get_key_size() is deprecated in %s%emcrypt_enc_get_key_size.php on line 5
int(24)
+
+Deprecated: Function mcrypt_module_open() is deprecated in %s%emcrypt_enc_get_key_size.php on line 6
+
+Deprecated: Function mcrypt_enc_get_key_size() is deprecated in %s%emcrypt_enc_get_key_size.php on line 7
int(32)
diff --git a/ext/mcrypt/tests/mcrypt_enc_get_mode_name.phpt b/ext/mcrypt/tests/mcrypt_enc_get_mode_name.phpt
index 21c41f98cb..7abdd78d5c 100644
--- a/ext/mcrypt/tests/mcrypt_enc_get_mode_name.phpt
+++ b/ext/mcrypt/tests/mcrypt_enc_get_mode_name.phpt
@@ -16,10 +16,33 @@ $td = mcrypt_module_open('des', '', 'ecb', '');
echo mcrypt_enc_get_modes_name($td) . "\n";
$td = mcrypt_module_open('des', '', 'cbc', '');
echo mcrypt_enc_get_modes_name($td) . "\n";
---EXPECT--
+--EXPECTF--
+Deprecated: Function mcrypt_module_open() is deprecated in %s%emcrypt_enc_get_mode_name.php on line 2
+
+Deprecated: Function mcrypt_enc_get_modes_name() is deprecated in %s%emcrypt_enc_get_mode_name.php on line 3
ECB
+
+Deprecated: Function mcrypt_module_open() is deprecated in %s%emcrypt_enc_get_mode_name.php on line 4
+
+Deprecated: Function mcrypt_enc_get_modes_name() is deprecated in %s%emcrypt_enc_get_mode_name.php on line 5
CBC
+
+Deprecated: Function mcrypt_module_open() is deprecated in %s%emcrypt_enc_get_mode_name.php on line 6
+
+Deprecated: Function mcrypt_enc_get_modes_name() is deprecated in %s%emcrypt_enc_get_mode_name.php on line 7
STREAM
+
+Deprecated: Function mcrypt_module_open() is deprecated in %s%emcrypt_enc_get_mode_name.php on line 8
+
+Deprecated: Function mcrypt_enc_get_modes_name() is deprecated in %s%emcrypt_enc_get_mode_name.php on line 9
OFB
+
+Deprecated: Function mcrypt_module_open() is deprecated in %s%emcrypt_enc_get_mode_name.php on line 10
+
+Deprecated: Function mcrypt_enc_get_modes_name() is deprecated in %s%emcrypt_enc_get_mode_name.php on line 11
ECB
+
+Deprecated: Function mcrypt_module_open() is deprecated in %s%emcrypt_enc_get_mode_name.php on line 12
+
+Deprecated: Function mcrypt_enc_get_modes_name() is deprecated in %s%emcrypt_enc_get_mode_name.php on line 13
CBC
diff --git a/ext/mcrypt/tests/mcrypt_enc_get_supported_key_sizes.phpt b/ext/mcrypt/tests/mcrypt_enc_get_supported_key_sizes.phpt
index 0ce1f15cf7..8cde780d9e 100644
--- a/ext/mcrypt/tests/mcrypt_enc_get_supported_key_sizes.phpt
+++ b/ext/mcrypt/tests/mcrypt_enc_get_supported_key_sizes.phpt
@@ -7,7 +7,10 @@ mcrypt_enc_get_supported_key_sizes
$td = mcrypt_module_open('rijndael-256', '', 'ecb', '');
$var = mcrypt_enc_get_supported_key_sizes($td);
var_dump($var);
---EXPECT--
+--EXPECTF--
+Deprecated: Function mcrypt_module_open() is deprecated in %s%emcrypt_enc_get_supported_key_sizes.php on line 2
+
+Deprecated: Function mcrypt_enc_get_supported_key_sizes() is deprecated in %s%emcrypt_enc_get_supported_key_sizes.php on line 3
array(3) {
[0]=>
int(16)
diff --git a/ext/mcrypt/tests/mcrypt_enc_is_block_algorithm.phpt b/ext/mcrypt/tests/mcrypt_enc_is_block_algorithm.phpt
index 21a0ed2b88..fbc86550f9 100644
--- a/ext/mcrypt/tests/mcrypt_enc_is_block_algorithm.phpt
+++ b/ext/mcrypt/tests/mcrypt_enc_is_block_algorithm.phpt
@@ -10,7 +10,18 @@ $td = mcrypt_module_open(MCRYPT_3DES, '', MCRYPT_MODE_CBC, '');
var_dump(mcrypt_enc_is_block_algorithm($td));
$td = mcrypt_module_open(MCRYPT_WAKE, '', MCRYPT_MODE_STREAM, '');
var_dump(mcrypt_enc_is_block_algorithm($td));
---EXPECT--
+--EXPECTF--
+Deprecated: Function mcrypt_module_open() is deprecated in %s%emcrypt_enc_is_block_algorithm.php on line 2
+
+Deprecated: Function mcrypt_enc_is_block_algorithm() is deprecated in %s%emcrypt_enc_is_block_algorithm.php on line 3
bool(true)
+
+Deprecated: Function mcrypt_module_open() is deprecated in %s%emcrypt_enc_is_block_algorithm.php on line 4
+
+Deprecated: Function mcrypt_enc_is_block_algorithm() is deprecated in %s%emcrypt_enc_is_block_algorithm.php on line 5
bool(true)
+
+Deprecated: Function mcrypt_module_open() is deprecated in %s%emcrypt_enc_is_block_algorithm.php on line 6
+
+Deprecated: Function mcrypt_enc_is_block_algorithm() is deprecated in %s%emcrypt_enc_is_block_algorithm.php on line 7
bool(false)
diff --git a/ext/mcrypt/tests/mcrypt_enc_is_block_algorithm_mode.phpt b/ext/mcrypt/tests/mcrypt_enc_is_block_algorithm_mode.phpt
index 69c9654175..181ca49600 100644
--- a/ext/mcrypt/tests/mcrypt_enc_is_block_algorithm_mode.phpt
+++ b/ext/mcrypt/tests/mcrypt_enc_is_block_algorithm_mode.phpt
@@ -10,7 +10,18 @@ $td = mcrypt_module_open(MCRYPT_RIJNDAEL_256, '', MCRYPT_MODE_CBC, '');
var_dump(mcrypt_enc_is_block_algorithm_mode($td));
$td = mcrypt_module_open(MCRYPT_WAKE, '', MCRYPT_MODE_STREAM, '');
var_dump(mcrypt_enc_is_block_algorithm_mode($td));
---EXPECT--
+--EXPECTF--
+Deprecated: Function mcrypt_module_open() is deprecated in %s%emcrypt_enc_is_block_algorithm_mode.php on line 2
+
+Deprecated: Function mcrypt_enc_is_block_algorithm_mode() is deprecated in %s%emcrypt_enc_is_block_algorithm_mode.php on line 3
bool(true)
+
+Deprecated: Function mcrypt_module_open() is deprecated in %s%emcrypt_enc_is_block_algorithm_mode.php on line 4
+
+Deprecated: Function mcrypt_enc_is_block_algorithm_mode() is deprecated in %s%emcrypt_enc_is_block_algorithm_mode.php on line 5
bool(true)
+
+Deprecated: Function mcrypt_module_open() is deprecated in %s%emcrypt_enc_is_block_algorithm_mode.php on line 6
+
+Deprecated: Function mcrypt_enc_is_block_algorithm_mode() is deprecated in %s%emcrypt_enc_is_block_algorithm_mode.php on line 7
bool(false)
diff --git a/ext/mcrypt/tests/mcrypt_enc_is_block_mode.phpt b/ext/mcrypt/tests/mcrypt_enc_is_block_mode.phpt
index 551f7a6975..e59d11dde4 100644
--- a/ext/mcrypt/tests/mcrypt_enc_is_block_mode.phpt
+++ b/ext/mcrypt/tests/mcrypt_enc_is_block_mode.phpt
@@ -12,8 +12,23 @@ $td = mcrypt_module_open(MCRYPT_ARCFOUR, '', MCRYPT_MODE_STREAM, '');
var_dump(mcrypt_enc_is_block_mode($td));
$td = mcrypt_module_open(MCRYPT_WAKE, '', MCRYPT_MODE_STREAM, '');
var_dump(mcrypt_enc_is_block_mode($td));
---EXPECT--
+--EXPECTF--
+Deprecated: Function mcrypt_module_open() is deprecated in %s%emcrypt_enc_is_block_mode.php on line 2
+
+Deprecated: Function mcrypt_enc_is_block_mode() is deprecated in %s%emcrypt_enc_is_block_mode.php on line 3
bool(true)
+
+Deprecated: Function mcrypt_module_open() is deprecated in %s%emcrypt_enc_is_block_mode.php on line 4
+
+Deprecated: Function mcrypt_enc_is_block_mode() is deprecated in %s%emcrypt_enc_is_block_mode.php on line 5
bool(true)
+
+Deprecated: Function mcrypt_module_open() is deprecated in %s%emcrypt_enc_is_block_mode.php on line 6
+
+Deprecated: Function mcrypt_enc_is_block_mode() is deprecated in %s%emcrypt_enc_is_block_mode.php on line 7
bool(false)
+
+Deprecated: Function mcrypt_module_open() is deprecated in %s%emcrypt_enc_is_block_mode.php on line 8
+
+Deprecated: Function mcrypt_enc_is_block_mode() is deprecated in %s%emcrypt_enc_is_block_mode.php on line 9
bool(false) \ No newline at end of file
diff --git a/ext/mcrypt/tests/mcrypt_enc_self_test.phpt b/ext/mcrypt/tests/mcrypt_enc_self_test.phpt
index a161e0e02b..2297b969c1 100644
--- a/ext/mcrypt/tests/mcrypt_enc_self_test.phpt
+++ b/ext/mcrypt/tests/mcrypt_enc_self_test.phpt
@@ -6,5 +6,8 @@ mcrypt_enc_self_test
<?php
$td = mcrypt_module_open(MCRYPT_RIJNDAEL_256, '', MCRYPT_MODE_CBC, '');
var_dump(mcrypt_enc_self_test($td));
---EXPECT--
+--EXPECTF--
+Deprecated: Function mcrypt_module_open() is deprecated in %s%emcrypt_enc_self_test.php on line 2
+
+Deprecated: Function mcrypt_enc_self_test() is deprecated in %s%emcrypt_enc_self_test.php on line 3
int(0) \ No newline at end of file
diff --git a/ext/mcrypt/tests/mcrypt_encrypt_3des_cbc.phpt b/ext/mcrypt/tests/mcrypt_encrypt_3des_cbc.phpt
index 8b3cbd8366..15b3c886fa 100644
--- a/ext/mcrypt/tests/mcrypt_encrypt_3des_cbc.phpt
+++ b/ext/mcrypt/tests/mcrypt_encrypt_3des_cbc.phpt
@@ -65,19 +65,27 @@ foreach ($ivs as $iv) {
key length=8
+Deprecated: Function mcrypt_encrypt() is deprecated in %s%emcrypt_encrypt_3des_cbc.php on line 40
+
Warning: mcrypt_encrypt(): Key of size 8 not supported by this algorithm. Only keys of size 24 supported in %s on line %d
string(0) ""
key length=20
+Deprecated: Function mcrypt_encrypt() is deprecated in %s%emcrypt_encrypt_3des_cbc.php on line 40
+
Warning: mcrypt_encrypt(): Key of size 20 not supported by this algorithm. Only keys of size 24 supported in %s on line %d
string(0) ""
key length=24
+
+Deprecated: Function mcrypt_encrypt() is deprecated in %s%emcrypt_encrypt_3des_cbc.php on line 40
string(112) "b85e21072239d60c63a80e7c9ae493cb741a1cd407e52f451c5f43a0d103f55a7b62617eb2e44213c2d44462d388bc0b8f119384b12c84ac"
key length=26
+Deprecated: Function mcrypt_encrypt() is deprecated in %s%emcrypt_encrypt_3des_cbc.php on line 40
+
Warning: mcrypt_encrypt(): Key of size 26 not supported by this algorithm. Only keys of size 24 supported in %s on line %d
string(0) ""
@@ -85,14 +93,20 @@ string(0) ""
iv length=4
+Deprecated: Function mcrypt_encrypt() is deprecated in %s%emcrypt_encrypt_3des_cbc.php on line 47
+
Warning: mcrypt_encrypt(): Received initialization vector of size 4, but size 8 is required for this encryption mode in %s on line %d
string(0) ""
iv length=8
+
+Deprecated: Function mcrypt_encrypt() is deprecated in %s%emcrypt_encrypt_3des_cbc.php on line 47
string(112) "b85e21072239d60c63a80e7c9ae493cb741a1cd407e52f451c5f43a0d103f55a7b62617eb2e44213c2d44462d388bc0b8f119384b12c84ac"
iv length=9
+Deprecated: Function mcrypt_encrypt() is deprecated in %s%emcrypt_encrypt_3des_cbc.php on line 47
+
Warning: mcrypt_encrypt(): Received initialization vector of size 9, but size 8 is required for this encryption mode in %s on line %d
string(0) ""
===DONE===
diff --git a/ext/mcrypt/tests/mcrypt_encrypt_3des_ecb.phpt b/ext/mcrypt/tests/mcrypt_encrypt_3des_ecb.phpt
index ea6b2c2160..ffcabf8f5f 100644
--- a/ext/mcrypt/tests/mcrypt_encrypt_3des_ecb.phpt
+++ b/ext/mcrypt/tests/mcrypt_encrypt_3des_ecb.phpt
@@ -57,30 +57,44 @@ foreach ($ivs as $iv) {
key length=8
+Deprecated: Function mcrypt_encrypt() is deprecated in %s%emcrypt_encrypt_3des_ecb.php on line 25
+
Warning: mcrypt_encrypt(): Key of size 8 not supported by this algorithm. Only keys of size 24 supported in %s on line %d
string(0) ""
key length=20
+Deprecated: Function mcrypt_encrypt() is deprecated in %s%emcrypt_encrypt_3des_ecb.php on line 25
+
Warning: mcrypt_encrypt(): Key of size 20 not supported by this algorithm. Only keys of size 24 supported in %s on line %d
string(0) ""
key length=24
+
+Deprecated: Function mcrypt_encrypt() is deprecated in %s%emcrypt_encrypt_3des_ecb.php on line 25
string(112) "923eedcb20e18e3efa466a6ca1b842b34e6ac46aa3690ef739d0d68a26eb64e1a6ad42e7d18312ae8a57ab927e1dc892e5ff56c061864f27"
key length=26
+Deprecated: Function mcrypt_encrypt() is deprecated in %s%emcrypt_encrypt_3des_ecb.php on line 25
+
Warning: mcrypt_encrypt(): Key of size 26 not supported by this algorithm. Only keys of size 24 supported in %s on line %d
string(0) ""
--- testing different iv lengths
iv length=4
+
+Deprecated: Function mcrypt_encrypt() is deprecated in %s%emcrypt_encrypt_3des_ecb.php on line 39
string(112) "923eedcb20e18e3efa466a6ca1b842b34e6ac46aa3690ef739d0d68a26eb64e1a6ad42e7d18312ae8a57ab927e1dc892e5ff56c061864f27"
iv length=8
+
+Deprecated: Function mcrypt_encrypt() is deprecated in %s%emcrypt_encrypt_3des_ecb.php on line 39
string(112) "923eedcb20e18e3efa466a6ca1b842b34e6ac46aa3690ef739d0d68a26eb64e1a6ad42e7d18312ae8a57ab927e1dc892e5ff56c061864f27"
iv length=9
+
+Deprecated: Function mcrypt_encrypt() is deprecated in %s%emcrypt_encrypt_3des_ecb.php on line 39
string(112) "923eedcb20e18e3efa466a6ca1b842b34e6ac46aa3690ef739d0d68a26eb64e1a6ad42e7d18312ae8a57ab927e1dc892e5ff56c061864f27"
===DONE===
diff --git a/ext/mcrypt/tests/mcrypt_encrypt_error.phpt b/ext/mcrypt/tests/mcrypt_encrypt_error.phpt
index 04226c20bc..98775d317b 100644
--- a/ext/mcrypt/tests/mcrypt_encrypt_error.phpt
+++ b/ext/mcrypt/tests/mcrypt_encrypt_error.phpt
@@ -41,11 +41,15 @@ var_dump( mcrypt_encrypt($cipher, $key, $data) );
-- Testing mcrypt_encrypt() function with more than expected no. of arguments --
+Deprecated: Function mcrypt_encrypt() is deprecated in %s%emcrypt_encrypt_error.php on line 19
+
Warning: mcrypt_encrypt() expects at most 5 parameters, 6 given in %s on line %d
NULL
-- Testing mcrypt_encrypt() function with less than expected no. of arguments --
+Deprecated: Function mcrypt_encrypt() is deprecated in %s%emcrypt_encrypt_error.php on line 26
+
Warning: mcrypt_encrypt() expects at least 4 parameters, 3 given in %s on line %d
NULL
===DONE===
diff --git a/ext/mcrypt/tests/mcrypt_encrypt_variation1.phpt b/ext/mcrypt/tests/mcrypt_encrypt_variation1.phpt
index fdbd901a51..88b6edef3d 100644
--- a/ext/mcrypt/tests/mcrypt_encrypt_variation1.phpt
+++ b/ext/mcrypt/tests/mcrypt_encrypt_variation1.phpt
@@ -124,106 +124,132 @@ fclose($fp);
*** Testing mcrypt_encrypt() : usage variation ***
--int 0--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation1.php(107)
Error: 2 - mcrypt_encrypt(): Module initialization failed, %s(%d)
bool(false)
--int 1--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation1.php(107)
Error: 2 - mcrypt_encrypt(): Module initialization failed, %s(%d)
bool(false)
--int 12345--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation1.php(107)
Error: 2 - mcrypt_encrypt(): Module initialization failed, %s(%d)
bool(false)
--int -12345--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation1.php(107)
Error: 2 - mcrypt_encrypt(): Module initialization failed, %s(%d)
bool(false)
--float 10.5--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation1.php(107)
Error: 2 - mcrypt_encrypt(): Module initialization failed, %s(%d)
bool(false)
--float -10.5--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation1.php(107)
Error: 2 - mcrypt_encrypt(): Module initialization failed, %s(%d)
bool(false)
--float 12.3456789000e10--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation1.php(107)
Error: 2 - mcrypt_encrypt(): Module initialization failed, %s(%d)
bool(false)
--float -12.3456789000e10--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation1.php(107)
Error: 2 - mcrypt_encrypt(): Module initialization failed, %s(%d)
bool(false)
--float .5--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation1.php(107)
Error: 2 - mcrypt_encrypt(): Module initialization failed, %s(%d)
bool(false)
--empty array--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation1.php(107)
Error: 2 - mcrypt_encrypt() expects parameter 1 to be string, array given, %s(%d)
NULL
--int indexed array--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation1.php(107)
Error: 2 - mcrypt_encrypt() expects parameter 1 to be string, array given, %s(%d)
NULL
--associative array--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation1.php(107)
Error: 2 - mcrypt_encrypt() expects parameter 1 to be string, array given, %s(%d)
NULL
--nested arrays--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation1.php(107)
Error: 2 - mcrypt_encrypt() expects parameter 1 to be string, array given, %s(%d)
NULL
--uppercase NULL--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation1.php(107)
Error: 2 - mcrypt_encrypt(): Module initialization failed, %s(%d)
bool(false)
--lowercase null--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation1.php(107)
Error: 2 - mcrypt_encrypt(): Module initialization failed, %s(%d)
bool(false)
--lowercase true--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation1.php(107)
Error: 2 - mcrypt_encrypt(): Module initialization failed, %s(%d)
bool(false)
--lowercase false--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation1.php(107)
Error: 2 - mcrypt_encrypt(): Module initialization failed, %s(%d)
bool(false)
--uppercase TRUE--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation1.php(107)
Error: 2 - mcrypt_encrypt(): Module initialization failed, %s(%d)
bool(false)
--uppercase FALSE--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation1.php(107)
Error: 2 - mcrypt_encrypt(): Module initialization failed, %s(%d)
bool(false)
--empty string DQ--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation1.php(107)
Error: 2 - mcrypt_encrypt(): Module initialization failed, %s(%d)
bool(false)
--empty string SQ--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation1.php(107)
Error: 2 - mcrypt_encrypt(): Module initialization failed, %s(%d)
bool(false)
--instance of classWithToString--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation1.php(107)
Error: 2 - mcrypt_encrypt(): Module initialization failed, %s(%d)
bool(false)
--instance of classWithoutToString--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation1.php(107)
Error: 2 - mcrypt_encrypt() expects parameter 1 to be string, object given, %s(%d)
NULL
--undefined var--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation1.php(107)
Error: 2 - mcrypt_encrypt(): Module initialization failed, %s(%d)
bool(false)
--unset var--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation1.php(107)
Error: 2 - mcrypt_encrypt(): Module initialization failed, %s(%d)
bool(false)
--resource--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation1.php(107)
Error: 2 - mcrypt_encrypt() expects parameter 1 to be string, resource given, %s(%d)
NULL
===DONE===
diff --git a/ext/mcrypt/tests/mcrypt_encrypt_variation2.phpt b/ext/mcrypt/tests/mcrypt_encrypt_variation2.phpt
index b1bf7f74bd..fa0cd81665 100644
--- a/ext/mcrypt/tests/mcrypt_encrypt_variation2.phpt
+++ b/ext/mcrypt/tests/mcrypt_encrypt_variation2.phpt
@@ -124,106 +124,132 @@ fclose($fp);
*** Testing mcrypt_encrypt() : usage variation ***
--int 0--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation2.php(107)
Error: 2 - mcrypt_encrypt(): Key of size %d not supported by this algorithm. Only keys of size 24 supported, %s(%d)
string(0) ""
--int 1--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation2.php(107)
Error: 2 - mcrypt_encrypt(): Key of size %d not supported by this algorithm. Only keys of size 24 supported, %s(%d)
string(0) ""
--int 12345--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation2.php(107)
Error: 2 - mcrypt_encrypt(): Key of size %d not supported by this algorithm. Only keys of size 24 supported, %s(%d)
string(0) ""
--int -12345--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation2.php(107)
Error: 2 - mcrypt_encrypt(): Key of size %d not supported by this algorithm. Only keys of size 24 supported, %s(%d)
string(0) ""
--float 10.5--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation2.php(107)
Error: 2 - mcrypt_encrypt(): Key of size %d not supported by this algorithm. Only keys of size 24 supported, %s(%d)
string(0) ""
--float -10.5--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation2.php(107)
Error: 2 - mcrypt_encrypt(): Key of size %d not supported by this algorithm. Only keys of size 24 supported, %s(%d)
string(0) ""
--float 12.3456789000e10--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation2.php(107)
Error: 2 - mcrypt_encrypt(): Key of size %d not supported by this algorithm. Only keys of size 24 supported, %s(%d)
string(0) ""
--float -12.3456789000e10--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation2.php(107)
Error: 2 - mcrypt_encrypt(): Key of size %d not supported by this algorithm. Only keys of size 24 supported, %s(%d)
string(0) ""
--float .5--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation2.php(107)
Error: 2 - mcrypt_encrypt(): Key of size %d not supported by this algorithm. Only keys of size 24 supported, %s(%d)
string(0) ""
--empty array--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation2.php(107)
Error: 2 - mcrypt_encrypt() expects parameter 2 to be string, array given, %s(%d)
string(0) ""
--int indexed array--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation2.php(107)
Error: 2 - mcrypt_encrypt() expects parameter 2 to be string, array given, %s(%d)
string(0) ""
--associative array--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation2.php(107)
Error: 2 - mcrypt_encrypt() expects parameter 2 to be string, array given, %s(%d)
string(0) ""
--nested arrays--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation2.php(107)
Error: 2 - mcrypt_encrypt() expects parameter 2 to be string, array given, %s(%d)
string(0) ""
--uppercase NULL--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation2.php(107)
Error: 2 - mcrypt_encrypt(): Key of size %d not supported by this algorithm. Only keys of size 24 supported, %s(%d)
string(0) ""
--lowercase null--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation2.php(107)
Error: 2 - mcrypt_encrypt(): Key of size %d not supported by this algorithm. Only keys of size 24 supported, %s(%d)
string(0) ""
--lowercase true--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation2.php(107)
Error: 2 - mcrypt_encrypt(): Key of size %d not supported by this algorithm. Only keys of size 24 supported, %s(%d)
string(0) ""
--lowercase false--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation2.php(107)
Error: 2 - mcrypt_encrypt(): Key of size %d not supported by this algorithm. Only keys of size 24 supported, %s(%d)
string(0) ""
--uppercase TRUE--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation2.php(107)
Error: 2 - mcrypt_encrypt(): Key of size %d not supported by this algorithm. Only keys of size 24 supported, %s(%d)
string(0) ""
--uppercase FALSE--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation2.php(107)
Error: 2 - mcrypt_encrypt(): Key of size %d not supported by this algorithm. Only keys of size 24 supported, %s(%d)
string(0) ""
--empty string DQ--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation2.php(107)
Error: 2 - mcrypt_encrypt(): Key of size %d not supported by this algorithm. Only keys of size 24 supported, %s(%d)
string(0) ""
--empty string SQ--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation2.php(107)
Error: 2 - mcrypt_encrypt(): Key of size %d not supported by this algorithm. Only keys of size 24 supported, %s(%d)
string(0) ""
--instance of classWithToString--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation2.php(107)
Error: 2 - mcrypt_encrypt(): Key of size %d not supported by this algorithm. Only keys of size 24 supported, %s(%d)
string(0) ""
--instance of classWithoutToString--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation2.php(107)
Error: 2 - mcrypt_encrypt() expects parameter 2 to be string, object given, %s(%d)
string(0) ""
--undefined var--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation2.php(107)
Error: 2 - mcrypt_encrypt(): Key of size %d not supported by this algorithm. Only keys of size 24 supported, %s(%d)
string(0) ""
--unset var--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation2.php(107)
Error: 2 - mcrypt_encrypt(): Key of size %d not supported by this algorithm. Only keys of size 24 supported, %s(%d)
string(0) ""
--resource--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation2.php(107)
Error: 2 - mcrypt_encrypt() expects parameter 2 to be string, resource given, %s(%d)
string(0) ""
===DONE===
diff --git a/ext/mcrypt/tests/mcrypt_encrypt_variation3.phpt b/ext/mcrypt/tests/mcrypt_encrypt_variation3.phpt
index 9fb8e7c550..17698d20d9 100644
--- a/ext/mcrypt/tests/mcrypt_encrypt_variation3.phpt
+++ b/ext/mcrypt/tests/mcrypt_encrypt_variation3.phpt
@@ -124,86 +124,112 @@ fclose($fp);
*** Testing mcrypt_encrypt() : usage variation ***
--int 0--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation3.php(107)
string(16) "51dc9cd9179b718b"
--int 1--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation3.php(107)
string(16) "619c335f8c4f9cbf"
--int 12345--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation3.php(107)
string(16) "b1258d67ab73de00"
--int -12345--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation3.php(107)
string(16) "8eecf134443bd6b9"
--float 10.5--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation3.php(107)
string(16) "34b5750a793baff5"
--float -10.5--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation3.php(107)
string(16) "7a605f2aacc8a11d"
--float 12.3456789000e10--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation3.php(107)
string(32) "74a0d7026ae586f476d4b17808851e86"
--float -12.3456789000e10--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation3.php(107)
string(32) "bfb155997017986c01090afebd62c7ca"
--float .5--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation3.php(107)
string(16) "cc60ac201164b6c7"
--empty array--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation3.php(107)
Error: 2 - mcrypt_encrypt() expects parameter 3 to be string, array given, %s(%d)
string(0) ""
--int indexed array--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation3.php(107)
Error: 2 - mcrypt_encrypt() expects parameter 3 to be string, array given, %s(%d)
string(0) ""
--associative array--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation3.php(107)
Error: 2 - mcrypt_encrypt() expects parameter 3 to be string, array given, %s(%d)
string(0) ""
--nested arrays--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation3.php(107)
Error: 2 - mcrypt_encrypt() expects parameter 3 to be string, array given, %s(%d)
string(0) ""
--uppercase NULL--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation3.php(107)
string(16) "6ece228c41457539"
--lowercase null--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation3.php(107)
string(16) "6ece228c41457539"
--lowercase true--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation3.php(107)
string(16) "619c335f8c4f9cbf"
--lowercase false--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation3.php(107)
string(16) "6ece228c41457539"
--uppercase TRUE--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation3.php(107)
string(16) "619c335f8c4f9cbf"
--uppercase FALSE--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation3.php(107)
string(16) "6ece228c41457539"
--empty string DQ--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation3.php(107)
string(16) "6ece228c41457539"
--empty string SQ--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation3.php(107)
string(16) "6ece228c41457539"
--instance of classWithToString--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation3.php(107)
string(32) "749c3b4d16731d98370128754b7c930f"
--instance of classWithoutToString--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation3.php(107)
Error: 2 - mcrypt_encrypt() expects parameter 3 to be string, object given, %s(%d)
string(0) ""
--undefined var--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation3.php(107)
string(16) "6ece228c41457539"
--unset var--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation3.php(107)
string(16) "6ece228c41457539"
--resource--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation3.php(107)
Error: 2 - mcrypt_encrypt() expects parameter 3 to be string, resource given, %s(%d)
string(0) ""
===DONE===
diff --git a/ext/mcrypt/tests/mcrypt_encrypt_variation4.phpt b/ext/mcrypt/tests/mcrypt_encrypt_variation4.phpt
index a041ab437e..363defdcf0 100644
--- a/ext/mcrypt/tests/mcrypt_encrypt_variation4.phpt
+++ b/ext/mcrypt/tests/mcrypt_encrypt_variation4.phpt
@@ -124,106 +124,132 @@ fclose($fp);
*** Testing mcrypt_encrypt() : usage variation ***
--int 0--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation4.php(107)
Error: 2 - mcrypt_encrypt(): Module initialization failed, %s(%d)
bool(false)
--int 1--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation4.php(107)
Error: 2 - mcrypt_encrypt(): Module initialization failed, %s(%d)
bool(false)
--int 12345--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation4.php(107)
Error: 2 - mcrypt_encrypt(): Module initialization failed, %s(%d)
bool(false)
--int -12345--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation4.php(107)
Error: 2 - mcrypt_encrypt(): Module initialization failed, %s(%d)
bool(false)
--float 10.5--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation4.php(107)
Error: 2 - mcrypt_encrypt(): Module initialization failed, %s(%d)
bool(false)
--float -10.5--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation4.php(107)
Error: 2 - mcrypt_encrypt(): Module initialization failed, %s(%d)
bool(false)
--float 12.3456789000e10--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation4.php(107)
Error: 2 - mcrypt_encrypt(): Module initialization failed, %s(%d)
bool(false)
--float -12.3456789000e10--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation4.php(107)
Error: 2 - mcrypt_encrypt(): Module initialization failed, %s(%d)
bool(false)
--float .5--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation4.php(107)
Error: 2 - mcrypt_encrypt(): Module initialization failed, %s(%d)
bool(false)
--empty array--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation4.php(107)
Error: 2 - mcrypt_encrypt() expects parameter 4 to be string, array given, %s(%d)
NULL
--int indexed array--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation4.php(107)
Error: 2 - mcrypt_encrypt() expects parameter 4 to be string, array given, %s(%d)
NULL
--associative array--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation4.php(107)
Error: 2 - mcrypt_encrypt() expects parameter 4 to be string, array given, %s(%d)
NULL
--nested arrays--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation4.php(107)
Error: 2 - mcrypt_encrypt() expects parameter 4 to be string, array given, %s(%d)
NULL
--uppercase NULL--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation4.php(107)
Error: 2 - mcrypt_encrypt(): Module initialization failed, %s(%d)
bool(false)
--lowercase null--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation4.php(107)
Error: 2 - mcrypt_encrypt(): Module initialization failed, %s(%d)
bool(false)
--lowercase true--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation4.php(107)
Error: 2 - mcrypt_encrypt(): Module initialization failed, %s(%d)
bool(false)
--lowercase false--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation4.php(107)
Error: 2 - mcrypt_encrypt(): Module initialization failed, %s(%d)
bool(false)
--uppercase TRUE--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation4.php(107)
Error: 2 - mcrypt_encrypt(): Module initialization failed, %s(%d)
bool(false)
--uppercase FALSE--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation4.php(107)
Error: 2 - mcrypt_encrypt(): Module initialization failed, %s(%d)
bool(false)
--empty string DQ--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation4.php(107)
Error: 2 - mcrypt_encrypt(): Module initialization failed, %s(%d)
bool(false)
--empty string SQ--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation4.php(107)
Error: 2 - mcrypt_encrypt(): Module initialization failed, %s(%d)
bool(false)
--instance of classWithToString--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation4.php(107)
Error: 2 - mcrypt_encrypt(): Module initialization failed, %s(%d)
bool(false)
--instance of classWithoutToString--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation4.php(107)
Error: 2 - mcrypt_encrypt() expects parameter 4 to be string, object given, %s(%d)
NULL
--undefined var--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation4.php(107)
Error: 2 - mcrypt_encrypt(): Module initialization failed, %s(%d)
bool(false)
--unset var--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation4.php(107)
Error: 2 - mcrypt_encrypt(): Module initialization failed, %s(%d)
bool(false)
--resource--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation4.php(107)
Error: 2 - mcrypt_encrypt() expects parameter 4 to be string, resource given, %s(%d)
NULL
===DONE===
diff --git a/ext/mcrypt/tests/mcrypt_encrypt_variation5.phpt b/ext/mcrypt/tests/mcrypt_encrypt_variation5.phpt
index fdba526840..7ad6dd5f9a 100644
--- a/ext/mcrypt/tests/mcrypt_encrypt_variation5.phpt
+++ b/ext/mcrypt/tests/mcrypt_encrypt_variation5.phpt
@@ -125,106 +125,132 @@ fclose($fp);
*** Testing mcrypt_encrypt() : usage variation ***
--int 0--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation5.php(108)
Error: 2 - mcrypt_encrypt(): Received initialization vector of size %d, but size 8 is required for this encryption mode, %s(%d)
string(0) ""
--int 1--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation5.php(108)
Error: 2 - mcrypt_encrypt(): Received initialization vector of size %d, but size 8 is required for this encryption mode, %s(%d)
string(0) ""
--int 12345--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation5.php(108)
Error: 2 - mcrypt_encrypt(): Received initialization vector of size %d, but size 8 is required for this encryption mode, %s(%d)
string(0) ""
--int -12345--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation5.php(108)
Error: 2 - mcrypt_encrypt(): Received initialization vector of size %d, but size 8 is required for this encryption mode, %s(%d)
string(0) ""
--float 10.5--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation5.php(108)
Error: 2 - mcrypt_encrypt(): Received initialization vector of size %d, but size 8 is required for this encryption mode, %s(%d)
string(0) ""
--float -10.5--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation5.php(108)
Error: 2 - mcrypt_encrypt(): Received initialization vector of size %d, but size 8 is required for this encryption mode, %s(%d)
string(0) ""
--float 12.3456789000e10--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation5.php(108)
Error: 2 - mcrypt_encrypt(): Received initialization vector of size %d, but size 8 is required for this encryption mode, %s(%d)
string(0) ""
--float -12.3456789000e10--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation5.php(108)
Error: 2 - mcrypt_encrypt(): Received initialization vector of size %d, but size 8 is required for this encryption mode, %s(%d)
string(0) ""
--float .5--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation5.php(108)
Error: 2 - mcrypt_encrypt(): Received initialization vector of size %d, but size 8 is required for this encryption mode, %s(%d)
string(0) ""
--empty array--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation5.php(108)
Error: 2 - mcrypt_encrypt() expects parameter 5 to be string, array given, %s(%d)
string(0) ""
--int indexed array--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation5.php(108)
Error: 2 - mcrypt_encrypt() expects parameter 5 to be string, array given, %s(%d)
string(0) ""
--associative array--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation5.php(108)
Error: 2 - mcrypt_encrypt() expects parameter 5 to be string, array given, %s(%d)
string(0) ""
--nested arrays--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation5.php(108)
Error: 2 - mcrypt_encrypt() expects parameter 5 to be string, array given, %s(%d)
string(0) ""
--uppercase NULL--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation5.php(108)
Error: 2 - mcrypt_encrypt(): Received initialization vector of size %d, but size 8 is required for this encryption mode, %s(%d)
string(0) ""
--lowercase null--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation5.php(108)
Error: 2 - mcrypt_encrypt(): Received initialization vector of size %d, but size 8 is required for this encryption mode, %s(%d)
string(0) ""
--lowercase true--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation5.php(108)
Error: 2 - mcrypt_encrypt(): Received initialization vector of size %d, but size 8 is required for this encryption mode, %s(%d)
string(0) ""
--lowercase false--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation5.php(108)
Error: 2 - mcrypt_encrypt(): Received initialization vector of size %d, but size 8 is required for this encryption mode, %s(%d)
string(0) ""
--uppercase TRUE--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation5.php(108)
Error: 2 - mcrypt_encrypt(): Received initialization vector of size %d, but size 8 is required for this encryption mode, %s(%d)
string(0) ""
--uppercase FALSE--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation5.php(108)
Error: 2 - mcrypt_encrypt(): Received initialization vector of size %d, but size 8 is required for this encryption mode, %s(%d)
string(0) ""
--empty string DQ--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation5.php(108)
Error: 2 - mcrypt_encrypt(): Received initialization vector of size %d, but size 8 is required for this encryption mode, %s(%d)
string(0) ""
--empty string SQ--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation5.php(108)
Error: 2 - mcrypt_encrypt(): Received initialization vector of size %d, but size 8 is required for this encryption mode, %s(%d)
string(0) ""
--instance of classWithToString--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation5.php(108)
Error: 2 - mcrypt_encrypt(): Received initialization vector of size %d, but size 8 is required for this encryption mode, %s(%d)
string(0) ""
--instance of classWithoutToString--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation5.php(108)
Error: 2 - mcrypt_encrypt() expects parameter 5 to be string, object given, %s(%d)
string(0) ""
--undefined var--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation5.php(108)
Error: 2 - mcrypt_encrypt(): Received initialization vector of size %d, but size 8 is required for this encryption mode, %s(%d)
string(0) ""
--unset var--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation5.php(108)
Error: 2 - mcrypt_encrypt(): Received initialization vector of size %d, but size 8 is required for this encryption mode, %s(%d)
string(0) ""
--resource--
+Error: 8192 - Function mcrypt_encrypt() is deprecated, %s%emcrypt_encrypt_variation5.php(108)
Error: 2 - mcrypt_encrypt() expects parameter 5 to be string, resource given, %s(%d)
string(0) ""
===DONE===
diff --git a/ext/mcrypt/tests/mcrypt_filters.phpt b/ext/mcrypt/tests/mcrypt_filters.phpt
index f528219500..c8146ea984 100644
--- a/ext/mcrypt/tests/mcrypt_filters.phpt
+++ b/ext/mcrypt/tests/mcrypt_filters.phpt
@@ -37,5 +37,9 @@ echo $data."\n";
--EXPECTF--
FOUND
FOUND
+
+Deprecated: stream_filter_append(): mcrypt and mdecrypt stream filters have been deprecated in %s%emcrypt_filters.php on line 17
32e14bd3c31f2bd666e4290ebdb166a7
+
+Deprecated: stream_filter_append(): mcrypt and mdecrypt stream filters have been deprecated in %s%emcrypt_filters.php on line 24
Secret secret secret data \ No newline at end of file
diff --git a/ext/mcrypt/tests/mcrypt_get_block_size.phpt b/ext/mcrypt/tests/mcrypt_get_block_size.phpt
index bf1f24df3b..9028b22668 100644
--- a/ext/mcrypt/tests/mcrypt_get_block_size.phpt
+++ b/ext/mcrypt/tests/mcrypt_get_block_size.phpt
@@ -7,7 +7,12 @@ mcrypt_get_block_size
var_dump(mcrypt_get_block_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC));
var_dump(mcrypt_get_block_size(MCRYPT_3DES, MCRYPT_MODE_CBC));
var_dump(mcrypt_get_block_size(MCRYPT_WAKE, MCRYPT_MODE_STREAM));
---EXPECT--
+--EXPECTF--
+Deprecated: Function mcrypt_get_block_size() is deprecated in %s%emcrypt_get_block_size.php on line 2
int(32)
+
+Deprecated: Function mcrypt_get_block_size() is deprecated in %s%emcrypt_get_block_size.php on line 3
int(8)
+
+Deprecated: Function mcrypt_get_block_size() is deprecated in %s%emcrypt_get_block_size.php on line 4
int(1)
diff --git a/ext/mcrypt/tests/mcrypt_get_cipher_name.phpt b/ext/mcrypt/tests/mcrypt_get_cipher_name.phpt
index 9d4961ae8f..6d46fe1bf2 100644
--- a/ext/mcrypt/tests/mcrypt_get_cipher_name.phpt
+++ b/ext/mcrypt/tests/mcrypt_get_cipher_name.phpt
@@ -8,8 +8,15 @@ echo mcrypt_get_cipher_name(MCRYPT_RIJNDAEL_256) . "\n";
echo mcrypt_get_cipher_name(MCRYPT_RC2) . "\n";
echo mcrypt_get_cipher_name(MCRYPT_ARCFOUR) . "\n";
echo mcrypt_get_cipher_name(MCRYPT_WAKE) . "\n";
---EXPECT--
+--EXPECTF--
+Deprecated: Function mcrypt_get_cipher_name() is deprecated in %s%emcrypt_get_cipher_name.php on line 2
Rijndael-256
+
+Deprecated: Function mcrypt_get_cipher_name() is deprecated in %s%emcrypt_get_cipher_name.php on line 3
RC2
+
+Deprecated: Function mcrypt_get_cipher_name() is deprecated in %s%emcrypt_get_cipher_name.php on line 4
RC4
+
+Deprecated: Function mcrypt_get_cipher_name() is deprecated in %s%emcrypt_get_cipher_name.php on line 5
WAKE
diff --git a/ext/mcrypt/tests/mcrypt_get_iv_size.phpt b/ext/mcrypt/tests/mcrypt_get_iv_size.phpt
index ad3599745c..ac3e4148cd 100644
--- a/ext/mcrypt/tests/mcrypt_get_iv_size.phpt
+++ b/ext/mcrypt/tests/mcrypt_get_iv_size.phpt
@@ -9,9 +9,16 @@ var_dump(mcrypt_get_iv_size(MCRYPT_3DES, MCRYPT_MODE_CBC));
var_dump(mcrypt_get_iv_size(MCRYPT_WAKE, MCRYPT_MODE_STREAM));
var_dump(mcrypt_get_iv_size(MCRYPT_XTEA, MCRYPT_MODE_STREAM));
--EXPECTF--
+Deprecated: Function mcrypt_get_iv_size() is deprecated in %s%emcrypt_get_iv_size.php on line 2
int(32)
+
+Deprecated: Function mcrypt_get_iv_size() is deprecated in %s%emcrypt_get_iv_size.php on line 3
int(8)
+
+Deprecated: Function mcrypt_get_iv_size() is deprecated in %s%emcrypt_get_iv_size.php on line 4
int(0)
+Deprecated: Function mcrypt_get_iv_size() is deprecated in %s%emcrypt_get_iv_size.php on line 5
+
Warning: mcrypt_get_iv_size(): Module initialization failed in %s on line %d
bool(false) \ No newline at end of file
diff --git a/ext/mcrypt/tests/mcrypt_get_key_size.phpt b/ext/mcrypt/tests/mcrypt_get_key_size.phpt
index 930cf4d640..7958efc2f3 100644
--- a/ext/mcrypt/tests/mcrypt_get_key_size.phpt
+++ b/ext/mcrypt/tests/mcrypt_get_key_size.phpt
@@ -7,7 +7,12 @@ mcrypt_get_key_size
var_dump(mcrypt_get_key_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC));
var_dump(mcrypt_get_key_size(MCRYPT_3DES, MCRYPT_MODE_CBC));
var_dump(mcrypt_get_key_size(MCRYPT_WAKE, MCRYPT_MODE_STREAM));
---EXPECT--
+--EXPECTF--
+Deprecated: Function mcrypt_get_key_size() is deprecated in %s%emcrypt_get_key_size.php on line 2
int(32)
+
+Deprecated: Function mcrypt_get_key_size() is deprecated in %s%emcrypt_get_key_size.php on line 3
int(24)
+
+Deprecated: Function mcrypt_get_key_size() is deprecated in %s%emcrypt_get_key_size.php on line 4
int(32)
diff --git a/ext/mcrypt/tests/mcrypt_list_algorithms.phpt b/ext/mcrypt/tests/mcrypt_list_algorithms.phpt
index 9baf9a64de..6a7bfd7c70 100644
--- a/ext/mcrypt/tests/mcrypt_list_algorithms.phpt
+++ b/ext/mcrypt/tests/mcrypt_list_algorithms.phpt
@@ -9,7 +9,8 @@ foreach (mcrypt_list_algorithms() as $algo) {
echo "FOUND\n";
}
}
---EXPECT--
+--EXPECTF--
+Deprecated: Function mcrypt_list_algorithms() is deprecated in %s%emcrypt_list_algorithms.php on line 2
FOUND
FOUND
FOUND
diff --git a/ext/mcrypt/tests/mcrypt_list_modes.phpt b/ext/mcrypt/tests/mcrypt_list_modes.phpt
index 0e5a6ae87b..8cebbcbb0c 100644
--- a/ext/mcrypt/tests/mcrypt_list_modes.phpt
+++ b/ext/mcrypt/tests/mcrypt_list_modes.phpt
@@ -5,7 +5,8 @@ mcrypt_list_modes
--FILE--
<?php
var_dump(mcrypt_list_modes());
---EXPECT--
+--EXPECTF--
+Deprecated: Function mcrypt_list_modes() is deprecated in %s%emcrypt_list_modes.php on line 2
array(8) {
[0]=>
string(3) "cbc"
diff --git a/ext/mcrypt/tests/mcrypt_module_get_algo_block_size.phpt b/ext/mcrypt/tests/mcrypt_module_get_algo_block_size.phpt
index c89a44ad8a..2ed4dbb7fd 100644
--- a/ext/mcrypt/tests/mcrypt_module_get_algo_block_size.phpt
+++ b/ext/mcrypt/tests/mcrypt_module_get_algo_block_size.phpt
@@ -8,12 +8,24 @@ var_dump(mcrypt_module_get_algo_block_size(MCRYPT_RIJNDAEL_256));
var_dump(mcrypt_module_get_algo_block_size(MCRYPT_RIJNDAEL_192));
var_dump(mcrypt_module_get_algo_block_size(MCRYPT_RC2));
var_dump(mcrypt_module_get_algo_block_size(MCRYPT_XTEA));
-var_dump(mcrypt_module_get_algo_block_size(MCRYPT_CAST_128));
+var_dump(mcrypt_module_get_algo_block_size(MCRYPT_CAST_256));
var_dump(mcrypt_module_get_algo_block_size(MCRYPT_BLOWFISH));
---EXPECT--
+?>
+--EXPECTF--
+Deprecated: Function mcrypt_module_get_algo_block_size() is deprecated in %s%emcrypt_module_get_algo_block_size.php on line 2
int(32)
+
+Deprecated: Function mcrypt_module_get_algo_block_size() is deprecated in %s%emcrypt_module_get_algo_block_size.php on line 3
int(24)
+
+Deprecated: Function mcrypt_module_get_algo_block_size() is deprecated in %s%emcrypt_module_get_algo_block_size.php on line 4
int(8)
+
+Deprecated: Function mcrypt_module_get_algo_block_size() is deprecated in %s%emcrypt_module_get_algo_block_size.php on line 5
int(8)
-int(8)
+
+Deprecated: Function mcrypt_module_get_algo_block_size() is deprecated in %s%emcrypt_module_get_algo_block_size.php on line 6
+int(16)
+
+Deprecated: Function mcrypt_module_get_algo_block_size() is deprecated in %s%emcrypt_module_get_algo_block_size.php on line 7
int(8)
diff --git a/ext/mcrypt/tests/mcrypt_module_get_algo_key_size.phpt b/ext/mcrypt/tests/mcrypt_module_get_algo_key_size.phpt
index 7d3841f3e2..5ab791d772 100644
--- a/ext/mcrypt/tests/mcrypt_module_get_algo_key_size.phpt
+++ b/ext/mcrypt/tests/mcrypt_module_get_algo_key_size.phpt
@@ -8,12 +8,24 @@ var_dump(mcrypt_module_get_algo_key_size(MCRYPT_RIJNDAEL_256));
var_dump(mcrypt_module_get_algo_key_size(MCRYPT_RIJNDAEL_192));
var_dump(mcrypt_module_get_algo_key_size(MCRYPT_RC2));
var_dump(mcrypt_module_get_algo_key_size(MCRYPT_XTEA));
-var_dump(mcrypt_module_get_algo_key_size(MCRYPT_CAST_128));
+var_dump(mcrypt_module_get_algo_key_size(MCRYPT_CAST_256));
var_dump(mcrypt_module_get_algo_key_size(MCRYPT_BLOWFISH));
---EXPECT--
+?>
+--EXPECTF--
+Deprecated: Function mcrypt_module_get_algo_key_size() is deprecated in %s%emcrypt_module_get_algo_key_size.php on line 2
int(32)
+
+Deprecated: Function mcrypt_module_get_algo_key_size() is deprecated in %s%emcrypt_module_get_algo_key_size.php on line 3
int(32)
+
+Deprecated: Function mcrypt_module_get_algo_key_size() is deprecated in %s%emcrypt_module_get_algo_key_size.php on line 4
int(128)
+
+Deprecated: Function mcrypt_module_get_algo_key_size() is deprecated in %s%emcrypt_module_get_algo_key_size.php on line 5
int(16)
-int(16)
+
+Deprecated: Function mcrypt_module_get_algo_key_size() is deprecated in %s%emcrypt_module_get_algo_key_size.php on line 6
+int(32)
+
+Deprecated: Function mcrypt_module_get_algo_key_size() is deprecated in %s%emcrypt_module_get_algo_key_size.php on line 7
int(56)
diff --git a/ext/mcrypt/tests/mcrypt_module_get_supported_key_sizes.phpt b/ext/mcrypt/tests/mcrypt_module_get_supported_key_sizes.phpt
index bf45bb488e..c850784b8a 100644
--- a/ext/mcrypt/tests/mcrypt_module_get_supported_key_sizes.phpt
+++ b/ext/mcrypt/tests/mcrypt_module_get_supported_key_sizes.phpt
@@ -6,7 +6,8 @@ mcrypt_module_get_supported_key_sizes
<?php
var_dump(mcrypt_module_get_supported_key_sizes(MCRYPT_RIJNDAEL_256));
var_dump(mcrypt_module_get_supported_key_sizes(MCRYPT_RC2));
---EXPECT--
+--EXPECTF--
+Deprecated: Function mcrypt_module_get_supported_key_sizes() is deprecated in %s%emcrypt_module_get_supported_key_sizes.php on line 2
array(3) {
[0]=>
int(16)
@@ -15,5 +16,7 @@ array(3) {
[2]=>
int(32)
}
+
+Deprecated: Function mcrypt_module_get_supported_key_sizes() is deprecated in %s%emcrypt_module_get_supported_key_sizes.php on line 3
array(0) {
} \ No newline at end of file
diff --git a/ext/mcrypt/tests/mcrypt_module_is_block_algorithm.phpt b/ext/mcrypt/tests/mcrypt_module_is_block_algorithm.phpt
index 2cfc87ddf5..27b965b41f 100644
--- a/ext/mcrypt/tests/mcrypt_module_is_block_algorithm.phpt
+++ b/ext/mcrypt/tests/mcrypt_module_is_block_algorithm.phpt
@@ -8,8 +8,15 @@ var_dump(mcrypt_module_is_block_algorithm(MCRYPT_RIJNDAEL_128));
var_dump(mcrypt_module_is_block_algorithm(MCRYPT_DES));
var_dump(mcrypt_module_is_block_algorithm(MCRYPT_WAKE));
var_dump(mcrypt_module_is_block_algorithm(MCRYPT_XTEA));
---EXPECT--
+--EXPECTF--
+Deprecated: Function mcrypt_module_is_block_algorithm() is deprecated in %s%emcrypt_module_is_block_algorithm.php on line 2
bool(true)
+
+Deprecated: Function mcrypt_module_is_block_algorithm() is deprecated in %s%emcrypt_module_is_block_algorithm.php on line 3
bool(true)
+
+Deprecated: Function mcrypt_module_is_block_algorithm() is deprecated in %s%emcrypt_module_is_block_algorithm.php on line 4
bool(false)
+
+Deprecated: Function mcrypt_module_is_block_algorithm() is deprecated in %s%emcrypt_module_is_block_algorithm.php on line 5
bool(true) \ No newline at end of file
diff --git a/ext/mcrypt/tests/mcrypt_module_is_block_algorithm_mode.phpt b/ext/mcrypt/tests/mcrypt_module_is_block_algorithm_mode.phpt
index 2924551414..57d06f4df7 100644
--- a/ext/mcrypt/tests/mcrypt_module_is_block_algorithm_mode.phpt
+++ b/ext/mcrypt/tests/mcrypt_module_is_block_algorithm_mode.phpt
@@ -8,8 +8,15 @@ var_dump(mcrypt_module_is_block_algorithm_mode(MCRYPT_MODE_CBC));
var_dump(mcrypt_module_is_block_algorithm_mode(MCRYPT_MODE_ECB));
var_dump(mcrypt_module_is_block_algorithm_mode(MCRYPT_MODE_STREAM));
var_dump(mcrypt_module_is_block_algorithm_mode(MCRYPT_MODE_OFB));
---EXPECT--
+--EXPECTF--
+Deprecated: Function mcrypt_module_is_block_algorithm_mode() is deprecated in %s%emcrypt_module_is_block_algorithm_mode.php on line 2
bool(true)
+
+Deprecated: Function mcrypt_module_is_block_algorithm_mode() is deprecated in %s%emcrypt_module_is_block_algorithm_mode.php on line 3
bool(true)
+
+Deprecated: Function mcrypt_module_is_block_algorithm_mode() is deprecated in %s%emcrypt_module_is_block_algorithm_mode.php on line 4
bool(false)
+
+Deprecated: Function mcrypt_module_is_block_algorithm_mode() is deprecated in %s%emcrypt_module_is_block_algorithm_mode.php on line 5
bool(true) \ No newline at end of file
diff --git a/ext/mcrypt/tests/mcrypt_module_is_block_mode.phpt b/ext/mcrypt/tests/mcrypt_module_is_block_mode.phpt
index 662279f596..117c1dd593 100644
--- a/ext/mcrypt/tests/mcrypt_module_is_block_mode.phpt
+++ b/ext/mcrypt/tests/mcrypt_module_is_block_mode.phpt
@@ -9,9 +9,18 @@ var_dump(mcrypt_module_is_block_mode(MCRYPT_MODE_ECB));
var_dump(mcrypt_module_is_block_mode(MCRYPT_MODE_STREAM));
var_dump(mcrypt_module_is_block_mode(MCRYPT_MODE_NOFB));
var_dump(mcrypt_module_is_block_mode(MCRYPT_MODE_OFB));
---EXPECT--
+--EXPECTF--
+Deprecated: Function mcrypt_module_is_block_mode() is deprecated in %s%emcrypt_module_is_block_mode.php on line 2
bool(true)
+
+Deprecated: Function mcrypt_module_is_block_mode() is deprecated in %s%emcrypt_module_is_block_mode.php on line 3
bool(true)
+
+Deprecated: Function mcrypt_module_is_block_mode() is deprecated in %s%emcrypt_module_is_block_mode.php on line 4
bool(false)
+
+Deprecated: Function mcrypt_module_is_block_mode() is deprecated in %s%emcrypt_module_is_block_mode.php on line 5
bool(false)
+
+Deprecated: Function mcrypt_module_is_block_mode() is deprecated in %s%emcrypt_module_is_block_mode.php on line 6
bool(false) \ No newline at end of file
diff --git a/ext/mcrypt/tests/mcrypt_module_open.phpt b/ext/mcrypt/tests/mcrypt_module_open.phpt
index 92f0d555b3..f8a10616a3 100644
--- a/ext/mcrypt/tests/mcrypt_module_open.phpt
+++ b/ext/mcrypt/tests/mcrypt_module_open.phpt
@@ -8,7 +8,10 @@ var_dump(mcrypt_module_open(MCRYPT_RIJNDAEL_256, '', MCRYPT_MODE_CBC, ''));
mcrypt_module_open('', '', '', '');
--EXPECTF--
+Deprecated: Function mcrypt_module_open() is deprecated in %s%emcrypt_module_open.php on line 2
resource(%d) of type (mcrypt)
+Deprecated: Function mcrypt_module_open() is deprecated in %s%emcrypt_module_open.php on line 3
+
Warning: mcrypt_module_open(): Could not open encryption module in %s on line %d
diff --git a/ext/mcrypt/tests/mcrypt_module_self_test.phpt b/ext/mcrypt/tests/mcrypt_module_self_test.phpt
index cf07bfe6a4..aa53c32173 100644
--- a/ext/mcrypt/tests/mcrypt_module_self_test.phpt
+++ b/ext/mcrypt/tests/mcrypt_module_self_test.phpt
@@ -7,7 +7,12 @@ mcrypt_module_self_test
var_dump(mcrypt_module_self_test(MCRYPT_RIJNDAEL_128));
var_dump(mcrypt_module_self_test(MCRYPT_RC2));
var_dump(mcrypt_module_self_test(''));
---EXPECT--
+--EXPECTF--
+Deprecated: Function mcrypt_module_self_test() is deprecated in %s%emcrypt_module_self_test.php on line 2
bool(true)
+
+Deprecated: Function mcrypt_module_self_test() is deprecated in %s%emcrypt_module_self_test.php on line 3
bool(true)
+
+Deprecated: Function mcrypt_module_self_test() is deprecated in %s%emcrypt_module_self_test.php on line 4
bool(false)
diff --git a/ext/mcrypt/tests/mcrypt_ofb.phpt b/ext/mcrypt/tests/mcrypt_ofb.phpt
index 5d87fcd855..76f7fe61c8 100644
--- a/ext/mcrypt/tests/mcrypt_ofb.phpt
+++ b/ext/mcrypt/tests/mcrypt_ofb.phpt
@@ -18,6 +18,15 @@ echo trim(mcrypt_decrypt($cipher, $key, $enc_data, MCRYPT_MODE_OFB, $iv)) . "\n"
mcrypt_decrypt($cipher, $key, $enc_data, MCRYPT_MODE_OFB);
--EXPECTF--
+Deprecated: Function mcrypt_get_iv_size() is deprecated in %s%emcrypt_ofb.php on line 6
+
+Deprecated: Function mcrypt_create_iv() is deprecated in %s%emcrypt_ofb.php on line 6
+
+Deprecated: Function mcrypt_decrypt() is deprecated in %s%emcrypt_ofb.php on line 7
+
+Deprecated: Function mcrypt_decrypt() is deprecated in %s%emcrypt_ofb.php on line 10
PHP Testfest 2008
+Deprecated: Function mcrypt_decrypt() is deprecated in %s%emcrypt_ofb.php on line 13
+
Warning: mcrypt_decrypt(): Encryption mode requires an initialization vector of size 16 in %s on line %d
diff --git a/ext/mcrypt/tests/mcrypt_rijndael128_128BitKey.phpt b/ext/mcrypt/tests/mcrypt_rijndael128_128BitKey.phpt
index 7889f67851..d9e7d96bbc 100644
--- a/ext/mcrypt/tests/mcrypt_rijndael128_128BitKey.phpt
+++ b/ext/mcrypt/tests/mcrypt_rijndael128_128BitKey.phpt
@@ -72,44 +72,62 @@ foreach ($ivs as $iv) {
key length=0
+Deprecated: Function mcrypt_encrypt() is deprecated in %s%emcrypt_rijndael128_128BitKey.php on line 45
+
Warning: mcrypt_encrypt(): Key of size 0 not supported by this algorithm. Only keys of sizes 16, 24 or 32 supported in %s on line %d
string(0) ""
key length=0
+Deprecated: Function mcrypt_encrypt() is deprecated in %s%emcrypt_rijndael128_128BitKey.php on line 45
+
Warning: mcrypt_encrypt(): Key of size 0 not supported by this algorithm. Only keys of sizes 16, 24 or 32 supported in %s on line %d
string(0) ""
key length=8
+Deprecated: Function mcrypt_encrypt() is deprecated in %s%emcrypt_rijndael128_128BitKey.php on line 45
+
Warning: mcrypt_encrypt(): Key of size 8 not supported by this algorithm. Only keys of sizes 16, 24 or 32 supported in %s on line %d
string(0) ""
key length=16
+
+Deprecated: Function mcrypt_encrypt() is deprecated in %s%emcrypt_rijndael128_128BitKey.php on line 45
string(128) "dc8f957ec530acf10cd95ba7da7b6405380fe19a2941e9a8de54680512f18491bc374e5464885ae6c2ae2aa7a6cdd2fbe12a06bbc4bd59dbbfaa15f09044f101"
--- testing different iv lengths
iv length=0
+Deprecated: Function mcrypt_decrypt() is deprecated in %s%emcrypt_rijndael128_128BitKey.php on line 53
+
Warning: mcrypt_decrypt(): Received initialization vector of size 0, but size 16 is required for this encryption mode in %s on line %d
string(0) ""
iv length=0
+Deprecated: Function mcrypt_decrypt() is deprecated in %s%emcrypt_rijndael128_128BitKey.php on line 53
+
Warning: mcrypt_decrypt(): Received initialization vector of size 0, but size 16 is required for this encryption mode in %s on line %d
string(0) ""
iv length=8
+Deprecated: Function mcrypt_decrypt() is deprecated in %s%emcrypt_rijndael128_128BitKey.php on line 53
+
Warning: mcrypt_decrypt(): Received initialization vector of size 8, but size 16 is required for this encryption mode in %s on line %d
string(0) ""
iv length=16
+
+Deprecated: Function mcrypt_decrypt() is deprecated in %s%emcrypt_rijndael128_128BitKey.php on line 53
string(32) "42adc8c0db19473f2c684ff2d6e828a5"
iv length=17
+Deprecated: Function mcrypt_decrypt() is deprecated in %s%emcrypt_rijndael128_128BitKey.php on line 53
+
Warning: mcrypt_decrypt(): Received initialization vector of size 17, but size 16 is required for this encryption mode in %s on line %d
string(0) ""
===DONE===
diff --git a/ext/mcrypt/tests/mcrypt_rijndael128_256BitKey.phpt b/ext/mcrypt/tests/mcrypt_rijndael128_256BitKey.phpt
index 672e1ee1b7..7fef8af36d 100644
--- a/ext/mcrypt/tests/mcrypt_rijndael128_256BitKey.phpt
+++ b/ext/mcrypt/tests/mcrypt_rijndael128_256BitKey.phpt
@@ -63,33 +63,53 @@ foreach ($keys as $key) {
key length=20
+Deprecated: Function mcrypt_encrypt() is deprecated in %s%emcrypt_rijndael128_256BitKey.php on line 43
+
Warning: mcrypt_encrypt(): Key of size 20 not supported by this algorithm. Only keys of sizes 16, 24 or 32 supported in %s on line %d
string(0) ""
+Deprecated: Function mcrypt_decrypt() is deprecated in %s%emcrypt_rijndael128_256BitKey.php on line 45
+
Warning: mcrypt_decrypt(): Key of size 20 not supported by this algorithm. Only keys of sizes 16, 24 or 32 supported in %s on line %d
string(0) ""
key length=24
+
+Deprecated: Function mcrypt_encrypt() is deprecated in %s%emcrypt_rijndael128_256BitKey.php on line 43
string(128) "8ecdf1ed5742aff16ef34c819c8d22c707c54f4d9ffc18e5f6ab79fe68c25705351e2c001a0b9f29e5def67570ca9da644efb69a8bb97940cb4bec094dae8bb5"
+
+Deprecated: Function mcrypt_decrypt() is deprecated in %s%emcrypt_rijndael128_256BitKey.php on line 45
string(128) "546869732069732074686520736563726574206d657373616765207768696368206d75737420626520656e637279707465640000000000000000000000000000"
key length=30
+Deprecated: Function mcrypt_encrypt() is deprecated in %s%emcrypt_rijndael128_256BitKey.php on line 43
+
Warning: mcrypt_encrypt(): Key of size 30 not supported by this algorithm. Only keys of sizes 16, 24 or 32 supported in %s on line %d
string(0) ""
+Deprecated: Function mcrypt_decrypt() is deprecated in %s%emcrypt_rijndael128_256BitKey.php on line 45
+
Warning: mcrypt_decrypt(): Key of size 30 not supported by this algorithm. Only keys of sizes 16, 24 or 32 supported in %s on line %d
string(0) ""
key length=32
+
+Deprecated: Function mcrypt_encrypt() is deprecated in %s%emcrypt_rijndael128_256BitKey.php on line 43
string(128) "f23bc103bfd0859a8318acee6d96e5f43dff68f3cdeae817a1e77c33492e32bdb82c5f660fcd1a2bfda70d9de4d5d8028ce179a9e2f7f9ee7dd61c7b4b409e95"
+
+Deprecated: Function mcrypt_decrypt() is deprecated in %s%emcrypt_rijndael128_256BitKey.php on line 45
string(128) "546869732069732074686520736563726574206d657373616765207768696368206d75737420626520656e637279707465640000000000000000000000000000"
key length=40
+Deprecated: Function mcrypt_encrypt() is deprecated in %s%emcrypt_rijndael128_256BitKey.php on line 43
+
Warning: mcrypt_encrypt(): Key of size 40 not supported by this algorithm. Only keys of sizes 16, 24 or 32 supported in %s on line %d
string(0) ""
+Deprecated: Function mcrypt_decrypt() is deprecated in %s%emcrypt_rijndael128_256BitKey.php on line 45
+
Warning: mcrypt_decrypt(): Key of size 40 not supported by this algorithm. Only keys of sizes 16, 24 or 32 supported in %s on line %d
string(0) ""
===DONE===
diff --git a/ext/mysqli/mysqli.c b/ext/mysqli/mysqli.c
index d26f32d652..a42dcc20ee 100644
--- a/ext/mysqli/mysqli.c
+++ b/ext/mysqli/mysqli.c
@@ -34,6 +34,7 @@
#include "php_mysqli_structs.h"
#include "mysqli_priv.h"
#include "zend_exceptions.h"
+#include "ext/spl/spl_exceptions.h"
#include "zend_interfaces.h"
ZEND_DECLARE_MODULE_GLOBALS(mysqli)
@@ -282,7 +283,7 @@ static void mysqli_warning_free_storage(zend_object *object)
/* {{{ mysqli_read_na */
static zval *mysqli_read_na(mysqli_object *obj, zval *retval)
{
- php_error_docref(NULL, E_ERROR, "Cannot read property");
+ zend_throw_error(NULL, "Cannot read property");
return NULL;
}
/* }}} */
@@ -290,7 +291,7 @@ static zval *mysqli_read_na(mysqli_object *obj, zval *retval)
/* {{{ mysqli_write_na */
static int mysqli_write_na(mysqli_object *obj, zval *newval)
{
- php_error_docref(NULL, E_ERROR, "Cannot write property");
+ zend_throw_error(NULL, "Cannot write property");
return FAILURE;
}
/* }}} */
@@ -582,9 +583,7 @@ PHP_MINIT_FUNCTION(mysqli)
mysqli_object_handlers.write_property = mysqli_write_property;
mysqli_object_handlers.get_property_ptr_ptr = std_hnd->get_property_ptr_ptr;
mysqli_object_handlers.has_property = mysqli_object_has_property;
-#if PHP_VERSION_ID >= 50300
mysqli_object_handlers.get_debug_info = mysqli_object_get_debug_info;
-#endif
memcpy(&mysqli_object_driver_handlers, &mysqli_object_handlers, sizeof(zend_object_handlers));
mysqli_object_driver_handlers.free_obj = mysqli_driver_free_storage;
memcpy(&mysqli_object_link_handlers, &mysqli_object_handlers, sizeof(zend_object_handlers));
@@ -603,11 +602,7 @@ PHP_MINIT_FUNCTION(mysqli)
"MySqli persistent connection", module_number);
INIT_CLASS_ENTRY(cex, "mysqli_sql_exception", mysqli_exception_methods);
-#ifdef HAVE_SPL
mysqli_exception_class_entry = zend_register_internal_class_ex(&cex, spl_ce_RuntimeException);
-#else
- mysqli_exception_class_entry = zend_register_internal_class_ex(&cex, zend_ce_exception);
-#endif
mysqli_exception_class_entry->ce_flags |= ZEND_ACC_FINAL;
zend_declare_property_long(mysqli_exception_class_entry, "code", sizeof("code")-1, 0, ZEND_ACC_PROTECTED);
zend_declare_property_string(mysqli_exception_class_entry, "sqlstate", sizeof("sqlstate")-1, "00000", ZEND_ACC_PROTECTED);
@@ -999,9 +994,7 @@ PHP_MINFO_FUNCTION(mysqli)
/* Dependancies */
static const zend_module_dep mysqli_deps[] = {
-#if defined(HAVE_SPL) && (PHP_VERSION_ID >= 50100)
ZEND_MOD_REQUIRED("spl")
-#endif
#if defined(MYSQLI_USE_MYSQLND)
ZEND_MOD_REQUIRED("mysqlnd")
#endif
@@ -1296,9 +1289,7 @@ void php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAMETERS, int override_flags
if (ce->constructor) {
fci.size = sizeof(fci);
- fci.function_table = &ce->function_table;
ZVAL_UNDEF(&fci.function_name);
- fci.symbol_table = NULL;
fci.object = Z_OBJ_P(return_value);
fci.retval = &retval;
fci.params = NULL;
@@ -1320,7 +1311,7 @@ void php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAMETERS, int override_flags
fcc.initialized = 1;
fcc.function_handler = ce->constructor;
- fcc.calling_scope = EG(scope);
+ fcc.calling_scope = zend_get_executed_scope();
fcc.called_scope = Z_OBJCE_P(return_value);
fcc.object = Z_OBJ_P(return_value);
diff --git a/ext/mysqli/mysqli_api.c b/ext/mysqli/mysqli_api.c
index 6793fc8065..f0730839de 100644
--- a/ext/mysqli/mysqli_api.c
+++ b/ext/mysqli/mysqli_api.c
@@ -354,7 +354,7 @@ PHP_FUNCTION(mysqli_stmt_bind_param)
RETURN_FALSE;
}
- if (types_len != argc - start) {
+ if (types_len != (size_t)(argc - start)) {
/* number of bind variables doesn't match number of elements in type definition string */
php_error_docref(NULL, E_WARNING, "Number of elements in type definition string doesn't match number of bind variables");
RETURN_FALSE;
@@ -596,7 +596,7 @@ PHP_FUNCTION(mysqli_stmt_bind_result)
MYSQLI_FETCH_RESOURCE_STMT(stmt, mysql_stmt, MYSQLI_STATUS_VALID);
- if (argc != mysql_stmt_field_count(stmt->stmt)) {
+ if ((uint)argc != mysql_stmt_field_count(stmt->stmt)) {
php_error_docref(NULL, E_WARNING, "Number of bind variables doesn't match number of fields in prepared statement");
RETURN_FALSE;
}
@@ -1268,7 +1268,11 @@ PHP_FUNCTION(mysqli_fetch_lengths)
MYSQL_RES *result;
zval *mysql_result;
unsigned int i, num_fields;
- zend_ulong *ret;
+#if defined(MYSQLI_USE_MYSQLND)
+ const size_t *ret;
+#else
+ const zend_ulong *ret;
+#endif
if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &mysql_result, mysqli_result_class_entry) == FAILURE) {
return;
@@ -1328,7 +1332,7 @@ PHP_FUNCTION(mysqli_field_seek)
}
MYSQLI_FETCH_RESOURCE(result, MYSQL_RES *, mysql_result, "mysqli_result", MYSQLI_STATUS_VALID);
- if (fieldnr < 0 || fieldnr >= mysql_num_fields(result)) {
+ if (fieldnr < 0 || (uint)fieldnr >= mysql_num_fields(result)) {
php_error_docref(NULL, E_WARNING, "Invalid field offset");
RETURN_FALSE;
}
@@ -1695,10 +1699,6 @@ static int mysqli_options_get_option_zval_type(int option)
{
switch (option) {
#ifdef MYSQLI_USE_MYSQLND
-#if PHP_MAJOR_VERSION == 6
- /* PHP-7 doesn't supprt unicode */
- case MYSQLND_OPT_NUMERIC_AND_DATETIME_AS_UNICODE:
-#endif
case MYSQLND_OPT_NET_CMD_BUFFER_SIZE:
case MYSQLND_OPT_NET_READ_BUFFER_SIZE:
#ifdef MYSQLND_STRING_TO_INT_CONVERSION
@@ -1779,11 +1779,7 @@ PHP_FUNCTION(mysqli_options)
MYSQLI_FETCH_RESOURCE_CONN(mysql, mysql_link, MYSQLI_STATUS_INITIALIZED);
#if !defined(MYSQLI_USE_MYSQLND)
-#if PHP_API_VERSION < 20100412
- if ((PG(open_basedir) && PG(open_basedir)[0] != '\0') || PG(safe_mode)) {
-#else
if (PG(open_basedir) && PG(open_basedir)[0] != '\0') {
-#endif
if(mysql_option == MYSQL_OPT_LOCAL_INFILE) {
RETURN_FALSE;
}
@@ -2339,7 +2335,7 @@ PHP_FUNCTION(mysqli_stmt_attr_set)
MYSQLI_FETCH_RESOURCE_STMT(stmt, mysql_stmt, MYSQLI_STATUS_VALID);
if (mode_in < 0) {
- php_error_docref(NULL, E_WARNING, "mode should be non-negative, %pd passed", mode_in);
+ php_error_docref(NULL, E_WARNING, "mode should be non-negative, " ZEND_LONG_FMT " passed", mode_in);
RETURN_FALSE;
}
diff --git a/ext/mysqli/mysqli_driver.c b/ext/mysqli/mysqli_driver.c
index 5a807dd9f9..36d0c58d10 100644
--- a/ext/mysqli/mysqli_driver.c
+++ b/ext/mysqli/mysqli_driver.c
@@ -150,7 +150,7 @@ const zend_function_entry mysqli_driver_methods[] = {
PHP_FALIAS(embedded_server_start, mysqli_embedded_server_start, NULL)
PHP_FALIAS(embedded_server_end, mysqli_embedded_server_end, NULL)
#endif
- {NULL, NULL, NULL}
+ PHP_FE_END
};
/* }}} */
diff --git a/ext/mysqli/mysqli_exception.c b/ext/mysqli/mysqli_exception.c
index 1aca7762a8..8663057fd2 100644
--- a/ext/mysqli/mysqli_exception.c
+++ b/ext/mysqli/mysqli_exception.c
@@ -32,7 +32,7 @@
/* {{{ mysqli_exception_methods[]
*/
const zend_function_entry mysqli_exception_methods[] = {
- {NULL, NULL, NULL}
+ PHP_FE_END
};
/* }}} */
diff --git a/ext/mysqli/mysqli_fe.c b/ext/mysqli/mysqli_fe.c
index 3ac899c660..59354e5096 100644
--- a/ext/mysqli/mysqli_fe.c
+++ b/ext/mysqli/mysqli_fe.c
@@ -63,10 +63,6 @@ ZEND_BEGIN_ARG_INFO(arginfo_class_mysqli_stmt_bind_param, 0)
ZEND_ARG_VARIADIC_INFO(1, vars)
ZEND_END_ARG_INFO()
-ZEND_BEGIN_ARG_INFO(all_args_force_by_ref, 0)
- ZEND_ARG_VARIADIC_INFO(1, vars)
-ZEND_END_ARG_INFO()
-
ZEND_BEGIN_ARG_INFO_EX(arginfo_mysqli_poll, 0, 0, 4)
ZEND_ARG_ARRAY_INFO(1, read, 1)
ZEND_ARG_ARRAY_INFO(1, write, 1)
@@ -243,18 +239,14 @@ ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_mysqli_fetch_object, 0, 0, 1)
-#if PHP_VERSION_ID > 50399
MYSQLI_ZEND_ARG_OBJ_INFO_RESULT()
ZEND_ARG_INFO(0, class_name)
ZEND_ARG_ARRAY_INFO(0, params, 0)
-#endif
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_mysqli_fetch_object, 0, 0, 0)
-#if PHP_VERSION_ID > 50399
ZEND_ARG_INFO(0, class_name)
ZEND_ARG_ARRAY_INFO(0, params, 0)
-#endif
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_mysqli_kill, 0, 0, 2)
@@ -269,6 +261,17 @@ ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_mysqli_query, 0, 0, 2)
MYSQLI_ZEND_ARG_OBJ_INFO_LINK()
ZEND_ARG_INFO(0, query)
+ ZEND_ARG_INFO(0, resultmode)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_mysqli_multi_query, 0, 0, 1)
+ MYSQLI_ZEND_ARG_OBJ_INFO_LINK()
+ ZEND_ARG_INFO(0, query)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_mysqli_real_query, 0, 0, 1)
+ MYSQLI_ZEND_ARG_OBJ_INFO_LINK()
+ ZEND_ARG_INFO(0, query)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_mysqli_prepare, 0, 0, 2)
@@ -283,6 +286,19 @@ ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_mysqli_query, 0, 0, 1)
ZEND_ARG_INFO(0, query)
+ ZEND_ARG_INFO(0, resultmode)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_mysqli_prepare, 0, 0, 1)
+ ZEND_ARG_INFO(0, query)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_mysqli_multi_query, 0, 0, 1)
+ ZEND_ARG_INFO(0, query)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_mysqli_real_query, 0, 0, 1)
+ ZEND_ARG_INFO(0, query)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_mysqli_options, 0, 0, 3)
@@ -450,7 +466,7 @@ const zend_function_entry mysqli_functions[] = {
PHP_FE(mysqli_insert_id, arginfo_mysqli_only_link)
PHP_FE(mysqli_kill, arginfo_mysqli_kill)
PHP_FE(mysqli_more_results, arginfo_mysqli_only_link)
- PHP_FE(mysqli_multi_query, arginfo_mysqli_query)
+ PHP_FE(mysqli_multi_query, arginfo_mysqli_multi_query)
PHP_FE(mysqli_next_result, arginfo_mysqli_only_link)
PHP_FE(mysqli_num_fields, arginfo_mysqli_only_result)
PHP_FE(mysqli_num_rows, arginfo_mysqli_only_result)
@@ -464,7 +480,7 @@ const zend_function_entry mysqli_functions[] = {
PHP_FE(mysqli_query, arginfo_mysqli_query)
PHP_FE(mysqli_real_connect, arginfo_mysqli_real_connect)
PHP_FE(mysqli_real_escape_string, arginfo_mysqli_real_escape_string)
- PHP_FE(mysqli_real_query, arginfo_mysqli_query)
+ PHP_FE(mysqli_real_query, arginfo_mysqli_real_query)
#if defined(MYSQLI_USE_MYSQLND)
PHP_FE(mysqli_reap_async_query, arginfo_mysqli_only_link)
#endif
@@ -550,7 +566,7 @@ const zend_function_entry mysqli_link_methods[] = {
PHP_FALIAS(get_warnings, mysqli_get_warnings, arginfo_mysqli_no_params)
PHP_FALIAS(init,mysqli_init_method, arginfo_mysqli_no_params)
PHP_FALIAS(kill,mysqli_kill, arginfo_class_mysqli_kill)
- PHP_FALIAS(multi_query, mysqli_multi_query, arginfo_class_mysqli_query)
+ PHP_FALIAS(multi_query, mysqli_multi_query, arginfo_class_mysqli_multi_query)
PHP_FALIAS(__construct, mysqli_link_construct, arginfo_mysqli_connect)
PHP_FALIAS(more_results, mysqli_more_results, arginfo_mysqli_no_params)
PHP_FALIAS(next_result, mysqli_next_result, arginfo_mysqli_no_params)
@@ -559,7 +575,7 @@ const zend_function_entry mysqli_link_methods[] = {
#if defined(MYSQLI_USE_MYSQLND)
ZEND_FENTRY(poll, ZEND_FN(mysqli_poll), arginfo_mysqli_poll, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
#endif
- PHP_FALIAS(prepare, mysqli_prepare, arginfo_class_mysqli_query)
+ PHP_FALIAS(prepare, mysqli_prepare, arginfo_class_mysqli_prepare)
PHP_FALIAS(query, mysqli_query, arginfo_class_mysqli_query)
PHP_FALIAS(real_connect, mysqli_real_connect, arginfo_class_mysqli_real_connect)
PHP_FALIAS(real_escape_string, mysqli_real_escape_string, arginfo_class_mysqli_real_escape_string)
@@ -567,7 +583,7 @@ const zend_function_entry mysqli_link_methods[] = {
PHP_FALIAS(reap_async_query, mysqli_reap_async_query, arginfo_mysqli_no_params)
#endif
PHP_FALIAS(escape_string, mysqli_real_escape_string, arginfo_class_mysqli_real_escape_string)
- PHP_FALIAS(real_query, mysqli_real_query, arginfo_class_mysqli_query)
+ PHP_FALIAS(real_query, mysqli_real_query, arginfo_class_mysqli_real_query)
PHP_FALIAS(release_savepoint, mysqli_release_savepoint, arginfo_class_mysqli_release_savepoint)
PHP_FALIAS(rollback, mysqli_rollback, arginfo_class_mysqli_rollback)
PHP_FALIAS(savepoint, mysqli_savepoint, arginfo_class_mysqli_savepoint)
@@ -583,7 +599,7 @@ const zend_function_entry mysqli_link_methods[] = {
PHP_FALIAS(thread_safe, mysqli_thread_safe, arginfo_mysqli_no_params)
PHP_FALIAS(use_result, mysqli_use_result, arginfo_mysqli_no_params)
PHP_FALIAS(refresh,mysqli_refresh, arginfo_class_mysqli_refresh)
- {NULL, NULL, NULL}
+ PHP_FE_END
};
/* }}} */
@@ -608,7 +624,7 @@ const zend_function_entry mysqli_result_methods[] = {
PHP_FALIAS(fetch_row, mysqli_fetch_row, arginfo_mysqli_no_params)
PHP_FALIAS(field_seek, mysqli_field_seek, arginfo_class_mysqli_result_and_fieldnr)
PHP_FALIAS(free_result, mysqli_free_result, arginfo_mysqli_no_params)
- {NULL, NULL, NULL}
+ PHP_FE_END
};
/* }}} */
@@ -636,12 +652,12 @@ const zend_function_entry mysqli_stmt_methods[] = {
PHP_FALIAS(send_long_data, mysqli_stmt_send_long_data, arginfo_class_mysqli_stmt_send_long_data)
PHP_FALIAS(free_result, mysqli_stmt_free_result, arginfo_mysqli_no_params)
PHP_FALIAS(reset, mysqli_stmt_reset, arginfo_mysqli_no_params)
- PHP_FALIAS(prepare, mysqli_stmt_prepare, arginfo_class_mysqli_query)
+ PHP_FALIAS(prepare, mysqli_stmt_prepare, arginfo_class_mysqli_prepare)
PHP_FALIAS(store_result, mysqli_stmt_store_result, arginfo_mysqli_no_params)
#if defined(MYSQLI_USE_MYSQLND)
PHP_FALIAS(get_result, mysqli_stmt_get_result, arginfo_mysqli_no_params)
#endif
- {NULL, NULL, NULL}
+ PHP_FE_END
};
/* }}} */
diff --git a/ext/mysqli/mysqli_nonapi.c b/ext/mysqli/mysqli_nonapi.c
index d763dde11a..19b4ddda85 100644
--- a/ext/mysqli/mysqli_nonapi.c
+++ b/ext/mysqli/mysqli_nonapi.c
@@ -156,7 +156,7 @@ void mysqli_common_connect(INTERNAL_FUNCTION_PARAMETERS, zend_bool is_real_conne
} else {
mysql->persistent = persistent = TRUE;
- hash_key = strpprintf(0, "mysqli_%s_%s%ld%s%s%s", SAFE_STR(hostname), SAFE_STR(socket),
+ hash_key = strpprintf(0, "mysqli_%s_%s" ZEND_LONG_FMT "%s%s%s", SAFE_STR(hostname), SAFE_STR(socket),
port, SAFE_STR(username), SAFE_STR(dbname),
SAFE_STR(passwd));
@@ -206,14 +206,14 @@ void mysqli_common_connect(INTERNAL_FUNCTION_PARAMETERS, zend_bool is_real_conne
}
}
if (MyG(max_links) != -1 && MyG(num_links) >= MyG(max_links)) {
- php_error_docref(NULL, E_WARNING, "Too many open links (%pd)", MyG(num_links));
+ php_error_docref(NULL, E_WARNING, "Too many open links (" ZEND_LONG_FMT ")", MyG(num_links));
goto err;
}
if (persistent && MyG(max_persistent) != -1 &&
(MyG(num_active_persistent) + MyG(num_inactive_persistent))>= MyG(max_persistent))
{
- php_error_docref(NULL, E_WARNING, "Too many open persistent links (%pd)",
+ php_error_docref(NULL, E_WARNING, "Too many open persistent links (" ZEND_LONG_FMT ")",
MyG(num_active_persistent) + MyG(num_inactive_persistent));
goto err;
}
@@ -1092,7 +1092,7 @@ PHP_FUNCTION(mysqli_begin_transaction)
}
MYSQLI_FETCH_RESOURCE_CONN(mysql, mysql_link, MYSQLI_STATUS_VALID);
if (flags < 0) {
- php_error_docref(NULL, E_WARNING, "Invalid value for parameter flags (%pd)", flags);
+ php_error_docref(NULL, E_WARNING, "Invalid value for parameter flags (" ZEND_LONG_FMT ")", flags);
err = TRUE;
}
if (!name_len) {
diff --git a/ext/mysqli/mysqli_priv.h b/ext/mysqli/mysqli_priv.h
index 6f1faede34..34c7a74779 100644
--- a/ext/mysqli/mysqli_priv.h
+++ b/ext/mysqli/mysqli_priv.h
@@ -78,10 +78,6 @@ extern void php_mysqli_report_error(const char *sqlstate, int errorno, const cha
extern void php_mysqli_report_index(const char *query, unsigned int status);
extern void php_mysqli_throw_sql_exception(char *sqlstate, int errorno, char *format, ...);
-#ifdef HAVE_SPL
-extern PHPAPI zend_class_entry *spl_ce_RuntimeException;
-#endif
-
#define PHP_MYSQLI_EXPORT(__type) PHP_MYSQLI_API __type
PHP_MYSQLI_EXPORT(zend_object *) mysqli_objects_new(zend_class_entry *);
diff --git a/ext/mysqli/mysqli_prop.c b/ext/mysqli/mysqli_prop.c
index e29b5685ad..2e1e333b39 100644
--- a/ext/mysqli/mysqli_prop.c
+++ b/ext/mysqli/mysqli_prop.c
@@ -15,8 +15,6 @@
| Author: Georg Richter <georg@php.net> |
| Andrey Hristov <andrey@php.net> |
+----------------------------------------------------------------------+
-
- $Id$
*/
#ifdef HAVE_CONFIG_H
@@ -289,7 +287,11 @@ static zval *result_type_read(mysqli_object *obj, zval *retval)
static zval *result_lengths_read(mysqli_object *obj, zval *retval)
{
MYSQL_RES *p;
- zend_ulong *ret;
+#if defined(MYSQLI_USE_MYSQLND)
+ const size_t *ret;
+#else
+ const zend_ulong *ret;
+#endif
uint field_count;
CHECK_STATUS(MYSQLI_STATUS_VALID);
diff --git a/ext/mysqli/mysqli_result_iterator.c b/ext/mysqli/mysqli_result_iterator.c
index e21e737036..f2c28d5e97 100644
--- a/ext/mysqli/mysqli_result_iterator.c
+++ b/ext/mysqli/mysqli_result_iterator.c
@@ -153,6 +153,7 @@ zend_object_iterator_funcs php_mysqli_result_iterator_funcs = {
php_mysqli_result_iterator_current_key,
php_mysqli_result_iterator_move_forward,
php_mysqli_result_iterator_rewind,
+ NULL
};
/* }}} */
diff --git a/ext/mysqli/mysqli_warning.c b/ext/mysqli/mysqli_warning.c
index 37a3ffd29e..19a51735ad 100644
--- a/ext/mysqli/mysqli_warning.c
+++ b/ext/mysqli/mysqli_warning.c
@@ -315,7 +315,7 @@ PHP_METHOD(mysqli_warning, __construct)
const zend_function_entry mysqli_warning_methods[] = {
PHP_ME(mysqli_warning, __construct, NULL, ZEND_ACC_PROTECTED)
PHP_ME(mysqli_warning, next, NULL, ZEND_ACC_PUBLIC)
- {NULL, NULL, NULL}
+ PHP_FE_END
};
/* }}} */
diff --git a/ext/mysqli/php_mysqli_structs.h b/ext/mysqli/php_mysqli_structs.h
index cac74b99f5..df41cbeb52 100644
--- a/ext/mysqli/php_mysqli_structs.h
+++ b/ext/mysqli/php_mysqli_structs.h
@@ -216,10 +216,6 @@ extern zend_object_iterator *php_mysqli_result_get_iterator(zend_class_entry *ce
extern void php_mysqli_fetch_into_hash_aux(zval *return_value, MYSQL_RES * result, zend_long fetchtype);
-#ifdef HAVE_SPL
-extern PHPAPI zend_class_entry *spl_ce_RuntimeException;
-#endif
-
#define MYSQLI_DISABLE_MQ if (mysql->multi_query) { \
mysql_set_server_option(mysql->mysql, MYSQL_OPTION_MULTI_STATEMENTS_OFF); \
mysql->multi_query = 0; \
diff --git a/ext/mysqli/tests/bug71863.phpt b/ext/mysqli/tests/bug71863.phpt
new file mode 100644
index 0000000000..ea0a78faeb
--- /dev/null
+++ b/ext/mysqli/tests/bug71863.phpt
@@ -0,0 +1,36 @@
+--TEST--
+Bug #71863 Segfault when EXPLAIN with "Unknown Column" Error
+--SKIPIF--
+<?php
+require_once('skipif.inc');
+require_once('skipifconnectfailure.inc');
+require_once("connect.inc");
+if (!$IS_MYSQLND) {
+ die("skip mysqlnd only test");
+}
+?>
+--FILE--
+<?php
+require_once("connect.inc");
+
+$req = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket);
+
+// create db and table for test
+mysqli_query($req, "DROP TABLE IF EXISTS test_bug_71863") or die(mysqli_error($req));
+mysqli_query($req, "CREATE TABLE test_bug_71863 (id INT UNSIGNED NOT NULL DEFAULT 0)") or die(mysqli_error($req));
+
+// segfault if EXPLAIN + "Unknown column" error
+mysqli_query($req, "EXPLAIN SELECT `id` FROM `test_bug_71863` WHERE `owner_id` = '2' AND `object_id` = '1' AND type = '0'") or die(mysqli_error($req)."\n");
+
+?>
+--CLEAN--
+<?php
+require_once("connect.inc");
+if (!$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket))
+ printf("[c001] [%d] %s\n", mysqli_connect_errno(), mysqli_connect_error());
+if (!mysqli_query($link, "DROP TABLE IF EXISTS test_bug_71863"))
+ printf("[c002] Cannot drop table, [%d] %s\n", mysqli_errno($link), mysqli_error($link));
+mysqli_close($link);
+?>
+--EXPECTF--
+%AUnknown column 'owner_id' in 'where clause'
diff --git a/ext/mysqli/tests/bug72701.phpt b/ext/mysqli/tests/bug72701.phpt
new file mode 100644
index 0000000000..f0eb174172
--- /dev/null
+++ b/ext/mysqli/tests/bug72701.phpt
@@ -0,0 +1,32 @@
+--TEST--
+Bug #72701 mysqli_get_host_info() wrong output
+--SKIPIF--
+<?php
+require_once('skipif.inc');
+require_once('skipifconnectfailure.inc');
+require_once("connect.inc");
+
+if ("127.0.0.1" != $host && "localhost" != $host) {
+ die("skip require 127.0.0.1 connection");
+}
+
+?>
+--FILE--
+<?php
+
+require_once("connect.inc");
+
+$con = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket);
+
+if (mysqli_connect_errno()) {
+ echo "Failed to connect to MySQL: " . mysqli_connect_error();
+}
+
+var_dump(preg_match(",(127.0.0.1|localhost) via .*,i", mysqli_get_host_info($con)));
+
+mysqli_close($con);
+?>
+==DONE==
+--EXPECT--
+int(1)
+==DONE==
diff --git a/ext/mysqli/tests/bug74595.phpt b/ext/mysqli/tests/bug74595.phpt
new file mode 100644
index 0000000000..9d017eaa82
--- /dev/null
+++ b/ext/mysqli/tests/bug74595.phpt
@@ -0,0 +1,25 @@
+--TEST--
+Bug #74595 (ReflectionMethod::getParameters returns incorrect number of parameters)
+--SKIPIF--
+<?php
+require_once('skipif.inc');
+?>
+--FILE--
+<?php
+$class = new ReflectionClass('mysqli');
+$method = $class->getMethod('query');
+var_dump($method->getParameters());
+?>
+--EXPECTF--
+array(2) {
+ [0]=>
+ object(ReflectionParameter)#%d (1) {
+ ["name"]=>
+ string(5) "query"
+ }
+ [1]=>
+ object(ReflectionParameter)#%d (1) {
+ ["name"]=>
+ string(10) "resultmode"
+ }
+}
diff --git a/ext/mysqli/tests/mysqli_class_mysqli_reflection.phpt b/ext/mysqli/tests/mysqli_class_mysqli_reflection.phpt
index 22829ee6ee..f85d68932b 100644
--- a/ext/mysqli/tests/mysqli_class_mysqli_reflection.phpt
+++ b/ext/mysqli/tests/mysqli_class_mysqli_reflection.phpt
@@ -732,7 +732,7 @@ isInternal: yes
isUserDefined: no
returnsReference: no
Modifiers: 256
-Number of Parameters: 1
+Number of Parameters: 2
Number of Required Parameters: 1
Inspecting parameter 'query' of method 'query'
@@ -742,6 +742,13 @@ isPassedByReference: no
isOptional: no
isDefaultValueAvailable: no
+Inspecting parameter 'resultmode' of method 'query'
+isArray: no
+allowsNull: no
+isPassedByReference: no
+isOptional: yes
+isDefaultValueAvailable: no
+
Inspecting method 'real_connect'
isFinal: no
isAbstract: no
diff --git a/ext/mysqli/tests/mysqli_fetch_object.phpt b/ext/mysqli/tests/mysqli_fetch_object.phpt
index 9706ceac84..dff91531ce 100644
--- a/ext/mysqli/tests/mysqli_fetch_object.phpt
+++ b/ext/mysqli/tests/mysqli_fetch_object.phpt
@@ -59,18 +59,25 @@ require_once('skipifconnectfailure.inc');
}
- $obj = mysqli_fetch_object($res, 'mysqli_fetch_object_construct', array());
-
- if (($obj->ID !== "3") || ($obj->label !== "c") || ($obj->a !== NULL) || ($obj->b !== NULL) || (get_class($obj) != 'mysqli_fetch_object_construct')) {
- printf("[006] Object seems wrong. [%d] %s\n", mysqli_errno($link), mysqli_error($link));
- var_dump($obj);
- }
+ try {
+ $obj = mysqli_fetch_object($res, 'mysqli_fetch_object_construct', array());
+ if (($obj->ID !== "3") || ($obj->label !== "c") || ($obj->a !== NULL) || ($obj->b !== NULL) || (get_class($obj) != 'mysqli_fetch_object_construct')) {
+ printf("[006] Object seems wrong. [%d] %s\n", mysqli_errno($link), mysqli_error($link));
+ var_dump($obj);
+ }
+ } catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+ }
- $obj = mysqli_fetch_object($res, 'mysqli_fetch_object_construct', array('a'));
- if (($obj->ID !== "4") || ($obj->label !== "d") || ($obj->a !== 'a') || ($obj->b !== NULL) || (get_class($obj) != 'mysqli_fetch_object_construct')) {
- printf("[007] Object seems wrong. [%d] %s\n", mysqli_errno($link), mysqli_error($link));
- var_dump($obj);
- }
+ try {
+ $obj = mysqli_fetch_object($res, 'mysqli_fetch_object_construct', array('a'));
+ if (($obj->ID !== "4") || ($obj->label !== "d") || ($obj->a !== 'a') || ($obj->b !== NULL) || (get_class($obj) != 'mysqli_fetch_object_construct')) {
+ printf("[007] Object seems wrong. [%d] %s\n", mysqli_errno($link), mysqli_error($link));
+ var_dump($obj);
+ }
+ } catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+ }
$obj = mysqli_fetch_object($res, 'mysqli_fetch_object_construct', array('a', 'b'));
if (($obj->ID !== "5") || ($obj->label !== "e") || ($obj->a !== 'a') || ($obj->b !== 'b') || (get_class($obj) != 'mysqli_fetch_object_construct')) {
@@ -140,12 +147,8 @@ require_once('skipifconnectfailure.inc');
--EXPECTF--
[E_WARNING] mysqli_fetch_object() expects at least 1 parameter, 0 given in %s on line %d
[E_WARNING] mysqli_fetch_object() expects parameter 1 to be mysqli_result, null given in %s on line %d
-[E_WARNING] Missing argument 1 for mysqli_fetch_object_construct::__construct() in %s on line %d
-[E_WARNING] Missing argument 2 for mysqli_fetch_object_construct::__construct() in %s on line %d
-[E_NOTICE] Undefined variable: a in %s on line %d
-[E_NOTICE] Undefined variable: b in %s on line %d
-[E_WARNING] Missing argument 2 for mysqli_fetch_object_construct::__construct() in %s on line %d
-[E_NOTICE] Undefined variable: b in %s on line %d
+Exception: Too few arguments to function mysqli_fetch_object_construct::__construct(), 0 passed and exactly 2 expected
+Exception: Too few arguments to function mysqli_fetch_object_construct::__construct(), 1 passed and exactly 2 expected
NULL
NULL
[E_WARNING] mysqli_fetch_object(): Couldn't fetch mysqli_result in %s on line %d
diff --git a/ext/mysqli/tests/mysqli_fetch_object_oo.phpt b/ext/mysqli/tests/mysqli_fetch_object_oo.phpt
index 82e311cc72..8fac044139 100644
--- a/ext/mysqli/tests/mysqli_fetch_object_oo.phpt
+++ b/ext/mysqli/tests/mysqli_fetch_object_oo.phpt
@@ -89,10 +89,14 @@ require_once('skipifconnectfailure.inc');
mysqli_fetch_object($res);
}
- $obj = $res->fetch_object('mysqli_fetch_object_construct', array('a'));
- if (($obj->ID !== "4") || ($obj->label !== "d") || ($obj->a !== 'a') || ($obj->b !== NULL) || (get_class($obj) != 'mysqli_fetch_object_construct')) {
- printf("[010] Object seems wrong. [%d] %s\n", $mysqli->errno, $mysqli->error);
- var_dump($obj);
+ try {
+ $obj = $res->fetch_object('mysqli_fetch_object_construct', array('a'));
+ if (($obj->ID !== "4") || ($obj->label !== "d") || ($obj->a !== 'a') || ($obj->b !== NULL) || (get_class($obj) != 'mysqli_fetch_object_construct')) {
+ printf("[010] Object seems wrong. [%d] %s\n", $mysqli->errno, $mysqli->error);
+ var_dump($obj);
+ }
+ } catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
}
$obj = $res->fetch_object('mysqli_fetch_object_construct', array('a', 'b'));
@@ -132,8 +136,7 @@ require_once('skipifconnectfailure.inc');
[0] Argument 2 passed to mysqli_result::fetch_object() must be of the type array, object given in %s on line %d
[0] Argument 2 passed to mysqli_result::fetch_object() must be of the type array, object given in %s on line %d
[0] Argument 2 passed to mysqli_result::fetch_object() must be of the type array, null given in %s on line %d
-[E_WARNING] Missing argument 2 for mysqli_fetch_object_construct::__construct() in %s on line %d
-[E_NOTICE] Undefined variable: b in %s on line %d
+Exception: Too few arguments to function mysqli_fetch_object_construct::__construct(), 1 passed and exactly 2 expected
NULL
NULL
[E_WARNING] mysqli_fetch_object(): Couldn't fetch mysqli_result in %s on line %d
diff --git a/ext/mysqli/tests/mysqli_get_client_stats.phpt b/ext/mysqli/tests/mysqli_get_client_stats.phpt
index f0c4129dee..3e80c78e74 100644
--- a/ext/mysqli/tests/mysqli_get_client_stats.phpt
+++ b/ext/mysqli/tests/mysqli_get_client_stats.phpt
@@ -958,7 +958,7 @@ if (!mysqli_query($link, "DROP SERVER IF EXISTS myself"))
mysqli_close($link);
?>
--EXPECTF--
-array(161) {
+array(163) {
[%u|b%"bytes_sent"]=>
%unicode|string%(1) "0"
[%u|b%"bytes_received"]=>
@@ -1125,10 +1125,14 @@ array(161) {
%unicode|string%(1) "0"
[%u|b%"mem_strndup_count"]=>
%unicode|string%(1) "0"
- [%u|b%"mem_estndup_count"]=>
+ [%u|b%"mem_estrdup_count"]=>
%unicode|string%(1) "0"
[%u|b%"mem_strdup_count"]=>
%unicode|string%(1) "0"
+ [%u|b%"mem_edupl_count"]=>
+ %unicode|string%(1) "0"
+ [%u|b%"mem_dupl_count"]=>
+ %unicode|string%(1) "0"
[%u|b%"proto_text_fetched_null"]=>
%unicode|string%(1) "0"
[%u|b%"proto_text_fetched_bit"]=>
diff --git a/ext/mysqlnd/config.w32 b/ext/mysqlnd/config.w32
index ff102310dd..52be8f563c 100644
--- a/ext/mysqlnd/config.w32
+++ b/ext/mysqlnd/config.w32
@@ -6,23 +6,26 @@ if (PHP_MYSQLND != "no") {
if (CHECK_LIB("ws2_32.lib", "mysqlnd")) {
mysqlnd_source =
- "mysqlnd.c " +
"mysqlnd_alloc.c " +
"mysqlnd_auth.c " +
"mysqlnd_block_alloc.c " +
+ "mysqlnd_connection.c " +
"mysqlnd_charset.c " +
+ "mysqlnd_commands.c " +
"mysqlnd_debug.c " +
"mysqlnd_driver.c " +
"mysqlnd_ext_plugin.c " +
"mysqlnd_loaddata.c " +
"mysqlnd_reverse_api.c " +
- "mysqlnd_net.c " +
"mysqlnd_plugin.c " +
+ "mysqlnd_protocol_frame_codec.c " +
"mysqlnd_ps.c " +
"mysqlnd_ps_codec.c " +
+ "mysqlnd_read_buffer.c " +
"mysqlnd_result.c " +
"mysqlnd_result_meta.c " +
"mysqlnd_statistics.c " +
+ "mysqlnd_vio.c " +
"mysqlnd_wireprotocol.c " +
"php_mysqlnd.c ";
EXTENSION("mysqlnd", mysqlnd_source, false, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1");
diff --git a/ext/mysqlnd/config9.m4 b/ext/mysqlnd/config9.m4
index 92cab94367..b5f20a9c8b 100644
--- a/ext/mysqlnd/config9.m4
+++ b/ext/mysqlnd/config9.m4
@@ -18,11 +18,11 @@ fi
dnl If some extension uses mysqlnd it will get compiled in PHP core
if test "$PHP_MYSQLND" != "no" || test "$PHP_MYSQLND_ENABLED" = "yes"; then
mysqlnd_ps_sources="mysqlnd_ps.c mysqlnd_ps_codec.c"
- mysqlnd_base_sources="mysqlnd.c mysqlnd_alloc.c mysqlnd_charset.c mysqlnd_wireprotocol.c \
- mysqlnd_loaddata.c mysqlnd_reverse_api.c mysqlnd_net.c \
+ mysqlnd_base_sources="mysqlnd_connection.c mysqlnd_alloc.c mysqlnd_charset.c mysqlnd_wireprotocol.c \
+ mysqlnd_loaddata.c mysqlnd_reverse_api.c mysqlnd_vio.c mysqlnd_protocol_frame_codec.c \
mysqlnd_statistics.c mysqlnd_driver.c mysqlnd_ext_plugin.c mysqlnd_auth.c \
- mysqlnd_result.c mysqlnd_result_meta.c mysqlnd_debug.c\
- mysqlnd_block_alloc.c mysqlnd_plugin.c php_mysqlnd.c"
+ mysqlnd_result.c mysqlnd_result_meta.c mysqlnd_debug.c mysqlnd_commands.c \
+ mysqlnd_block_alloc.c mysqlnd_read_buffer.c mysqlnd_plugin.c php_mysqlnd.c"
if test "$PHP_MYSQLND_COMPRESSION_SUPPORT" != "no"; then
diff --git a/ext/mysqlnd/mysqlnd.h b/ext/mysqlnd/mysqlnd.h
index b5c5906d45..b396954eeb 100644
--- a/ext/mysqlnd/mysqlnd.h
+++ b/ext/mysqlnd/mysqlnd.h
@@ -60,6 +60,7 @@
#include "mysqlnd_enum_n_def.h"
#include "mysqlnd_structs.h"
+#define MYSQLND_STR_W_LEN(str) str, (sizeof(str) - 1)
/* Library related */
PHPAPI void mysqlnd_library_init(void);
@@ -82,18 +83,23 @@ PHPAPI const MYSQLND_CHARSET * mysqlnd_find_charset_name(const char * const char
/* Connect */
-PHPAPI MYSQLND * mysqlnd_init(unsigned int client_flags, zend_bool persistent);
-PHPAPI MYSQLND * mysqlnd_connect(MYSQLND * conn,
- const char * host, const char * user,
- const char * passwd, unsigned int passwd_len,
- const char * db, unsigned int db_len,
- unsigned int port,
- const char * socket_or_pipe,
- unsigned int mysql_flags,
- unsigned int client_api_flags
- );
-
-#define mysqlnd_change_user(conn, user, passwd, db, silent) ((conn)->data)->m->change_user((conn)->data, (user), (passwd), (db), (silent), strlen((passwd)))
+#define mysqlnd_init(flags, persistent) mysqlnd_connection_init((flags), (persistent), NULL /*use default factory*/)
+#define mysqlnd_connect(conn, host, user, pass, pass_len, db, db_len, port, socket, mysql_flags, client_api_flags) \
+ mysqlnd_connection_connect((conn), (host), (user), (pass), (pass_len), (db), (db_len), (port), (socket), (mysql_flags), (client_api_flags))
+
+PHPAPI MYSQLND * mysqlnd_connection_init(const size_t client_flags, const zend_bool persistent, MYSQLND_CLASS_METHODS_TYPE(mysqlnd_object_factory) *object_factory);
+PHPAPI MYSQLND * mysqlnd_connection_connect(MYSQLND * conn,
+ const char * const host,
+ const char * const user,
+ const char * const passwd, unsigned int passwd_len,
+ const char * const db, unsigned int db_len,
+ unsigned int port,
+ const char * const socket_or_pipe,
+ unsigned int mysql_flags,
+ unsigned int client_api_flags
+ );
+
+#define mysqlnd_change_user(conn, user, passwd, db, silent) ((conn)->data)->m->change_user((conn)->data, (user), (passwd), (db), (silent), strlen((passwd)))
#define mysqlnd_change_user_ex(conn, user, passwd, db, silent, passwd_len) ((conn)->data)->m->change_user((conn)->data, (user), (passwd), (db), (silent), (passwd_len))
PHPAPI void mysqlnd_debug(const char *mode);
@@ -104,7 +110,7 @@ PHPAPI void mysqlnd_debug(const char *mode);
#define mysqlnd_fetch_all(result, flags, return_value) (result)->m.fetch_all((result), (flags), (return_value) ZEND_FILE_LINE_CC)
#define mysqlnd_result_fetch_field_data(res,offset,ret) (res)->m.fetch_field_data((res), (offset), (ret))
#define mysqlnd_get_connection_stats(conn, values) ((conn)->data)->m->get_statistics((conn)->data, (values) ZEND_FILE_LINE_CC)
-#define mysqlnd_get_client_stats(values) _mysqlnd_get_client_stats((values) ZEND_FILE_LINE_CC)
+#define mysqlnd_get_client_stats(values) _mysqlnd_get_client_stats(mysqlnd_global_stats, (values) ZEND_FILE_LINE_CC)
#define mysqlnd_close(conn,is_forced) (conn)->m->close((conn), (is_forced))
#define mysqlnd_query(conn, query_str, query_len) ((conn)->data)->m->query((conn)->data, (query_str), (query_len))
@@ -179,7 +185,7 @@ PHPAPI void mysqlnd_free_param_bind_dtor(MYSQLND_PARAM_BIND * param_bind);
PHPAPI void mysqlnd_free_result_bind_dtor(MYSQLND_RESULT_BIND * result_bind);
-PHPAPI const char * mysqlnd_field_type_name(enum mysqlnd_field_types field_type);
+PHPAPI const char * mysqlnd_field_type_name(const enum mysqlnd_field_types field_type);
/* LOAD DATA LOCAL */
void mysqlnd_local_infile_default(MYSQLND_CONN_DATA * conn);
@@ -192,7 +198,6 @@ void mysqlnd_local_infile_default(MYSQLND_CONN_DATA * conn);
#define mysqlnd_savepoint(conn, name) ((conn)->data)->m->tx_savepoint((conn)->data, (name))
#define mysqlnd_release_savepoint(conn, name) ((conn)->data)->m->tx_savepoint_release((conn)->data, (name))
#define mysqlnd_list_dbs(conn, wild) ((conn)->data)->m->list_method((conn)->data, wild? "SHOW DATABASES LIKE %s":"SHOW DATABASES", (wild), NULL)
-#define mysqlnd_list_fields(conn, tab,wild) ((conn)->data)->m->list_fields((conn)->data, (tab), (wild))
#define mysqlnd_list_processes(conn) ((conn)->data)->m->list_method((conn)->data, "SHOW PROCESSLIST", NULL, NULL)
#define mysqlnd_list_tables(conn, wild) ((conn)->data)->m->list_method((conn)->data, wild? "SHOW TABLES LIKE %s":"SHOW TABLES", (wild), NULL)
#define mysqlnd_dump_debug_info(conn) ((conn)->data)->m->server_dump_debug_information((conn)->data)
@@ -217,14 +222,14 @@ PHPAPI zend_ulong mysqlnd_old_escape_string(char * newstr, const char * escapest
/* PS */
-#define mysqlnd_stmt_init(conn) ((conn)->data)->m->stmt_init(((conn)->data))
-#define mysqlnd_stmt_store_result(stmt) (!mysqlnd_stmt_field_count((stmt)) ? PASS:((stmt)->m->store_result((stmt))? PASS:FAIL))
-#define mysqlnd_stmt_get_result(stmt) (stmt)->m->get_result((stmt))
-#define mysqlnd_stmt_more_results(stmt) (stmt)->m->more_results((stmt))
-#define mysqlnd_stmt_next_result(stmt) (stmt)->m->next_result((stmt))
-#define mysqlnd_stmt_data_seek(stmt, row) (stmt)->m->seek_data((stmt), (row))
-#define mysqlnd_stmt_prepare(stmt, q, qlen) (stmt)->m->prepare((stmt), (q), (qlen))
-#define mysqlnd_stmt_execute(stmt) (stmt)->m->execute((stmt))
+#define mysqlnd_stmt_init(conn) ((conn)->data)->m->stmt_init(((conn)->data))
+#define mysqlnd_stmt_store_result(stmt) (!mysqlnd_stmt_field_count((stmt)) ? PASS:((stmt)->m->store_result((stmt))? PASS:FAIL))
+#define mysqlnd_stmt_get_result(stmt) (stmt)->m->get_result((stmt))
+#define mysqlnd_stmt_more_results(stmt) (stmt)->m->more_results((stmt))
+#define mysqlnd_stmt_next_result(stmt) (stmt)->m->next_result((stmt))
+#define mysqlnd_stmt_data_seek(stmt, row) (stmt)->m->seek_data((stmt), (row))
+#define mysqlnd_stmt_prepare(stmt, q, qlen) (stmt)->m->prepare((stmt), (q), (qlen))
+#define mysqlnd_stmt_execute(stmt) (stmt)->m->execute((stmt))
#define mysqlnd_stmt_send_long_data(stmt,p,d,l) (stmt)->m->send_long_data((stmt), (p), (d), (l))
#define mysqlnd_stmt_alloc_param_bind(stmt) (stmt)->m->alloc_parameter_bind((stmt))
#define mysqlnd_stmt_free_param_bind(stmt,bind) (stmt)->m->free_parameter_bind((stmt), (bind))
@@ -238,10 +243,10 @@ PHPAPI zend_ulong mysqlnd_old_escape_string(char * newstr, const char * escapest
#define mysqlnd_stmt_param_metadata(stmt) (stmt)->m->get_parameter_metadata((stmt))
#define mysqlnd_stmt_result_metadata(stmt) (stmt)->m->get_result_metadata((stmt))
-#define mysqlnd_stmt_free_result(stmt) (stmt)->m->free_result((stmt))
-#define mysqlnd_stmt_close(stmt, implicit) (stmt)->m->dtor((stmt), (implicit))
-#define mysqlnd_stmt_reset(stmt) (stmt)->m->reset((stmt))
-#define mysqlnd_stmt_flush(stmt) (stmt)->m->flush((stmt))
+#define mysqlnd_stmt_free_result(stmt) (stmt)->m->free_result((stmt))
+#define mysqlnd_stmt_close(stmt, implicit) (stmt)->m->dtor((stmt), (implicit))
+#define mysqlnd_stmt_reset(stmt) (stmt)->m->reset((stmt))
+#define mysqlnd_stmt_flush(stmt) (stmt)->m->flush((stmt))
#define mysqlnd_stmt_attr_get(stmt, attr, value) (stmt)->m->get_attribute((stmt), (attr), (value))
@@ -251,11 +256,54 @@ PHPAPI zend_ulong mysqlnd_old_escape_string(char * newstr, const char * escapest
/* Performance statistics */
-PHPAPI void _mysqlnd_get_client_stats(zval *return_value ZEND_FILE_LINE_DC);
+PHPAPI extern MYSQLND_STATS * mysqlnd_global_stats;
+PHPAPI extern const MYSQLND_STRING mysqlnd_stats_values_names[];
+PHPAPI void _mysqlnd_get_client_stats(MYSQLND_STATS * stats, zval *return_value ZEND_FILE_LINE_DC);
+
+
+#ifndef MYSQLND_CORE_STATISTICS_DISABLED
+
+#define MYSQLND_INC_GLOBAL_STATISTIC(statistic) \
+ MYSQLND_INC_STATISTIC(MYSQLND_G(collect_statistics), mysqlnd_global_stats, (statistic))
+
+#define MYSQLND_DEC_GLOBAL_STATISTIC(statistic) \
+ MYSQLND_DEC_STATISTIC(MYSQLND_G(collect_statistics), mysqlnd_global_stats, (statistic))
+
+#define MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(statistic1, value1, statistic2, value2) \
+ MYSQLND_INC_STATISTIC_W_VALUE2(MYSQLND_G(collect_statistics), mysqlnd_global_stats, (statistic1), (value1), (statistic2), (value2))
+
+#define MYSQLND_INC_CONN_STATISTIC(conn_stats, statistic) \
+ MYSQLND_INC_STATISTIC(MYSQLND_G(collect_statistics), mysqlnd_global_stats, (statistic)); \
+ MYSQLND_INC_STATISTIC(MYSQLND_G(collect_statistics), (conn_stats), (statistic));
+
+#define MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn_stats, statistic, value) \
+ MYSQLND_INC_STATISTIC_W_VALUE(MYSQLND_G(collect_statistics), mysqlnd_global_stats, (statistic), (value)); \
+ MYSQLND_INC_STATISTIC_W_VALUE(MYSQLND_G(collect_statistics), (conn_stats), (statistic), (value));
+
+#define MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn_stats, statistic1, value1, statistic2, value2) \
+ MYSQLND_INC_STATISTIC_W_VALUE2(MYSQLND_G(collect_statistics), mysqlnd_global_stats, (statistic1), (value1), (statistic2), (value2)); \
+ MYSQLND_INC_STATISTIC_W_VALUE2(MYSQLND_G(collect_statistics), (conn_stats), (statistic1), (value1), (statistic2), (value2));
+
+#define MYSQLND_INC_CONN_STATISTIC_W_VALUE3(conn_stats, statistic1, value1, statistic2, value2, statistic3, value3) \
+ MYSQLND_INC_STATISTIC_W_VALUE3(MYSQLND_G(collect_statistics), mysqlnd_global_stats, (statistic1), (value1), (statistic2), (value2), (statistic3), (value3)); \
+ MYSQLND_INC_STATISTIC_W_VALUE3(MYSQLND_G(collect_statistics), (conn_stats), (statistic1), (value1), (statistic2), (value2), (statistic3), (value3));
+
+#else
+
+#define MYSQLND_INC_GLOBAL_STATISTIC(statistic)
+#define MYSQLND_DEC_GLOBAL_STATISTIC(statistic)
+#define MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(statistic1, value1, statistic2, value2)
+#define MYSQLND_INC_CONN_STATISTIC(conn_stats, statistic)
+#define MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn_stats, statistic, value)
+#define MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn_stats, statistic1, value1, statistic2, value2)
+#define MYSQLND_INC_CONN_STATISTIC_W_VALUE3(conn_stats, statistic1, value1, statistic2, value2, statistic3, value3)
+
+#endif /* MYSQLND_CORE_STATISTICS_DISABLED */
+
/* double check the class name to avoid naming conflicts when using these: */
-#define MYSQLND_METHOD(class, method) php_##class##_##method##_pub
-#define MYSQLND_METHOD_PRIVATE(class, method) php_##class##_##method##_priv
+#define MYSQLND_METHOD(class, method) mysqlnd_##class##_##method##_pub
+#define MYSQLND_METHOD_PRIVATE(class, method) mysqlnd_##class##_##method##_priv
ZEND_BEGIN_MODULE_GLOBALS(mysqlnd)
char * debug; /* The actual string */
diff --git a/ext/mysqlnd/mysqlnd_alloc.c b/ext/mysqlnd/mysqlnd_alloc.c
index aa4c598a73..b7e1ee4b98 100644
--- a/ext/mysqlnd/mysqlnd_alloc.c
+++ b/ext/mysqlnd/mysqlnd_alloc.c
@@ -14,11 +14,9 @@
+----------------------------------------------------------------------+
| Authors: Andrey Hristov <andrey@php.net> |
| Ulf Wendel <uw@php.net> |
- | Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
-/* $Id: mysqlnd_debug.c 309303 2011-03-16 12:42:59Z andrey $ */
#include "php.h"
#include "mysqlnd.h"
#include "mysqlnd_priv.h"
@@ -26,6 +24,7 @@
#include "mysqlnd_wireprotocol.h"
#include "mysqlnd_statistics.h"
+#define MYSQLND_DEBUG_MEMORY 1
static const char mysqlnd_emalloc_name[] = "_mysqlnd_emalloc";
static const char mysqlnd_pemalloc_name[] = "_mysqlnd_pemalloc";
@@ -39,6 +38,7 @@ static const char mysqlnd_malloc_name[] = "_mysqlnd_malloc";
static const char mysqlnd_calloc_name[] = "_mysqlnd_calloc";
static const char mysqlnd_realloc_name[] = "_mysqlnd_realloc";
static const char mysqlnd_free_name[] = "_mysqlnd_free";
+static const char mysqlnd_pememdup_name[] = "_mysqlnd_pememdup";
static const char mysqlnd_pestrndup_name[] = "_mysqlnd_pestrndup";
static const char mysqlnd_pestrdup_name[] = "_mysqlnd_pestrdup";
@@ -62,6 +62,8 @@ PHPAPI const char * mysqlnd_debug_std_no_trace_funcs[] =
NULL /* must be always last */
};
+#if MYSQLND_DEBUG_MEMORY
+
#if ZEND_DEBUG
#else
@@ -74,7 +76,7 @@ PHPAPI const char * mysqlnd_debug_std_no_trace_funcs[] =
#define FAKE_PTR(p) (collect_memory_statistics && (p)? (((char *)(p)) + sizeof(size_t)) : (p))
/* {{{ _mysqlnd_emalloc */
-void * _mysqlnd_emalloc(size_t size MYSQLND_MEM_D)
+static void * _mysqlnd_emalloc(size_t size MYSQLND_MEM_D)
{
void *ret;
zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
@@ -85,8 +87,8 @@ void * _mysqlnd_emalloc(size_t size MYSQLND_MEM_D)
#if PHP_DEBUG
{
- char * fn = strrchr(__zend_orig_filename, PHP_DIR_SEPARATOR);
- TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_orig_filename, __zend_orig_lineno);
+ char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR);
+ TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno);
}
#endif
@@ -94,7 +96,7 @@ void * _mysqlnd_emalloc(size_t size MYSQLND_MEM_D)
/* -1 is also "true" */
if (*threshold) {
#endif
- ret = _emalloc(REAL_SIZE(size) ZEND_FILE_LINE_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
+ ret = emalloc_rel(REAL_SIZE(size));
#if PHP_DEBUG
--*threshold;
} else if (*threshold == 0) {
@@ -114,7 +116,7 @@ void * _mysqlnd_emalloc(size_t size MYSQLND_MEM_D)
/* {{{ _mysqlnd_pemalloc */
-void * _mysqlnd_pemalloc(size_t size, zend_bool persistent MYSQLND_MEM_D)
+static void * _mysqlnd_pemalloc(size_t size, zend_bool persistent MYSQLND_MEM_D)
{
void *ret;
zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
@@ -125,8 +127,8 @@ void * _mysqlnd_pemalloc(size_t size, zend_bool persistent MYSQLND_MEM_D)
#if PHP_DEBUG
{
- char * fn = strrchr(__zend_orig_filename, PHP_DIR_SEPARATOR);
- TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_orig_filename, __zend_orig_lineno);
+ char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR);
+ TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno);
}
#endif
@@ -134,7 +136,7 @@ void * _mysqlnd_pemalloc(size_t size, zend_bool persistent MYSQLND_MEM_D)
/* -1 is also "true" */
if (*threshold) {
#endif
- ret = (persistent) ? __zend_malloc(REAL_SIZE(size)) : _emalloc(REAL_SIZE(size) ZEND_FILE_LINE_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
+ ret = pemalloc_rel(REAL_SIZE(size), persistent);
#if PHP_DEBUG
--*threshold;
} else if (*threshold == 0) {
@@ -157,7 +159,7 @@ void * _mysqlnd_pemalloc(size_t size, zend_bool persistent MYSQLND_MEM_D)
/* {{{ _mysqlnd_ecalloc */
-void * _mysqlnd_ecalloc(unsigned int nmemb, size_t size MYSQLND_MEM_D)
+static void * _mysqlnd_ecalloc(unsigned int nmemb, size_t size MYSQLND_MEM_D)
{
void *ret;
zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
@@ -168,8 +170,8 @@ void * _mysqlnd_ecalloc(unsigned int nmemb, size_t size MYSQLND_MEM_D)
#if PHP_DEBUG
{
- char * fn = strrchr(__zend_orig_filename, PHP_DIR_SEPARATOR);
- TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_orig_filename, __zend_orig_lineno);
+ char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR);
+ TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno);
}
#endif
TRACE_ALLOC_INF_FMT("before: %lu", zend_memory_usage(FALSE));
@@ -178,7 +180,7 @@ void * _mysqlnd_ecalloc(unsigned int nmemb, size_t size MYSQLND_MEM_D)
/* -1 is also "true" */
if (*threshold) {
#endif
- ret = _ecalloc(nmemb, REAL_SIZE(size) ZEND_FILE_LINE_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
+ ret = ecalloc_rel(nmemb, REAL_SIZE(size));
#if PHP_DEBUG
--*threshold;
} else if (*threshold == 0) {
@@ -198,7 +200,7 @@ void * _mysqlnd_ecalloc(unsigned int nmemb, size_t size MYSQLND_MEM_D)
/* {{{ _mysqlnd_pecalloc */
-void * _mysqlnd_pecalloc(unsigned int nmemb, size_t size, zend_bool persistent MYSQLND_MEM_D)
+static void * _mysqlnd_pecalloc(unsigned int nmemb, size_t size, zend_bool persistent MYSQLND_MEM_D)
{
void *ret;
zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
@@ -208,8 +210,8 @@ void * _mysqlnd_pecalloc(unsigned int nmemb, size_t size, zend_bool persistent M
TRACE_ALLOC_ENTER(mysqlnd_pecalloc_name);
#if PHP_DEBUG
{
- char * fn = strrchr(__zend_orig_filename, PHP_DIR_SEPARATOR);
- TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_orig_filename, __zend_orig_lineno);
+ char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR);
+ TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno);
}
#endif
@@ -217,7 +219,7 @@ void * _mysqlnd_pecalloc(unsigned int nmemb, size_t size, zend_bool persistent M
/* -1 is also "true" */
if (*threshold) {
#endif
- ret = (persistent) ? __zend_calloc(nmemb, REAL_SIZE(size)) : _ecalloc(nmemb, REAL_SIZE(size) ZEND_FILE_LINE_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
+ ret = pecalloc_rel(nmemb, REAL_SIZE(size), persistent);
#if PHP_DEBUG
--*threshold;
} else if (*threshold == 0) {
@@ -240,7 +242,7 @@ void * _mysqlnd_pecalloc(unsigned int nmemb, size_t size, zend_bool persistent M
/* {{{ _mysqlnd_erealloc */
-void * _mysqlnd_erealloc(void *ptr, size_t new_size MYSQLND_MEM_D)
+static void * _mysqlnd_erealloc(void *ptr, size_t new_size MYSQLND_MEM_D)
{
void *ret;
zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
@@ -252,8 +254,8 @@ void * _mysqlnd_erealloc(void *ptr, size_t new_size MYSQLND_MEM_D)
#if PHP_DEBUG
{
- char * fn = strrchr(__zend_orig_filename, PHP_DIR_SEPARATOR);
- TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_orig_filename, __zend_orig_lineno);
+ char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR);
+ TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno);
}
#endif
TRACE_ALLOC_INF_FMT("ptr=%p old_size=%lu, new_size=%lu", ptr, old_size, new_size);
@@ -262,7 +264,7 @@ void * _mysqlnd_erealloc(void *ptr, size_t new_size MYSQLND_MEM_D)
/* -1 is also "true" */
if (*threshold) {
#endif
- ret = _erealloc(REAL_PTR(ptr), REAL_SIZE(new_size) ZEND_FILE_LINE_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
+ ret = erealloc_rel(REAL_PTR(ptr), REAL_SIZE(new_size));
#if PHP_DEBUG
--*threshold;
} else if (*threshold == 0) {
@@ -281,7 +283,7 @@ void * _mysqlnd_erealloc(void *ptr, size_t new_size MYSQLND_MEM_D)
/* {{{ _mysqlnd_perealloc */
-void * _mysqlnd_perealloc(void *ptr, size_t new_size, zend_bool persistent MYSQLND_MEM_D)
+static void * _mysqlnd_perealloc(void *ptr, size_t new_size, zend_bool persistent MYSQLND_MEM_D)
{
void *ret;
zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
@@ -293,8 +295,8 @@ void * _mysqlnd_perealloc(void *ptr, size_t new_size, zend_bool persistent MYSQL
#if PHP_DEBUG
{
- char * fn = strrchr(__zend_orig_filename, PHP_DIR_SEPARATOR);
- TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_orig_filename, __zend_orig_lineno);
+ char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR);
+ TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno);
}
#endif
TRACE_ALLOC_INF_FMT("ptr=%p old_size=%lu new_size=%lu persistent=%u", ptr, old_size, new_size, persistent);
@@ -303,7 +305,7 @@ void * _mysqlnd_perealloc(void *ptr, size_t new_size, zend_bool persistent MYSQL
/* -1 is also "true" */
if (*threshold) {
#endif
- ret = perealloc(REAL_PTR(ptr), REAL_SIZE(new_size), persistent);
+ ret = perealloc_rel(REAL_PTR(ptr), REAL_SIZE(new_size), persistent);
#if PHP_DEBUG
--*threshold;
} else if (*threshold == 0) {
@@ -325,7 +327,7 @@ void * _mysqlnd_perealloc(void *ptr, size_t new_size, zend_bool persistent MYSQL
/* {{{ _mysqlnd_efree */
-void _mysqlnd_efree(void *ptr MYSQLND_MEM_D)
+static void _mysqlnd_efree(void *ptr MYSQLND_MEM_D)
{
size_t free_amount = 0;
zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
@@ -333,8 +335,8 @@ void _mysqlnd_efree(void *ptr MYSQLND_MEM_D)
#if PHP_DEBUG
{
- char * fn = strrchr(__zend_orig_filename, PHP_DIR_SEPARATOR);
- TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_orig_filename, __zend_orig_lineno);
+ char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR);
+ TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno);
}
#endif
TRACE_ALLOC_INF_FMT("ptr=%p", ptr);
@@ -344,7 +346,7 @@ void _mysqlnd_efree(void *ptr MYSQLND_MEM_D)
free_amount = *(size_t *)(((char*)ptr) - sizeof(size_t));
TRACE_ALLOC_INF_FMT("ptr=%p size=%u", ((char*)ptr) - sizeof(size_t), (unsigned int) free_amount);
}
- _efree(REAL_PTR(ptr) ZEND_FILE_LINE_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
+ efree_rel(REAL_PTR(ptr));
}
if (collect_memory_statistics) {
@@ -356,7 +358,7 @@ void _mysqlnd_efree(void *ptr MYSQLND_MEM_D)
/* {{{ _mysqlnd_pefree */
-void _mysqlnd_pefree(void *ptr, zend_bool persistent MYSQLND_MEM_D)
+static void _mysqlnd_pefree(void *ptr, zend_bool persistent MYSQLND_MEM_D)
{
size_t free_amount = 0;
zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
@@ -364,8 +366,8 @@ void _mysqlnd_pefree(void *ptr, zend_bool persistent MYSQLND_MEM_D)
#if PHP_DEBUG
{
- char * fn = strrchr(__zend_orig_filename, PHP_DIR_SEPARATOR);
- TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_orig_filename, __zend_orig_lineno);
+ char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR);
+ TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno);
}
#endif
TRACE_ALLOC_INF_FMT("ptr=%p persistent=%u", ptr, persistent);
@@ -375,7 +377,7 @@ void _mysqlnd_pefree(void *ptr, zend_bool persistent MYSQLND_MEM_D)
free_amount = *(size_t *)(((char*)ptr) - sizeof(size_t));
TRACE_ALLOC_INF_FMT("ptr=%p size=%u", ((char*)ptr) - sizeof(size_t), (unsigned int) free_amount);
}
- (persistent) ? free(REAL_PTR(ptr)) : _efree(REAL_PTR(ptr) ZEND_FILE_LINE_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
+ pefree_rel(REAL_PTR(ptr), persistent);
}
if (collect_memory_statistics) {
@@ -388,7 +390,7 @@ void _mysqlnd_pefree(void *ptr, zend_bool persistent MYSQLND_MEM_D)
/* {{{ _mysqlnd_malloc */
-void * _mysqlnd_malloc(size_t size MYSQLND_MEM_D)
+static void * _mysqlnd_malloc(size_t size MYSQLND_MEM_D)
{
void *ret;
zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
@@ -399,8 +401,8 @@ void * _mysqlnd_malloc(size_t size MYSQLND_MEM_D)
#if PHP_DEBUG
{
- char * fn = strrchr(__zend_orig_filename, PHP_DIR_SEPARATOR);
- TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_orig_filename, __zend_orig_lineno);
+ char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR);
+ TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno);
}
#endif
@@ -427,7 +429,7 @@ void * _mysqlnd_malloc(size_t size MYSQLND_MEM_D)
/* {{{ _mysqlnd_calloc */
-void * _mysqlnd_calloc(unsigned int nmemb, size_t size MYSQLND_MEM_D)
+static void * _mysqlnd_calloc(unsigned int nmemb, size_t size MYSQLND_MEM_D)
{
void *ret;
zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
@@ -438,8 +440,8 @@ void * _mysqlnd_calloc(unsigned int nmemb, size_t size MYSQLND_MEM_D)
#if PHP_DEBUG
{
- char * fn = strrchr(__zend_orig_filename, PHP_DIR_SEPARATOR);
- TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_orig_filename, __zend_orig_lineno);
+ char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR);
+ TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno);
}
#endif
@@ -466,7 +468,7 @@ void * _mysqlnd_calloc(unsigned int nmemb, size_t size MYSQLND_MEM_D)
/* {{{ _mysqlnd_realloc */
-void * _mysqlnd_realloc(void *ptr, size_t new_size MYSQLND_MEM_D)
+static void * _mysqlnd_realloc(void *ptr, size_t new_size MYSQLND_MEM_D)
{
void *ret;
zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
@@ -477,8 +479,8 @@ void * _mysqlnd_realloc(void *ptr, size_t new_size MYSQLND_MEM_D)
#if PHP_DEBUG
{
- char * fn = strrchr(__zend_orig_filename, PHP_DIR_SEPARATOR);
- TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_orig_filename, __zend_orig_lineno);
+ char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR);
+ TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno);
}
#endif
TRACE_ALLOC_INF_FMT("ptr=%p new_size=%lu ", new_size, ptr);
@@ -508,7 +510,7 @@ void * _mysqlnd_realloc(void *ptr, size_t new_size MYSQLND_MEM_D)
/* {{{ _mysqlnd_free */
-void _mysqlnd_free(void *ptr MYSQLND_MEM_D)
+static void _mysqlnd_free(void *ptr MYSQLND_MEM_D)
{
size_t free_amount = 0;
zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
@@ -516,8 +518,8 @@ void _mysqlnd_free(void *ptr MYSQLND_MEM_D)
#if PHP_DEBUG
{
- char * fn = strrchr(__zend_orig_filename, PHP_DIR_SEPARATOR);
- TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_orig_filename, __zend_orig_lineno);
+ char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR);
+ TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno);
}
#endif
TRACE_ALLOC_INF_FMT("ptr=%p", ptr);
@@ -537,13 +539,40 @@ void _mysqlnd_free(void *ptr MYSQLND_MEM_D)
}
/* }}} */
-#define SMART_STR_START_SIZE 2048
-#define SMART_STR_PREALLOC 512
-#include "zend_smart_str.h"
+
+/* {{{ _mysqlnd_pememdup */
+static char * _mysqlnd_pememdup(const char * const ptr, size_t length, zend_bool persistent MYSQLND_MEM_D)
+{
+ char * ret;
+ zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
+ TRACE_ALLOC_ENTER(mysqlnd_pememdup_name);
+
+#if PHP_DEBUG
+ {
+ char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR);
+ TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno);
+ }
+#endif
+ TRACE_ALLOC_INF_FMT("ptr=%p", ptr);
+
+ ret = pemalloc_rel(REAL_SIZE(length + 1), persistent);
+ {
+ char * dest = (char *) FAKE_PTR(ret);
+ memcpy(dest, ptr, length);
+ }
+
+ if (collect_memory_statistics) {
+ *(size_t *) ret = length;
+ MYSQLND_INC_GLOBAL_STATISTIC(persistent? STAT_MEM_DUP_COUNT : STAT_MEM_EDUP_COUNT);
+ }
+
+ TRACE_ALLOC_RETURN(FAKE_PTR(ret));
+}
+/* }}} */
/* {{{ _mysqlnd_pestrndup */
-char * _mysqlnd_pestrndup(const char * const ptr, size_t length, zend_bool persistent MYSQLND_MEM_D)
+static char * _mysqlnd_pestrndup(const char * const ptr, size_t length, zend_bool persistent MYSQLND_MEM_D)
{
char * ret;
zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
@@ -551,13 +580,13 @@ char * _mysqlnd_pestrndup(const char * const ptr, size_t length, zend_bool persi
#if PHP_DEBUG
{
- char * fn = strrchr(__zend_orig_filename, PHP_DIR_SEPARATOR);
- TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_orig_filename, __zend_orig_lineno);
+ char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR);
+ TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno);
}
#endif
TRACE_ALLOC_INF_FMT("ptr=%p", ptr);
- ret = (persistent) ? __zend_malloc(REAL_SIZE(length + 1)) : _emalloc(REAL_SIZE(length + 1) ZEND_FILE_LINE_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
+ ret = pemalloc_rel(REAL_SIZE(length + 1), persistent);
{
size_t l = length;
char * p = (char *) ptr;
@@ -578,8 +607,13 @@ char * _mysqlnd_pestrndup(const char * const ptr, size_t length, zend_bool persi
/* }}} */
+#define SMART_STR_START_SIZE 2048
+#define SMART_STR_PREALLOC 512
+#include "zend_smart_str.h"
+
+
/* {{{ _mysqlnd_pestrdup */
-char * _mysqlnd_pestrdup(const char * const ptr, zend_bool persistent MYSQLND_MEM_D)
+static char * _mysqlnd_pestrdup(const char * const ptr, zend_bool persistent MYSQLND_MEM_D)
{
char * ret;
smart_str tmp_str = {0, 0};
@@ -588,8 +622,8 @@ char * _mysqlnd_pestrdup(const char * const ptr, zend_bool persistent MYSQLND_ME
TRACE_ALLOC_ENTER(mysqlnd_pestrdup_name);
#if PHP_DEBUG
{
- char * fn = strrchr(__zend_orig_filename, PHP_DIR_SEPARATOR);
- TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_orig_filename, __zend_orig_lineno);
+ char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR);
+ TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno);
}
#endif
TRACE_ALLOC_INF_FMT("ptr=%p", ptr);
@@ -597,7 +631,7 @@ char * _mysqlnd_pestrdup(const char * const ptr, zend_bool persistent MYSQLND_ME
smart_str_appendc(&tmp_str, *p);
} while (*p++);
- ret = (persistent) ? __zend_malloc(ZSTR_LEN(tmp_str.s) + sizeof(size_t)) : _emalloc(REAL_SIZE(ZSTR_LEN(tmp_str.s) + sizeof(size_t)) ZEND_FILE_LINE_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
+ ret = pemalloc_rel(ZSTR_LEN(tmp_str.s) + sizeof(size_t), persistent);
memcpy(FAKE_PTR(ret), ZSTR_VAL(tmp_str.s), ZSTR_LEN(tmp_str.s));
if (ret && collect_memory_statistics) {
@@ -611,8 +645,20 @@ char * _mysqlnd_pestrdup(const char * const ptr, zend_bool persistent MYSQLND_ME
/* }}} */
+
+
+#endif /* MYSQLND_DEBUG_MEMORY */
+
+/* {{{ _mysqlnd_sprintf_free */
+static void _mysqlnd_sprintf_free(char * p)
+{
+ efree(p);
+}
+/* }}} */
+
+
/* {{{ _mysqlnd_sprintf */
-PHPAPI int _mysqlnd_sprintf(char ** pbuf, size_t max_len, const char *format, ...)
+static int _mysqlnd_sprintf(char ** pbuf, size_t max_len, const char *format, ...)
{
int len;
va_list ap;
@@ -623,30 +669,21 @@ PHPAPI int _mysqlnd_sprintf(char ** pbuf, size_t max_len, const char *format, ..
}
/* }}} */
-
-/* {{{ _mysqlnd_sprintf_free */
-PHPAPI void _mysqlnd_sprintf_free(char * p)
-{
- efree(p);
-}
-/* }}} */
-
/* {{{ _mysqlnd_vsprintf */
-PHPAPI int _mysqlnd_vsprintf(char ** pbuf, size_t max_len, const char * format, va_list ap)
+static int _mysqlnd_vsprintf(char ** pbuf, size_t max_len, const char * format, va_list ap)
{
return vspprintf(pbuf, max_len, format, ap);
}
/* }}} */
-#define MYSQLND_DEBUG_MEMORY 1
#if MYSQLND_DEBUG_MEMORY == 0
/* {{{ mysqlnd_zend_mm_emalloc */
static void * mysqlnd_zend_mm_emalloc(size_t size MYSQLND_MEM_D)
{
- return emalloc(size);
+ return emalloc_rel(size);
}
/* }}} */
@@ -654,7 +691,7 @@ static void * mysqlnd_zend_mm_emalloc(size_t size MYSQLND_MEM_D)
/* {{{ mysqlnd_zend_mm_pemalloc */
static void * mysqlnd_zend_mm_pemalloc(size_t size, zend_bool persistent MYSQLND_MEM_D)
{
- return pemalloc(size, persistent);
+ return pemalloc_rel(size, persistent);
}
/* }}} */
@@ -662,7 +699,7 @@ static void * mysqlnd_zend_mm_pemalloc(size_t size, zend_bool persistent MYSQLND
/* {{{ mysqlnd_zend_mm_ecalloc */
static void * mysqlnd_zend_mm_ecalloc(unsigned int nmemb, size_t size MYSQLND_MEM_D)
{
- return ecalloc(nmemb, size);
+ return ecalloc_rel(nmemb, size);
}
/* }}} */
@@ -670,7 +707,7 @@ static void * mysqlnd_zend_mm_ecalloc(unsigned int nmemb, size_t size MYSQLND_ME
/* {{{ mysqlnd_zend_mm_pecalloc */
static void * mysqlnd_zend_mm_pecalloc(unsigned int nmemb, size_t size, zend_bool persistent MYSQLND_MEM_D)
{
- return pecalloc(nmemb, size, persistent);
+ return pecalloc_rel(nmemb, size, persistent);
}
/* }}} */
@@ -678,7 +715,7 @@ static void * mysqlnd_zend_mm_pecalloc(unsigned int nmemb, size_t size, zend_boo
/* {{{ mysqlnd_zend_mm_erealloc */
static void * mysqlnd_zend_mm_erealloc(void *ptr, size_t new_size MYSQLND_MEM_D)
{
- return erealloc(ptr, new_size);
+ return erealloc_rel(ptr, new_size);
}
/* }}} */
@@ -686,7 +723,7 @@ static void * mysqlnd_zend_mm_erealloc(void *ptr, size_t new_size MYSQLND_MEM_D)
/* {{{ mysqlnd_zend_mm_perealloc */
static void * mysqlnd_zend_mm_perealloc(void *ptr, size_t new_size, zend_bool persistent MYSQLND_MEM_D)
{
- return perealloc(ptr, new_size, persistent);
+ return perealloc_rel(ptr, new_size, persistent);
}
/* }}} */
@@ -694,7 +731,7 @@ static void * mysqlnd_zend_mm_perealloc(void *ptr, size_t new_size, zend_bool pe
/* {{{ mysqlnd_zend_mm_efree */
static void mysqlnd_zend_mm_efree(void * ptr MYSQLND_MEM_D)
{
- efree(ptr);
+ efree_rel(ptr);
}
/* }}} */
@@ -702,7 +739,7 @@ static void mysqlnd_zend_mm_efree(void * ptr MYSQLND_MEM_D)
/* {{{ mysqlnd_zend_mm_pefree */
static void mysqlnd_zend_mm_pefree(void * ptr, zend_bool persistent MYSQLND_MEM_D)
{
- pefree(ptr, persistent);
+ pefree_rel(ptr, persistent);
}
/* }}} */
@@ -739,10 +776,22 @@ static void mysqlnd_zend_mm_free(void * ptr MYSQLND_MEM_D)
/* }}} */
+/* {{{ mysqlnd_zend_mm_pememdup */
+static char * mysqlnd_zend_mm_pememdup(const char * const ptr, size_t length, zend_bool persistent MYSQLND_MEM_D)
+{
+ char * dest = pemalloc_rel(length, persistent);
+ if (dest) {
+ memcpy(dest, ptr, length);
+ }
+ return dest;
+}
+/* }}} */
+
+
/* {{{ mysqlnd_zend_mm_pestrndup */
static char * mysqlnd_zend_mm_pestrndup(const char * const ptr, size_t length, zend_bool persistent MYSQLND_MEM_D)
{
- return pestrndup(ptr, length, persistent);
+ return persistent? zend_strndup(ptr, length ) : estrndup_rel(ptr, length);
}
/* }}} */
@@ -750,7 +799,7 @@ static char * mysqlnd_zend_mm_pestrndup(const char * const ptr, size_t length, z
/* {{{ mysqlnd_zend_mm_pestrdup */
static char * mysqlnd_zend_mm_pestrdup(const char * const ptr, zend_bool persistent MYSQLND_MEM_D)
{
- return pestrdup(ptr, persistent);
+ return pestrdup_rel(ptr, persistent);
}
/* }}} */
@@ -759,7 +808,7 @@ static char * mysqlnd_zend_mm_pestrdup(const char * const ptr, zend_bool persist
PHPAPI struct st_mysqlnd_allocator_methods mysqlnd_allocator =
{
-#if MYSQLND_DEBUG_MEMORY
+#if MYSQLND_DEBUG_MEMORY == 1
_mysqlnd_emalloc,
_mysqlnd_pemalloc,
_mysqlnd_ecalloc,
@@ -772,6 +821,7 @@ PHPAPI struct st_mysqlnd_allocator_methods mysqlnd_allocator =
_mysqlnd_calloc,
_mysqlnd_realloc,
_mysqlnd_free,
+ _mysqlnd_pememdup,
_mysqlnd_pestrndup,
_mysqlnd_pestrdup,
_mysqlnd_sprintf,
@@ -790,10 +840,12 @@ PHPAPI struct st_mysqlnd_allocator_methods mysqlnd_allocator =
mysqlnd_zend_mm_calloc,
mysqlnd_zend_mm_realloc,
mysqlnd_zend_mm_free,
+ mysqlnd_zend_mm_pememdup,
mysqlnd_zend_mm_pestrndup,
- mysqlnd_zend_mm_pestrdup
- sprintf,
- mysqlnd_zend_mm_efree,
+ mysqlnd_zend_mm_pestrdup,
+ _mysqlnd_sprintf,
+ _mysqlnd_vsprintf,
+ _mysqlnd_sprintf_free,
#endif
};
diff --git a/ext/mysqlnd/mysqlnd_alloc.h b/ext/mysqlnd/mysqlnd_alloc.h
index fa3edaa68d..c648f1446e 100644
--- a/ext/mysqlnd/mysqlnd_alloc.h
+++ b/ext/mysqlnd/mysqlnd_alloc.h
@@ -14,7 +14,6 @@
+----------------------------------------------------------------------+
| Authors: Andrey Hristov <andrey@php.net> |
| Ulf Wendel <uw@php.net> |
- | Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
@@ -23,7 +22,7 @@
PHPAPI extern const char * mysqlnd_debug_std_no_trace_funcs[];
-#define MYSQLND_MEM_D ZEND_FILE_LINE_ORIG_DC
+#define MYSQLND_MEM_D ZEND_FILE_LINE_DC
#define MYSQLND_MEM_C ZEND_FILE_LINE_CC
struct st_mysqlnd_allocator_methods
@@ -40,6 +39,7 @@ struct st_mysqlnd_allocator_methods
void * (*m_calloc)(unsigned int nmemb, size_t size MYSQLND_MEM_D);
void * (*m_realloc)(void *ptr, size_t new_size MYSQLND_MEM_D);
void (*m_free)(void *ptr MYSQLND_MEM_D);
+ char * (*m_pememdup)(const char * const ptr, size_t size, zend_bool persistent MYSQLND_MEM_D);
char * (*m_pestrndup)(const char * const ptr, size_t size, zend_bool persistent MYSQLND_MEM_D);
char * (*m_pestrdup)(const char * const ptr, zend_bool persistent MYSQLND_MEM_D);
int (*m_sprintf)(char **pbuf, size_t max_len, const char *format, ...);
@@ -49,24 +49,6 @@ struct st_mysqlnd_allocator_methods
PHPAPI extern struct st_mysqlnd_allocator_methods mysqlnd_allocator;
-PHPAPI void * _mysqlnd_emalloc(size_t size MYSQLND_MEM_D);
-PHPAPI void * _mysqlnd_pemalloc(size_t size, zend_bool persistent MYSQLND_MEM_D);
-PHPAPI void * _mysqlnd_ecalloc(unsigned int nmemb, size_t size MYSQLND_MEM_D);
-PHPAPI void * _mysqlnd_pecalloc(unsigned int nmemb, size_t size, zend_bool persistent MYSQLND_MEM_D);
-PHPAPI void * _mysqlnd_erealloc(void *ptr, size_t new_size MYSQLND_MEM_D);
-PHPAPI void * _mysqlnd_perealloc(void *ptr, size_t new_size, zend_bool persistent MYSQLND_MEM_D);
-PHPAPI void _mysqlnd_efree(void *ptr MYSQLND_MEM_D);
-PHPAPI void _mysqlnd_pefree(void *ptr, zend_bool persistent MYSQLND_MEM_D);
-PHPAPI void * _mysqlnd_malloc(size_t size MYSQLND_MEM_D);
-PHPAPI void * _mysqlnd_calloc(unsigned int nmemb, size_t size MYSQLND_MEM_D);
-PHPAPI void * _mysqlnd_realloc(void *ptr, size_t new_size MYSQLND_MEM_D);
-PHPAPI void _mysqlnd_free(void *ptr MYSQLND_MEM_D);
-PHPAPI char * _mysqlnd_pestrndup(const char * const ptr, size_t size, zend_bool persistent MYSQLND_MEM_D);
-PHPAPI char * _mysqlnd_pestrdup(const char * const ptr, zend_bool persistent MYSQLND_MEM_D);
-PHPAPI int _mysqlnd_sprintf(char **pbuf, size_t max_len, const char *format, ...);
-PHPAPI void _mysqlnd_sprintf_free(char * p);
-PHPAPI int _mysqlnd_vsprintf(char **pbuf, size_t max_len, const char *format, va_list ap);
-
#define mnd_emalloc(size) mysqlnd_allocator.m_emalloc((size) MYSQLND_MEM_C)
#define mnd_pemalloc(size, pers) mysqlnd_allocator.m_pemalloc((size), (pers) MYSQLND_MEM_C)
#define mnd_ecalloc(nmemb, size) mysqlnd_allocator.m_ecalloc((nmemb), (size) MYSQLND_MEM_C)
@@ -79,12 +61,29 @@ PHPAPI int _mysqlnd_vsprintf(char **pbuf, size_t max_len, const char *format, v
#define mnd_calloc(nmemb, size) mysqlnd_allocator.m_calloc((nmemb), (size) MYSQLND_MEM_C)
#define mnd_realloc(ptr, new_size) mysqlnd_allocator.m_realloc((ptr), (new_size) MYSQLND_MEM_C)
#define mnd_free(ptr) mysqlnd_allocator.m_free((ptr) MYSQLND_MEM_C)
+#define mnd_pememdup(ptr, size, pers) mysqlnd_allocator.m_pememdup((ptr), (size), (pers) MYSQLND_MEM_C)
#define mnd_pestrndup(ptr, size, pers) mysqlnd_allocator.m_pestrndup((ptr), (size), (pers) MYSQLND_MEM_C)
#define mnd_pestrdup(ptr, pers) mysqlnd_allocator.m_pestrdup((ptr), (pers) MYSQLND_MEM_C)
#define mnd_sprintf(p, mx_len, fmt,...) mysqlnd_allocator.m_sprintf((p), (mx_len), (fmt), __VA_ARGS__)
#define mnd_vsprintf(p, mx_len, fmt,ap) mysqlnd_allocator.m_vsprintf((p), (mx_len), (fmt), (ap))
#define mnd_sprintf_free(p) mysqlnd_allocator.m_sprintf_free((p))
+static inline MYSQLND_STRING mnd_dup_cstring(const MYSQLND_CSTRING str, const zend_bool persistent)
+{
+ const MYSQLND_STRING ret = {(char*) mnd_pemalloc(str.l + 1, persistent), str.l};
+ if (ret.s) {
+ memcpy(ret.s, str.s, str.l);
+ ret.s[str.l] = '\0';
+ }
+ return ret;
+}
+
+static inline MYSQLND_CSTRING mnd_str2c(const MYSQLND_STRING str)
+{
+ const MYSQLND_CSTRING ret = {str.s, str.l};
+ return ret;
+}
+
#endif /* MYSQLND_ALLOC_H */
/*
diff --git a/ext/mysqlnd/mysqlnd_auth.c b/ext/mysqlnd/mysqlnd_auth.c
index 082f69592e..a721781a14 100644
--- a/ext/mysqlnd/mysqlnd_auth.c
+++ b/ext/mysqlnd/mysqlnd_auth.c
@@ -14,20 +14,220 @@
+----------------------------------------------------------------------+
| Authors: Andrey Hristov <andrey@php.net> |
| Ulf Wendel <uw@php.net> |
- | Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
-/* $Id: mysqlnd.c 307377 2011-01-11 13:02:57Z andrey $ */
#include "php.h"
#include "mysqlnd.h"
#include "mysqlnd_structs.h"
+#include "mysqlnd_auth.h"
#include "mysqlnd_wireprotocol.h"
+#include "mysqlnd_connection.h"
#include "mysqlnd_priv.h"
-#include "mysqlnd_result.h"
#include "mysqlnd_charset.h"
#include "mysqlnd_debug.h"
+static const char * const mysqlnd_old_passwd = "mysqlnd cannot connect to MySQL 4.1+ using the old insecure authentication. "
+"Please use an administration tool to reset your password with the command SET PASSWORD = PASSWORD('your_existing_password'). This will "
+"store a new, and more secure, hash value in mysql.user. If this user is used in other scripts executed by PHP 5.2 or earlier you might need to remove the old-passwords "
+"flag from your my.cnf file";
+
+
+/* {{{ mysqlnd_run_authentication */
+enum_func_status
+mysqlnd_run_authentication(
+ MYSQLND_CONN_DATA * conn,
+ const char * const user,
+ const char * const passwd,
+ const size_t passwd_len,
+ const char * const db,
+ const size_t db_len,
+ const MYSQLND_STRING auth_plugin_data,
+ const char * const auth_protocol,
+ unsigned int charset_no,
+ const MYSQLND_SESSION_OPTIONS * const session_options,
+ zend_ulong mysql_flags,
+ zend_bool silent,
+ zend_bool is_change_user
+ )
+{
+ enum_func_status ret = FAIL;
+ zend_bool first_call = TRUE;
+
+ char * switch_to_auth_protocol = NULL;
+ size_t switch_to_auth_protocol_len = 0;
+ char * requested_protocol = NULL;
+ zend_uchar * plugin_data;
+ size_t plugin_data_len;
+
+ DBG_ENTER("mysqlnd_run_authentication");
+
+ plugin_data_len = auth_plugin_data.l;
+ plugin_data = mnd_emalloc(plugin_data_len + 1);
+ if (!plugin_data) {
+ goto end;
+ }
+ memcpy(plugin_data, auth_plugin_data.s, plugin_data_len);
+ plugin_data[plugin_data_len] = '\0';
+
+ requested_protocol = mnd_pestrdup(auth_protocol? auth_protocol : MYSQLND_DEFAULT_AUTH_PROTOCOL, FALSE);
+ if (!requested_protocol) {
+ goto end;
+ }
+
+ do {
+ struct st_mysqlnd_authentication_plugin * auth_plugin = conn->m->fetch_auth_plugin_by_name(requested_protocol);
+
+ if (!auth_plugin) {
+ php_error_docref(NULL, E_WARNING, "The server requested authentication method unknown to the client [%s]", requested_protocol);
+ SET_CLIENT_ERROR(conn->error_info, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "The server requested authentication method unknown to the client");
+ goto end;
+ }
+ DBG_INF("plugin found");
+
+ {
+ zend_uchar * switch_to_auth_protocol_data = NULL;
+ size_t switch_to_auth_protocol_data_len = 0;
+ zend_uchar * scrambled_data = NULL;
+ size_t scrambled_data_len = 0;
+
+ switch_to_auth_protocol = NULL;
+ switch_to_auth_protocol_len = 0;
+
+ if (conn->authentication_plugin_data.s) {
+ mnd_pefree(conn->authentication_plugin_data.s, conn->persistent);
+ conn->authentication_plugin_data.s = NULL;
+ }
+ conn->authentication_plugin_data.l = plugin_data_len;
+ conn->authentication_plugin_data.s = mnd_pemalloc(conn->authentication_plugin_data.l, conn->persistent);
+ if (!conn->authentication_plugin_data.s) {
+ SET_OOM_ERROR(conn->error_info);
+ goto end;
+ }
+ memcpy(conn->authentication_plugin_data.s, plugin_data, plugin_data_len);
+
+ DBG_INF_FMT("salt(%d)=[%.*s]", plugin_data_len, plugin_data_len, plugin_data);
+ /* The data should be allocated with malloc() */
+ scrambled_data =
+ auth_plugin->methods.get_auth_data(NULL, &scrambled_data_len, conn, user, passwd, passwd_len,
+ plugin_data, plugin_data_len, session_options,
+ conn->protocol_frame_codec->data, mysql_flags);
+ if (conn->error_info->error_no) {
+ goto end;
+ }
+ if (FALSE == is_change_user) {
+ ret = mysqlnd_auth_handshake(conn, user, passwd, passwd_len, db, db_len, session_options, mysql_flags,
+ charset_no,
+ first_call,
+ requested_protocol,
+ scrambled_data, scrambled_data_len,
+ &switch_to_auth_protocol, &switch_to_auth_protocol_len,
+ &switch_to_auth_protocol_data, &switch_to_auth_protocol_data_len
+ );
+ } else {
+ ret = mysqlnd_auth_change_user(conn, user, strlen(user), passwd, passwd_len, db, db_len, silent,
+ first_call,
+ requested_protocol,
+ scrambled_data, scrambled_data_len,
+ &switch_to_auth_protocol, &switch_to_auth_protocol_len,
+ &switch_to_auth_protocol_data, &switch_to_auth_protocol_data_len
+ );
+ }
+ first_call = FALSE;
+ free(scrambled_data);
+
+ DBG_INF_FMT("switch_to_auth_protocol=%s", switch_to_auth_protocol? switch_to_auth_protocol:"n/a");
+ if (requested_protocol && switch_to_auth_protocol) {
+ mnd_efree(requested_protocol);
+ requested_protocol = switch_to_auth_protocol;
+ }
+
+ if (plugin_data) {
+ mnd_efree(plugin_data);
+ }
+ plugin_data_len = switch_to_auth_protocol_data_len;
+ plugin_data = switch_to_auth_protocol_data;
+ }
+ DBG_INF_FMT("conn->error_info->error_no = %d", conn->error_info->error_no);
+ } while (ret == FAIL && conn->error_info->error_no == 0 && switch_to_auth_protocol != NULL);
+
+ if (ret == PASS) {
+ DBG_INF_FMT("saving requested_protocol=%s", requested_protocol);
+ conn->m->set_client_option(conn, MYSQLND_OPT_AUTH_PROTOCOL, requested_protocol);
+ }
+end:
+ if (plugin_data) {
+ mnd_efree(plugin_data);
+ }
+ if (requested_protocol) {
+ mnd_efree(requested_protocol);
+ }
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_switch_to_ssl_if_needed */
+static enum_func_status
+mysqlnd_switch_to_ssl_if_needed(MYSQLND_CONN_DATA * conn,
+ unsigned int charset_no,
+ size_t server_capabilities,
+ const MYSQLND_SESSION_OPTIONS * const session_options,
+ zend_ulong mysql_flags)
+{
+ enum_func_status ret = FAIL;
+ const MYSQLND_CHARSET * charset;
+ DBG_ENTER("mysqlnd_switch_to_ssl_if_needed");
+
+ if (session_options->charset_name && (charset = mysqlnd_find_charset_name(session_options->charset_name))) {
+ charset_no = charset->nr;
+ }
+
+ {
+ size_t client_capabilities = mysql_flags;
+ struct st_mysqlnd_protocol_command * command = conn->command_factory(COM_ENABLE_SSL, conn, client_capabilities, server_capabilities, charset_no);
+ if (command) {
+ ret = command->run(command);
+ command->free_command(command);
+ }
+ }
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_connect_run_authentication */
+enum_func_status
+mysqlnd_connect_run_authentication(
+ MYSQLND_CONN_DATA * conn,
+ const char * const user,
+ const char * const passwd,
+ const char * const db,
+ size_t db_len,
+ size_t passwd_len,
+ MYSQLND_STRING authentication_plugin_data,
+ const char * const authentication_protocol,
+ const unsigned int charset_no,
+ size_t server_capabilities,
+ const MYSQLND_SESSION_OPTIONS * const session_options,
+ zend_ulong mysql_flags
+ )
+{
+ enum_func_status ret = FAIL;
+ DBG_ENTER("mysqlnd_connect_run_authentication");
+
+ ret = mysqlnd_switch_to_ssl_if_needed(conn, charset_no, server_capabilities, session_options, mysql_flags);
+ if (PASS == ret) {
+ ret = mysqlnd_run_authentication(conn, user, passwd, passwd_len, db, db_len,
+ authentication_plugin_data, authentication_protocol,
+ charset_no, session_options, mysql_flags, FALSE /*silent*/, FALSE/*is_change*/);
+ }
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
/* {{{ mysqlnd_auth_handshake */
enum_func_status
mysqlnd_auth_handshake(MYSQLND_CONN_DATA * conn,
@@ -36,7 +236,7 @@ mysqlnd_auth_handshake(MYSQLND_CONN_DATA * conn,
const size_t passwd_len,
const char * const db,
const size_t db_len,
- const MYSQLND_OPTIONS * const options,
+ const MYSQLND_SESSION_OPTIONS * const session_options,
zend_ulong mysql_flags,
unsigned int server_charset_no,
zend_bool use_full_blown_auth_packet,
@@ -57,34 +257,34 @@ mysqlnd_auth_handshake(MYSQLND_CONN_DATA * conn,
DBG_ENTER("mysqlnd_auth_handshake");
- auth_resp_packet = conn->protocol->m.get_auth_response_packet(conn->protocol, FALSE);
+ auth_resp_packet = conn->payload_decoder_factory->m.get_auth_response_packet(conn->payload_decoder_factory, FALSE);
if (!auth_resp_packet) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
goto end;
}
if (use_full_blown_auth_packet != TRUE) {
- change_auth_resp_packet = conn->protocol->m.get_change_auth_response_packet(conn->protocol, FALSE);
+ change_auth_resp_packet = conn->payload_decoder_factory->m.get_change_auth_response_packet(conn->payload_decoder_factory, FALSE);
if (!change_auth_resp_packet) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
goto end;
}
change_auth_resp_packet->auth_data = auth_plugin_data;
change_auth_resp_packet->auth_data_len = auth_plugin_data_len;
- if (!PACKET_WRITE(change_auth_resp_packet, conn)) {
- CONN_SET_STATE(conn, CONN_QUIT_SENT);
- SET_CLIENT_ERROR(*conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
+ if (!PACKET_WRITE(change_auth_resp_packet)) {
+ SET_CONNECTION_STATE(&conn->state, CONN_QUIT_SENT);
+ SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
goto end;
}
} else {
- auth_packet = conn->protocol->m.get_auth_packet(conn->protocol, FALSE);
+ auth_packet = conn->payload_decoder_factory->m.get_auth_packet(conn->payload_decoder_factory, FALSE);
auth_packet->client_flags = mysql_flags;
- auth_packet->max_packet_size = options->max_allowed_packet;
- if (options->charset_name && (charset = mysqlnd_find_charset_name(options->charset_name))) {
+ auth_packet->max_packet_size = session_options->max_allowed_packet;
+ if (session_options->charset_name && (charset = mysqlnd_find_charset_name(session_options->charset_name))) {
auth_packet->charset_no = charset->nr;
} else {
auth_packet->charset_no = server_charset_no;
@@ -103,7 +303,7 @@ mysqlnd_auth_handshake(MYSQLND_CONN_DATA * conn,
auth_packet->connect_attr = conn->options->connect_attr;
}
- if (!PACKET_WRITE(auth_packet, conn)) {
+ if (!PACKET_WRITE(auth_packet)) {
goto end;
}
}
@@ -111,12 +311,12 @@ mysqlnd_auth_handshake(MYSQLND_CONN_DATA * conn,
conn->charset = mysqlnd_find_charset_nr(auth_packet->charset_no);
}
- if (FAIL == PACKET_READ(auth_resp_packet, conn) || auth_resp_packet->response_code >= 0xFE) {
+ if (FAIL == PACKET_READ(auth_resp_packet) || auth_resp_packet->response_code >= 0xFE) {
if (auth_resp_packet->response_code == 0xFE) {
/* old authentication with new server !*/
if (!auth_resp_packet->new_auth_protocol) {
DBG_ERR(mysqlnd_old_passwd);
- SET_CLIENT_ERROR(*conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, mysqlnd_old_passwd);
+ SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, mysqlnd_old_passwd);
} else {
*switch_to_auth_protocol = mnd_pestrndup(auth_resp_packet->new_auth_protocol, auth_resp_packet->new_auth_protocol_len, FALSE);
*switch_to_auth_protocol_len = auth_resp_packet->new_auth_protocol_len;
@@ -134,12 +334,12 @@ mysqlnd_auth_handshake(MYSQLND_CONN_DATA * conn,
strlcpy(conn->error_info->sqlstate, auth_resp_packet->sqlstate, sizeof(conn->error_info->sqlstate));
DBG_ERR_FMT("ERROR:%u [SQLSTATE:%s] %s", auth_resp_packet->error_no, auth_resp_packet->sqlstate, auth_resp_packet->error);
}
- SET_CLIENT_ERROR(*conn->error_info, auth_resp_packet->error_no, UNKNOWN_SQLSTATE, auth_resp_packet->error);
+ SET_CLIENT_ERROR(conn->error_info, auth_resp_packet->error_no, UNKNOWN_SQLSTATE, auth_resp_packet->error);
}
goto end;
}
- SET_NEW_MESSAGE(conn->last_message, conn->last_message_len, auth_resp_packet->message, auth_resp_packet->message_len, conn->persistent);
+ SET_NEW_MESSAGE(conn->last_message.s, conn->last_message.l, auth_resp_packet->message, auth_resp_packet->message_len, conn->persistent);
ret = PASS;
end:
PACKET_FREE(change_auth_resp_packet);
@@ -178,33 +378,33 @@ mysqlnd_auth_change_user(MYSQLND_CONN_DATA * const conn,
DBG_ENTER("mysqlnd_auth_change_user");
- chg_user_resp = conn->protocol->m.get_change_user_response_packet(conn->protocol, FALSE);
+ chg_user_resp = conn->payload_decoder_factory->m.get_change_user_response_packet(conn->payload_decoder_factory, FALSE);
if (!chg_user_resp) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
goto end;
}
if (use_full_blown_auth_packet != TRUE) {
- change_auth_resp_packet = conn->protocol->m.get_change_auth_response_packet(conn->protocol, FALSE);
+ change_auth_resp_packet = conn->payload_decoder_factory->m.get_change_auth_response_packet(conn->payload_decoder_factory, FALSE);
if (!change_auth_resp_packet) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
goto end;
}
change_auth_resp_packet->auth_data = auth_plugin_data;
change_auth_resp_packet->auth_data_len = auth_plugin_data_len;
- if (!PACKET_WRITE(change_auth_resp_packet, conn)) {
- CONN_SET_STATE(conn, CONN_QUIT_SENT);
- SET_CLIENT_ERROR(*conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
+ if (!PACKET_WRITE(change_auth_resp_packet)) {
+ SET_CONNECTION_STATE(&conn->state, CONN_QUIT_SENT);
+ SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
goto end;
}
} else {
- auth_packet = conn->protocol->m.get_auth_packet(conn->protocol, FALSE);
+ auth_packet = conn->payload_decoder_factory->m.get_auth_packet(conn->payload_decoder_factory, FALSE);
if (!auth_packet) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
goto end;
}
@@ -223,21 +423,21 @@ mysqlnd_auth_change_user(MYSQLND_CONN_DATA * const conn,
auth_packet->charset_no = conn->charset->nr;
}
- if (!PACKET_WRITE(auth_packet, conn)) {
- CONN_SET_STATE(conn, CONN_QUIT_SENT);
- SET_CLIENT_ERROR(*conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
+ if (!PACKET_WRITE(auth_packet)) {
+ SET_CONNECTION_STATE(&conn->state, CONN_QUIT_SENT);
+ SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
goto end;
}
}
- ret = PACKET_READ(chg_user_resp, conn);
- COPY_CLIENT_ERROR(*conn->error_info, chg_user_resp->error_info);
+ ret = PACKET_READ(chg_user_resp);
+ COPY_CLIENT_ERROR(conn->error_info, chg_user_resp->error_info);
if (0xFE == chg_user_resp->response_code) {
ret = FAIL;
if (!chg_user_resp->new_auth_protocol) {
DBG_ERR(mysqlnd_old_passwd);
- SET_CLIENT_ERROR(*conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, mysqlnd_old_passwd);
+ SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, mysqlnd_old_passwd);
} else {
*switch_to_auth_protocol = mnd_pestrndup(chg_user_resp->new_auth_protocol, chg_user_resp->new_auth_protocol_len, FALSE);
*switch_to_auth_protocol_len = chg_user_resp->new_auth_protocol_len;
@@ -260,36 +460,36 @@ mysqlnd_auth_change_user(MYSQLND_CONN_DATA * const conn,
When it gets fixed, there should be one more check here
*/
if (conn->m->get_server_version(conn) > 50113L &&conn->m->get_server_version(conn) < 50118L) {
- MYSQLND_PACKET_OK * redundant_error_packet = conn->protocol->m.get_ok_packet(conn->protocol, FALSE);
+ MYSQLND_PACKET_OK * redundant_error_packet = conn->payload_decoder_factory->m.get_ok_packet(conn->payload_decoder_factory, FALSE);
if (redundant_error_packet) {
- PACKET_READ(redundant_error_packet, conn);
+ PACKET_READ(redundant_error_packet);
PACKET_FREE(redundant_error_packet);
DBG_INF_FMT("Server is %u, buggy, sends two ERR messages", conn->m->get_server_version(conn));
} else {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
}
}
}
if (ret == PASS) {
char * tmp = NULL;
- /* if we get conn->user as parameter and then we first free it, then estrndup it, we will crash */
+ /* if we get conn->username as parameter and then we first free it, then estrndup it, we will crash */
tmp = mnd_pestrndup(user, user_len, conn->persistent);
- if (conn->user) {
- mnd_pefree(conn->user, conn->persistent);
+ if (conn->username.s) {
+ mnd_pefree(conn->username.s, conn->persistent);
}
- conn->user = tmp;
+ conn->username.s = tmp;
tmp = mnd_pestrdup(passwd, conn->persistent);
- if (conn->passwd) {
- mnd_pefree(conn->passwd, conn->persistent);
+ if (conn->password.s) {
+ mnd_pefree(conn->password.s, conn->persistent);
}
- conn->passwd = tmp;
+ conn->password.s = tmp;
- if (conn->last_message) {
- mnd_pefree(conn->last_message, conn->persistent);
- conn->last_message = NULL;
+ if (conn->last_message.s) {
+ mnd_pefree(conn->last_message.s, conn->persistent);
+ conn->last_message.s = NULL;
}
- memset(conn->upsert_status, 0, sizeof(*conn->upsert_status));
+ UPSERT_STATUS_RESET(conn->upsert_status);
/* set charset for old servers */
if (conn->m->get_server_version(conn) < 50123) {
ret = conn->m->set_charset(conn, old_cs->name);
@@ -297,7 +497,7 @@ mysqlnd_auth_change_user(MYSQLND_CONN_DATA * const conn,
} else if (ret == FAIL && chg_user_resp->server_asked_323_auth == TRUE) {
/* old authentication with new server !*/
DBG_ERR(mysqlnd_old_passwd);
- SET_CLIENT_ERROR(*conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, mysqlnd_old_passwd);
+ SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, mysqlnd_old_passwd);
}
end:
PACKET_FREE(change_auth_resp_packet);
@@ -325,7 +525,7 @@ php_mysqlnd_crypt(zend_uchar *buffer, const zend_uchar *s1, const zend_uchar *s2
/* {{{ php_mysqlnd_scramble */
-void php_mysqlnd_scramble(zend_uchar * const buffer, const zend_uchar * const scramble, const zend_uchar * const password, size_t password_len)
+void php_mysqlnd_scramble(zend_uchar * const buffer, const zend_uchar * const scramble, const zend_uchar * const password, const size_t password_len)
{
PHP_SHA1_CTX context;
zend_uchar sha1[SHA1_MAX_LENGTH];
@@ -359,8 +559,8 @@ mysqlnd_native_auth_get_auth_data(struct st_mysqlnd_authentication_plugin * self
size_t * auth_data_len,
MYSQLND_CONN_DATA * conn, const char * const user, const char * const passwd,
const size_t passwd_len, zend_uchar * auth_plugin_data, size_t auth_plugin_data_len,
- const MYSQLND_OPTIONS * const options,
- const MYSQLND_NET_OPTIONS * const net_options,
+ const MYSQLND_SESSION_OPTIONS * const session_options,
+ const MYSQLND_PFC_DATA * const pfc_data,
zend_ulong mysql_flags
)
{
@@ -371,7 +571,7 @@ mysqlnd_native_auth_get_auth_data(struct st_mysqlnd_authentication_plugin * self
/* 5.5.x reports 21 as scramble length because it needs to show the length of the data before the plugin name */
if (auth_plugin_data_len < SCRAMBLE_LENGTH) {
/* mysql_native_password only works with SCRAMBLE_LENGTH scramble */
- SET_CLIENT_ERROR(*conn->error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE, "The server sent wrong length for scramble");
+ SET_CLIENT_ERROR(conn->error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE, "The server sent wrong length for scramble");
DBG_ERR_FMT("The server sent wrong length for scramble %u. Expected %u", auth_plugin_data_len, SCRAMBLE_LENGTH);
DBG_RETURN(NULL);
}
@@ -419,8 +619,8 @@ mysqlnd_pam_auth_get_auth_data(struct st_mysqlnd_authentication_plugin * self,
size_t * auth_data_len,
MYSQLND_CONN_DATA * conn, const char * const user, const char * const passwd,
const size_t passwd_len, zend_uchar * auth_plugin_data, size_t auth_plugin_data_len,
- const MYSQLND_OPTIONS * const options,
- const MYSQLND_NET_OPTIONS * const net_options,
+ const MYSQLND_SESSION_OPTIONS * const session_options,
+ const MYSQLND_PFC_DATA * const pfc_data,
zend_ulong mysql_flags
)
{
@@ -480,18 +680,18 @@ mysqlnd_xor_string(char * dst, const size_t dst_len, const char * xor_str, const
/* {{{ mysqlnd_sha256_get_rsa_key */
static RSA *
mysqlnd_sha256_get_rsa_key(MYSQLND_CONN_DATA * conn,
- const MYSQLND_OPTIONS * const options,
- const MYSQLND_NET_OPTIONS * const net_options
+ const MYSQLND_SESSION_OPTIONS * const session_options,
+ const MYSQLND_PFC_DATA * const pfc_data
)
{
RSA * ret = NULL;
- const char * fname = (net_options->sha256_server_public_key && net_options->sha256_server_public_key[0] != '\0')?
- net_options->sha256_server_public_key:
+ const char * fname = (pfc_data->sha256_server_public_key && pfc_data->sha256_server_public_key[0] != '\0')?
+ pfc_data->sha256_server_public_key:
MYSQLND_G(sha256_server_public_key);
php_stream * stream;
DBG_ENTER("mysqlnd_sha256_get_rsa_key");
DBG_INF_FMT("options_s256_pk=[%s] MYSQLND_G(sha256_server_public_key)=[%s]",
- net_options->sha256_server_public_key? net_options->sha256_server_public_key:"n/a",
+ pfc_data->sha256_server_public_key? pfc_data->sha256_server_public_key:"n/a",
MYSQLND_G(sha256_server_public_key)? MYSQLND_G(sha256_server_public_key):"n/a");
if (!fname || fname[0] == '\0') {
MYSQLND_PACKET_SHA256_PK_REQUEST * pk_req_packet = NULL;
@@ -499,28 +699,28 @@ mysqlnd_sha256_get_rsa_key(MYSQLND_CONN_DATA * conn,
do {
DBG_INF("requesting the public key from the server");
- pk_req_packet = conn->protocol->m.get_sha256_pk_request_packet(conn->protocol, FALSE);
+ pk_req_packet = conn->payload_decoder_factory->m.get_sha256_pk_request_packet(conn->payload_decoder_factory, FALSE);
if (!pk_req_packet) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
break;
}
- pk_resp_packet = conn->protocol->m.get_sha256_pk_request_response_packet(conn->protocol, FALSE);
+ pk_resp_packet = conn->payload_decoder_factory->m.get_sha256_pk_request_response_packet(conn->payload_decoder_factory, FALSE);
if (!pk_resp_packet) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
PACKET_FREE(pk_req_packet);
break;
}
- if (! PACKET_WRITE(pk_req_packet, conn)) {
+ if (! PACKET_WRITE(pk_req_packet)) {
DBG_ERR_FMT("Error while sending public key request packet");
php_error(E_WARNING, "Error while sending public key request packet. PID=%d", getpid());
- CONN_SET_STATE(conn, CONN_QUIT_SENT);
+ SET_CONNECTION_STATE(&conn->state, CONN_QUIT_SENT);
break;
}
- if (FAIL == PACKET_READ(pk_resp_packet, conn) || NULL == pk_resp_packet->public_key) {
+ if (FAIL == PACKET_READ(pk_resp_packet) || NULL == pk_resp_packet->public_key) {
DBG_ERR_FMT("Error while receiving public key");
php_error(E_WARNING, "Error while receiving public key. PID=%d", getpid());
- CONN_SET_STATE(conn, CONN_QUIT_SENT);
+ SET_CONNECTION_STATE(&conn->state, CONN_QUIT_SENT);
break;
}
DBG_INF_FMT("Public key(%d):\n%s", pk_resp_packet->public_key_len, pk_resp_packet->public_key);
@@ -537,7 +737,7 @@ mysqlnd_sha256_get_rsa_key(MYSQLND_CONN_DATA * conn,
DBG_INF_FMT("ret=%p", ret);
DBG_RETURN(ret);
- SET_CLIENT_ERROR(*conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE,
+ SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE,
"sha256_server_public_key is not set for the connection or as mysqlnd.sha256_server_public_key");
DBG_ERR("server_public_key is not set");
DBG_RETURN(NULL);
@@ -569,8 +769,8 @@ mysqlnd_sha256_auth_get_auth_data(struct st_mysqlnd_authentication_plugin * self
size_t * auth_data_len,
MYSQLND_CONN_DATA * conn, const char * const user, const char * const passwd,
const size_t passwd_len, zend_uchar * auth_plugin_data, size_t auth_plugin_data_len,
- const MYSQLND_OPTIONS * const options,
- const MYSQLND_NET_OPTIONS * const net_options,
+ const MYSQLND_SESSION_OPTIONS * const session_options,
+ const MYSQLND_PFC_DATA * const pfc_data,
zend_ulong mysql_flags
)
{
@@ -580,7 +780,7 @@ mysqlnd_sha256_auth_get_auth_data(struct st_mysqlnd_authentication_plugin * self
DBG_INF_FMT("salt(%d)=[%.*s]", auth_plugin_data_len, auth_plugin_data_len, auth_plugin_data);
- if (conn->net->data->ssl) {
+ if (conn->protocol_frame_codec->data->ssl) {
DBG_INF("simple clear text under SSL");
/* clear text under SSL */
*auth_data_len = passwd_len;
@@ -588,7 +788,7 @@ mysqlnd_sha256_auth_get_auth_data(struct st_mysqlnd_authentication_plugin * self
memcpy(ret, passwd, passwd_len);
} else {
*auth_data_len = 0;
- server_public_key = mysqlnd_sha256_get_rsa_key(conn, options, net_options);
+ server_public_key = mysqlnd_sha256_get_rsa_key(conn, session_options, pfc_data);
if (server_public_key) {
int server_public_key_len;
@@ -605,7 +805,7 @@ mysqlnd_sha256_auth_get_auth_data(struct st_mysqlnd_authentication_plugin * self
*/
if ((size_t) server_public_key_len - 41 <= passwd_len) {
/* password message is to long */
- SET_CLIENT_ERROR(*conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "password is too long");
+ SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "password is too long");
DBG_ERR("password is too long");
DBG_RETURN(NULL);
}
diff --git a/ext/mysqlnd/mysqlnd_auth.h b/ext/mysqlnd/mysqlnd_auth.h
new file mode 100644
index 0000000000..a8c359b9fd
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_auth.h
@@ -0,0 +1,126 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 7 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2017 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Andrey Hristov <andrey@php.net> |
+ | Ulf Wendel <uw@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef MYSQLND_AUTH_H
+#define MYSQLND_AUTH_H
+enum_func_status
+mysqlnd_auth_handshake(MYSQLND_CONN_DATA * conn,
+ const char * const user,
+ const char * const passwd,
+ const size_t passwd_len,
+ const char * const db,
+ const size_t db_len,
+ const MYSQLND_SESSION_OPTIONS * const session_options,
+ zend_ulong mysql_flags,
+ unsigned int server_charset_no,
+ zend_bool use_full_blown_auth_packet,
+ const char * const auth_protocol,
+ const zend_uchar * const auth_plugin_data,
+ const size_t auth_plugin_data_len,
+ char ** switch_to_auth_protocol,
+ size_t * switch_to_auth_protocol_len,
+ zend_uchar ** switch_to_auth_protocol_data,
+ size_t * switch_to_auth_protocol_data_len
+ );
+
+enum_func_status
+mysqlnd_auth_handshake(MYSQLND_CONN_DATA * conn,
+ const char * const user,
+ const char * const passwd,
+ const size_t passwd_len,
+ const char * const db,
+ const size_t db_len,
+ const MYSQLND_SESSION_OPTIONS * const session_options,
+ zend_ulong mysql_flags,
+ unsigned int server_charset_no,
+ zend_bool use_full_blown_auth_packet,
+ const char * const auth_protocol,
+ const zend_uchar * const auth_plugin_data,
+ const size_t auth_plugin_data_len,
+ char ** switch_to_auth_protocol,
+ size_t * switch_to_auth_protocol_len,
+ zend_uchar ** switch_to_auth_protocol_data,
+ size_t * switch_to_auth_protocol_data_len
+ );
+
+enum_func_status
+mysqlnd_auth_change_user(MYSQLND_CONN_DATA * const conn,
+ const char * const user,
+ const size_t user_len,
+ const char * const passwd,
+ const size_t passwd_len,
+ const char * const db,
+ const size_t db_len,
+ const zend_bool silent,
+ zend_bool use_full_blown_auth_packet,
+ const char * const auth_protocol,
+ zend_uchar * auth_plugin_data,
+ size_t auth_plugin_data_len,
+ char ** switch_to_auth_protocol,
+ size_t * switch_to_auth_protocol_len,
+ zend_uchar ** switch_to_auth_protocol_data,
+ size_t * switch_to_auth_protocol_data_len
+ );
+
+
+enum_func_status
+mysqlnd_connect_run_authentication(
+ MYSQLND_CONN_DATA * conn,
+ const char * const user,
+ const char * const passwd,
+ const char * const db,
+ size_t db_len,
+ size_t passwd_len,
+ MYSQLND_STRING authentication_plugin_data,
+ const char * const authentication_protocol,
+ const unsigned int charset_no,
+ size_t server_capabilities,
+ const MYSQLND_SESSION_OPTIONS * const session_options,
+ zend_ulong mysql_flags
+ );
+
+enum_func_status
+mysqlnd_run_authentication(
+ MYSQLND_CONN_DATA * conn,
+ const char * const user,
+ const char * const passwd,
+ const size_t passwd_len,
+ const char * const db,
+ const size_t db_len,
+ const MYSQLND_STRING auth_plugin_data,
+ const char * const auth_protocol,
+ unsigned int charset_no,
+ const MYSQLND_SESSION_OPTIONS * const session_options,
+ zend_ulong mysql_flags,
+ zend_bool silent,
+ zend_bool is_change_user
+ );
+
+PHPAPI void php_mysqlnd_scramble(zend_uchar * const buffer, const zend_uchar * const scramble, const zend_uchar * const pass, const size_t pass_len);
+
+#endif /* MYSQLND_AUTH_H */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/mysqlnd/mysqlnd_block_alloc.c b/ext/mysqlnd/mysqlnd_block_alloc.c
index d7c89dd8e9..4dbed8b4de 100644
--- a/ext/mysqlnd/mysqlnd_block_alloc.c
+++ b/ext/mysqlnd/mysqlnd_block_alloc.c
@@ -14,12 +14,9 @@
+----------------------------------------------------------------------+
| Authors: Andrey Hristov <andrey@php.net> |
| Ulf Wendel <uw@php.net> |
- | Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
-/* $Id$ */
-
#include "php.h"
#include "mysqlnd.h"
#include "mysqlnd_block_alloc.h"
@@ -29,9 +26,8 @@
/* {{{ mysqlnd_mempool_free_chunk */
static void
-mysqlnd_mempool_free_chunk(MYSQLND_MEMORY_POOL_CHUNK * chunk)
+mysqlnd_mempool_free_chunk(MYSQLND_MEMORY_POOL * pool, MYSQLND_MEMORY_POOL_CHUNK * chunk)
{
- MYSQLND_MEMORY_POOL * pool = chunk->pool;
DBG_ENTER("mysqlnd_mempool_free_chunk");
if (chunk->from_pool) {
/* Try to back-off and guess if this is the last block allocated */
@@ -42,7 +38,6 @@ mysqlnd_mempool_free_chunk(MYSQLND_MEMORY_POOL_CHUNK * chunk)
*/
pool->free_size += chunk->size;
}
- pool->refcount--;
} else {
mnd_efree(chunk->ptr);
}
@@ -54,11 +49,10 @@ mysqlnd_mempool_free_chunk(MYSQLND_MEMORY_POOL_CHUNK * chunk)
/* {{{ mysqlnd_mempool_resize_chunk */
static enum_func_status
-mysqlnd_mempool_resize_chunk(MYSQLND_MEMORY_POOL_CHUNK * chunk, unsigned int size)
+mysqlnd_mempool_resize_chunk(MYSQLND_MEMORY_POOL * pool, MYSQLND_MEMORY_POOL_CHUNK * chunk, unsigned int size)
{
DBG_ENTER("mysqlnd_mempool_resize_chunk");
if (chunk->from_pool) {
- MYSQLND_MEMORY_POOL * pool = chunk->pool;
/* Try to back-off and guess if this is the last block allocated */
if (chunk->ptr == (pool->arena + (pool->arena_size - pool->free_size - chunk->size))) {
/*
@@ -75,8 +69,7 @@ mysqlnd_mempool_resize_chunk(MYSQLND_MEMORY_POOL_CHUNK * chunk, unsigned int siz
chunk->ptr = new_ptr;
pool->free_size += chunk->size;
chunk->size = size;
- chunk->pool = NULL; /* now we have no pool memory */
- pool->refcount--;
+ chunk->from_pool = FALSE; /* now we have no pool memory */
} else {
/* If the chunk is > than asked size then free_memory increases, otherwise decreases*/
pool->free_size += (chunk->size - size);
@@ -94,8 +87,7 @@ mysqlnd_mempool_resize_chunk(MYSQLND_MEMORY_POOL_CHUNK * chunk, unsigned int siz
memcpy(new_ptr, chunk->ptr, chunk->size);
chunk->ptr = new_ptr;
chunk->size = size;
- chunk->pool = NULL; /* now we have non-pool memory */
- pool->refcount--;
+ chunk->from_pool = FALSE; /* now we have non-pool memory */
}
}
} else {
@@ -119,25 +111,21 @@ MYSQLND_MEMORY_POOL_CHUNK * mysqlnd_mempool_get_chunk(MYSQLND_MEMORY_POOL * pool
chunk = mnd_emalloc(sizeof(MYSQLND_MEMORY_POOL_CHUNK));
if (chunk) {
- chunk->free_chunk = mysqlnd_mempool_free_chunk;
- chunk->resize_chunk = mysqlnd_mempool_resize_chunk;
chunk->size = size;
/*
Should not go over MYSQLND_MAX_PACKET_SIZE, since we
expect non-arena memory in mysqlnd_wireprotocol.c . We
realloc the non-arena memory.
*/
- chunk->pool = pool;
if (size > pool->free_size) {
chunk->from_pool = FALSE;
chunk->ptr = mnd_emalloc(size);
if (!chunk->ptr) {
- chunk->free_chunk(chunk);
+ pool->free_chunk(pool, chunk);
chunk = NULL;
}
} else {
chunk->from_pool = TRUE;
- ++pool->refcount;
chunk->ptr = pool->arena + (pool->arena_size - pool->free_size);
/* Last step, update free_size */
pool->free_size -= size;
@@ -157,8 +145,9 @@ mysqlnd_mempool_create(size_t arena_size)
DBG_ENTER("mysqlnd_mempool_create");
if (ret) {
ret->get_chunk = mysqlnd_mempool_get_chunk;
+ ret->free_chunk = mysqlnd_mempool_free_chunk;
+ ret->resize_chunk = mysqlnd_mempool_resize_chunk;
ret->free_size = ret->arena_size = arena_size ? arena_size : 0;
- ret->refcount = 0;
/* OOM ? */
ret->arena = mnd_emalloc(ret->arena_size);
if (!ret->arena) {
diff --git a/ext/mysqlnd/mysqlnd_block_alloc.h b/ext/mysqlnd/mysqlnd_block_alloc.h
index 92e37bd53f..8a2e2a6746 100644
--- a/ext/mysqlnd/mysqlnd_block_alloc.h
+++ b/ext/mysqlnd/mysqlnd_block_alloc.h
@@ -14,7 +14,6 @@
+----------------------------------------------------------------------+
| Authors: Andrey Hristov <andrey@php.net> |
| Ulf Wendel <uw@php.net> |
- | Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
diff --git a/ext/mysqlnd/mysqlnd_charset.c b/ext/mysqlnd/mysqlnd_charset.c
index ac08b9b72d..908ebd0639 100644
--- a/ext/mysqlnd/mysqlnd_charset.c
+++ b/ext/mysqlnd/mysqlnd_charset.c
@@ -19,7 +19,6 @@
*/
#include "php.h"
-#include "php_globals.h"
#include "mysqlnd.h"
#include "mysqlnd_priv.h"
#include "mysqlnd_debug.h"
diff --git a/ext/mysqlnd/mysqlnd_commands.c b/ext/mysqlnd/mysqlnd_commands.c
new file mode 100644
index 0000000000..9bea4358ea
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_commands.c
@@ -0,0 +1,1470 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 7 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2017 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Andrey Hristov <andrey@php.net> |
+ | Ulf Wendel <uw@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "php.h"
+#include "mysqlnd.h"
+#include "mysqlnd_connection.h"
+#include "mysqlnd_priv.h"
+#include "mysqlnd_auth.h"
+#include "mysqlnd_wireprotocol.h"
+#include "mysqlnd_statistics.h"
+#include "mysqlnd_debug.h"
+
+
+struct st_mysqlnd_protocol_no_params_command
+{
+ struct st_mysqlnd_protocol_command parent;
+ struct st_mysqlnd_protocol_no_params_command_context
+ {
+ MYSQLND_CONN_DATA * conn;
+ } context;
+};
+
+/* {{{ mysqlnd_com_no_params_free_command */
+static void
+mysqlnd_com_no_params_free_command(void * command)
+{
+ DBG_ENTER("mysqlnd_com_no_params_free_command");
+ mnd_efree(command);
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/************************** COM_SET_OPTION ******************************************/
+struct st_mysqlnd_protocol_com_set_option_command
+{
+ struct st_mysqlnd_protocol_command parent;
+ struct st_mysqlnd_com_set_option_context
+ {
+ MYSQLND_CONN_DATA * conn;
+ enum_mysqlnd_server_option option;
+ } context;
+};
+
+
+/* {{{ mysqlnd_com_set_option_run */
+enum_func_status
+mysqlnd_com_set_option_run(void *cmd)
+{
+ struct st_mysqlnd_protocol_com_set_option_command * command = (struct st_mysqlnd_protocol_com_set_option_command *) cmd;
+ zend_uchar buffer[2];
+ enum_func_status ret = FAIL;
+ MYSQLND_CONN_DATA * conn = command->context.conn;
+ enum_mysqlnd_server_option option = command->context.option;
+ func_mysqlnd_protocol_payload_decoder_factory__send_command send_command = conn->payload_decoder_factory->m.send_command;
+ func_mysqlnd_protocol_payload_decoder_factory__send_command_handle_response send_command_handle_response = conn->payload_decoder_factory->m.send_command_handle_response;
+
+ DBG_ENTER("mysqlnd_com_set_option_run");
+ int2store(buffer, (unsigned int) option);
+
+ ret = send_command(conn->payload_decoder_factory, COM_SET_OPTION, buffer, sizeof(buffer), FALSE,
+ &conn->state,
+ conn->error_info,
+ conn->upsert_status,
+ conn->stats,
+ conn->m->send_close,
+ conn);
+ if (PASS == ret) {
+ ret = send_command_handle_response(conn->payload_decoder_factory, PROT_EOF_PACKET, FALSE, COM_SET_OPTION, TRUE,
+ conn->error_info, conn->upsert_status, &conn->last_message, conn->persistent);
+ }
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_com_set_option_create_command */
+static struct st_mysqlnd_protocol_command *
+mysqlnd_com_set_option_create_command(va_list args)
+{
+ struct st_mysqlnd_protocol_com_set_option_command * command;
+ DBG_ENTER("mysqlnd_com_set_option_create_command");
+ command = mnd_ecalloc(1, sizeof(struct st_mysqlnd_protocol_com_set_option_command));
+ if (command) {
+ command->context.conn = va_arg(args, MYSQLND_CONN_DATA *);
+ command->context.option = va_arg(args, enum_mysqlnd_server_option);
+
+ command->parent.free_command = mysqlnd_com_no_params_free_command;
+ command->parent.run = mysqlnd_com_set_option_run;
+ }
+
+ DBG_RETURN((struct st_mysqlnd_protocol_command *) command);
+}
+/* }}} */
+
+
+/************************** COM_DEBUG ******************************************/
+/* {{{ mysqlnd_com_debug_run */
+static enum_func_status
+mysqlnd_com_debug_run(void *cmd)
+{
+ struct st_mysqlnd_protocol_no_params_command * command = (struct st_mysqlnd_protocol_no_params_command *) cmd;
+ enum_func_status ret = FAIL;
+ MYSQLND_CONN_DATA * conn = command->context.conn;
+ func_mysqlnd_protocol_payload_decoder_factory__send_command send_command = conn->payload_decoder_factory->m.send_command;
+ func_mysqlnd_protocol_payload_decoder_factory__send_command_handle_response send_command_handle_response = conn->payload_decoder_factory->m.send_command_handle_response;
+
+ DBG_ENTER("mysqlnd_com_debug_run");
+
+ ret = send_command(conn->payload_decoder_factory, COM_DEBUG, NULL, 0, FALSE,
+ &conn->state,
+ conn->error_info,
+ conn->upsert_status,
+ conn->stats,
+ conn->m->send_close,
+ conn);
+ if (PASS == ret) {
+ ret = send_command_handle_response(conn->payload_decoder_factory, PROT_EOF_PACKET, FALSE, COM_DEBUG, TRUE,
+ conn->error_info, conn->upsert_status, &conn->last_message, conn->persistent);
+ }
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_com_debug_create_command */
+static struct st_mysqlnd_protocol_command *
+mysqlnd_com_debug_create_command(va_list args)
+{
+ struct st_mysqlnd_protocol_no_params_command * command;
+ DBG_ENTER("mysqlnd_com_debug_create_command");
+ command = mnd_ecalloc(1, sizeof(struct st_mysqlnd_protocol_no_params_command));
+ if (command) {
+ command->context.conn = va_arg(args, MYSQLND_CONN_DATA *);
+ command->parent.free_command = mysqlnd_com_no_params_free_command;
+
+ command->parent.run = mysqlnd_com_debug_run;
+ }
+
+ DBG_RETURN((struct st_mysqlnd_protocol_command *) command);
+}
+/* }}} */
+
+
+/************************** COM_INIT_DB ******************************************/
+struct st_mysqlnd_protocol_com_init_db_command
+{
+ struct st_mysqlnd_protocol_command parent;
+ struct st_mysqlnd_com_init_db_context
+ {
+ MYSQLND_CONN_DATA * conn;
+ MYSQLND_CSTRING db;
+ } context;
+};
+
+
+/* {{{ mysqlnd_com_init_db_run */
+static enum_func_status
+mysqlnd_com_init_db_run(void *cmd)
+{
+ struct st_mysqlnd_protocol_com_init_db_command * command = (struct st_mysqlnd_protocol_com_init_db_command *) cmd;
+ enum_func_status ret = FAIL;
+ MYSQLND_CONN_DATA * conn = command->context.conn;
+ const MYSQLND_CSTRING db = command->context.db;
+ func_mysqlnd_protocol_payload_decoder_factory__send_command send_command = conn->payload_decoder_factory->m.send_command;
+ func_mysqlnd_protocol_payload_decoder_factory__send_command_handle_response send_command_handle_response = conn->payload_decoder_factory->m.send_command_handle_response;
+
+ DBG_ENTER("mysqlnd_com_init_db_run");
+
+ ret = send_command(conn->payload_decoder_factory, COM_INIT_DB, (zend_uchar*) command->context.db.s, command->context.db.l, FALSE,
+ &conn->state,
+ conn->error_info,
+ conn->upsert_status,
+ conn->stats,
+ conn->m->send_close,
+ conn);
+ if (PASS == ret) {
+ ret = send_command_handle_response(conn->payload_decoder_factory, PROT_OK_PACKET, FALSE, COM_INIT_DB, TRUE,
+ conn->error_info, conn->upsert_status, &conn->last_message, conn->persistent);
+ }
+
+ /*
+ The server sends 0 but libmysql doesn't read it and has established
+ a protocol of giving back -1. Thus we have to follow it :(
+ */
+ UPSERT_STATUS_SET_AFFECTED_ROWS_TO_ERROR(conn->upsert_status);
+ if (ret == PASS) {
+ if (conn->connect_or_select_db.s) {
+ mnd_pefree(conn->connect_or_select_db.s, conn->persistent);
+ }
+ conn->connect_or_select_db.s = mnd_pestrndup(db.s, db.l, conn->persistent);
+ conn->connect_or_select_db.l = db.l;
+ if (!conn->connect_or_select_db.s) {
+ /* OOM */
+ SET_OOM_ERROR(conn->error_info);
+ ret = FAIL;
+ }
+ }
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_com_init_db_create_command */
+static struct st_mysqlnd_protocol_command *
+mysqlnd_com_init_db_create_command(va_list args)
+{
+ struct st_mysqlnd_protocol_com_init_db_command * command;
+ DBG_ENTER("mysqlnd_com_init_db_create_command");
+ command = mnd_ecalloc(1, sizeof(struct st_mysqlnd_protocol_com_init_db_command));
+ if (command) {
+ command->context.conn = va_arg(args, MYSQLND_CONN_DATA *);
+ command->context.db = va_arg(args, MYSQLND_CSTRING);
+
+ command->parent.free_command = mysqlnd_com_no_params_free_command;
+ command->parent.run = mysqlnd_com_init_db_run;
+ }
+
+ DBG_RETURN((struct st_mysqlnd_protocol_command *) command);
+}
+/* }}} */
+
+
+/************************** COM_PING ******************************************/
+/* {{{ mysqlnd_com_ping_run */
+static enum_func_status
+mysqlnd_com_ping_run(void *cmd)
+{
+ struct st_mysqlnd_protocol_no_params_command * command = (struct st_mysqlnd_protocol_no_params_command *) cmd;
+ enum_func_status ret = FAIL;
+ MYSQLND_CONN_DATA * conn = command->context.conn;
+ func_mysqlnd_protocol_payload_decoder_factory__send_command send_command = conn->payload_decoder_factory->m.send_command;
+ func_mysqlnd_protocol_payload_decoder_factory__send_command_handle_response send_command_handle_response = conn->payload_decoder_factory->m.send_command_handle_response;
+
+ DBG_ENTER("mysqlnd_com_ping_run");
+
+ ret = send_command(conn->payload_decoder_factory, COM_PING, NULL, 0, TRUE,
+ &conn->state,
+ conn->error_info,
+ conn->upsert_status,
+ conn->stats,
+ conn->m->send_close,
+ conn);
+ if (PASS == ret) {
+ ret = send_command_handle_response(conn->payload_decoder_factory, PROT_OK_PACKET, TRUE, COM_PING, TRUE,
+ conn->error_info, conn->upsert_status, &conn->last_message, conn->persistent);
+ }
+ /*
+ The server sends 0 but libmysql doesn't read it and has established
+ a protocol of giving back -1. Thus we have to follow it :(
+ */
+ UPSERT_STATUS_SET_AFFECTED_ROWS_TO_ERROR(conn->upsert_status);
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_com_ping_create_command */
+static struct st_mysqlnd_protocol_command *
+mysqlnd_com_ping_create_command(va_list args)
+{
+ struct st_mysqlnd_protocol_no_params_command * command;
+ DBG_ENTER("mysqlnd_com_ping_create_command");
+ command = mnd_ecalloc(1, sizeof(struct st_mysqlnd_protocol_no_params_command));
+ if (command) {
+ command->context.conn = va_arg(args, MYSQLND_CONN_DATA *);
+ command->parent.free_command = mysqlnd_com_no_params_free_command;
+
+ command->parent.run = mysqlnd_com_ping_run;
+ }
+
+ DBG_RETURN((struct st_mysqlnd_protocol_command *) command);
+}
+/* }}} */
+
+
+/************************** COM_STATISTICS ******************************************/
+struct st_mysqlnd_protocol_com_statistics_command
+{
+ struct st_mysqlnd_protocol_command parent;
+ struct st_mysqlnd_com_statistics_context
+ {
+ MYSQLND_CONN_DATA * conn;
+ zend_string ** message;
+ } context;
+};
+
+
+/* {{{ mysqlnd_com_statistics_run */
+static enum_func_status
+mysqlnd_com_statistics_run(void *cmd)
+{
+ struct st_mysqlnd_protocol_com_statistics_command * command = (struct st_mysqlnd_protocol_com_statistics_command *) cmd;
+ enum_func_status ret = FAIL;
+ MYSQLND_CONN_DATA * conn = command->context.conn;
+ zend_string **message = command->context.message;
+ func_mysqlnd_protocol_payload_decoder_factory__send_command send_command = conn->payload_decoder_factory->m.send_command;
+
+ DBG_ENTER("mysqlnd_com_statistics_run");
+
+ ret = send_command(conn->payload_decoder_factory, COM_STATISTICS, NULL, 0, FALSE,
+ &conn->state,
+ conn->error_info,
+ conn->upsert_status,
+ conn->stats,
+ conn->m->send_close,
+ conn);
+
+ if (PASS == ret) {
+ MYSQLND_PACKET_STATS * stats_header = conn->payload_decoder_factory->m.get_stats_packet(conn->payload_decoder_factory, FALSE);
+ if (!stats_header) {
+ SET_OOM_ERROR(conn->error_info);
+ } else {
+ if (PASS == (ret = PACKET_READ(stats_header))) {
+ /* will be freed by Zend, thus don't use the mnd_ allocator */
+ *message = zend_string_init(stats_header->message.s, stats_header->message.l, 0);
+ DBG_INF(ZSTR_VAL(*message));
+ }
+ PACKET_FREE(stats_header);
+ }
+ }
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_com_statistics_create_command */
+static struct st_mysqlnd_protocol_command *
+mysqlnd_com_statistics_create_command(va_list args)
+{
+ struct st_mysqlnd_protocol_com_statistics_command * command;
+ DBG_ENTER("mysqlnd_com_statistics_create_command");
+ command = mnd_ecalloc(1, sizeof(struct st_mysqlnd_protocol_com_statistics_command));
+ if (command) {
+ command->context.conn = va_arg(args, MYSQLND_CONN_DATA *);
+ command->context.message = va_arg(args, zend_string **);
+
+ command->parent.free_command = mysqlnd_com_no_params_free_command;
+ command->parent.run = mysqlnd_com_statistics_run;
+ }
+
+ DBG_RETURN((struct st_mysqlnd_protocol_command *) command);
+}
+/* }}} */
+
+/************************** COM_PROCESS_KILL ******************************************/
+struct st_mysqlnd_protocol_com_process_kill_command
+{
+ struct st_mysqlnd_protocol_command parent;
+ struct st_mysqlnd_com_process_kill_context
+ {
+ MYSQLND_CONN_DATA * conn;
+ unsigned int process_id;
+ zend_bool read_response;
+ } context;
+};
+
+
+/* {{{ mysqlnd_com_process_kill_run */
+enum_func_status
+mysqlnd_com_process_kill_run(void *cmd)
+{
+ struct st_mysqlnd_protocol_com_process_kill_command * command = (struct st_mysqlnd_protocol_com_process_kill_command *) cmd;
+ zend_uchar buff[4];
+ enum_func_status ret = FAIL;
+ MYSQLND_CONN_DATA * conn = command->context.conn;
+ zend_bool read_response = command->context.read_response;
+ func_mysqlnd_protocol_payload_decoder_factory__send_command send_command = conn->payload_decoder_factory->m.send_command;
+ func_mysqlnd_protocol_payload_decoder_factory__send_command_handle_response send_command_handle_response = conn->payload_decoder_factory->m.send_command_handle_response;
+
+ DBG_ENTER("mysqlnd_com_process_kill_run");
+ int4store(buff, command->context.process_id);
+
+ ret = send_command(conn->payload_decoder_factory, COM_PROCESS_KILL, buff, 4, FALSE,
+ &conn->state,
+ conn->error_info,
+ conn->upsert_status,
+ conn->stats,
+ conn->m->send_close,
+ conn);
+ if (PASS == ret && read_response) {
+ ret = send_command_handle_response(conn->payload_decoder_factory, PROT_OK_PACKET, FALSE, COM_PROCESS_KILL, TRUE,
+ conn->error_info, conn->upsert_status, &conn->last_message, conn->persistent);
+ }
+
+ if (read_response) {
+ /*
+ The server sends 0 but libmysql doesn't read it and has established
+ a protocol of giving back -1. Thus we have to follow it :(
+ */
+ UPSERT_STATUS_SET_AFFECTED_ROWS_TO_ERROR(conn->upsert_status);
+ } else if (PASS == ret) {
+ SET_CONNECTION_STATE(&conn->state, CONN_QUIT_SENT);
+ conn->m->send_close(conn);
+ }
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_com_process_kill_create_command */
+static struct st_mysqlnd_protocol_command *
+mysqlnd_com_process_kill_create_command(va_list args)
+{
+ struct st_mysqlnd_protocol_com_process_kill_command * command;
+ DBG_ENTER("mysqlnd_com_process_kill_create_command");
+ command = mnd_ecalloc(1, sizeof(struct st_mysqlnd_protocol_com_process_kill_command));
+ if (command) {
+ command->context.conn = va_arg(args, MYSQLND_CONN_DATA *);
+ command->context.process_id = va_arg(args, unsigned int);
+ command->context.read_response = va_arg(args, unsigned int)? TRUE:FALSE;
+
+ command->parent.free_command = mysqlnd_com_no_params_free_command;
+ command->parent.run = mysqlnd_com_process_kill_run;
+ }
+
+ DBG_RETURN((struct st_mysqlnd_protocol_command *) command);
+}
+/* }}} */
+
+/************************** COM_REFRESH ******************************************/
+struct st_mysqlnd_protocol_com_refresh_command
+{
+ struct st_mysqlnd_protocol_command parent;
+ struct st_mysqlnd_com_refresh_context
+ {
+ MYSQLND_CONN_DATA * conn;
+ uint8_t options;
+ } context;
+};
+
+
+/* {{{ mysqlnd_com_refresh_run */
+enum_func_status
+mysqlnd_com_refresh_run(void *cmd)
+{
+ struct st_mysqlnd_protocol_com_refresh_command * command = (struct st_mysqlnd_protocol_com_refresh_command *) cmd;
+ zend_uchar bits[1];
+ enum_func_status ret = FAIL;
+ MYSQLND_CONN_DATA * conn = command->context.conn;
+ func_mysqlnd_protocol_payload_decoder_factory__send_command send_command = conn->payload_decoder_factory->m.send_command;
+ func_mysqlnd_protocol_payload_decoder_factory__send_command_handle_response send_command_handle_response = conn->payload_decoder_factory->m.send_command_handle_response;
+
+ DBG_ENTER("mysqlnd_com_refresh_run");
+ int1store(bits, command->context.options);
+
+ ret = send_command(conn->payload_decoder_factory, COM_REFRESH, bits, 1, FALSE,
+ &conn->state,
+ conn->error_info,
+ conn->upsert_status,
+ conn->stats,
+ conn->m->send_close,
+ conn);
+ if (PASS == ret) {
+ ret = send_command_handle_response(conn->payload_decoder_factory, PROT_OK_PACKET, FALSE, COM_REFRESH, TRUE,
+ conn->error_info, conn->upsert_status, &conn->last_message, conn->persistent);
+ }
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_com_refresh_create_command */
+static struct st_mysqlnd_protocol_command *
+mysqlnd_com_refresh_create_command(va_list args)
+{
+ struct st_mysqlnd_protocol_com_refresh_command * command;
+ DBG_ENTER("mysqlnd_com_refresh_create_command");
+ command = mnd_ecalloc(1, sizeof(struct st_mysqlnd_protocol_com_refresh_command));
+ if (command) {
+ command->context.conn = va_arg(args, MYSQLND_CONN_DATA *);
+ command->context.options = va_arg(args, unsigned int);
+
+ command->parent.free_command = mysqlnd_com_no_params_free_command;
+ command->parent.run = mysqlnd_com_refresh_run;
+ }
+
+ DBG_RETURN((struct st_mysqlnd_protocol_command *) command);
+}
+/* }}} */
+
+
+/************************** COM_SHUTDOWN ******************************************/
+struct st_mysqlnd_protocol_com_shutdown_command
+{
+ struct st_mysqlnd_protocol_command parent;
+ struct st_mysqlnd_com_shutdown_context
+ {
+ MYSQLND_CONN_DATA * conn;
+ uint8_t level;
+ } context;
+};
+
+
+/* {{{ mysqlnd_com_shutdown_run */
+enum_func_status
+mysqlnd_com_shutdown_run(void *cmd)
+{
+ struct st_mysqlnd_protocol_com_shutdown_command * command = (struct st_mysqlnd_protocol_com_shutdown_command *) cmd;
+ zend_uchar bits[1];
+ enum_func_status ret = FAIL;
+ MYSQLND_CONN_DATA * conn = command->context.conn;
+ func_mysqlnd_protocol_payload_decoder_factory__send_command send_command = conn->payload_decoder_factory->m.send_command;
+ func_mysqlnd_protocol_payload_decoder_factory__send_command_handle_response send_command_handle_response = conn->payload_decoder_factory->m.send_command_handle_response;
+
+ DBG_ENTER("mysqlnd_com_shutdown_run");
+ int1store(bits, command->context.level);
+
+ ret = send_command(conn->payload_decoder_factory, COM_SHUTDOWN, bits, 1, FALSE,
+ &conn->state,
+ conn->error_info,
+ conn->upsert_status,
+ conn->stats,
+ conn->m->send_close,
+ conn);
+ if (PASS == ret) {
+ ret = send_command_handle_response(conn->payload_decoder_factory, PROT_OK_PACKET, FALSE, COM_SHUTDOWN, TRUE,
+ conn->error_info, conn->upsert_status, &conn->last_message, conn->persistent);
+ }
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_com_shutdown_create_command */
+static struct st_mysqlnd_protocol_command *
+mysqlnd_com_shutdown_create_command(va_list args)
+{
+ struct st_mysqlnd_protocol_com_shutdown_command * command;
+ DBG_ENTER("mysqlnd_com_shutdown_create_command");
+ command = mnd_ecalloc(1, sizeof(struct st_mysqlnd_protocol_com_shutdown_command));
+ if (command) {
+ command->context.conn = va_arg(args, MYSQLND_CONN_DATA *);
+ command->context.level = va_arg(args, unsigned int);
+
+ command->parent.free_command = mysqlnd_com_no_params_free_command;
+ command->parent.run = mysqlnd_com_shutdown_run;
+ }
+
+ DBG_RETURN((struct st_mysqlnd_protocol_command *) command);
+}
+/* }}} */
+
+
+/************************** COM_QUIT ******************************************/
+struct st_mysqlnd_protocol_com_quit_command
+{
+ struct st_mysqlnd_protocol_command parent;
+ struct st_mysqlnd_com_quit_context
+ {
+ MYSQLND_CONN_DATA * conn;
+ } context;
+};
+
+
+/* {{{ mysqlnd_com_quit_run */
+enum_func_status
+mysqlnd_com_quit_run(void *cmd)
+{
+ struct st_mysqlnd_protocol_com_quit_command * command = (struct st_mysqlnd_protocol_com_quit_command *) cmd;
+ enum_func_status ret = FAIL;
+ MYSQLND_CONN_DATA * conn = command->context.conn;
+ func_mysqlnd_protocol_payload_decoder_factory__send_command send_command = conn->payload_decoder_factory->m.send_command;
+
+ DBG_ENTER("mysqlnd_com_quit_run");
+
+ ret = send_command(conn->payload_decoder_factory, COM_QUIT, NULL, 0, TRUE,
+ &conn->state,
+ conn->error_info,
+ conn->upsert_status,
+ conn->stats,
+ conn->m->send_close,
+ conn);
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_com_quit_create_command */
+static struct st_mysqlnd_protocol_command *
+mysqlnd_com_quit_create_command(va_list args)
+{
+ struct st_mysqlnd_protocol_com_quit_command * command;
+ DBG_ENTER("mysqlnd_com_quit_create_command");
+ command = mnd_ecalloc(1, sizeof(struct st_mysqlnd_protocol_com_quit_command));
+ if (command) {
+ command->context.conn = va_arg(args, MYSQLND_CONN_DATA *);
+
+ command->parent.free_command = mysqlnd_com_no_params_free_command;
+ command->parent.run = mysqlnd_com_quit_run;
+ }
+
+ DBG_RETURN((struct st_mysqlnd_protocol_command *) command);
+}
+/* }}} */
+
+/************************** COM_QUERY ******************************************/
+struct st_mysqlnd_protocol_com_query_command
+{
+ struct st_mysqlnd_protocol_command parent;
+ struct st_mysqlnd_com_query_context
+ {
+ MYSQLND_CONN_DATA * conn;
+ MYSQLND_CSTRING query;
+ } context;
+};
+
+
+/* {{{ mysqlnd_com_query_run */
+static enum_func_status
+mysqlnd_com_query_run(void *cmd)
+{
+ struct st_mysqlnd_protocol_com_query_command * command = (struct st_mysqlnd_protocol_com_query_command *) cmd;
+ enum_func_status ret = FAIL;
+ MYSQLND_CONN_DATA * conn = command->context.conn;
+ func_mysqlnd_protocol_payload_decoder_factory__send_command send_command = conn->payload_decoder_factory->m.send_command;
+
+ DBG_ENTER("mysqlnd_com_query_run");
+
+ ret = send_command(conn->payload_decoder_factory, COM_QUERY, (zend_uchar*) command->context.query.s, command->context.query.l, FALSE,
+ &conn->state,
+ conn->error_info,
+ conn->upsert_status,
+ conn->stats,
+ conn->m->send_close,
+ conn);
+
+ if (PASS == ret) {
+ SET_CONNECTION_STATE(&conn->state, CONN_QUERY_SENT);
+ }
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_com_query_create_command */
+static struct st_mysqlnd_protocol_command *
+mysqlnd_com_query_create_command(va_list args)
+{
+ struct st_mysqlnd_protocol_com_query_command * command;
+ DBG_ENTER("mysqlnd_com_query_create_command");
+ command = mnd_ecalloc(1, sizeof(struct st_mysqlnd_protocol_com_query_command));
+ if (command) {
+ command->context.conn = va_arg(args, MYSQLND_CONN_DATA *);
+ command->context.query = va_arg(args, MYSQLND_CSTRING);
+
+ command->parent.free_command = mysqlnd_com_no_params_free_command;
+ command->parent.run = mysqlnd_com_query_run;
+ }
+
+ DBG_RETURN((struct st_mysqlnd_protocol_command *) command);
+}
+/* }}} */
+
+/************************** COM_CHANGE_USER ******************************************/
+struct st_mysqlnd_protocol_com_change_user_command
+{
+ struct st_mysqlnd_protocol_command parent;
+ struct st_mysqlnd_com_change_user_context
+ {
+ MYSQLND_CONN_DATA * conn;
+ MYSQLND_CSTRING payload;
+ zend_bool silent;
+ } context;
+};
+
+
+/* {{{ mysqlnd_com_change_user_run */
+static enum_func_status
+mysqlnd_com_change_user_run(void *cmd)
+{
+ struct st_mysqlnd_protocol_com_change_user_command * command = (struct st_mysqlnd_protocol_com_change_user_command *) cmd;
+ enum_func_status ret = FAIL;
+ MYSQLND_CONN_DATA * conn = command->context.conn;
+ func_mysqlnd_protocol_payload_decoder_factory__send_command send_command = conn->payload_decoder_factory->m.send_command;
+
+ DBG_ENTER("mysqlnd_com_change_user_run");
+
+ ret = send_command(conn->payload_decoder_factory, COM_CHANGE_USER, (zend_uchar*) command->context.payload.s, command->context.payload.l, command->context.silent,
+ &conn->state,
+ conn->error_info,
+ conn->upsert_status,
+ conn->stats,
+ conn->m->send_close,
+ conn);
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_com_change_user_create_command */
+static struct st_mysqlnd_protocol_command *
+mysqlnd_com_change_user_create_command(va_list args)
+{
+ struct st_mysqlnd_protocol_com_change_user_command * command;
+ DBG_ENTER("mysqlnd_com_change_user_create_command");
+ command = mnd_ecalloc(1, sizeof(struct st_mysqlnd_protocol_com_change_user_command));
+ if (command) {
+ command->context.conn = va_arg(args, MYSQLND_CONN_DATA *);
+ command->context.payload = va_arg(args, MYSQLND_CSTRING);
+ command->context.silent = va_arg(args, unsigned int);
+
+ command->parent.free_command = mysqlnd_com_no_params_free_command;
+ command->parent.run = mysqlnd_com_change_user_run;
+ }
+
+ DBG_RETURN((struct st_mysqlnd_protocol_command *) command);
+}
+/* }}} */
+
+
+/************************** COM_REAP_RESULT ******************************************/
+struct st_mysqlnd_protocol_com_reap_result_command
+{
+ struct st_mysqlnd_protocol_command parent;
+ struct st_mysqlnd_com_reap_result_context
+ {
+ MYSQLND_CONN_DATA * conn;
+ } context;
+};
+
+
+/* {{{ mysqlnd_com_reap_result_run */
+static enum_func_status
+mysqlnd_com_reap_result_run(void *cmd)
+{
+ struct st_mysqlnd_protocol_com_reap_result_command * command = (struct st_mysqlnd_protocol_com_reap_result_command *) cmd;
+ enum_func_status ret = FAIL;
+ MYSQLND_CONN_DATA * conn = command->context.conn;
+ const enum_mysqlnd_connection_state state = GET_CONNECTION_STATE(&conn->state);
+
+ DBG_ENTER("mysqlnd_com_reap_result_run");
+ if (state <= CONN_READY || state == CONN_QUIT_SENT) {
+ php_error_docref(NULL, E_WARNING, "Connection not opened, clear or has been closed");
+ DBG_ERR_FMT("Connection not opened, clear or has been closed. State=%u", state);
+ DBG_RETURN(ret);
+ }
+ ret = conn->m->query_read_result_set_header(conn, NULL);
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_com_reap_result_create_command */
+static struct st_mysqlnd_protocol_command *
+mysqlnd_com_reap_result_create_command(va_list args)
+{
+ struct st_mysqlnd_protocol_com_reap_result_command * command;
+ DBG_ENTER("mysqlnd_com_reap_result_create_command");
+ command = mnd_ecalloc(1, sizeof(struct st_mysqlnd_protocol_com_reap_result_command));
+ if (command) {
+ command->context.conn = va_arg(args, MYSQLND_CONN_DATA *);
+
+ command->parent.free_command = mysqlnd_com_no_params_free_command;
+ command->parent.run = mysqlnd_com_reap_result_run;
+ }
+
+ DBG_RETURN((struct st_mysqlnd_protocol_command *) command);
+}
+/* }}} */
+
+
+/************************** COM_STMT_PREPARE ******************************************/
+struct st_mysqlnd_protocol_com_stmt_prepare_command
+{
+ struct st_mysqlnd_protocol_command parent;
+ struct st_mysqlnd_com_stmt_prepare_context
+ {
+ MYSQLND_CONN_DATA * conn;
+ MYSQLND_CSTRING query;
+ } context;
+};
+
+
+/* {{{ mysqlnd_com_stmt_prepare_run */
+static enum_func_status
+mysqlnd_com_stmt_prepare_run(void *cmd)
+{
+ struct st_mysqlnd_protocol_com_stmt_prepare_command * command = (struct st_mysqlnd_protocol_com_stmt_prepare_command *) cmd;
+ enum_func_status ret = FAIL;
+ MYSQLND_CONN_DATA * conn = command->context.conn;
+ func_mysqlnd_protocol_payload_decoder_factory__send_command send_command = conn->payload_decoder_factory->m.send_command;
+
+ DBG_ENTER("mysqlnd_com_stmt_prepare_run");
+
+ ret = send_command(conn->payload_decoder_factory, COM_STMT_PREPARE, (zend_uchar*) command->context.query.s, command->context.query.l, FALSE,
+ &conn->state,
+ conn->error_info,
+ conn->upsert_status,
+ conn->stats,
+ conn->m->send_close,
+ conn);
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_com_stmt_prepare_create_command */
+static struct st_mysqlnd_protocol_command *
+mysqlnd_com_stmt_prepare_create_command(va_list args)
+{
+ struct st_mysqlnd_protocol_com_stmt_prepare_command * command;
+ DBG_ENTER("mysqlnd_com_stmt_prepare_create_command");
+ command = mnd_ecalloc(1, sizeof(struct st_mysqlnd_protocol_com_stmt_prepare_command));
+ if (command) {
+ command->context.conn = va_arg(args, MYSQLND_CONN_DATA *);
+ command->context.query = va_arg(args, MYSQLND_CSTRING);
+
+ command->parent.free_command = mysqlnd_com_no_params_free_command;
+ command->parent.run = mysqlnd_com_stmt_prepare_run;
+ }
+
+ DBG_RETURN((struct st_mysqlnd_protocol_command *) command);
+}
+/* }}} */
+
+
+/************************** COM_STMT_EXECUTE ******************************************/
+struct st_mysqlnd_protocol_com_stmt_execute_command
+{
+ struct st_mysqlnd_protocol_command parent;
+ struct st_mysqlnd_com_stmt_execute_context
+ {
+ MYSQLND_CONN_DATA * conn;
+ MYSQLND_CSTRING payload;
+ } context;
+};
+
+
+/* {{{ mysqlnd_com_stmt_execute_run */
+static enum_func_status
+mysqlnd_com_stmt_execute_run(void *cmd)
+{
+ struct st_mysqlnd_protocol_com_stmt_execute_command * command = (struct st_mysqlnd_protocol_com_stmt_execute_command *) cmd;
+ enum_func_status ret = FAIL;
+ MYSQLND_CONN_DATA * conn = command->context.conn;
+ func_mysqlnd_protocol_payload_decoder_factory__send_command send_command = conn->payload_decoder_factory->m.send_command;
+
+ DBG_ENTER("mysqlnd_com_stmt_execute_run");
+
+ ret = send_command(conn->payload_decoder_factory, COM_STMT_EXECUTE, (zend_uchar*) command->context.payload.s, command->context.payload.l, FALSE,
+ &conn->state,
+ conn->error_info,
+ conn->upsert_status,
+ conn->stats,
+ conn->m->send_close,
+ conn);
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_com_stmt_execute_create_command */
+static struct st_mysqlnd_protocol_command *
+mysqlnd_com_stmt_execute_create_command(va_list args)
+{
+ struct st_mysqlnd_protocol_com_stmt_execute_command * command;
+ DBG_ENTER("mysqlnd_com_stmt_execute_create_command");
+ command = mnd_ecalloc(1, sizeof(struct st_mysqlnd_protocol_com_stmt_execute_command));
+ if (command) {
+ command->context.conn = va_arg(args, MYSQLND_CONN_DATA *);
+ command->context.payload = va_arg(args, MYSQLND_CSTRING);
+
+ command->parent.free_command = mysqlnd_com_no_params_free_command;
+ command->parent.run = mysqlnd_com_stmt_execute_run;
+ }
+
+ DBG_RETURN((struct st_mysqlnd_protocol_command *) command);
+}
+/* }}} */
+
+
+/************************** COM_STMT_FETCH ******************************************/
+struct st_mysqlnd_protocol_com_stmt_fetch_command
+{
+ struct st_mysqlnd_protocol_command parent;
+ struct st_mysqlnd_com_stmt_fetch_context
+ {
+ MYSQLND_CONN_DATA * conn;
+ MYSQLND_CSTRING payload;
+ } context;
+};
+
+
+/* {{{ mysqlnd_com_stmt_fetch_run */
+static enum_func_status
+mysqlnd_com_stmt_fetch_run(void *cmd)
+{
+ struct st_mysqlnd_protocol_com_stmt_fetch_command * command = (struct st_mysqlnd_protocol_com_stmt_fetch_command *) cmd;
+ enum_func_status ret = FAIL;
+ MYSQLND_CONN_DATA * conn = command->context.conn;
+ func_mysqlnd_protocol_payload_decoder_factory__send_command send_command = conn->payload_decoder_factory->m.send_command;
+
+ DBG_ENTER("mysqlnd_com_stmt_fetch_run");
+
+ ret = send_command(conn->payload_decoder_factory, COM_STMT_FETCH, (zend_uchar*) command->context.payload.s, command->context.payload.l, FALSE,
+ &conn->state,
+ conn->error_info,
+ conn->upsert_status,
+ conn->stats,
+ conn->m->send_close,
+ conn);
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_com_stmt_fetch_create_command */
+static struct st_mysqlnd_protocol_command *
+mysqlnd_com_stmt_fetch_create_command(va_list args)
+{
+ struct st_mysqlnd_protocol_com_stmt_fetch_command * command;
+ DBG_ENTER("mysqlnd_com_stmt_fetch_create_command");
+ command = mnd_ecalloc(1, sizeof(struct st_mysqlnd_protocol_com_stmt_fetch_command));
+ if (command) {
+ command->context.conn = va_arg(args, MYSQLND_CONN_DATA *);
+ command->context.payload = va_arg(args, MYSQLND_CSTRING);
+
+ command->parent.free_command = mysqlnd_com_no_params_free_command;
+ command->parent.run = mysqlnd_com_stmt_fetch_run;
+ }
+
+ DBG_RETURN((struct st_mysqlnd_protocol_command *) command);
+}
+/* }}} */
+
+
+/************************** COM_STMT_RESET ******************************************/
+struct st_mysqlnd_protocol_com_stmt_reset_command
+{
+ struct st_mysqlnd_protocol_command parent;
+ struct st_mysqlnd_com_stmt_reset_context
+ {
+ MYSQLND_CONN_DATA * conn;
+ zend_ulong stmt_id;
+ } context;
+};
+
+
+/* {{{ mysqlnd_com_stmt_reset_run */
+static enum_func_status
+mysqlnd_com_stmt_reset_run(void *cmd)
+{
+ zend_uchar cmd_buf[MYSQLND_STMT_ID_LENGTH /* statement id */];
+ struct st_mysqlnd_protocol_com_stmt_reset_command * command = (struct st_mysqlnd_protocol_com_stmt_reset_command *) cmd;
+ enum_func_status ret = FAIL;
+ MYSQLND_CONN_DATA * conn = command->context.conn;
+ func_mysqlnd_protocol_payload_decoder_factory__send_command send_command = conn->payload_decoder_factory->m.send_command;
+ func_mysqlnd_protocol_payload_decoder_factory__send_command_handle_response send_command_handle_response = conn->payload_decoder_factory->m.send_command_handle_response;
+
+ DBG_ENTER("mysqlnd_com_stmt_reset_run");
+
+ int4store(cmd_buf, command->context.stmt_id);
+ ret = send_command(conn->payload_decoder_factory, COM_STMT_RESET, cmd_buf, sizeof(cmd_buf), FALSE,
+ &conn->state,
+ conn->error_info,
+ conn->upsert_status,
+ conn->stats,
+ conn->m->send_close,
+ conn);
+ if (PASS == ret) {
+ ret = send_command_handle_response(conn->payload_decoder_factory, PROT_OK_PACKET, FALSE, COM_STMT_RESET, TRUE,
+ conn->error_info, conn->upsert_status, &conn->last_message, conn->persistent);
+ }
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_com_stmt_reset_create_command */
+static struct st_mysqlnd_protocol_command *
+mysqlnd_com_stmt_reset_create_command(va_list args)
+{
+ struct st_mysqlnd_protocol_com_stmt_reset_command * command;
+ DBG_ENTER("mysqlnd_com_stmt_reset_create_command");
+ command = mnd_ecalloc(1, sizeof(struct st_mysqlnd_protocol_com_stmt_reset_command));
+ if (command) {
+ command->context.conn = va_arg(args, MYSQLND_CONN_DATA *);
+ command->context.stmt_id = va_arg(args, size_t);
+
+ command->parent.free_command = mysqlnd_com_no_params_free_command;
+ command->parent.run = mysqlnd_com_stmt_reset_run;
+ }
+
+ DBG_RETURN((struct st_mysqlnd_protocol_command *) command);
+}
+/* }}} */
+
+
+/************************** COM_STMT_SEND_LONG_DATA ******************************************/
+struct st_mysqlnd_protocol_com_stmt_send_long_data_command
+{
+ struct st_mysqlnd_protocol_command parent;
+ struct st_mysqlnd_com_stmt_send_long_data_context
+ {
+ MYSQLND_CONN_DATA * conn;
+ MYSQLND_CSTRING payload;
+ } context;
+};
+
+
+/* {{{ mysqlnd_com_stmt_send_long_data_run */
+static enum_func_status
+mysqlnd_com_stmt_send_long_data_run(void *cmd)
+{
+ struct st_mysqlnd_protocol_com_stmt_send_long_data_command * command = (struct st_mysqlnd_protocol_com_stmt_send_long_data_command *) cmd;
+ enum_func_status ret = FAIL;
+ MYSQLND_CONN_DATA * conn = command->context.conn;
+ func_mysqlnd_protocol_payload_decoder_factory__send_command send_command = conn->payload_decoder_factory->m.send_command;
+
+ DBG_ENTER("mysqlnd_com_stmt_send_long_data_run");
+
+ ret = send_command(conn->payload_decoder_factory, COM_STMT_SEND_LONG_DATA, (zend_uchar*) command->context.payload.s, command->context.payload.l, FALSE,
+ &conn->state,
+ conn->error_info,
+ conn->upsert_status,
+ conn->stats,
+ conn->m->send_close,
+ conn);
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_com_stmt_send_long_data_create_command */
+static struct st_mysqlnd_protocol_command *
+mysqlnd_com_stmt_send_long_data_create_command(va_list args)
+{
+ struct st_mysqlnd_protocol_com_stmt_send_long_data_command * command;
+ DBG_ENTER("mysqlnd_com_stmt_send_long_data_create_command");
+ command = mnd_ecalloc(1, sizeof(struct st_mysqlnd_protocol_com_stmt_send_long_data_command));
+ if (command) {
+ command->context.conn = va_arg(args, MYSQLND_CONN_DATA *);
+ command->context.payload = va_arg(args, MYSQLND_CSTRING);
+
+ command->parent.free_command = mysqlnd_com_no_params_free_command;
+ command->parent.run = mysqlnd_com_stmt_send_long_data_run;
+ }
+
+ DBG_RETURN((struct st_mysqlnd_protocol_command *) command);
+}
+/* }}} */
+
+
+/************************** COM_STMT_CLOSE ******************************************/
+struct st_mysqlnd_protocol_com_stmt_close_command
+{
+ struct st_mysqlnd_protocol_command parent;
+ struct st_mysqlnd_com_stmt_close_context
+ {
+ MYSQLND_CONN_DATA * conn;
+ zend_ulong stmt_id;
+ } context;
+};
+
+
+/* {{{ mysqlnd_com_stmt_close_run */
+static enum_func_status
+mysqlnd_com_stmt_close_run(void *cmd)
+{
+ zend_uchar cmd_buf[MYSQLND_STMT_ID_LENGTH /* statement id */];
+ struct st_mysqlnd_protocol_com_stmt_close_command * command = (struct st_mysqlnd_protocol_com_stmt_close_command *) cmd;
+ enum_func_status ret = FAIL;
+ MYSQLND_CONN_DATA * conn = command->context.conn;
+ func_mysqlnd_protocol_payload_decoder_factory__send_command send_command = conn->payload_decoder_factory->m.send_command;
+
+ DBG_ENTER("mysqlnd_com_stmt_close_run");
+
+ int4store(cmd_buf, command->context.stmt_id);
+ ret = send_command(conn->payload_decoder_factory, COM_STMT_CLOSE, cmd_buf, sizeof(cmd_buf), FALSE,
+ &conn->state,
+ conn->error_info,
+ conn->upsert_status,
+ conn->stats,
+ conn->m->send_close,
+ conn);
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_com_stmt_close_create_command */
+static struct st_mysqlnd_protocol_command *
+mysqlnd_com_stmt_close_create_command(va_list args)
+{
+ struct st_mysqlnd_protocol_com_stmt_close_command * command;
+ DBG_ENTER("mysqlnd_com_stmt_close_create_command");
+ command = mnd_ecalloc(1, sizeof(struct st_mysqlnd_protocol_com_stmt_close_command));
+ if (command) {
+ command->context.conn = va_arg(args, MYSQLND_CONN_DATA *);
+ command->context.stmt_id = va_arg(args, size_t);
+
+ command->parent.free_command = mysqlnd_com_no_params_free_command;
+ command->parent.run = mysqlnd_com_stmt_close_run;
+ }
+
+ DBG_RETURN((struct st_mysqlnd_protocol_command *) command);
+}
+/* }}} */
+
+
+
+/************************** COM_ENABLE_SSL ******************************************/
+struct st_mysqlnd_protocol_com_enable_ssl_command
+{
+ struct st_mysqlnd_protocol_command parent;
+ struct st_mysqlnd_com_enable_ssl_context
+ {
+ MYSQLND_CONN_DATA * conn;
+ size_t client_capabilities;
+ size_t server_capabilities;
+ unsigned int charset_no;
+ } context;
+};
+
+
+/* {{{ mysqlnd_com_enable_ssl_run */
+static enum_func_status
+mysqlnd_com_enable_ssl_run(void *cmd)
+{
+ struct st_mysqlnd_protocol_com_enable_ssl_command * command = (struct st_mysqlnd_protocol_com_enable_ssl_command *) cmd;
+ enum_func_status ret = FAIL;
+ MYSQLND_CONN_DATA * conn = command->context.conn;
+ MYSQLND_PACKET_AUTH * auth_packet;
+ size_t client_capabilities = command->context.client_capabilities;
+ size_t server_capabilities = command->context.server_capabilities;
+
+ DBG_ENTER("mysqlnd_com_enable_ssl_run");
+ DBG_INF_FMT("client_capability_flags=%lu", client_capabilities);
+ DBG_INF_FMT("CLIENT_LONG_PASSWORD= %d", client_capabilities & CLIENT_LONG_PASSWORD? 1:0);
+ DBG_INF_FMT("CLIENT_FOUND_ROWS= %d", client_capabilities & CLIENT_FOUND_ROWS? 1:0);
+ DBG_INF_FMT("CLIENT_LONG_FLAG= %d", client_capabilities & CLIENT_LONG_FLAG? 1:0);
+ DBG_INF_FMT("CLIENT_NO_SCHEMA= %d", client_capabilities & CLIENT_NO_SCHEMA? 1:0);
+ DBG_INF_FMT("CLIENT_COMPRESS= %d", client_capabilities & CLIENT_COMPRESS? 1:0);
+ DBG_INF_FMT("CLIENT_ODBC= %d", client_capabilities & CLIENT_ODBC? 1:0);
+ DBG_INF_FMT("CLIENT_LOCAL_FILES= %d", client_capabilities & CLIENT_LOCAL_FILES? 1:0);
+ DBG_INF_FMT("CLIENT_IGNORE_SPACE= %d", client_capabilities & CLIENT_IGNORE_SPACE? 1:0);
+ DBG_INF_FMT("CLIENT_PROTOCOL_41= %d", client_capabilities & CLIENT_PROTOCOL_41? 1:0);
+ DBG_INF_FMT("CLIENT_INTERACTIVE= %d", client_capabilities & CLIENT_INTERACTIVE? 1:0);
+ DBG_INF_FMT("CLIENT_SSL= %d", client_capabilities & CLIENT_SSL? 1:0);
+ DBG_INF_FMT("CLIENT_IGNORE_SIGPIPE= %d", client_capabilities & CLIENT_IGNORE_SIGPIPE? 1:0);
+ DBG_INF_FMT("CLIENT_TRANSACTIONS= %d", client_capabilities & CLIENT_TRANSACTIONS? 1:0);
+ DBG_INF_FMT("CLIENT_RESERVED= %d", client_capabilities & CLIENT_RESERVED? 1:0);
+ DBG_INF_FMT("CLIENT_SECURE_CONNECTION=%d", client_capabilities & CLIENT_SECURE_CONNECTION? 1:0);
+ DBG_INF_FMT("CLIENT_MULTI_STATEMENTS=%d", client_capabilities & CLIENT_MULTI_STATEMENTS? 1:0);
+ DBG_INF_FMT("CLIENT_MULTI_RESULTS= %d", client_capabilities & CLIENT_MULTI_RESULTS? 1:0);
+ DBG_INF_FMT("CLIENT_PS_MULTI_RESULTS=%d", client_capabilities & CLIENT_PS_MULTI_RESULTS? 1:0);
+ DBG_INF_FMT("CLIENT_CONNECT_ATTRS= %d", client_capabilities & CLIENT_PLUGIN_AUTH? 1:0);
+ DBG_INF_FMT("CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA= %d", client_capabilities & CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA? 1:0);
+ DBG_INF_FMT("CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS= %d", client_capabilities & CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS? 1:0);
+ DBG_INF_FMT("CLIENT_SESSION_TRACK= %d", client_capabilities & CLIENT_SESSION_TRACK? 1:0);
+ DBG_INF_FMT("CLIENT_SSL_VERIFY_SERVER_CERT= %d", client_capabilities & CLIENT_SSL_VERIFY_SERVER_CERT? 1:0);
+ DBG_INF_FMT("CLIENT_REMEMBER_OPTIONS= %d", client_capabilities & CLIENT_REMEMBER_OPTIONS? 1:0);
+
+ auth_packet = conn->payload_decoder_factory->m.get_auth_packet(conn->payload_decoder_factory, FALSE);
+ if (!auth_packet) {
+ SET_OOM_ERROR(conn->error_info);
+ goto end;
+ }
+ auth_packet->client_flags = client_capabilities;
+ auth_packet->max_packet_size = MYSQLND_ASSEMBLED_PACKET_MAX_SIZE;
+
+ auth_packet->charset_no = command->context.charset_no;
+
+#ifdef MYSQLND_SSL_SUPPORTED
+ if (client_capabilities & CLIENT_SSL) {
+ const zend_bool server_has_ssl = (server_capabilities & CLIENT_SSL)? TRUE:FALSE;
+ if (server_has_ssl == FALSE) {
+ goto close_conn;
+ } else {
+ enum mysqlnd_ssl_peer verify = client_capabilities & CLIENT_SSL_VERIFY_SERVER_CERT?
+ MYSQLND_SSL_PEER_VERIFY:
+ (client_capabilities & CLIENT_SSL_DONT_VERIFY_SERVER_CERT?
+ MYSQLND_SSL_PEER_DONT_VERIFY:
+ MYSQLND_SSL_PEER_DEFAULT);
+ DBG_INF("Switching to SSL");
+ if (!PACKET_WRITE(auth_packet)) {
+ goto close_conn;
+ }
+
+ conn->vio->data->m.set_client_option(conn->vio, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, (const char *) &verify);
+
+ if (FAIL == conn->vio->data->m.enable_ssl(conn->vio)) {
+ goto end;
+ }
+ }
+ }
+#else
+ auth_packet->client_flags &= ~CLIENT_SSL;
+ if (!PACKET_WRITE(auth_packet)) {
+ goto close_conn;
+ }
+#endif
+ ret = PASS;
+end:
+ PACKET_FREE(auth_packet);
+ DBG_RETURN(ret);
+
+close_conn:
+ SET_CONNECTION_STATE(&conn->state, CONN_QUIT_SENT);
+ conn->m->send_close(conn);
+ SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
+ PACKET_FREE(auth_packet);
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_com_enable_ssl_create_command */
+static struct st_mysqlnd_protocol_command *
+mysqlnd_com_enable_ssl_create_command(va_list args)
+{
+ struct st_mysqlnd_protocol_com_enable_ssl_command * command;
+ DBG_ENTER("mysqlnd_com_enable_ssl_create_command");
+ command = mnd_ecalloc(1, sizeof(struct st_mysqlnd_protocol_com_enable_ssl_command));
+ if (command) {
+ command->context.conn = va_arg(args, MYSQLND_CONN_DATA *);
+ command->context.client_capabilities = va_arg(args, size_t);
+ command->context.server_capabilities = va_arg(args, size_t);
+ command->context.charset_no = va_arg(args, unsigned int);
+
+ command->parent.free_command = mysqlnd_com_no_params_free_command;
+ command->parent.run = mysqlnd_com_enable_ssl_run;
+ }
+
+ DBG_RETURN((struct st_mysqlnd_protocol_command *) command);
+}
+/* }}} */
+
+/************************** COM_READ_HANDSHAKE ******************************************/
+struct st_mysqlnd_protocol_com_handshake_command
+{
+ struct st_mysqlnd_protocol_command parent;
+ struct st_mysqlnd_com_handshake_context
+ {
+ MYSQLND_CONN_DATA * conn;
+ MYSQLND_CSTRING user;
+ MYSQLND_CSTRING passwd;
+ MYSQLND_CSTRING database;
+ size_t client_flags;
+ } context;
+};
+
+
+/* {{{ mysqlnd_com_handshake_run */
+static enum_func_status
+mysqlnd_com_handshake_run(void *cmd)
+{
+ struct st_mysqlnd_protocol_com_handshake_command * command = (struct st_mysqlnd_protocol_com_handshake_command *) cmd;
+ const char * user = command->context.user.s;
+
+ const char * passwd = command->context.passwd.s;
+ size_t passwd_len = command->context.passwd.l;
+
+ const char * db = command->context.database.s;
+ size_t db_len = command->context.database.l;
+
+ size_t mysql_flags = command->context.client_flags;
+
+ MYSQLND_CONN_DATA * conn = command->context.conn;
+ MYSQLND_PACKET_GREET * greet_packet;
+
+ DBG_ENTER("mysqlnd_conn_data::connect_handshake");
+ DBG_INF_FMT("stream=%p", conn->vio->data->m.get_stream(conn->vio));
+ DBG_INF_FMT("[user=%s] [db=%s:%d] [flags=%llu]", user, db, db_len, mysql_flags);
+
+ greet_packet = conn->payload_decoder_factory->m.get_greet_packet(conn->payload_decoder_factory, FALSE);
+ if (!greet_packet) {
+ SET_OOM_ERROR(conn->error_info);
+ DBG_RETURN(FAIL); /* OOM */
+ }
+
+ if (FAIL == PACKET_READ(greet_packet)) {
+ DBG_ERR("Error while reading greeting packet");
+ php_error_docref(NULL, E_WARNING, "Error while reading greeting packet. PID=%d", getpid());
+ goto err;
+ } else if (greet_packet->error_no) {
+ DBG_ERR_FMT("errorno=%u error=%s", greet_packet->error_no, greet_packet->error);
+ SET_CLIENT_ERROR(conn->error_info, greet_packet->error_no, greet_packet->sqlstate, greet_packet->error);
+ goto err;
+ } else if (greet_packet->pre41) {
+ DBG_ERR_FMT("Connecting to 3.22, 3.23 & 4.0 is not supported. Server is %-.32s", greet_packet->server_version);
+ php_error_docref(NULL, E_WARNING, "Connecting to 3.22, 3.23 & 4.0 "
+ " is not supported. Server is %-.32s", greet_packet->server_version);
+ SET_CLIENT_ERROR(conn->error_info, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE,
+ "Connecting to 3.22, 3.23 & 4.0 servers is not supported");
+ goto err;
+ }
+
+ conn->thread_id = greet_packet->thread_id;
+ conn->protocol_version = greet_packet->protocol_version;
+ conn->server_version = mnd_pestrdup(greet_packet->server_version, conn->persistent);
+
+ conn->greet_charset = mysqlnd_find_charset_nr(greet_packet->charset_no);
+ if (!conn->greet_charset) {
+ php_error_docref(NULL, E_WARNING,
+ "Server sent charset (%d) unknown to the client. Please, report to the developers", greet_packet->charset_no);
+ SET_CLIENT_ERROR(conn->error_info, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE,
+ "Server sent charset unknown to the client. Please, report to the developers");
+ goto err;
+ }
+
+ conn->server_capabilities = greet_packet->server_capabilities;
+
+ if (FAIL == mysqlnd_connect_run_authentication(conn, user, passwd, db, db_len, (size_t) passwd_len,
+ greet_packet->authentication_plugin_data, greet_packet->auth_protocol,
+ greet_packet->charset_no, greet_packet->server_capabilities,
+ conn->options, mysql_flags))
+ {
+ goto err;
+ }
+
+ UPSERT_STATUS_RESET(conn->upsert_status);
+ UPSERT_STATUS_SET_SERVER_STATUS(conn->upsert_status, greet_packet->server_status);
+
+ PACKET_FREE(greet_packet);
+ DBG_RETURN(PASS);
+err:
+ conn->server_capabilities = 0;
+ PACKET_FREE(greet_packet);
+ DBG_RETURN(FAIL);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_com_handshake_create_command */
+static struct st_mysqlnd_protocol_command *
+mysqlnd_com_handshake_create_command(va_list args)
+{
+ struct st_mysqlnd_protocol_com_handshake_command * command;
+ DBG_ENTER("mysqlnd_com_handshake_create_command");
+ command = mnd_ecalloc(1, sizeof(struct st_mysqlnd_protocol_com_handshake_command));
+ if (command) {
+ command->context.conn = va_arg(args, MYSQLND_CONN_DATA *);
+ command->context.user = *va_arg(args, const MYSQLND_CSTRING *);
+ command->context.passwd = *va_arg(args, const MYSQLND_CSTRING *);
+ command->context.database = *va_arg(args, const MYSQLND_CSTRING *);
+ command->context.client_flags = va_arg(args, size_t);
+
+ command->parent.free_command = mysqlnd_com_no_params_free_command;
+ command->parent.run = mysqlnd_com_handshake_run;
+ }
+
+ DBG_RETURN((struct st_mysqlnd_protocol_command *) command);
+}
+/* }}} */
+
+
+
+/* {{{ mysqlnd_get_command */
+static struct st_mysqlnd_protocol_command *
+mysqlnd_get_command(enum php_mysqlnd_server_command command, ...)
+{
+ struct st_mysqlnd_protocol_command * ret;
+ va_list args;
+ DBG_ENTER("mysqlnd_get_command");
+
+ va_start(args, command);
+ switch (command) {
+ case COM_SET_OPTION:
+ ret = mysqlnd_com_set_option_create_command(args);
+ break;
+ case COM_DEBUG:
+ ret = mysqlnd_com_debug_create_command(args);
+ break;
+ case COM_INIT_DB:
+ ret = mysqlnd_com_init_db_create_command(args);
+ break;
+ case COM_PING:
+ ret = mysqlnd_com_ping_create_command(args);
+ break;
+ case COM_STATISTICS:
+ ret = mysqlnd_com_statistics_create_command(args);
+ break;
+ case COM_PROCESS_KILL:
+ ret = mysqlnd_com_process_kill_create_command(args);
+ break;
+ case COM_REFRESH:
+ ret = mysqlnd_com_refresh_create_command(args);
+ break;
+ case COM_SHUTDOWN:
+ ret = mysqlnd_com_shutdown_create_command(args);
+ break;
+ case COM_QUIT:
+ ret = mysqlnd_com_quit_create_command(args);
+ break;
+ case COM_QUERY:
+ ret = mysqlnd_com_query_create_command(args);
+ break;
+ case COM_REAP_RESULT:
+ ret = mysqlnd_com_reap_result_create_command(args);
+ break;
+ case COM_CHANGE_USER:
+ ret = mysqlnd_com_change_user_create_command(args);
+ break;
+ case COM_STMT_PREPARE:
+ ret = mysqlnd_com_stmt_prepare_create_command(args);
+ break;
+ case COM_STMT_EXECUTE:
+ ret = mysqlnd_com_stmt_execute_create_command(args);
+ break;
+ case COM_STMT_FETCH:
+ ret = mysqlnd_com_stmt_fetch_create_command(args);
+ break;
+ case COM_STMT_RESET:
+ ret = mysqlnd_com_stmt_reset_create_command(args);
+ break;
+ case COM_STMT_SEND_LONG_DATA:
+ ret = mysqlnd_com_stmt_send_long_data_create_command(args);
+ break;
+ case COM_STMT_CLOSE:
+ ret = mysqlnd_com_stmt_close_create_command(args);
+ break;
+ case COM_ENABLE_SSL:
+ ret = mysqlnd_com_enable_ssl_create_command(args);
+ break;
+ case COM_HANDSHAKE:
+ ret = mysqlnd_com_handshake_create_command(args);
+ break;
+ default:
+ break;
+ }
+ va_end(args);
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+func_mysqlnd__command_factory mysqlnd_command_factory = mysqlnd_get_command;
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/mysqlnd/mysqlnd_commands.h b/ext/mysqlnd/mysqlnd_commands.h
new file mode 100644
index 0000000000..99f21cb0f4
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_commands.h
@@ -0,0 +1,34 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 7 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2017 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Andrey Hristov <andrey@php.net> |
+ | Ulf Wendel <uw@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef MYSQLND_COMMANDS_H
+#define MYSQLND_COMMANDS_H
+
+extern func_mysqlnd__command_factory mysqlnd_command_factory;
+
+#endif /* MYSQLND_COMMANDS_H */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/mysqlnd/mysqlnd.c b/ext/mysqlnd/mysqlnd_connection.c
index 40ca48e3f4..d15147ba6e 100644
--- a/ext/mysqlnd/mysqlnd.c
+++ b/ext/mysqlnd/mysqlnd_connection.c
@@ -14,55 +14,218 @@
+----------------------------------------------------------------------+
| Authors: Andrey Hristov <andrey@php.net> |
| Ulf Wendel <uw@php.net> |
- | Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
-/* $Id$ */
#include "php.h"
#include "mysqlnd.h"
+#include "mysqlnd_connection.h"
+#include "mysqlnd_vio.h"
+#include "mysqlnd_protocol_frame_codec.h"
+#include "mysqlnd_auth.h"
#include "mysqlnd_wireprotocol.h"
#include "mysqlnd_priv.h"
#include "mysqlnd_result.h"
#include "mysqlnd_statistics.h"
#include "mysqlnd_charset.h"
#include "mysqlnd_debug.h"
+#include "mysqlnd_ext_plugin.h"
#include "zend_smart_str.h"
-/*
- TODO :
- - Don't bind so tightly the metadata with the result set. This means
- that the metadata reading should not expect a MYSQLND_RES pointer, it
- does not need it, but return a pointer to the metadata (MYSQLND_FIELD *).
- For normal statements we will then just assign it to a member of
- MYSQLND_RES. For PS statements, it will stay as part of the statement
- (MYSQLND_STMT) between prepare and execute. At execute the new metadata
- will be sent by the server, so we will discard the old one and then
- finally attach it to the result set. This will make the code more clean,
- as a prepared statement won't have anymore stmt->result != NULL, as it
- is now, just to have where to store the metadata.
-
- - Change mysqlnd_simple_command to accept a heap dynamic array of MYSQLND_STRING
- terminated by a string with ptr being NULL. Thus, multi-part messages can be
- sent to the network like writev() and this can save at least for
- mysqlnd_stmt_send_long_data() new malloc. This change will probably make the
- code in few other places cleaner.
-*/
extern MYSQLND_CHARSET *mysqlnd_charsets;
-
-
-PHPAPI const char * const mysqlnd_old_passwd = "mysqlnd cannot connect to MySQL 4.1+ using the old insecure authentication. "
-"Please use an administration tool to reset your password with the command SET PASSWORD = PASSWORD('your_existing_password'). This will "
-"store a new, and more secure, hash value in mysql.user. If this user is used in other scripts executed by PHP 5.2 or earlier you might need to remove the old-passwords "
-"flag from your my.cnf file";
-
PHPAPI const char * const mysqlnd_server_gone = "MySQL server has gone away";
PHPAPI const char * const mysqlnd_out_of_sync = "Commands out of sync; you can't run this command now";
PHPAPI const char * const mysqlnd_out_of_memory = "Out of memory";
-PHPAPI MYSQLND_STATS *mysqlnd_global_stats = NULL;
+PHPAPI MYSQLND_STATS * mysqlnd_global_stats = NULL;
+
+
+/* {{{ mysqlnd_upsert_status::reset */
+void
+MYSQLND_METHOD(mysqlnd_upsert_status, reset)(MYSQLND_UPSERT_STATUS * const upsert_status)
+{
+ upsert_status->warning_count = 0;
+ upsert_status->server_status = 0;
+ upsert_status->affected_rows = 0;
+ upsert_status->last_insert_id = 0;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_upsert_status::set_affected_rows_to_error */
+void
+MYSQLND_METHOD(mysqlnd_upsert_status, set_affected_rows_to_error)(MYSQLND_UPSERT_STATUS * upsert_status)
+{
+ upsert_status->affected_rows = (uint64_t) ~0;
+}
+/* }}} */
+
+
+MYSQLND_CLASS_METHODS_START(mysqlnd_upsert_status)
+ MYSQLND_METHOD(mysqlnd_upsert_status, reset),
+ MYSQLND_METHOD(mysqlnd_upsert_status, set_affected_rows_to_error),
+MYSQLND_CLASS_METHODS_END;
+
+
+/* {{{ mysqlnd_upsert_status_init */
+void
+mysqlnd_upsert_status_init(MYSQLND_UPSERT_STATUS * const upsert_status)
+{
+ upsert_status->m = &MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_upsert_status);
+ upsert_status->m->reset(upsert_status);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_error_list_pdtor */
+static void
+mysqlnd_error_list_pdtor(void * pDest)
+{
+ MYSQLND_ERROR_LIST_ELEMENT * element = (MYSQLND_ERROR_LIST_ELEMENT *) pDest;
+
+ DBG_ENTER("mysqlnd_error_list_pdtor");
+ if (element->error) {
+ mnd_pefree(element->error, TRUE);
+ }
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_error_info::reset */
+static void
+MYSQLND_METHOD(mysqlnd_error_info, reset)(MYSQLND_ERROR_INFO * const info)
+{
+ DBG_ENTER("mysqlnd_error_info::reset");
+
+ info->error_no = 0;
+ info->error[0] = '\0';
+ memset(info->sqlstate, 0, sizeof(info->sqlstate));
+ if (info->error_list) {
+ zend_llist_clean(info->error_list);
+ }
+
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_error_info::set_client_error */
+static void
+MYSQLND_METHOD(mysqlnd_error_info, set_client_error)(MYSQLND_ERROR_INFO * const info,
+ const unsigned int err_no,
+ const char * const sqlstate,
+ const char * const error)
+{
+ DBG_ENTER("mysqlnd_error_info::set_client_error");
+ if (err_no) {
+ info->error_no = err_no;
+ strlcpy(info->sqlstate, sqlstate, sizeof(info->sqlstate));
+ strlcpy(info->error, error, sizeof(info->error));
+ if (info->error_list) {
+ MYSQLND_ERROR_LIST_ELEMENT error_for_the_list = {0};
+
+ error_for_the_list.error_no = err_no;
+ strlcpy(error_for_the_list.sqlstate, sqlstate, sizeof(error_for_the_list.sqlstate));
+ error_for_the_list.error = mnd_pestrdup(error, TRUE);
+ if (error_for_the_list.error) {
+ DBG_INF_FMT("adding error [%s] to the list", error_for_the_list.error);
+ zend_llist_add_element(info->error_list, &error_for_the_list);
+ }
+ }
+ } else {
+ info->m->reset(info);
+ }
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+MYSQLND_CLASS_METHODS_START(mysqlnd_error_info)
+ MYSQLND_METHOD(mysqlnd_error_info, reset),
+ MYSQLND_METHOD(mysqlnd_error_info, set_client_error),
+MYSQLND_CLASS_METHODS_END;
+
+
+
+/* {{{ mysqlnd_error_info_init */
+PHPAPI enum_func_status
+mysqlnd_error_info_init(MYSQLND_ERROR_INFO * const info, const zend_bool persistent)
+{
+ DBG_ENTER("mysqlnd_error_info_init");
+ info->m = mysqlnd_error_info_get_methods();
+ info->m->reset(info);
+
+ info->error_list = mnd_pecalloc(1, sizeof(zend_llist), persistent);
+ if (info->error_list) {
+ zend_llist_init(info->error_list, sizeof(MYSQLND_ERROR_LIST_ELEMENT), (llist_dtor_func_t) mysqlnd_error_list_pdtor, persistent);
+ }
+ info->persistent = persistent;
+ DBG_RETURN(info->error_list? PASS:FAIL);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_error_info_free_contents */
+PHPAPI void
+mysqlnd_error_info_free_contents(MYSQLND_ERROR_INFO * const info)
+{
+ DBG_ENTER("mysqlnd_error_info_free_contents");
+ info->m->reset(info);
+ if (info->error_list) {
+ mnd_pefree(info->error_list, info->persistent);
+ info->error_list = NULL;
+ }
+
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+
+
+/* {{{ mysqlnd_connection_state::get */
+static enum mysqlnd_connection_state
+MYSQLND_METHOD(mysqlnd_connection_state, get)(const struct st_mysqlnd_connection_state * const state_struct)
+{
+ DBG_ENTER("mysqlnd_connection_state::get");
+ DBG_INF_FMT("State=%u", state_struct->state);
+ DBG_RETURN(state_struct->state);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_connection_state::set */
+static void
+MYSQLND_METHOD(mysqlnd_connection_state, set)(struct st_mysqlnd_connection_state * const state_struct, const enum mysqlnd_connection_state state)
+{
+ DBG_ENTER("mysqlnd_connection_state::set");
+ DBG_INF_FMT("New state=%u", state);
+ state_struct->state = state;
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+MYSQLND_CLASS_METHODS_START(mysqlnd_connection_state)
+ MYSQLND_METHOD(mysqlnd_connection_state, get),
+ MYSQLND_METHOD(mysqlnd_connection_state, set),
+MYSQLND_CLASS_METHODS_END;
+
+
+/* {{{ mysqlnd_connection_state_init */
+PHPAPI void
+mysqlnd_connection_state_init(struct st_mysqlnd_connection_state * const state)
+{
+ DBG_ENTER("mysqlnd_connection_state_init");
+ state->m = &MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_connection_state);
+ state->state = CONN_ALLOCED;
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
/* {{{ mysqlnd_conn_data::free_options */
@@ -118,36 +281,40 @@ MYSQLND_METHOD(mysqlnd_conn_data, free_contents)(MYSQLND_CONN_DATA * conn)
conn->current_result = NULL;
}
- if (conn->net) {
- conn->net->data->m.free_contents(conn->net);
+ if (conn->protocol_frame_codec) {
+ conn->protocol_frame_codec->data->m.free_contents(conn->protocol_frame_codec);
+ }
+
+ if (conn->vio) {
+ conn->vio->data->m.free_contents(conn->vio);
}
DBG_INF("Freeing memory of members");
- if (conn->host) {
- mnd_pefree(conn->host, pers);
- conn->host = NULL;
+ if (conn->hostname.s) {
+ mnd_pefree(conn->hostname.s, pers);
+ conn->hostname.s = NULL;
}
- if (conn->user) {
- mnd_pefree(conn->user, pers);
- conn->user = NULL;
+ if (conn->username.s) {
+ mnd_pefree(conn->username.s, pers);
+ conn->username.s = NULL;
}
- if (conn->passwd) {
- mnd_pefree(conn->passwd, pers);
- conn->passwd = NULL;
+ if (conn->password.s) {
+ mnd_pefree(conn->password.s, pers);
+ conn->password.s = NULL;
}
- if (conn->connect_or_select_db) {
- mnd_pefree(conn->connect_or_select_db, pers);
- conn->connect_or_select_db = NULL;
+ if (conn->connect_or_select_db.s) {
+ mnd_pefree(conn->connect_or_select_db.s, pers);
+ conn->connect_or_select_db.s = NULL;
}
- if (conn->unix_socket) {
- mnd_pefree(conn->unix_socket, pers);
- conn->unix_socket = NULL;
+ if (conn->unix_socket.s) {
+ mnd_pefree(conn->unix_socket.s, pers);
+ conn->unix_socket.s = NULL;
}
- DBG_INF_FMT("scheme=%s", conn->scheme);
- if (conn->scheme) {
- mnd_pefree(conn->scheme, pers);
- conn->scheme = NULL;
+ DBG_INF_FMT("scheme=%s", conn->scheme.s);
+ if (conn->scheme.s) {
+ mnd_pefree(conn->scheme.s, pers);
+ conn->scheme.s = NULL;
}
if (conn->server_version) {
mnd_pefree(conn->server_version, pers);
@@ -157,19 +324,15 @@ MYSQLND_METHOD(mysqlnd_conn_data, free_contents)(MYSQLND_CONN_DATA * conn)
mnd_pefree(conn->host_info, pers);
conn->host_info = NULL;
}
- if (conn->auth_plugin_data) {
- mnd_pefree(conn->auth_plugin_data, pers);
- conn->auth_plugin_data = NULL;
- }
- if (conn->last_message) {
- mnd_pefree(conn->last_message, pers);
- conn->last_message = NULL;
+ if (conn->authentication_plugin_data.s) {
+ mnd_pefree(conn->authentication_plugin_data.s, pers);
+ conn->authentication_plugin_data.s = NULL;
}
- if (conn->error_info->error_list) {
- zend_llist_clean(conn->error_info->error_list);
- mnd_pefree(conn->error_info->error_list, pers);
- conn->error_info->error_list = NULL;
+ if (conn->last_message.s) {
+ mnd_pefree(conn->last_message.s, pers);
+ conn->last_message.s = NULL;
}
+
conn->charset = NULL;
conn->greet_charset = NULL;
@@ -188,202 +351,33 @@ MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, dtor)(MYSQLND_CONN_DATA * conn)
conn->m->free_contents(conn);
conn->m->free_options(conn);
- if (conn->net) {
- mysqlnd_net_free(conn->net, conn->stats, conn->error_info);
- conn->net = NULL;
- }
-
- if (conn->protocol) {
- mysqlnd_protocol_free(conn->protocol);
- conn->protocol = NULL;
- }
-
- if (conn->stats) {
- mysqlnd_stats_end(conn->stats, conn->persistent);
- }
-
- mnd_pefree(conn, conn->persistent);
-
- DBG_VOID_RETURN;
-}
-/* }}} */
-
-
-/* {{{ mysqlnd_conn_data::simple_command_handle_response */
-static enum_func_status
-MYSQLND_METHOD(mysqlnd_conn_data, simple_command_handle_response)(MYSQLND_CONN_DATA * conn, enum mysqlnd_packet_type ok_packet,
- zend_bool silent, enum php_mysqlnd_server_command command,
- zend_bool ignore_upsert_status)
-{
- enum_func_status ret = FAIL;
-
- DBG_ENTER("mysqlnd_conn_data::simple_command_handle_response");
- DBG_INF_FMT("silent=%u packet=%u command=%s", silent, ok_packet, mysqlnd_command_to_text[command]);
-
- switch (ok_packet) {
- case PROT_OK_PACKET:{
- MYSQLND_PACKET_OK * ok_response = conn->protocol->m.get_ok_packet(conn->protocol, FALSE);
- if (!ok_response) {
- SET_OOM_ERROR(*conn->error_info);
- break;
- }
- if (FAIL == (ret = PACKET_READ(ok_response, conn))) {
- if (!silent) {
- DBG_ERR_FMT("Error while reading %s's OK packet", mysqlnd_command_to_text[command]);
- php_error_docref(NULL, E_WARNING, "Error while reading %s's OK packet. PID=%u",
- mysqlnd_command_to_text[command], getpid());
- }
- } else {
- DBG_INF_FMT("OK from server");
- if (0xFF == ok_response->field_count) {
- /* The server signalled error. Set the error */
- SET_CLIENT_ERROR(*conn->error_info, ok_response->error_no, ok_response->sqlstate, ok_response->error);
- ret = FAIL;
- /*
- Cover a protocol design error: error packet does not
- contain the server status. Therefore, the client has no way
- to find out whether there are more result sets of
- a multiple-result-set statement pending. Luckily, in 5.0 an
- error always aborts execution of a statement, wherever it is
- a multi-statement or a stored procedure, so it should be
- safe to unconditionally turn off the flag here.
- */
- conn->upsert_status->server_status &= ~SERVER_MORE_RESULTS_EXISTS;
- SET_ERROR_AFF_ROWS(conn);
- } else {
- SET_NEW_MESSAGE(conn->last_message, conn->last_message_len,
- ok_response->message, ok_response->message_len,
- conn->persistent);
-
- if (!ignore_upsert_status) {
- memset(conn->upsert_status, 0, sizeof(*conn->upsert_status));
- conn->upsert_status->warning_count = ok_response->warning_count;
- conn->upsert_status->server_status = ok_response->server_status;
- conn->upsert_status->affected_rows = ok_response->affected_rows;
- conn->upsert_status->last_insert_id = ok_response->last_insert_id;
- }
- }
- }
- PACKET_FREE(ok_response);
- break;
- }
- case PROT_EOF_PACKET:{
- MYSQLND_PACKET_EOF * ok_response = conn->protocol->m.get_eof_packet(conn->protocol, FALSE);
- if (!ok_response) {
- SET_OOM_ERROR(*conn->error_info);
- break;
- }
- if (FAIL == (ret = PACKET_READ(ok_response, conn))) {
- SET_CLIENT_ERROR(*conn->error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE,
- "Malformed packet");
- if (!silent) {
- DBG_ERR_FMT("Error while reading %s's EOF packet", mysqlnd_command_to_text[command]);
- php_error_docref(NULL, E_WARNING, "Error while reading %s's EOF packet. PID=%d",
- mysqlnd_command_to_text[command], getpid());
- }
- } else if (0xFF == ok_response->field_count) {
- /* The server signalled error. Set the error */
- SET_CLIENT_ERROR(*conn->error_info, ok_response->error_no, ok_response->sqlstate, ok_response->error);
- SET_ERROR_AFF_ROWS(conn);
- } else if (0xFE != ok_response->field_count) {
- SET_CLIENT_ERROR(*conn->error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE, "Malformed packet");
- if (!silent) {
- DBG_ERR_FMT("EOF packet expected, field count wasn't 0xFE but 0x%2X", ok_response->field_count);
- php_error_docref(NULL, E_WARNING, "EOF packet expected, field count wasn't 0xFE but 0x%2X",
- ok_response->field_count);
- }
- } else {
- DBG_INF_FMT("OK from server");
- }
- PACKET_FREE(ok_response);
- break;
- }
- default:
- SET_CLIENT_ERROR(*conn->error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE, "Malformed packet");
- php_error_docref(NULL, E_ERROR, "Wrong response packet %u passed to the function", ok_packet);
- break;
+ if (conn->error_info) {
+ mysqlnd_error_info_free_contents(conn->error_info);
+ conn->error_info = NULL;
}
- DBG_INF(ret == PASS ? "PASS":"FAIL");
- DBG_RETURN(ret);
-}
-/* }}} */
-
-/* {{{ mysqlnd_conn_data::simple_command_send_request */
-static enum_func_status
-MYSQLND_METHOD(mysqlnd_conn_data, simple_command_send_request)(MYSQLND_CONN_DATA * conn, enum php_mysqlnd_server_command command,
- const zend_uchar * const arg, size_t arg_len, zend_bool silent, zend_bool ignore_upsert_status)
-{
- enum_func_status ret = PASS;
- MYSQLND_PACKET_COMMAND * cmd_packet;
-
- DBG_ENTER("mysqlnd_conn_data::simple_command_send_request");
- DBG_INF_FMT("command=%s silent=%u", mysqlnd_command_to_text[command], silent);
- DBG_INF_FMT("conn->server_status=%u", conn->upsert_status->server_status);
- DBG_INF_FMT("sending %u bytes", arg_len + 1); /* + 1 is for the command */
-
- switch (CONN_GET_STATE(conn)) {
- case CONN_READY:
- break;
- case CONN_QUIT_SENT:
- SET_CLIENT_ERROR(*conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
- DBG_ERR("Server is gone");
- DBG_RETURN(FAIL);
- default:
- SET_CLIENT_ERROR(*conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
- DBG_ERR_FMT("Command out of sync. State=%u", CONN_GET_STATE(conn));
- DBG_RETURN(FAIL);
+ if (conn->protocol_frame_codec) {
+ mysqlnd_pfc_free(conn->protocol_frame_codec, conn->stats, conn->error_info);
+ conn->protocol_frame_codec = NULL;
}
- SET_ERROR_AFF_ROWS(conn);
- SET_EMPTY_ERROR(*conn->error_info);
-
- cmd_packet = conn->protocol->m.get_command_packet(conn->protocol, FALSE);
- if (!cmd_packet) {
- SET_OOM_ERROR(*conn->error_info);
- DBG_RETURN(FAIL);
+ if (conn->vio) {
+ mysqlnd_vio_free(conn->vio, conn->stats, conn->error_info);
+ conn->vio = NULL;
}
- cmd_packet->command = command;
- if (arg && arg_len) {
- cmd_packet->argument = arg;
- cmd_packet->arg_len = arg_len;
+ if (conn->payload_decoder_factory) {
+ mysqlnd_protocol_payload_decoder_factory_free(conn->payload_decoder_factory);
+ conn->payload_decoder_factory = NULL;
}
- MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_COM_QUIT + command - 1 /* because of COM_SLEEP */ );
-
- if (! PACKET_WRITE(cmd_packet, conn)) {
- if (!silent) {
- DBG_ERR_FMT("Error while sending %s packet", mysqlnd_command_to_text[command]);
- php_error(E_WARNING, "Error while sending %s packet. PID=%d", mysqlnd_command_to_text[command], getpid());
- }
- CONN_SET_STATE(conn, CONN_QUIT_SENT);
- conn->m->send_close(conn);
- DBG_ERR("Server is gone");
- ret = FAIL;
+ if (conn->stats) {
+ mysqlnd_stats_end(conn->stats, conn->persistent);
}
- PACKET_FREE(cmd_packet);
- DBG_RETURN(ret);
-}
-/* }}} */
-
-
-/* {{{ mysqlnd_conn_data::simple_command */
-static enum_func_status
-MYSQLND_METHOD(mysqlnd_conn_data, simple_command)(MYSQLND_CONN_DATA * conn, enum php_mysqlnd_server_command command,
- const zend_uchar * const arg, size_t arg_len, enum mysqlnd_packet_type ok_packet, zend_bool silent,
- zend_bool ignore_upsert_status)
-{
- enum_func_status ret;
- DBG_ENTER("mysqlnd_conn_data::simple_command");
- ret = conn->m->simple_command_send_request(conn, command, arg, arg_len, silent, ignore_upsert_status);
- if (PASS == ret && ok_packet != PROT_LAST) {
- ret = conn->m->simple_command_handle_response(conn, ok_packet, silent, command, ignore_upsert_status);
- }
+ mnd_pefree(conn, conn->persistent);
- DBG_INF(ret == PASS ? "PASS":"FAIL");
- DBG_RETURN(ret);
+ DBG_VOID_RETURN;
}
/* }}} */
@@ -392,14 +386,15 @@ MYSQLND_METHOD(mysqlnd_conn_data, simple_command)(MYSQLND_CONN_DATA * conn, enum
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, set_server_option)(MYSQLND_CONN_DATA * const conn, enum_mysqlnd_server_option option)
{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, set_server_option);
- zend_uchar buffer[2];
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), set_server_option);
enum_func_status ret = FAIL;
DBG_ENTER("mysqlnd_conn_data::set_server_option");
if (PASS == conn->m->local_tx_start(conn, this_func)) {
-
- int2store(buffer, (unsigned int) option);
- ret = conn->m->simple_command(conn, COM_SET_OPTION, buffer, sizeof(buffer), PROT_EOF_PACKET, FALSE, TRUE);
+ struct st_mysqlnd_protocol_command * command = conn->command_factory(COM_SET_OPTION, conn, option);
+ if (command) {
+ ret = command->run(command);
+ command->free_command(command);
+ }
conn->m->local_tx_end(conn, this_func, ret);
}
@@ -415,9 +410,9 @@ MYSQLND_METHOD(mysqlnd_conn_data, restart_psession)(MYSQLND_CONN_DATA * conn)
DBG_ENTER("mysqlnd_conn_data::restart_psession");
MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_CONNECT_REUSED);
/* Free here what should not be seen by the next script */
- if (conn->last_message) {
- mnd_pefree(conn->last_message, conn->persistent);
- conn->last_message = NULL;
+ if (conn->last_message.s) {
+ mnd_pefree(conn->last_message.s, conn->persistent);
+ conn->last_message.s = NULL;
}
DBG_RETURN(PASS);
}
@@ -434,103 +429,6 @@ MYSQLND_METHOD(mysqlnd_conn_data, end_psession)(MYSQLND_CONN_DATA * conn)
/* }}} */
-/* {{{ mysqlnd_switch_to_ssl_if_needed */
-static enum_func_status
-mysqlnd_switch_to_ssl_if_needed(
- MYSQLND_CONN_DATA * conn,
- const MYSQLND_PACKET_GREET * const greet_packet,
- const MYSQLND_OPTIONS * const options,
- zend_ulong mysql_flags)
-{
- enum_func_status ret = FAIL;
- const MYSQLND_CHARSET * charset;
- MYSQLND_PACKET_AUTH * auth_packet;
- DBG_ENTER("mysqlnd_switch_to_ssl_if_needed");
- DBG_INF_FMT("client_capability_flags=%lu", mysql_flags);
- DBG_INF_FMT("CLIENT_LONG_PASSWORD= %d", mysql_flags & CLIENT_LONG_PASSWORD? 1:0);
- DBG_INF_FMT("CLIENT_FOUND_ROWS= %d", mysql_flags & CLIENT_FOUND_ROWS? 1:0);
- DBG_INF_FMT("CLIENT_LONG_FLAG= %d", mysql_flags & CLIENT_LONG_FLAG? 1:0);
- DBG_INF_FMT("CLIENT_NO_SCHEMA= %d", mysql_flags & CLIENT_NO_SCHEMA? 1:0);
- DBG_INF_FMT("CLIENT_COMPRESS= %d", mysql_flags & CLIENT_COMPRESS? 1:0);
- DBG_INF_FMT("CLIENT_ODBC= %d", mysql_flags & CLIENT_ODBC? 1:0);
- DBG_INF_FMT("CLIENT_LOCAL_FILES= %d", mysql_flags & CLIENT_LOCAL_FILES? 1:0);
- DBG_INF_FMT("CLIENT_IGNORE_SPACE= %d", mysql_flags & CLIENT_IGNORE_SPACE? 1:0);
- DBG_INF_FMT("CLIENT_PROTOCOL_41= %d", mysql_flags & CLIENT_PROTOCOL_41? 1:0);
- DBG_INF_FMT("CLIENT_INTERACTIVE= %d", mysql_flags & CLIENT_INTERACTIVE? 1:0);
- DBG_INF_FMT("CLIENT_SSL= %d", mysql_flags & CLIENT_SSL? 1:0);
- DBG_INF_FMT("CLIENT_IGNORE_SIGPIPE= %d", mysql_flags & CLIENT_IGNORE_SIGPIPE? 1:0);
- DBG_INF_FMT("CLIENT_TRANSACTIONS= %d", mysql_flags & CLIENT_TRANSACTIONS? 1:0);
- DBG_INF_FMT("CLIENT_RESERVED= %d", mysql_flags & CLIENT_RESERVED? 1:0);
- DBG_INF_FMT("CLIENT_SECURE_CONNECTION=%d", mysql_flags & CLIENT_SECURE_CONNECTION? 1:0);
- DBG_INF_FMT("CLIENT_MULTI_STATEMENTS=%d", mysql_flags & CLIENT_MULTI_STATEMENTS? 1:0);
- DBG_INF_FMT("CLIENT_MULTI_RESULTS= %d", mysql_flags & CLIENT_MULTI_RESULTS? 1:0);
- DBG_INF_FMT("CLIENT_PS_MULTI_RESULTS=%d", mysql_flags & CLIENT_PS_MULTI_RESULTS? 1:0);
- DBG_INF_FMT("CLIENT_CONNECT_ATTRS= %d", mysql_flags & CLIENT_PLUGIN_AUTH? 1:0);
- DBG_INF_FMT("CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA= %d", mysql_flags & CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA? 1:0);
- DBG_INF_FMT("CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS= %d", mysql_flags & CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS? 1:0);
- DBG_INF_FMT("CLIENT_SESSION_TRACK= %d", mysql_flags & CLIENT_SESSION_TRACK? 1:0);
- DBG_INF_FMT("CLIENT_SSL_DONT_VERIFY_SERVER_CERT= %d", mysql_flags & CLIENT_SSL_DONT_VERIFY_SERVER_CERT? 1:0);
- DBG_INF_FMT("CLIENT_SSL_VERIFY_SERVER_CERT= %d", mysql_flags & CLIENT_SSL_VERIFY_SERVER_CERT? 1:0);
- DBG_INF_FMT("CLIENT_REMEMBER_OPTIONS= %d", mysql_flags & CLIENT_REMEMBER_OPTIONS? 1:0);
-
- auth_packet = conn->protocol->m.get_auth_packet(conn->protocol, FALSE);
- if (!auth_packet) {
- SET_OOM_ERROR(*conn->error_info);
- goto end;
- }
- auth_packet->client_flags = mysql_flags;
- auth_packet->max_packet_size = MYSQLND_ASSEMBLED_PACKET_MAX_SIZE;
-
- if (options->charset_name && (charset = mysqlnd_find_charset_name(options->charset_name))) {
- auth_packet->charset_no = charset->nr;
- } else {
- auth_packet->charset_no = greet_packet->charset_no;
- }
-
-#ifdef MYSQLND_SSL_SUPPORTED
- if (mysql_flags & CLIENT_SSL) {
- zend_bool server_has_ssl = (greet_packet->server_capabilities & CLIENT_SSL)? TRUE:FALSE;
- if (server_has_ssl == FALSE) {
- goto close_conn;
- } else {
- enum mysqlnd_ssl_peer verify = mysql_flags & CLIENT_SSL_VERIFY_SERVER_CERT?
- MYSQLND_SSL_PEER_VERIFY:
- (mysql_flags & CLIENT_SSL_DONT_VERIFY_SERVER_CERT?
- MYSQLND_SSL_PEER_DONT_VERIFY:
- MYSQLND_SSL_PEER_DEFAULT);
- DBG_INF("Switching to SSL");
- if (!PACKET_WRITE(auth_packet, conn)) {
- goto close_conn;
- }
-
- conn->net->data->m.set_client_option(conn->net, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, (const char *) &verify);
-
- if (FAIL == conn->net->data->m.enable_ssl(conn->net)) {
- goto end;
- }
- }
- }
-#else
- auth_packet->client_flags &= ~CLIENT_SSL;
- if (!PACKET_WRITE(auth_packet, conn)) {
- goto close_conn;
- }
-#endif
- ret = PASS;
-end:
- PACKET_FREE(auth_packet);
- DBG_RETURN(ret);
-
-close_conn:
- CONN_SET_STATE(conn, CONN_QUIT_SENT);
- conn->m->send_close(conn);
- SET_CLIENT_ERROR(*conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
- PACKET_FREE(auth_packet);
- DBG_RETURN(ret);
-}
-/* }}} */
-
-
/* {{{ mysqlnd_conn_data::fetch_auth_plugin_by_name */
static struct st_mysqlnd_authentication_plugin *
MYSQLND_METHOD(mysqlnd_conn_data, fetch_auth_plugin_by_name)(const char * const requested_protocol)
@@ -549,169 +447,6 @@ MYSQLND_METHOD(mysqlnd_conn_data, fetch_auth_plugin_by_name)(const char * const
/* }}} */
-/* {{{ mysqlnd_run_authentication */
-static enum_func_status
-mysqlnd_run_authentication(
- MYSQLND_CONN_DATA * conn,
- const char * const user,
- const char * const passwd,
- const size_t passwd_len,
- const char * const db,
- const size_t db_len,
- const zend_uchar * const auth_plugin_data,
- const size_t auth_plugin_data_len,
- const char * const auth_protocol,
- unsigned int charset_no,
- const MYSQLND_OPTIONS * const options,
- zend_ulong mysql_flags,
- zend_bool silent,
- zend_bool is_change_user
- )
-{
- enum_func_status ret = FAIL;
- zend_bool first_call = TRUE;
-
- char * switch_to_auth_protocol = NULL;
- size_t switch_to_auth_protocol_len = 0;
- char * requested_protocol = NULL;
- zend_uchar * plugin_data;
- size_t plugin_data_len;
-
- DBG_ENTER("mysqlnd_run_authentication");
-
- plugin_data_len = auth_plugin_data_len;
- plugin_data = mnd_emalloc(plugin_data_len + 1);
- if (!plugin_data) {
- goto end;
- }
- memcpy(plugin_data, auth_plugin_data, plugin_data_len);
- plugin_data[plugin_data_len] = '\0';
-
- requested_protocol = mnd_pestrdup(auth_protocol? auth_protocol : MYSQLND_DEFAULT_AUTH_PROTOCOL, FALSE);
- if (!requested_protocol) {
- goto end;
- }
-
- do {
- struct st_mysqlnd_authentication_plugin * auth_plugin = conn->m->fetch_auth_plugin_by_name(requested_protocol);
-
- if (!auth_plugin) {
- php_error_docref(NULL, E_WARNING, "The server requested authentication method unknown to the client [%s]", requested_protocol);
- SET_CLIENT_ERROR(*conn->error_info, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "The server requested authentication method unknown to the client");
- goto end;
- }
- DBG_INF("plugin found");
-
- {
- zend_uchar * switch_to_auth_protocol_data = NULL;
- size_t switch_to_auth_protocol_data_len = 0;
- zend_uchar * scrambled_data = NULL;
- size_t scrambled_data_len = 0;
-
- switch_to_auth_protocol = NULL;
- switch_to_auth_protocol_len = 0;
-
- if (conn->auth_plugin_data) {
- mnd_pefree(conn->auth_plugin_data, conn->persistent);
- conn->auth_plugin_data = NULL;
- }
- conn->auth_plugin_data_len = plugin_data_len;
- conn->auth_plugin_data = mnd_pemalloc(conn->auth_plugin_data_len, conn->persistent);
- if (!conn->auth_plugin_data) {
- SET_OOM_ERROR(*conn->error_info);
- goto end;
- }
- memcpy(conn->auth_plugin_data, plugin_data, plugin_data_len);
-
- DBG_INF_FMT("salt(%d)=[%.*s]", plugin_data_len, plugin_data_len, plugin_data);
- /* The data should be allocated with malloc() */
- scrambled_data =
- auth_plugin->methods.get_auth_data(NULL, &scrambled_data_len, conn, user, passwd, passwd_len,
- plugin_data, plugin_data_len, options, &conn->net->data->options, mysql_flags);
- if (conn->error_info->error_no) {
- goto end;
- }
- if (FALSE == is_change_user) {
- ret = mysqlnd_auth_handshake(conn, user, passwd, passwd_len, db, db_len, options, mysql_flags,
- charset_no,
- first_call,
- requested_protocol,
- scrambled_data, scrambled_data_len,
- &switch_to_auth_protocol, &switch_to_auth_protocol_len,
- &switch_to_auth_protocol_data, &switch_to_auth_protocol_data_len
- );
- } else {
- ret = mysqlnd_auth_change_user(conn, user, strlen(user), passwd, passwd_len, db, db_len, silent,
- first_call,
- requested_protocol,
- scrambled_data, scrambled_data_len,
- &switch_to_auth_protocol, &switch_to_auth_protocol_len,
- &switch_to_auth_protocol_data, &switch_to_auth_protocol_data_len
- );
- }
- first_call = FALSE;
- free(scrambled_data);
-
- DBG_INF_FMT("switch_to_auth_protocol=%s", switch_to_auth_protocol? switch_to_auth_protocol:"n/a");
- if (requested_protocol && switch_to_auth_protocol) {
- mnd_efree(requested_protocol);
- requested_protocol = switch_to_auth_protocol;
- }
-
- if (plugin_data) {
- mnd_efree(plugin_data);
- }
- plugin_data_len = switch_to_auth_protocol_data_len;
- plugin_data = switch_to_auth_protocol_data;
- }
- DBG_INF_FMT("conn->error_info->error_no = %d", conn->error_info->error_no);
- } while (ret == FAIL && conn->error_info->error_no == 0 && switch_to_auth_protocol != NULL);
-
- if (ret == PASS) {
- DBG_INF_FMT("saving requested_protocol=%s", requested_protocol);
- conn->m->set_client_option(conn, MYSQLND_OPT_AUTH_PROTOCOL, requested_protocol);
- }
-end:
- if (plugin_data) {
- mnd_efree(plugin_data);
- }
- if (requested_protocol) {
- mnd_efree(requested_protocol);
- }
-
- DBG_RETURN(ret);
-}
-/* }}} */
-
-
-/* {{{ mysqlnd_connect_run_authentication */
-static enum_func_status
-mysqlnd_connect_run_authentication(
- MYSQLND_CONN_DATA * conn,
- const char * const user,
- const char * const passwd,
- const char * const db,
- size_t db_len,
- size_t passwd_len,
- const MYSQLND_PACKET_GREET * const greet_packet,
- const MYSQLND_OPTIONS * const options,
- zend_ulong mysql_flags
- )
-{
- enum_func_status ret = FAIL;
- DBG_ENTER("mysqlnd_connect_run_authentication");
-
- ret = mysqlnd_switch_to_ssl_if_needed(conn, greet_packet, options, mysql_flags);
- if (PASS == ret) {
- ret = mysqlnd_run_authentication(conn, user, passwd, passwd_len, db, db_len,
- greet_packet->auth_plugin_data, greet_packet->auth_plugin_data_len, greet_packet->auth_protocol,
- greet_packet->charset_no, options, mysql_flags, FALSE /*silent*/, FALSE/*is_change*/);
- }
- DBG_RETURN(ret);
-}
-/* }}} */
-
-
/* {{{ mysqlnd_conn_data::execute_init_commands */
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, execute_init_commands)(MYSQLND_CONN_DATA * conn)
@@ -748,7 +483,10 @@ MYSQLND_METHOD(mysqlnd_conn_data, execute_init_commands)(MYSQLND_CONN_DATA * con
static unsigned int
MYSQLND_METHOD(mysqlnd_conn_data, get_updated_connect_flags)(MYSQLND_CONN_DATA * conn, unsigned int mysql_flags)
{
- MYSQLND_NET * net = conn->net;
+#ifdef MYSQLND_COMPRESSION_ENABLED
+ MYSQLND_PFC * pfc = conn->protocol_frame_codec;
+#endif
+ MYSQLND_VIO * vio = conn->vio;
DBG_ENTER("mysqlnd_conn_data::get_updated_connect_flags");
/* we allow load data local infile by default */
@@ -761,7 +499,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, get_updated_connect_flags)(MYSQLND_CONN_DATA *
mysql_flags &= ~CLIENT_COMPRESS;
}
#else
- if (net && net->data->options.flags & MYSQLND_NET_FLAG_USE_COMPRESSION) {
+ if (pfc && pfc->data->flags & MYSQLND_PROTOCOL_FLAG_USE_COMPRESSION) {
mysql_flags |= CLIENT_COMPRESS;
}
#endif
@@ -770,8 +508,11 @@ MYSQLND_METHOD(mysqlnd_conn_data, get_updated_connect_flags)(MYSQLND_CONN_DATA *
mysql_flags &= ~CLIENT_SSL;
}
#else
- if (net && (net->data->options.ssl_key || net->data->options.ssl_cert ||
- net->data->options.ssl_ca || net->data->options.ssl_capath || net->data->options.ssl_cipher))
+ if (vio && (vio->data->options.ssl_key ||
+ vio->data->options.ssl_cert ||
+ vio->data->options.ssl_ca ||
+ vio->data->options.ssl_capath ||
+ vio->data->options.ssl_cipher))
{
mysql_flags |= CLIENT_SSL;
}
@@ -785,80 +526,62 @@ MYSQLND_METHOD(mysqlnd_conn_data, get_updated_connect_flags)(MYSQLND_CONN_DATA *
/* {{{ mysqlnd_conn_data::connect_handshake */
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, connect_handshake)(MYSQLND_CONN_DATA * conn,
- const char * const host, const char * const user,
- const char * const passwd, const unsigned int passwd_len,
- const char * const db, const unsigned int db_len,
+ const MYSQLND_CSTRING * const scheme,
+ const MYSQLND_CSTRING * const username,
+ const MYSQLND_CSTRING * const password,
+ const MYSQLND_CSTRING * const database,
const unsigned int mysql_flags)
{
- MYSQLND_PACKET_GREET * greet_packet;
- MYSQLND_NET * net = conn->net;
-
+ enum_func_status ret = FAIL;
DBG_ENTER("mysqlnd_conn_data::connect_handshake");
- greet_packet = conn->protocol->m.get_greet_packet(conn->protocol, FALSE);
- if (!greet_packet) {
- SET_OOM_ERROR(*conn->error_info);
- DBG_RETURN(FAIL); /* OOM */
- }
-
- if (FAIL == net->data->m.connect_ex(conn->net, conn->scheme, conn->scheme_len, conn->persistent,
- conn->stats, conn->error_info))
+ if (PASS == conn->vio->data->m.connect(conn->vio, *scheme, conn->persistent, conn->stats, conn->error_info) &&
+ PASS == conn->protocol_frame_codec->data->m.reset(conn->protocol_frame_codec, conn->stats, conn->error_info))
{
- goto err;
- }
-
- DBG_INF_FMT("stream=%p", net->data->m.get_stream(net));
-
- if (FAIL == PACKET_READ(greet_packet, conn)) {
- DBG_ERR("Error while reading greeting packet");
- php_error_docref(NULL, E_WARNING, "Error while reading greeting packet. PID=%d", getpid());
- goto err;
- } else if (greet_packet->error_no) {
- DBG_ERR_FMT("errorno=%u error=%s", greet_packet->error_no, greet_packet->error);
- SET_CLIENT_ERROR(*conn->error_info, greet_packet->error_no, greet_packet->sqlstate, greet_packet->error);
- goto err;
- } else if (greet_packet->pre41) {
- DBG_ERR_FMT("Connecting to 3.22, 3.23 & 4.0 is not supported. Server is %-.32s", greet_packet->server_version);
- php_error_docref(NULL, E_WARNING, "Connecting to 3.22, 3.23 & 4.0 "
- " is not supported. Server is %-.32s", greet_packet->server_version);
- SET_CLIENT_ERROR(*conn->error_info, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE,
- "Connecting to 3.22, 3.23 & 4.0 servers is not supported");
- goto err;
- }
-
- conn->thread_id = greet_packet->thread_id;
- conn->protocol_version = greet_packet->protocol_version;
- conn->server_version = mnd_pestrdup(greet_packet->server_version, conn->persistent);
-
- conn->greet_charset = mysqlnd_find_charset_nr(greet_packet->charset_no);
- if (!conn->greet_charset) {
- php_error_docref(NULL, E_WARNING,
- "Server sent charset (%d) unknown to the client. Please, report to the developers", greet_packet->charset_no);
- SET_CLIENT_ERROR(*conn->error_info, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE,
- "Server sent charset unknown to the client. Please, report to the developers");
- goto err;
+ size_t client_flags = mysql_flags;
+ struct st_mysqlnd_protocol_command * command = conn->command_factory(COM_HANDSHAKE, conn, username, password, database, client_flags);
+ if (command) {
+ ret = command->run(command);
+ command->free_command(command);
+ }
}
+ DBG_RETURN(ret);
+}
+/* }}} */
- conn->client_flag = mysql_flags;
- conn->server_capabilities = greet_packet->server_capabilities;
-
- if (FAIL == mysqlnd_connect_run_authentication(conn, user, passwd, db, db_len, (size_t) passwd_len,
- greet_packet, conn->options, mysql_flags))
- {
- goto err;
+/* {{{ mysqlnd_conn_data::get_scheme */
+static MYSQLND_STRING
+MYSQLND_METHOD(mysqlnd_conn_data, get_scheme)(MYSQLND_CONN_DATA * conn, MYSQLND_CSTRING hostname, MYSQLND_CSTRING *socket_or_pipe, unsigned int port, zend_bool * unix_socket, zend_bool * named_pipe)
+{
+ MYSQLND_STRING transport;
+ DBG_ENTER("mysqlnd_conn_data::get_scheme");
+#ifndef PHP_WIN32
+ if (hostname.l == sizeof("localhost") - 1 && !strncasecmp(hostname.s, "localhost", hostname.l)) {
+ DBG_INF_FMT("socket=%s", socket_or_pipe->s? socket_or_pipe->s:"n/a");
+ if (!socket_or_pipe->s) {
+ socket_or_pipe->s = "/tmp/mysql.sock";
+ socket_or_pipe->l = strlen(socket_or_pipe->s);
+ }
+ transport.l = mnd_sprintf(&transport.s, 0, "unix://%s", socket_or_pipe->s);
+ *unix_socket = TRUE;
+#else
+ if (hostname.l == sizeof(".") - 1 && hostname.s[0] == '.') {
+ /* named pipe in socket */
+ if (!socket_or_pipe->s) {
+ socket_or_pipe->s = "\\\\.\\pipe\\MySQL";
+ socket_or_pipe->l = strlen(socket_or_pipe->s);
+ }
+ transport.l = mnd_sprintf(&transport.s, 0, "pipe://%s", socket_or_pipe->s);
+ *named_pipe = TRUE;
+#endif
+ } else {
+ if (!port) {
+ port = 3306;
+ }
+ transport.l = mnd_sprintf(&transport.s, 0, "tcp://%s:%u", hostname.s, port);
}
- memset(conn->upsert_status, 0, sizeof(*conn->upsert_status));
- conn->upsert_status->warning_count = 0;
- conn->upsert_status->server_status = greet_packet->server_status;
- conn->upsert_status->affected_rows = 0;
-
- PACKET_FREE(greet_packet);
- DBG_RETURN(PASS);
-err:
- conn->client_flag = 0;
- conn->server_capabilities = 0;
- PACKET_FREE(greet_packet);
- DBG_RETURN(FAIL);
+ DBG_INF_FMT("transport=%s", transport.s? transport.s:"OOM");
+ DBG_RETURN(transport);
}
/* }}} */
@@ -866,22 +589,23 @@ err:
/* {{{ mysqlnd_conn_data::connect */
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, connect)(MYSQLND_CONN_DATA * conn,
- const char *host, const char *user,
- const char *passwd, unsigned int passwd_len,
- const char *db, unsigned int db_len,
- unsigned int port,
- const char *socket_or_pipe,
- unsigned int mysql_flags
- )
-{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, connect);
- size_t host_len;
+ MYSQLND_CSTRING hostname,
+ MYSQLND_CSTRING username,
+ MYSQLND_CSTRING password,
+ MYSQLND_CSTRING database,
+ unsigned int port,
+ MYSQLND_CSTRING socket_or_pipe,
+ unsigned int mysql_flags
+ )
+{
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), connect);
zend_bool unix_socket = FALSE;
zend_bool named_pipe = FALSE;
zend_bool reconnect = FALSE;
zend_bool saved_compression = FALSE;
zend_bool local_tx_started = FALSE;
- MYSQLND_NET * net = conn->net;
+ MYSQLND_PFC * pfc = conn->protocol_frame_codec;
+ MYSQLND_STRING transport = { NULL, 0 };
DBG_ENTER("mysqlnd_conn_data::connect");
DBG_INF_FMT("conn=%p", conn);
@@ -891,189 +615,161 @@ MYSQLND_METHOD(mysqlnd_conn_data, connect)(MYSQLND_CONN_DATA * conn,
}
local_tx_started = TRUE;
- SET_EMPTY_ERROR(*conn->error_info);
- SET_ERROR_AFF_ROWS(conn);
+ SET_EMPTY_ERROR(conn->error_info);
+ UPSERT_STATUS_SET_AFFECTED_ROWS_TO_ERROR(conn->upsert_status);
DBG_INF_FMT("host=%s user=%s db=%s port=%u flags=%u persistent=%u state=%u",
- host?host:"", user?user:"", db?db:"", port, mysql_flags,
- conn? conn->persistent:0, conn? CONN_GET_STATE(conn):-1);
+ hostname.s?hostname.s:"", username.s?username.s:"", database.s?database.s:"", port, mysql_flags,
+ conn? conn->persistent:0, conn? (int)GET_CONNECTION_STATE(&conn->state):-1);
- if (CONN_GET_STATE(conn) > CONN_ALLOCED && CONN_GET_STATE(conn) ) {
+ if (GET_CONNECTION_STATE(&conn->state) > CONN_ALLOCED) {
DBG_INF("Connecting on a connected handle.");
- if (CONN_GET_STATE(conn) < CONN_QUIT_SENT) {
+ if (GET_CONNECTION_STATE(&conn->state) < CONN_QUIT_SENT) {
MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_CLOSE_IMPLICIT);
reconnect = TRUE;
conn->m->send_close(conn);
}
conn->m->free_contents(conn);
- MYSQLND_DEC_CONN_STATISTIC(conn->stats, STAT_OPENED_CONNECTIONS);
- if (conn->persistent) {
- MYSQLND_DEC_CONN_STATISTIC(conn->stats, STAT_OPENED_PERSISTENT_CONNECTIONS);
- }
/* Now reconnect using the same handle */
- if (net->data->compressed) {
+ if (pfc->data->compressed) {
/*
- we need to save the state. As we will re-connect, net->compressed should be off, or
+ we need to save the state. As we will re-connect, pfc->compressed should be off, or
we will look for a compression header as part of the greet message, but there will
be none.
*/
saved_compression = TRUE;
- net->data->compressed = FALSE;
+ pfc->data->compressed = FALSE;
}
- if (net->data->ssl) {
- net->data->ssl = FALSE;
+ if (pfc->data->ssl) {
+ pfc->data->ssl = FALSE;
}
} else {
unsigned int max_allowed_size = MYSQLND_ASSEMBLED_PACKET_MAX_SIZE;
conn->m->set_client_option(conn, MYSQLND_OPT_MAX_ALLOWED_PACKET, (char *)&max_allowed_size);
}
- if (!host || !host[0]) {
- host = "localhost";
+ if (!hostname.s || !hostname.s[0]) {
+ hostname.s = "localhost";
+ hostname.l = strlen(hostname.s);
}
- if (!user) {
+ if (!username.s) {
DBG_INF_FMT("no user given, using empty string");
- user = "";
+ username.s = "";
+ username.l = 0;
}
- if (!passwd) {
+ if (!password.s) {
DBG_INF_FMT("no password given, using empty string");
- passwd = "";
- passwd_len = 0;
+ password.s = "";
+ password.l = 0;
}
- if (!db) {
+ if (!database.s) {
DBG_INF_FMT("no db given, using empty string");
- db = "";
- db_len = 0;
+ database.s = "";
+ database.l = 0;
} else {
mysql_flags |= CLIENT_CONNECT_WITH_DB;
}
- host_len = strlen(host);
- {
- char * transport = NULL;
- int transport_len;
-#ifndef PHP_WIN32
- if (host_len == sizeof("localhost") - 1 && !strncasecmp(host, "localhost", host_len)) {
- DBG_INF_FMT("socket=%s", socket_or_pipe? socket_or_pipe:"n/a");
- if (!socket_or_pipe) {
- socket_or_pipe = "/tmp/mysql.sock";
- }
- transport_len = mnd_sprintf(&transport, 0, "unix://%s", socket_or_pipe);
- unix_socket = TRUE;
-#else
- if (host_len == sizeof(".") - 1 && host[0] == '.') {
- /* named pipe in socket */
- if (!socket_or_pipe) {
- socket_or_pipe = "\\\\.\\pipe\\MySQL";
- }
- transport_len = mnd_sprintf(&transport, 0, "pipe://%s", socket_or_pipe);
- named_pipe = TRUE;
-#endif
- } else {
- if (!port) {
- port = 3306;
- }
- transport_len = mnd_sprintf(&transport, 0, "tcp://%s:%u", host, port);
- }
- if (!transport) {
- SET_OOM_ERROR(*conn->error_info);
- goto err; /* OOM */
- }
- DBG_INF_FMT("transport=%s conn->scheme=%s", transport, conn->scheme);
- conn->scheme = mnd_pestrndup(transport, transport_len, conn->persistent);
- conn->scheme_len = transport_len;
- mnd_sprintf_free(transport);
- transport = NULL;
- if (!conn->scheme) {
- goto err; /* OOM */
- }
- }
+ transport = conn->m->get_scheme(conn, hostname, &socket_or_pipe, port, &unix_socket, &named_pipe);
mysql_flags = conn->m->get_updated_connect_flags(conn, mysql_flags);
- if (FAIL == conn->m->connect_handshake(conn, host, user, passwd, passwd_len, db, db_len, mysql_flags)) {
- goto err;
+ {
+ const MYSQLND_CSTRING scheme = { transport.s, transport.l };
+ if (FAIL == conn->m->connect_handshake(conn, &scheme, &username, &password, &database, mysql_flags)) {
+ goto err;
+ }
}
{
- CONN_SET_STATE(conn, CONN_READY);
+ SET_CONNECTION_STATE(&conn->state, CONN_READY);
if (saved_compression) {
- net->data->compressed = TRUE;
+ pfc->data->compressed = TRUE;
}
/*
If a connect on a existing handle is performed and mysql_flags is
passed which doesn't CLIENT_COMPRESS, then we need to overwrite the value
which we set based on saved_compression.
*/
- net->data->compressed = mysql_flags & CLIENT_COMPRESS? TRUE:FALSE;
+ pfc->data->compressed = mysql_flags & CLIENT_COMPRESS? TRUE:FALSE;
+
+
+ conn->scheme.s = mnd_pestrndup(transport.s, transport.l, conn->persistent);
+ conn->scheme.l = transport.l;
+ if (transport.s) {
+ mnd_sprintf_free(transport.s);
+ transport.s = NULL;
+ }
+
+ if (!conn->scheme.s) {
+ goto err; /* OOM */
+ }
- conn->user_len = strlen(user);
- conn->user = mnd_pestrndup(user, conn->user_len, conn->persistent);
- conn->passwd = mnd_pestrndup(passwd, passwd_len, conn->persistent);
- conn->passwd_len = passwd_len;
+ conn->username.l = username.l;
+ conn->username.s = mnd_pestrndup(username.s, conn->username.l, conn->persistent);
+ conn->password.l = password.l;
+ conn->password.s = mnd_pestrndup(password.s, conn->password.l, conn->persistent);
conn->port = port;
- conn->connect_or_select_db = mnd_pestrndup(db, db_len, conn->persistent);
- conn->connect_or_select_db_len = db_len;
+ conn->connect_or_select_db.l = database.l;
+ conn->connect_or_select_db.s = mnd_pestrndup(database.s, conn->connect_or_select_db.l, conn->persistent);
- if (!conn->user || !conn->passwd || !conn->connect_or_select_db) {
- SET_OOM_ERROR(*conn->error_info);
+ if (!conn->username.s || !conn->password.s|| !conn->connect_or_select_db.s) {
+ SET_OOM_ERROR(conn->error_info);
goto err; /* OOM */
}
if (!unix_socket && !named_pipe) {
- conn->host = mnd_pestrndup(host, host_len, conn->persistent);
- if (!conn->host) {
- SET_OOM_ERROR(*conn->error_info);
+ conn->hostname.s = mnd_pestrndup(hostname.s, hostname.l, conn->persistent);
+ if (!conn->hostname.s) {
+ SET_OOM_ERROR(conn->error_info);
goto err; /* OOM */
}
- conn->host_len = host_len;
+ conn->hostname.l = hostname.l;
{
char *p;
- mnd_sprintf(&p, 0, "%s via TCP/IP", conn->host);
+ mnd_sprintf(&p, 0, "%s via TCP/IP", conn->hostname.s);
if (!p) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
goto err; /* OOM */
}
conn->host_info = mnd_pestrdup(p, conn->persistent);
mnd_sprintf_free(p);
if (!conn->host_info) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
goto err; /* OOM */
}
}
} else {
- conn->unix_socket = mnd_pestrdup(socket_or_pipe, conn->persistent);
+ conn->unix_socket.s = mnd_pestrdup(socket_or_pipe.s, conn->persistent);
if (unix_socket) {
conn->host_info = mnd_pestrdup("Localhost via UNIX socket", conn->persistent);
} else if (named_pipe) {
char *p;
- mnd_sprintf(&p, 0, "%s via named pipe", conn->unix_socket);
+ mnd_sprintf(&p, 0, "%s via named pipe", conn->unix_socket.s);
if (!p) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
goto err; /* OOM */
}
conn->host_info = mnd_pestrdup(p, conn->persistent);
mnd_sprintf_free(p);
if (!conn->host_info) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
goto err; /* OOM */
}
} else {
php_error_docref(NULL, E_WARNING, "Impossible. Should be either socket or a pipe. Report a bug!");
}
- if (!conn->unix_socket || !conn->host_info) {
- SET_OOM_ERROR(*conn->error_info);
+ if (!conn->unix_socket.s || !conn->host_info) {
+ SET_OOM_ERROR(conn->error_info);
goto err; /* OOM */
}
- conn->unix_socket_len = strlen(conn->unix_socket);
+ conn->unix_socket.l = strlen(conn->unix_socket.s);
}
- conn->max_packet_size = MYSQLND_ASSEMBLED_PACKET_MAX_SIZE;
- /* todo: check if charset is available */
- SET_EMPTY_ERROR(*conn->error_info);
+ SET_EMPTY_ERROR(conn->error_info);
mysqlnd_local_infile_default(conn);
@@ -1095,12 +791,15 @@ MYSQLND_METHOD(mysqlnd_conn_data, connect)(MYSQLND_CONN_DATA * conn,
DBG_RETURN(PASS);
}
err:
+ if (transport.s) {
+ mnd_sprintf_free(transport.s);
+ transport.s = NULL;
+ }
- DBG_ERR_FMT("[%u] %.128s (trying to connect via %s)", conn->error_info->error_no, conn->error_info->error, conn->scheme);
+ DBG_ERR_FMT("[%u] %.128s (trying to connect via %s)", conn->error_info->error_no, conn->error_info->error, conn->scheme.s);
if (!conn->error_info->error_no) {
- SET_CLIENT_ERROR(*conn->error_info, CR_CONNECTION_ERROR, UNKNOWN_SQLSTATE, conn->error_info->error? conn->error_info->error:"Unknown error");
- php_error_docref(NULL, E_WARNING, "[%u] %.128s (trying to connect via %s)",
- conn->error_info->error_no, conn->error_info->error, conn->scheme);
+ SET_CLIENT_ERROR(conn->error_info, CR_CONNECTION_ERROR, UNKNOWN_SQLSTATE, conn->error_info->error? conn->error_info->error:"Unknown error");
+ php_error_docref(NULL, E_WARNING, "[%u] %.128s (trying to connect via %s)", conn->error_info->error_no, conn->error_info->error, conn->scheme.s);
}
conn->m->free_contents(conn);
@@ -1117,15 +816,15 @@ err:
/* {{{ mysqlnd_conn::connect */
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn, connect)(MYSQLND * conn_handle,
- const char * host, const char * user,
- const char * passwd, unsigned int passwd_len,
- const char * db, unsigned int db_len,
- unsigned int port,
- const char * socket_or_pipe,
- unsigned int mysql_flags
- )
-{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, connect);
+ const MYSQLND_CSTRING hostname,
+ const MYSQLND_CSTRING username,
+ const MYSQLND_CSTRING password,
+ const MYSQLND_CSTRING database,
+ unsigned int port,
+ const MYSQLND_CSTRING socket_or_pipe,
+ unsigned int mysql_flags)
+{
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), connect);
enum_func_status ret = FAIL;
MYSQLND_CONN_DATA * conn = conn_handle->data;
@@ -1133,7 +832,7 @@ MYSQLND_METHOD(mysqlnd_conn, connect)(MYSQLND * conn_handle,
if (PASS == conn->m->local_tx_start(conn, this_func)) {
mysqlnd_options4(conn_handle, MYSQL_OPT_CONNECT_ATTR_ADD, "_client_name", "mysqlnd");
- ret = conn->m->connect(conn, host, user, passwd, passwd_len, db, db_len, port, socket_or_pipe, mysql_flags);
+ ret = conn->m->connect(conn, hostname, username, password, database, port, socket_or_pipe, mysql_flags);
conn->m->local_tx_end(conn, this_func, FAIL);
}
@@ -1142,57 +841,15 @@ MYSQLND_METHOD(mysqlnd_conn, connect)(MYSQLND * conn_handle,
/* }}} */
-/* {{{ mysqlnd_connect */
-PHPAPI MYSQLND * mysqlnd_connect(MYSQLND * conn_handle,
- const char * host, const char * user,
- const char * passwd, unsigned int passwd_len,
- const char * db, unsigned int db_len,
- unsigned int port,
- const char * socket_or_pipe,
- unsigned int mysql_flags,
- unsigned int client_api_flags
- )
-{
- enum_func_status ret = FAIL;
- zend_bool self_alloced = FALSE;
-
- DBG_ENTER("mysqlnd_connect");
- DBG_INF_FMT("host=%s user=%s db=%s port=%u flags=%u", host?host:"", user?user:"", db?db:"", port, mysql_flags);
-
- if (!conn_handle) {
- self_alloced = TRUE;
- if (!(conn_handle = mysqlnd_init(client_api_flags, FALSE))) {
- /* OOM */
- DBG_RETURN(NULL);
- }
- }
-
- ret = conn_handle->m->connect(conn_handle, host, user, passwd, passwd_len, db, db_len, port, socket_or_pipe, mysql_flags);
-
- if (ret == FAIL) {
- if (self_alloced) {
- /*
- We have alloced, thus there are no references to this
- object - we are free to kill it!
- */
- conn_handle->m->dtor(conn_handle);
- }
- DBG_RETURN(NULL);
- }
- DBG_RETURN(conn_handle);
-}
-/* }}} */
-
-
/* {{{ mysqlnd_conn_data::query */
/*
If conn->error_info->error_no is not zero, then we had an error.
Still the result from the query is PASS
*/
static enum_func_status
-MYSQLND_METHOD(mysqlnd_conn_data, query)(MYSQLND_CONN_DATA * conn, const char * query, unsigned int query_len)
+MYSQLND_METHOD(mysqlnd_conn_data, query)(MYSQLND_CONN_DATA * conn, const char * const query, const size_t query_len)
{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, query);
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), query);
enum_func_status ret = FAIL;
DBG_ENTER("mysqlnd_conn_data::query");
DBG_INF_FMT("conn=%p conn=%llu query=%s", conn, conn->thread_id, query);
@@ -1202,8 +859,8 @@ MYSQLND_METHOD(mysqlnd_conn_data, query)(MYSQLND_CONN_DATA * conn, const char *
PASS == conn->m->reap_query(conn, MYSQLND_REAP_RESULT_IMPLICIT))
{
ret = PASS;
- if (conn->last_query_type == QUERY_UPSERT && conn->upsert_status->affected_rows) {
- MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn->stats, STAT_ROWS_AFFECTED_NORMAL, conn->upsert_status->affected_rows);
+ if (conn->last_query_type == QUERY_UPSERT && UPSERT_STATUS_GET_AFFECTED_ROWS(conn->upsert_status)) {
+ MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn->stats, STAT_ROWS_AFFECTED_NORMAL, UPSERT_STATUS_GET_AFFECTED_ROWS(conn->upsert_status));
}
}
conn->m->local_tx_end(conn, this_func, ret);
@@ -1215,25 +872,29 @@ MYSQLND_METHOD(mysqlnd_conn_data, query)(MYSQLND_CONN_DATA * conn, const char *
/* {{{ mysqlnd_conn_data::send_query */
static enum_func_status
-MYSQLND_METHOD(mysqlnd_conn_data, send_query)(MYSQLND_CONN_DATA * conn, const char * query, unsigned int query_len,
+MYSQLND_METHOD(mysqlnd_conn_data, send_query)(MYSQLND_CONN_DATA * conn, const char * const query, const size_t query_len,
enum_mysqlnd_send_query_type type, zval *read_cb, zval *err_cb)
{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, send_query);
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), send_query);
enum_func_status ret = FAIL;
DBG_ENTER("mysqlnd_conn_data::send_query");
DBG_INF_FMT("conn=%llu query=%s", conn->thread_id, query);
- DBG_INF_FMT("conn->server_status=%u", conn->upsert_status->server_status);
+ DBG_INF_FMT("conn->server_status=%u", UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status));
- if (PASS == conn->m->local_tx_start(conn, this_func)) {
- ret = conn->m->simple_command(conn, COM_QUERY, (zend_uchar *) query, query_len,
- PROT_LAST /* we will handle the OK packet*/,
- FALSE, FALSE);
- if (PASS == ret) {
- CONN_SET_STATE(conn, CONN_QUERY_SENT);
+ if (type == MYSQLND_SEND_QUERY_IMPLICIT || PASS == conn->m->local_tx_start(conn, this_func))
+ {
+ const MYSQLND_CSTRING query_string = {query, query_len};
+ struct st_mysqlnd_protocol_command * command = conn->command_factory(COM_QUERY, conn, query_string);
+ if (command) {
+ ret = command->run(command);
+ command->free_command(command);
+ }
+
+ if (type == MYSQLND_SEND_QUERY_EXPLICIT) {
+ conn->m->local_tx_end(conn, this_func, ret);
}
- conn->m->local_tx_end(conn, this_func, ret);
}
- DBG_INF_FMT("conn->server_status=%u", conn->upsert_status->server_status);
+ DBG_INF_FMT("conn->server_status=%u", UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status));
DBG_RETURN(ret);
}
/* }}} */
@@ -1243,302 +904,35 @@ MYSQLND_METHOD(mysqlnd_conn_data, send_query)(MYSQLND_CONN_DATA * conn, const ch
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, reap_query)(MYSQLND_CONN_DATA * conn, enum_mysqlnd_reap_result_type type)
{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, reap_query);
- enum_mysqlnd_connection_state state = CONN_GET_STATE(conn);
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), reap_query);
enum_func_status ret = FAIL;
DBG_ENTER("mysqlnd_conn_data::reap_query");
DBG_INF_FMT("conn=%llu", conn->thread_id);
- DBG_INF_FMT("conn->server_status=%u", conn->upsert_status->server_status);
- if (PASS == conn->m->local_tx_start(conn, this_func)) {
- if (state <= CONN_READY || state == CONN_QUIT_SENT) {
- php_error_docref(NULL, E_WARNING, "Connection not opened, clear or has been closed");
- DBG_ERR_FMT("Connection not opened, clear or has been closed. State=%u", state);
- DBG_RETURN(ret);
- }
- ret = conn->m->query_read_result_set_header(conn, NULL);
-
- conn->m->local_tx_end(conn, this_func, ret);
- }
- DBG_INF_FMT("conn->server_status=%u", conn->upsert_status->server_status);
- DBG_RETURN(ret);
-}
-/* }}} */
-
-
-#include "php_network.h"
-
-/* {{{ mysqlnd_stream_array_to_fd_set */
-MYSQLND ** mysqlnd_stream_array_check_for_readiness(MYSQLND ** conn_array)
-{
- int cnt = 0;
- MYSQLND **p = conn_array, **p_p;
- MYSQLND **ret = NULL;
-
- while (*p) {
- if (CONN_GET_STATE((*p)->data) <= CONN_READY || CONN_GET_STATE((*p)->data) == CONN_QUIT_SENT) {
- cnt++;
- }
- p++;
- }
- if (cnt) {
- MYSQLND **ret_p = ret = ecalloc(cnt + 1, sizeof(MYSQLND *));
- p_p = p = conn_array;
- while (*p) {
- if (CONN_GET_STATE((*p)->data) <= CONN_READY || CONN_GET_STATE((*p)->data) == CONN_QUIT_SENT) {
- *ret_p = *p;
- *p = NULL;
- ret_p++;
- } else {
- *p_p = *p;
- p_p++;
- }
- p++;
- }
- *ret_p = NULL;
- }
- return ret;
-}
-/* }}} */
-
-
-/* {{{ mysqlnd_stream_array_to_fd_set */
-static int mysqlnd_stream_array_to_fd_set(MYSQLND ** conn_array, fd_set * fds, php_socket_t * max_fd)
-{
- php_socket_t this_fd;
- php_stream *stream = NULL;
- unsigned int cnt = 0;
- MYSQLND **p = conn_array;
- DBG_ENTER("mysqlnd_stream_array_to_fd_set");
-
- while (*p) {
- /* get the fd.
- * NB: Most other code will NOT use the PHP_STREAM_CAST_INTERNAL flag
- * when casting. It is only used here so that the buffered data warning
- * is not displayed.
- * */
- stream = (*p)->data->net->data->m.get_stream((*p)->data->net);
- DBG_INF_FMT("conn=%llu stream=%p", (*p)->data->thread_id, stream);
- if (stream != NULL && SUCCESS == php_stream_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL,
- (void*)&this_fd, 1) && ZEND_VALID_SOCKET(this_fd)) {
-
- PHP_SAFE_FD_SET(this_fd, fds);
-
- if (this_fd > *max_fd) {
- *max_fd = this_fd;
- }
- cnt++;
+ DBG_INF_FMT("conn->server_status=%u", UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status));
+ if (type == MYSQLND_REAP_RESULT_IMPLICIT || PASS == conn->m->local_tx_start(conn, this_func))
+ {
+ struct st_mysqlnd_protocol_command * command = conn->command_factory(COM_REAP_RESULT, conn);
+ if (command) {
+ ret = command->run(command);
+ command->free_command(command);
}
- p++;
- }
- DBG_RETURN(cnt ? 1 : 0);
-}
-/* }}} */
-
-/* {{{ mysqlnd_stream_array_from_fd_set */
-static int mysqlnd_stream_array_from_fd_set(MYSQLND ** conn_array, fd_set * fds)
-{
- php_socket_t this_fd;
- php_stream *stream = NULL;
- int ret = 0;
- zend_bool disproportion = FALSE;
- MYSQLND **fwd = conn_array, **bckwd = conn_array;
- DBG_ENTER("mysqlnd_stream_array_from_fd_set");
-
- while (*fwd) {
- stream = (*fwd)->data->net->data->m.get_stream((*fwd)->data->net);
- DBG_INF_FMT("conn=%llu stream=%p", (*fwd)->data->thread_id, stream);
- if (stream != NULL && SUCCESS == php_stream_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL,
- (void*)&this_fd, 1) && ZEND_VALID_SOCKET(this_fd)) {
- if (PHP_SAFE_FD_ISSET(this_fd, fds)) {
- if (disproportion) {
- *bckwd = *fwd;
- }
- bckwd++;
- fwd++;
- ret++;
- continue;
- }
+ if (type == MYSQLND_REAP_RESULT_EXPLICIT) {
+ conn->m->local_tx_end(conn, this_func, ret);
}
- disproportion = TRUE;
- fwd++;
}
- *bckwd = NULL;/* NULL-terminate the list */
-
+ DBG_INF_FMT("conn->server_status=%u", UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status));
DBG_RETURN(ret);
}
/* }}} */
-#ifndef PHP_WIN32
-#define php_select(m, r, w, e, t) select(m, r, w, e, t)
-#else
-#include "win32/select.h"
-#endif
-
-
-/* {{{ mysqlnd_poll */
-PHPAPI enum_func_status
-mysqlnd_poll(MYSQLND **r_array, MYSQLND **e_array, MYSQLND ***dont_poll, long sec, long usec, int * desc_num)
-{
- struct timeval tv;
- struct timeval *tv_p = NULL;
- fd_set rfds, wfds, efds;
- php_socket_t max_fd = 0;
- int retval, sets = 0;
- int set_count, max_set_count = 0;
-
- DBG_ENTER("_mysqlnd_poll");
- if (sec < 0 || usec < 0) {
- php_error_docref(NULL, E_WARNING, "Negative values passed for sec and/or usec");
- DBG_RETURN(FAIL);
- }
-
- FD_ZERO(&rfds);
- FD_ZERO(&wfds);
- FD_ZERO(&efds);
-
- if (r_array != NULL) {
- *dont_poll = mysqlnd_stream_array_check_for_readiness(r_array);
- set_count = mysqlnd_stream_array_to_fd_set(r_array, &rfds, &max_fd);
- if (set_count > max_set_count) {
- max_set_count = set_count;
- }
- sets += set_count;
- }
-
- if (e_array != NULL) {
- set_count = mysqlnd_stream_array_to_fd_set(e_array, &efds, &max_fd);
- if (set_count > max_set_count) {
- max_set_count = set_count;
- }
- sets += set_count;
- }
-
- if (!sets) {
- php_error_docref(NULL, E_WARNING, *dont_poll ? "All arrays passed are clear":"No stream arrays were passed");
- DBG_ERR_FMT(*dont_poll ? "All arrays passed are clear":"No stream arrays were passed");
- DBG_RETURN(FAIL);
- }
-
- PHP_SAFE_MAX_FD(max_fd, max_set_count);
-
- /* Solaris + BSD do not like microsecond values which are >= 1 sec */
- if (usec > 999999) {
- tv.tv_sec = sec + (usec / 1000000);
- tv.tv_usec = usec % 1000000;
- } else {
- tv.tv_sec = sec;
- tv.tv_usec = usec;
- }
-
- tv_p = &tv;
-
- retval = php_select(max_fd + 1, &rfds, &wfds, &efds, tv_p);
-
- if (retval == -1) {
- php_error_docref(NULL, E_WARNING, "unable to select [%d]: %s (max_fd=%d)",
- errno, strerror(errno), max_fd);
- DBG_RETURN(FAIL);
- }
-
- if (r_array != NULL) {
- mysqlnd_stream_array_from_fd_set(r_array, &rfds);
- }
- if (e_array != NULL) {
- mysqlnd_stream_array_from_fd_set(e_array, &efds);
- }
-
- *desc_num = retval;
- DBG_RETURN(PASS);
-}
-/* }}} */
-
-
-/*
- COM_FIELD_LIST is special, different from a SHOW FIELDS FROM :
- - There is no result set header - status from the command, which
- impacts us to allocate big chunk of memory for reading the metadata.
- - The EOF packet is consumed by the metadata packet reader.
-*/
-
-/* {{{ mysqlnd_conn_data::list_fields */
-MYSQLND_RES *
-MYSQLND_METHOD(mysqlnd_conn_data, list_fields)(MYSQLND_CONN_DATA * conn, const char *table, const char *achtung_wild)
-{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, list_fields);
- /* db + \0 + wild + \0 (for wild) */
- zend_uchar buff[MYSQLND_MAX_ALLOWED_DB_LEN * 2 + 1 + 1], *p;
- size_t table_len, wild_len;
- MYSQLND_RES * result = NULL;
- DBG_ENTER("mysqlnd_conn_data::list_fields");
- DBG_INF_FMT("conn=%llu table=%s wild=%s", conn->thread_id, table? table:"",achtung_wild? achtung_wild:"");
-
- if (PASS == conn->m->local_tx_start(conn, this_func)) {
- do {
- p = buff;
- if (table && (table_len = strlen(table))) {
- size_t to_copy = MIN(table_len, MYSQLND_MAX_ALLOWED_DB_LEN);
- memcpy(p, table, to_copy);
- p += to_copy;
- *p++ = '\0';
- }
-
- if (achtung_wild && (wild_len = strlen(achtung_wild))) {
- size_t to_copy = MIN(wild_len, MYSQLND_MAX_ALLOWED_DB_LEN);
- memcpy(p, achtung_wild, to_copy);
- p += to_copy;
- *p++ = '\0';
- }
-
- if (PASS != conn->m->simple_command(conn, COM_FIELD_LIST, buff, p - buff,
- PROT_LAST /* we will handle the OK packet*/,
- FALSE, TRUE)) {
- conn->m->local_tx_end(conn, 0, FAIL);
- break;
- }
-
- /*
- Prepare for the worst case.
- MyISAM goes to 2500 BIT columns, double it for safety.
- */
- result = conn->m->result_init(5000, conn->persistent);
- if (!result) {
- break;
- }
-
- if (FAIL == result->m.read_result_metadata(result, conn)) {
- DBG_ERR("Error occurred while reading metadata");
- result->m.free_result(result, TRUE);
- result = NULL;
- break;
- }
-
- result->type = MYSQLND_RES_NORMAL;
- result->unbuf = mysqlnd_result_unbuffered_init(result->field_count, FALSE, result->persistent);
- if (!result->unbuf) {
- /* OOM */
- SET_OOM_ERROR(*conn->error_info);
- result->m.free_result(result, TRUE);
- result = NULL;
- break;
- }
- result->unbuf->eof_reached = TRUE;
- } while (0);
- conn->m->local_tx_end(conn, this_func, result == NULL? FAIL:PASS);
- }
-
- DBG_RETURN(result);
-}
-/* }}} */
-
-
/* {{{ mysqlnd_conn_data::list_method */
MYSQLND_RES *
-MYSQLND_METHOD(mysqlnd_conn_data, list_method)(MYSQLND_CONN_DATA * conn, const char * query, const char *achtung_wild, char *par1)
+MYSQLND_METHOD(mysqlnd_conn_data, list_method)(MYSQLND_CONN_DATA * conn, const char * const query, const char * const achtung_wild, const char * const par1)
{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, list_method);
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), list_method);
char * show_query = NULL;
size_t show_query_len;
MYSQLND_RES * result = NULL;
@@ -1574,9 +968,9 @@ MYSQLND_METHOD(mysqlnd_conn_data, list_method)(MYSQLND_CONN_DATA * conn, const c
/* }}} */
-/* {{{ mysqlnd_conn_data::errno */
+/* {{{ mysqlnd_conn_data::err_no */
static unsigned int
-MYSQLND_METHOD(mysqlnd_conn_data, errno)(const MYSQLND_CONN_DATA * const conn)
+MYSQLND_METHOD(mysqlnd_conn_data, err_no)(const MYSQLND_CONN_DATA * const conn)
{
return conn->error_info->error_no;
}
@@ -1616,17 +1010,17 @@ static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, ssl_set)(MYSQLND_CONN_DATA * const conn, const char * key, const char * const cert,
const char * const ca, const char * const capath, const char * const cipher)
{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, ssl_set);
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), ssl_set);
enum_func_status ret = FAIL;
- MYSQLND_NET * net = conn->net;
+ MYSQLND_VIO * vio = conn->vio;
DBG_ENTER("mysqlnd_conn_data::ssl_set");
if (PASS == conn->m->local_tx_start(conn, this_func)) {
- ret = (PASS == net->data->m.set_client_option(net, MYSQLND_OPT_SSL_KEY, key) &&
- PASS == net->data->m.set_client_option(net, MYSQLND_OPT_SSL_CERT, cert) &&
- PASS == net->data->m.set_client_option(net, MYSQLND_OPT_SSL_CA, ca) &&
- PASS == net->data->m.set_client_option(net, MYSQLND_OPT_SSL_CAPATH, capath) &&
- PASS == net->data->m.set_client_option(net, MYSQLND_OPT_SSL_CIPHER, cipher)) ? PASS : FAIL;
+ ret = (PASS == vio->data->m.set_client_option(vio, MYSQLND_OPT_SSL_KEY, key) &&
+ PASS == vio->data->m.set_client_option(vio, MYSQLND_OPT_SSL_CERT, cert) &&
+ PASS == vio->data->m.set_client_option(vio, MYSQLND_OPT_SSL_CA, ca) &&
+ PASS == vio->data->m.set_client_option(vio, MYSQLND_OPT_SSL_CAPATH, capath) &&
+ PASS == vio->data->m.set_client_option(vio, MYSQLND_OPT_SSL_CIPHER, cipher)) ? PASS : FAIL;
conn->m->local_tx_end(conn, this_func, ret);
}
@@ -1639,14 +1033,14 @@ MYSQLND_METHOD(mysqlnd_conn_data, ssl_set)(MYSQLND_CONN_DATA * const conn, const
static zend_ulong
MYSQLND_METHOD(mysqlnd_conn_data, escape_string)(MYSQLND_CONN_DATA * const conn, char * newstr, const char * escapestr, size_t escapestr_len)
{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, escape_string);
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), escape_string);
zend_ulong ret = FAIL;
DBG_ENTER("mysqlnd_conn_data::escape_string");
DBG_INF_FMT("conn=%llu", conn->thread_id);
if (PASS == conn->m->local_tx_start(conn, this_func)) {
- DBG_INF_FMT("server_status=%u", conn->upsert_status->server_status);
- if (conn->upsert_status->server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES) {
+ DBG_INF_FMT("server_status=%u", UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status));
+ if (UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status) & SERVER_STATUS_NO_BACKSLASH_ESCAPES) {
ret = mysqlnd_cset_escape_quotes(conn->charset, newstr, escapestr, escapestr_len);
} else {
ret = mysqlnd_cset_escape_slashes(conn->charset, newstr, escapestr, escapestr_len);
@@ -1662,12 +1056,16 @@ MYSQLND_METHOD(mysqlnd_conn_data, escape_string)(MYSQLND_CONN_DATA * const conn,
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, dump_debug_info)(MYSQLND_CONN_DATA * const conn)
{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, server_dump_debug_information);
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), server_dump_debug_information);
enum_func_status ret = FAIL;
DBG_ENTER("mysqlnd_conn_data::dump_debug_info");
DBG_INF_FMT("conn=%llu", conn->thread_id);
if (PASS == conn->m->local_tx_start(conn, this_func)) {
- ret = conn->m->simple_command(conn, COM_DEBUG, NULL, 0, PROT_EOF_PACKET, FALSE, TRUE);
+ struct st_mysqlnd_protocol_command * command = conn->command_factory(COM_DEBUG, conn);
+ if (command) {
+ ret = command->run(command);
+ command->free_command(command);
+ }
conn->m->local_tx_end(conn, this_func, ret);
}
@@ -1679,33 +1077,22 @@ MYSQLND_METHOD(mysqlnd_conn_data, dump_debug_info)(MYSQLND_CONN_DATA * const con
/* {{{ mysqlnd_conn_data::select_db */
static enum_func_status
-MYSQLND_METHOD(mysqlnd_conn_data, select_db)(MYSQLND_CONN_DATA * const conn, const char * const db, unsigned int db_len)
+MYSQLND_METHOD(mysqlnd_conn_data, select_db)(MYSQLND_CONN_DATA * const conn, const char * const db, const size_t db_len)
{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, select_db);
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), select_db);
enum_func_status ret = FAIL;
DBG_ENTER("mysqlnd_conn_data::select_db");
DBG_INF_FMT("conn=%llu db=%s", conn->thread_id, db);
if (PASS == conn->m->local_tx_start(conn, this_func)) {
- ret = conn->m->simple_command(conn, COM_INIT_DB, (zend_uchar*) db, db_len, PROT_OK_PACKET, FALSE, TRUE);
- /*
- The server sends 0 but libmysql doesn't read it and has established
- a protocol of giving back -1. Thus we have to follow it :(
- */
- SET_ERROR_AFF_ROWS(conn);
- if (ret == PASS) {
- if (conn->connect_or_select_db) {
- mnd_pefree(conn->connect_or_select_db, conn->persistent);
- }
- conn->connect_or_select_db = mnd_pestrndup(db, db_len, conn->persistent);
- conn->connect_or_select_db_len = db_len;
- if (!conn->connect_or_select_db) {
- /* OOM */
- SET_OOM_ERROR(*conn->error_info);
- ret = FAIL;
- }
+ const MYSQLND_CSTRING database = {db, db_len};
+ struct st_mysqlnd_protocol_command * command = conn->command_factory(COM_INIT_DB, conn, database);
+ if (command) {
+ ret = command->run(command);
+ command->free_command(command);
}
+
conn->m->local_tx_end(conn, this_func, ret);
}
DBG_RETURN(ret);
@@ -1717,20 +1104,18 @@ MYSQLND_METHOD(mysqlnd_conn_data, select_db)(MYSQLND_CONN_DATA * const conn, con
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, ping)(MYSQLND_CONN_DATA * const conn)
{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, ping);
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), ping);
enum_func_status ret = FAIL;
DBG_ENTER("mysqlnd_conn_data::ping");
DBG_INF_FMT("conn=%llu", conn->thread_id);
if (PASS == conn->m->local_tx_start(conn, this_func)) {
- ret = conn->m->simple_command(conn, COM_PING, NULL, 0, PROT_OK_PACKET, TRUE, TRUE);
- /*
- The server sends 0 but libmysql doesn't read it and has established
- a protocol of giving back -1. Thus we have to follow it :(
- */
- SET_ERROR_AFF_ROWS(conn);
-
+ struct st_mysqlnd_protocol_command * command = conn->command_factory(COM_PING, conn);
+ if (command) {
+ ret = command->run(command);
+ command->free_command(command);
+ }
conn->m->local_tx_end(conn, this_func, ret);
}
DBG_INF_FMT("ret=%u", ret);
@@ -1743,33 +1128,18 @@ MYSQLND_METHOD(mysqlnd_conn_data, ping)(MYSQLND_CONN_DATA * const conn)
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, statistic)(MYSQLND_CONN_DATA * conn, zend_string **message)
{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, get_server_statistics);
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), get_server_statistics);
enum_func_status ret = FAIL;
- MYSQLND_PACKET_STATS * stats_header;
DBG_ENTER("mysqlnd_conn_data::statistic");
DBG_INF_FMT("conn=%llu", conn->thread_id);
if (PASS == conn->m->local_tx_start(conn, this_func)) {
- do {
- ret = conn->m->simple_command(conn, COM_STATISTICS, NULL, 0, PROT_LAST, FALSE, TRUE);
- if (FAIL == ret) {
- break;
- }
- stats_header = conn->protocol->m.get_stats_packet(conn->protocol, FALSE);
- if (!stats_header) {
- SET_OOM_ERROR(*conn->error_info);
- break;
- }
-
- if (PASS == (ret = PACKET_READ(stats_header, conn))) {
- /* will be freed by Zend, thus don't use the mnd_ allocator */
- *message = zend_string_init(stats_header->message, stats_header->message_len, 0);
- DBG_INF(ZSTR_VAL(*message));
- }
- PACKET_FREE(stats_header);
- } while (0);
-
+ struct st_mysqlnd_protocol_command * command = conn->command_factory(COM_STATISTICS, conn, message);
+ if (command) {
+ ret = command->run(command);
+ command->free_command(command);
+ }
conn->m->local_tx_end(conn, this_func, ret);
}
DBG_RETURN(ret);
@@ -1781,29 +1151,21 @@ MYSQLND_METHOD(mysqlnd_conn_data, statistic)(MYSQLND_CONN_DATA * conn, zend_stri
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, kill)(MYSQLND_CONN_DATA * conn, unsigned int pid)
{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, kill_connection);
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), kill_connection);
enum_func_status ret = FAIL;
- zend_uchar buff[4];
DBG_ENTER("mysqlnd_conn_data::kill");
DBG_INF_FMT("conn=%llu pid=%u", conn->thread_id, pid);
if (PASS == conn->m->local_tx_start(conn, this_func)) {
- int4store(buff, pid);
-
- /* If we kill ourselves don't expect OK packet, PROT_LAST will skip it */
- if (pid != conn->thread_id) {
- ret = conn->m->simple_command(conn, COM_PROCESS_KILL, buff, 4, PROT_OK_PACKET, FALSE, TRUE);
- /*
- The server sends 0 but libmysql doesn't read it and has established
- a protocol of giving back -1. Thus we have to follow it :(
- */
- SET_ERROR_AFF_ROWS(conn);
- } else if (PASS == (ret = conn->m->simple_command(conn, COM_PROCESS_KILL, buff, 4, PROT_LAST, FALSE, TRUE))) {
- CONN_SET_STATE(conn, CONN_QUIT_SENT);
- conn->m->send_close(conn);
+ unsigned int process_id = pid;
+ /* 'unsigned char' is promoted to 'int' when passed through '...' */
+ unsigned int read_response = (pid != conn->thread_id);
+ struct st_mysqlnd_protocol_command * command = conn->command_factory(COM_PROCESS_KILL, conn, process_id, read_response);
+ if (command) {
+ ret = command->run(command);
+ command->free_command(command);
}
-
conn->m->local_tx_end(conn, this_func, ret);
}
DBG_RETURN(ret);
@@ -1815,7 +1177,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, kill)(MYSQLND_CONN_DATA * conn, unsigned int p
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, set_charset)(MYSQLND_CONN_DATA * const conn, const char * const csname)
{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, set_charset);
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), set_charset);
enum_func_status ret = FAIL;
const MYSQLND_CHARSET * const charset = mysqlnd_find_charset_name(csname);
@@ -1823,7 +1185,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, set_charset)(MYSQLND_CONN_DATA * const conn, c
DBG_INF_FMT("conn=%llu cs=%s", conn->thread_id, csname);
if (!charset) {
- SET_CLIENT_ERROR(*conn->error_info, CR_CANT_FIND_CHARSET, UNKNOWN_SQLSTATE,
+ SET_CLIENT_ERROR(conn->error_info, CR_CANT_FIND_CHARSET, UNKNOWN_SQLSTATE,
"Invalid characterset or character set not supported");
DBG_RETURN(ret);
}
@@ -1854,17 +1216,18 @@ MYSQLND_METHOD(mysqlnd_conn_data, set_charset)(MYSQLND_CONN_DATA * const conn, c
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, refresh)(MYSQLND_CONN_DATA * const conn, uint8_t options)
{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, refresh_server);
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), refresh_server);
enum_func_status ret = FAIL;
- zend_uchar bits[1];
DBG_ENTER("mysqlnd_conn_data::refresh");
DBG_INF_FMT("conn=%llu options=%lu", conn->thread_id, options);
if (PASS == conn->m->local_tx_start(conn, this_func)) {
- int1store(bits, options);
-
- ret = conn->m->simple_command(conn, COM_REFRESH, bits, 1, PROT_OK_PACKET, FALSE, TRUE);
-
+ unsigned int options_param = (unsigned int) options;
+ struct st_mysqlnd_protocol_command * command = conn->command_factory(COM_REFRESH, conn, options_param);
+ if (command) {
+ ret = command->run(command);
+ command->free_command(command);
+ }
conn->m->local_tx_end(conn, this_func, ret);
}
DBG_RETURN(ret);
@@ -1876,17 +1239,18 @@ MYSQLND_METHOD(mysqlnd_conn_data, refresh)(MYSQLND_CONN_DATA * const conn, uint8
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, shutdown)(MYSQLND_CONN_DATA * const conn, uint8_t level)
{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, shutdown_server);
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), shutdown_server);
enum_func_status ret = FAIL;
- zend_uchar bits[1];
DBG_ENTER("mysqlnd_conn_data::shutdown");
DBG_INF_FMT("conn=%llu level=%lu", conn->thread_id, level);
if (PASS == conn->m->local_tx_start(conn, this_func)) {
- int1store(bits, level);
-
- ret = conn->m->simple_command(conn, COM_SHUTDOWN, bits, 1, PROT_OK_PACKET, FALSE, TRUE);
-
+ unsigned int level_param = (unsigned int) level;
+ struct st_mysqlnd_protocol_command * command = conn->command_factory(COM_SHUTDOWN, conn, level_param);
+ if (command) {
+ ret = command->run(command);
+ command->free_command(command);
+ }
conn->m->local_tx_end(conn, this_func, ret);
}
DBG_RETURN(ret);
@@ -1899,29 +1263,32 @@ static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, send_close)(MYSQLND_CONN_DATA * const conn)
{
enum_func_status ret = PASS;
- MYSQLND_NET * net = conn->net;
- php_stream * net_stream = net->data->m.get_stream(net);
- enum mysqlnd_connection_state state;
+ MYSQLND_VIO * vio = conn->vio;
+ php_stream * net_stream = vio->data->m.get_stream(vio);
+ enum mysqlnd_connection_state state = GET_CONNECTION_STATE(&conn->state);
DBG_ENTER("mysqlnd_send_close");
- DBG_INF_FMT("conn=%llu net->data->stream->abstract=%p", conn->thread_id, net_stream? net_stream->abstract:NULL);
+ DBG_INF_FMT("conn=%llu vio->data->stream->abstract=%p", conn->thread_id, net_stream? net_stream->abstract:NULL);
+ DBG_INF_FMT("state=%u", state);
- if (CONN_GET_STATE(conn) >= CONN_READY) {
- MYSQLND_DEC_CONN_STATISTIC(conn->stats, STAT_OPENED_CONNECTIONS);
+ if (state >= CONN_READY) {
+ MYSQLND_DEC_GLOBAL_STATISTIC(STAT_OPENED_CONNECTIONS);
if (conn->persistent) {
- MYSQLND_DEC_CONN_STATISTIC(conn->stats, STAT_OPENED_PERSISTENT_CONNECTIONS);
+ MYSQLND_DEC_GLOBAL_STATISTIC(STAT_OPENED_PERSISTENT_CONNECTIONS);
}
}
- state = CONN_GET_STATE(conn);
- DBG_INF_FMT("state=%u", state);
switch (state) {
case CONN_READY:
DBG_INF("Connection clean, sending COM_QUIT");
if (net_stream) {
- ret = conn->m->simple_command(conn, COM_QUIT, NULL, 0, PROT_LAST, TRUE, TRUE);
- net->data->m.close_stream(net, conn->stats, conn->error_info);
+ struct st_mysqlnd_protocol_command * command = conn->command_factory(COM_QUIT, conn);
+ if (command) {
+ ret = command->run(command);
+ command->free_command(command);
+ }
+ vio->data->m.close_stream(vio, conn->stats, conn->error_info);
}
- CONN_SET_STATE(conn, CONN_QUIT_SENT);
+ SET_CONNECTION_STATE(&conn->state, CONN_QUIT_SENT);
break;
case CONN_SENDING_LOAD_DATA:
/*
@@ -1932,7 +1299,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, send_close)(MYSQLND_CONN_DATA * const conn)
case CONN_QUERY_SENT:
case CONN_FETCHING_DATA:
MYSQLND_INC_GLOBAL_STATISTIC(STAT_CLOSE_IN_MIDDLE);
- DBG_ERR_FMT("Brutally closing connection [%p][%s]", conn, conn->scheme);
+ DBG_ERR_FMT("Brutally closing connection [%p][%s]", conn, conn->scheme.s);
/*
Do nothing, the connection will be brutally closed
and the server will catch it and free close from its side.
@@ -1945,11 +1312,11 @@ MYSQLND_METHOD(mysqlnd_conn_data, send_close)(MYSQLND_CONN_DATA * const conn)
Fall-through
*/
- CONN_SET_STATE(conn, CONN_QUIT_SENT);
+ SET_CONNECTION_STATE(&conn->state, CONN_QUIT_SENT);
/* Fall-through */
case CONN_QUIT_SENT:
/* The user has killed its own connection */
- net->data->m.close_stream(net, conn->stats, conn->error_info);
+ vio->data->m.close_stream(vio, conn->stats, conn->error_info);
break;
}
@@ -1991,28 +1358,6 @@ MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, free_reference)(MYSQLND_CONN_DATA * co
/* }}} */
-/* {{{ mysqlnd_conn_data::get_state */
-static enum mysqlnd_connection_state
-MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, get_state)(const MYSQLND_CONN_DATA * const conn)
-{
- DBG_ENTER("mysqlnd_conn_data::get_state");
- DBG_RETURN(conn->state);
-}
-/* }}} */
-
-
-/* {{{ mysqlnd_conn_data::set_state */
-static void
-MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, set_state)(MYSQLND_CONN_DATA * const conn, enum mysqlnd_connection_state new_state)
-{
- DBG_ENTER("mysqlnd_conn_data::set_state");
- DBG_INF_FMT("New state=%u", new_state);
- conn->state = new_state;
- DBG_VOID_RETURN;
-}
-/* }}} */
-
-
/* {{{ mysqlnd_conn_data::field_count */
static unsigned int
MYSQLND_METHOD(mysqlnd_conn_data, field_count)(const MYSQLND_CONN_DATA * const conn)
@@ -2026,7 +1371,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, field_count)(const MYSQLND_CONN_DATA * const c
static unsigned int
MYSQLND_METHOD(mysqlnd_conn_data, server_status)(const MYSQLND_CONN_DATA * const conn)
{
- return conn->upsert_status->server_status;
+ return UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status);
}
/* }}} */
@@ -2035,7 +1380,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, server_status)(const MYSQLND_CONN_DATA * const
static uint64_t
MYSQLND_METHOD(mysqlnd_conn_data, insert_id)(const MYSQLND_CONN_DATA * const conn)
{
- return conn->upsert_status->last_insert_id;
+ return UPSERT_STATUS_GET_LAST_INSERT_ID(conn->upsert_status);
}
/* }}} */
@@ -2044,7 +1389,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, insert_id)(const MYSQLND_CONN_DATA * const con
static uint64_t
MYSQLND_METHOD(mysqlnd_conn_data, affected_rows)(const MYSQLND_CONN_DATA * const conn)
{
- return conn->upsert_status->affected_rows;
+ return UPSERT_STATUS_GET_AFFECTED_ROWS(conn->upsert_status);
}
/* }}} */
@@ -2053,7 +1398,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, affected_rows)(const MYSQLND_CONN_DATA * const
static unsigned int
MYSQLND_METHOD(mysqlnd_conn_data, warning_count)(const MYSQLND_CONN_DATA * const conn)
{
- return conn->upsert_status->warning_count;
+ return UPSERT_STATUS_GET_WARNINGS(conn->upsert_status);
}
/* }}} */
@@ -2062,10 +1407,11 @@ MYSQLND_METHOD(mysqlnd_conn_data, warning_count)(const MYSQLND_CONN_DATA * const
static const char *
MYSQLND_METHOD(mysqlnd_conn_data, info)(const MYSQLND_CONN_DATA * const conn)
{
- return conn->last_message;
+ return conn->last_message.s;
}
/* }}} */
+
/* {{{ mysqlnd_get_client_info */
PHPAPI const char * mysqlnd_get_client_info()
{
@@ -2155,7 +1501,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, more_results)(const MYSQLND_CONN_DATA * const
{
DBG_ENTER("mysqlnd_conn_data::more_results");
/* (conn->state == CONN_NEXT_RESULT_PENDING) too */
- DBG_RETURN(conn->upsert_status->server_status & SERVER_MORE_RESULTS_EXISTS? TRUE:FALSE);
+ DBG_RETURN(UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status) & SERVER_MORE_RESULTS_EXISTS? TRUE:FALSE);
}
/* }}} */
@@ -2164,7 +1510,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, more_results)(const MYSQLND_CONN_DATA * const
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, next_result)(MYSQLND_CONN_DATA * const conn)
{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, next_result);
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), next_result);
enum_func_status ret = FAIL;
DBG_ENTER("mysqlnd_conn_data::next_result");
@@ -2172,12 +1518,12 @@ MYSQLND_METHOD(mysqlnd_conn_data, next_result)(MYSQLND_CONN_DATA * const conn)
if (PASS == conn->m->local_tx_start(conn, this_func)) {
do {
- if (CONN_GET_STATE(conn) != CONN_NEXT_RESULT_PENDING) {
+ if (GET_CONNECTION_STATE(&conn->state) != CONN_NEXT_RESULT_PENDING) {
break;
}
- SET_EMPTY_ERROR(*conn->error_info);
- SET_ERROR_AFF_ROWS(conn);
+ SET_EMPTY_ERROR(conn->error_info);
+ UPSERT_STATUS_SET_AFFECTED_ROWS_TO_ERROR(conn->upsert_status);
/*
We are sure that there is a result set, since conn->state is set accordingly
in mysqlnd_store_result() or mysqlnd_fetch_row_unbuffered()
@@ -2190,15 +1536,15 @@ MYSQLND_METHOD(mysqlnd_conn_data, next_result)(MYSQLND_CONN_DATA * const conn)
if (!conn->error_info->error_no) {
DBG_ERR_FMT("Serious error. %s::%u", __FILE__, __LINE__);
php_error_docref(NULL, E_WARNING, "Serious error. PID=%d", getpid());
- CONN_SET_STATE(conn, CONN_QUIT_SENT);
+ SET_CONNECTION_STATE(&conn->state, CONN_QUIT_SENT);
conn->m->send_close(conn);
} else {
DBG_INF_FMT("Error from the server : (%u) %s", conn->error_info->error_no, conn->error_info->error);
}
break;
}
- if (conn->last_query_type == QUERY_UPSERT && conn->upsert_status->affected_rows) {
- MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn->stats, STAT_ROWS_AFFECTED_NORMAL, conn->upsert_status->affected_rows);
+ if (conn->last_query_type == QUERY_UPSERT && UPSERT_STATUS_GET_AFFECTED_ROWS(conn->upsert_status)) {
+ MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn->stats, STAT_ROWS_AFFECTED_NORMAL, UPSERT_STATUS_GET_AFFECTED_ROWS(conn->upsert_status));
}
} while (0);
conn->m->local_tx_end(conn, this_func, ret);
@@ -2210,7 +1556,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, next_result)(MYSQLND_CONN_DATA * const conn)
/* {{{ mysqlnd_field_type_name */
-PHPAPI const char *mysqlnd_field_type_name(enum mysqlnd_field_types field_type)
+PHPAPI const char * mysqlnd_field_type_name(const enum mysqlnd_field_types field_type)
{
switch(field_type) {
case FIELD_TYPE_JSON:
@@ -2265,14 +1611,14 @@ PHPAPI const char *mysqlnd_field_type_name(enum mysqlnd_field_types field_type)
/* {{{ mysqlnd_conn_data::change_user */
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, change_user)(MYSQLND_CONN_DATA * const conn,
- const char * user,
- const char * passwd,
- const char * db,
- zend_bool silent,
- size_t passwd_len
- )
-{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, change_user);
+ const char * user,
+ const char * passwd,
+ const char * db,
+ zend_bool silent,
+ size_t passwd_len
+ )
+{
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), change_user);
enum_func_status ret = FAIL;
DBG_ENTER("mysqlnd_conn_data::change_user");
@@ -2283,14 +1629,15 @@ MYSQLND_METHOD(mysqlnd_conn_data, change_user)(MYSQLND_CONN_DATA * const conn,
goto end;
}
- SET_EMPTY_ERROR(*conn->error_info);
- SET_ERROR_AFF_ROWS(conn);
+ SET_EMPTY_ERROR(conn->error_info);
+ UPSERT_STATUS_SET_AFFECTED_ROWS_TO_ERROR(conn->upsert_status);
if (!user) {
user = "";
}
if (!passwd) {
passwd = "";
+ passwd_len = 0;
}
if (!db) {
db = "";
@@ -2298,7 +1645,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, change_user)(MYSQLND_CONN_DATA * const conn,
/* XXX: passwords that have \0 inside work during auth, but in this case won't work with change user */
ret = mysqlnd_run_authentication(conn, user, passwd, passwd_len, db, strlen(db),
- conn->auth_plugin_data, conn->auth_plugin_data_len, conn->options->auth_protocol,
+ conn->authentication_plugin_data, conn->options->auth_protocol,
0 /*charset not used*/, conn->options, conn->server_capabilities, silent, TRUE/*is_change*/);
/*
@@ -2316,11 +1663,11 @@ end:
/* {{{ mysqlnd_conn_data::set_client_option */
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, set_client_option)(MYSQLND_CONN_DATA * const conn,
- enum mysqlnd_option option,
+ enum_mysqlnd_client_option option,
const char * const value
)
{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, set_client_option);
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), set_client_option);
enum_func_status ret = PASS;
DBG_ENTER("mysqlnd_conn_data::set_client_option");
DBG_INF_FMT("conn=%llu option=%u", conn->thread_id, option);
@@ -2329,7 +1676,6 @@ MYSQLND_METHOD(mysqlnd_conn_data, set_client_option)(MYSQLND_CONN_DATA * const c
goto end;
}
switch (option) {
- case MYSQL_OPT_COMPRESS:
#ifdef WHEN_SUPPORTED_BY_MYSQLI
case MYSQL_OPT_READ_TIMEOUT:
case MYSQL_OPT_WRITE_TIMEOUT:
@@ -2341,10 +1687,13 @@ MYSQLND_METHOD(mysqlnd_conn_data, set_client_option)(MYSQLND_CONN_DATA * const c
case MYSQLND_OPT_SSL_CIPHER:
case MYSQL_OPT_SSL_VERIFY_SERVER_CERT:
case MYSQL_OPT_CONNECT_TIMEOUT:
- case MYSQLND_OPT_NET_CMD_BUFFER_SIZE:
case MYSQLND_OPT_NET_READ_BUFFER_SIZE:
+ ret = conn->vio->data->m.set_client_option(conn->vio, option, value);
+ break;
+ case MYSQLND_OPT_NET_CMD_BUFFER_SIZE:
+ case MYSQL_OPT_COMPRESS:
case MYSQL_SERVER_PUBLIC_KEY:
- ret = conn->net->data->m.set_client_option(conn->net, option, value);
+ ret = conn->protocol_frame_codec->data->m.set_client_option(conn->protocol_frame_codec, option, value);
break;
#ifdef MYSQLND_STRING_TO_INT_CONVERSION
case MYSQLND_OPT_INT_AND_FLOAT_NATIVE:
@@ -2389,7 +1738,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, set_client_option)(MYSQLND_CONN_DATA * const c
{
char * new_charset_name;
if (!mysqlnd_find_charset_name(value)) {
- SET_CLIENT_ERROR(*conn->error_info, CR_CANT_FIND_CHARSET, UNKNOWN_SQLSTATE, "Unknown character set");
+ SET_CLIENT_ERROR(conn->error_info, CR_CANT_FIND_CHARSET, UNKNOWN_SQLSTATE, "Unknown character set");
ret = FAIL;
break;
}
@@ -2475,7 +1824,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, set_client_option)(MYSQLND_CONN_DATA * const c
conn->m->local_tx_end(conn, this_func, ret);
DBG_RETURN(ret);
oom:
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
conn->m->local_tx_end(conn, this_func, FAIL);
end:
DBG_RETURN(FAIL);
@@ -2486,12 +1835,12 @@ end:
/* {{{ mysqlnd_conn_data::set_client_option_2d */
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, set_client_option_2d)(MYSQLND_CONN_DATA * const conn,
- enum mysqlnd_option option,
+ const enum_mysqlnd_client_option option,
const char * const key,
const char * const value
)
{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, set_client_option_2d);
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), set_client_option_2d);
enum_func_status ret = PASS;
DBG_ENTER("mysqlnd_conn_data::set_client_option_2d");
DBG_INF_FMT("conn=%llu option=%u", conn->thread_id, option);
@@ -2522,7 +1871,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, set_client_option_2d)(MYSQLND_CONN_DATA * cons
conn->m->local_tx_end(conn, this_func, ret);
DBG_RETURN(ret);
oom:
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
conn->m->local_tx_end(conn, this_func, FAIL);
end:
DBG_RETURN(FAIL);
@@ -2534,7 +1883,7 @@ end:
static MYSQLND_RES *
MYSQLND_METHOD(mysqlnd_conn_data, use_result)(MYSQLND_CONN_DATA * const conn, const unsigned int flags)
{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, use_result);
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), use_result);
MYSQLND_RES * result = NULL;
DBG_ENTER("mysqlnd_conn_data::use_result");
@@ -2547,8 +1896,8 @@ MYSQLND_METHOD(mysqlnd_conn_data, use_result)(MYSQLND_CONN_DATA * const conn, co
}
/* Nothing to store for UPSERT/LOAD DATA */
- if (conn->last_query_type != QUERY_SELECT || CONN_GET_STATE(conn) != CONN_FETCHING_DATA) {
- SET_CLIENT_ERROR(*conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
+ if (conn->last_query_type != QUERY_SELECT || GET_CONNECTION_STATE(&conn->state) != CONN_FETCHING_DATA) {
+ SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
DBG_ERR("Command out of sync");
break;
}
@@ -2576,7 +1925,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, use_result)(MYSQLND_CONN_DATA * const conn, co
static MYSQLND_RES *
MYSQLND_METHOD(mysqlnd_conn_data, store_result)(MYSQLND_CONN_DATA * const conn, const unsigned int flags)
{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, store_result);
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), store_result);
MYSQLND_RES * result = NULL;
DBG_ENTER("mysqlnd_conn_data::store_result");
@@ -2590,8 +1939,8 @@ MYSQLND_METHOD(mysqlnd_conn_data, store_result)(MYSQLND_CONN_DATA * const conn,
}
/* Nothing to store for UPSERT/LOAD DATA*/
- if (conn->last_query_type != QUERY_SELECT || CONN_GET_STATE(conn) != CONN_FETCHING_DATA) {
- SET_CLIENT_ERROR(*conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
+ if (conn->last_query_type != QUERY_SELECT || GET_CONNECTION_STATE(&conn->state) != CONN_FETCHING_DATA) {
+ SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
DBG_ERR("Command out of sync");
break;
}
@@ -2611,7 +1960,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, store_result)(MYSQLND_CONN_DATA * const conn,
}
}
if (!(f & (MYSQLND_STORE_NO_COPY | MYSQLND_STORE_COPY))) {
- SET_CLIENT_ERROR(*conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "Unknown fetch mode");
+ SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "Unknown fetch mode");
DBG_ERR("Unknown fetch mode");
break;
}
@@ -2632,7 +1981,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, store_result)(MYSQLND_CONN_DATA * const conn,
/* {{{ mysqlnd_conn_data::get_connection_stats */
static void
MYSQLND_METHOD(mysqlnd_conn_data, get_connection_stats)(const MYSQLND_CONN_DATA * const conn,
- zval * return_value ZEND_FILE_LINE_DC)
+ zval * return_value ZEND_FILE_LINE_DC)
{
DBG_ENTER("mysqlnd_conn_data::get_connection_stats");
mysqlnd_fill_stats_hash(conn->stats, mysqlnd_stats_values_names, return_value ZEND_FILE_LINE_CC);
@@ -2645,7 +1994,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, get_connection_stats)(const MYSQLND_CONN_DATA
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, set_autocommit)(MYSQLND_CONN_DATA * conn, unsigned int mode)
{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, set_autocommit);
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), set_autocommit);
enum_func_status ret = FAIL;
DBG_ENTER("mysqlnd_conn_data::set_autocommit");
@@ -2756,7 +2105,7 @@ mysqlnd_escape_string_for_tx_name_in_comment(const char * const name)
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, tx_commit_or_rollback)(MYSQLND_CONN_DATA * conn, const zend_bool commit, const unsigned int flags, const char * const name)
{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, tx_commit_or_rollback);
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), tx_commit_or_rollback);
enum_func_status ret = FAIL;
DBG_ENTER("mysqlnd_conn_data::tx_commit_or_rollback");
@@ -2780,7 +2129,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, tx_commit_or_rollback)(MYSQLND_CONN_DATA * con
name_esc = NULL;
}
if (!query) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
break;
}
@@ -2800,7 +2149,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, tx_commit_or_rollback)(MYSQLND_CONN_DATA * con
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, tx_begin)(MYSQLND_CONN_DATA * conn, const unsigned int mode, const char * const name)
{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, tx_begin);
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), tx_begin);
enum_func_status ret = FAIL;
DBG_ENTER("mysqlnd_conn_data::tx_begin");
@@ -2843,7 +2192,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, tx_begin)(MYSQLND_CONN_DATA * conn, const unsi
name_esc = NULL;
}
if (!query) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
break;
}
ret = conn->m->query(conn, query, query_len);
@@ -2862,7 +2211,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, tx_begin)(MYSQLND_CONN_DATA * conn, const unsi
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, tx_savepoint)(MYSQLND_CONN_DATA * conn, const char * const name)
{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, tx_savepoint);
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), tx_savepoint);
enum_func_status ret = FAIL;
DBG_ENTER("mysqlnd_conn_data::tx_savepoint");
@@ -2871,12 +2220,12 @@ MYSQLND_METHOD(mysqlnd_conn_data, tx_savepoint)(MYSQLND_CONN_DATA * conn, const
char * query;
unsigned int query_len;
if (!name) {
- SET_CLIENT_ERROR(*conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "Savepoint name not provided");
+ SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "Savepoint name not provided");
break;
}
query_len = mnd_sprintf(&query, 0, "SAVEPOINT `%s`", name);
if (!query) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
break;
}
ret = conn->m->query(conn, query, query_len);
@@ -2894,7 +2243,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, tx_savepoint)(MYSQLND_CONN_DATA * conn, const
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, tx_savepoint_release)(MYSQLND_CONN_DATA * conn, const char * const name)
{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, tx_savepoint_release);
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), tx_savepoint_release);
enum_func_status ret = FAIL;
DBG_ENTER("mysqlnd_conn_data::tx_savepoint_release");
@@ -2903,12 +2252,12 @@ MYSQLND_METHOD(mysqlnd_conn_data, tx_savepoint_release)(MYSQLND_CONN_DATA * conn
char * query;
unsigned int query_len;
if (!name) {
- SET_CLIENT_ERROR(*conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "Savepoint name not provided");
+ SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "Savepoint name not provided");
break;
}
query_len = mnd_sprintf(&query, 0, "RELEASE SAVEPOINT `%s`", name);
if (!query) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
break;
}
ret = conn->m->query(conn, query, query_len);
@@ -2923,8 +2272,8 @@ MYSQLND_METHOD(mysqlnd_conn_data, tx_savepoint_release)(MYSQLND_CONN_DATA * conn
/* {{{ mysqlnd_conn_data::negotiate_client_api_capabilities */
-static unsigned int
-MYSQLND_METHOD(mysqlnd_conn_data, negotiate_client_api_capabilities)(MYSQLND_CONN_DATA * const conn, const unsigned int flags)
+static size_t
+MYSQLND_METHOD(mysqlnd_conn_data, negotiate_client_api_capabilities)(MYSQLND_CONN_DATA * const conn, const size_t flags)
{
unsigned int ret = 0;
DBG_ENTER("mysqlnd_conn_data::negotiate_client_api_capabilities");
@@ -2939,7 +2288,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, negotiate_client_api_capabilities)(MYSQLND_CON
/* {{{ mysqlnd_conn_data::get_client_api_capabilities */
-static unsigned int
+static size_t
MYSQLND_METHOD(mysqlnd_conn_data, get_client_api_capabilities)(const MYSQLND_CONN_DATA * const conn)
{
DBG_ENTER("mysqlnd_conn_data::get_client_api_capabilities");
@@ -2950,18 +2299,17 @@ MYSQLND_METHOD(mysqlnd_conn_data, get_client_api_capabilities)(const MYSQLND_CON
/* {{{ mysqlnd_conn_data::local_tx_start */
static enum_func_status
-MYSQLND_METHOD(mysqlnd_conn_data, local_tx_start)(MYSQLND_CONN_DATA * conn, size_t this_func)
+MYSQLND_METHOD(mysqlnd_conn_data, local_tx_start)(MYSQLND_CONN_DATA * conn, const size_t this_func)
{
- enum_func_status ret = PASS;
DBG_ENTER("mysqlnd_conn_data::local_tx_start");
- DBG_RETURN(ret);
+ DBG_RETURN(PASS);
}
/* }}} */
/* {{{ mysqlnd_conn_data::local_tx_end */
static enum_func_status
-MYSQLND_METHOD(mysqlnd_conn_data, local_tx_end)(MYSQLND_CONN_DATA * conn, size_t this_func, enum_func_status status)
+MYSQLND_METHOD(mysqlnd_conn_data, local_tx_end)(MYSQLND_CONN_DATA * conn, const size_t this_func, const enum_func_status status)
{
DBG_ENTER("mysqlnd_conn_data::local_tx_end");
DBG_RETURN(status);
@@ -2969,27 +2317,19 @@ MYSQLND_METHOD(mysqlnd_conn_data, local_tx_end)(MYSQLND_CONN_DATA * conn, size_t
/* }}} */
-/* {{{ mysqlnd_conn_data::init */
-static enum_func_status
-MYSQLND_METHOD(mysqlnd_conn_data, init)(MYSQLND_CONN_DATA * conn)
+/* {{{ _mysqlnd_stmt_init */
+MYSQLND_STMT *
+MYSQLND_METHOD(mysqlnd_conn_data, stmt_init)(MYSQLND_CONN_DATA * const conn)
{
- DBG_ENTER("mysqlnd_conn_data::init");
- mysqlnd_stats_init(&conn->stats, STAT_LAST, conn->persistent);
- SET_ERROR_AFF_ROWS(conn);
-
- conn->net = mysqlnd_net_init(conn->persistent, conn->stats, conn->error_info);
- conn->protocol = mysqlnd_protocol_init(conn->persistent);
-
- DBG_RETURN(conn->stats && conn->net && conn->protocol? PASS:FAIL);
+ MYSQLND_STMT * ret;
+ DBG_ENTER("mysqlnd_conn_data::stmt_init");
+ ret = conn->object_factory.get_prepared_statement(conn, conn->persistent);
+ DBG_RETURN(ret);
}
/* }}} */
-MYSQLND_STMT * _mysqlnd_stmt_init(MYSQLND_CONN_DATA * const conn);
-
-
MYSQLND_CLASS_METHODS_START(mysqlnd_conn_data)
- MYSQLND_METHOD(mysqlnd_conn_data, init),
MYSQLND_METHOD(mysqlnd_conn_data, connect),
MYSQLND_METHOD(mysqlnd_conn_data, escape_string),
@@ -3002,7 +2342,7 @@ MYSQLND_CLASS_METHODS_START(mysqlnd_conn_data)
MYSQLND_METHOD(mysqlnd_conn_data, next_result),
MYSQLND_METHOD(mysqlnd_conn_data, more_results),
- _mysqlnd_stmt_init,
+ MYSQLND_METHOD(mysqlnd_conn_data, stmt_init),
MYSQLND_METHOD(mysqlnd_conn_data, shutdown),
MYSQLND_METHOD(mysqlnd_conn_data, refresh),
@@ -3013,7 +2353,7 @@ MYSQLND_CLASS_METHODS_START(mysqlnd_conn_data)
MYSQLND_METHOD(mysqlnd_conn_data, dump_debug_info),
MYSQLND_METHOD(mysqlnd_conn_data, change_user),
- MYSQLND_METHOD(mysqlnd_conn_data, errno),
+ MYSQLND_METHOD(mysqlnd_conn_data, err_no),
MYSQLND_METHOD(mysqlnd_conn_data, error),
MYSQLND_METHOD(mysqlnd_conn_data, sqlstate),
MYSQLND_METHOD(mysqlnd_conn_data, thread_id),
@@ -3027,7 +2367,6 @@ MYSQLND_CLASS_METHODS_START(mysqlnd_conn_data)
MYSQLND_METHOD(mysqlnd_conn_data, get_proto_info),
MYSQLND_METHOD(mysqlnd_conn_data, info),
MYSQLND_METHOD(mysqlnd_conn_data, charset_name),
- MYSQLND_METHOD(mysqlnd_conn_data, list_fields),
MYSQLND_METHOD(mysqlnd_conn_data, list_method),
MYSQLND_METHOD(mysqlnd_conn_data, insert_id),
@@ -3048,11 +2387,7 @@ MYSQLND_CLASS_METHODS_START(mysqlnd_conn_data)
MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, get_reference),
MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, free_reference),
- MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, get_state),
- MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, set_state),
- MYSQLND_METHOD(mysqlnd_conn_data, simple_command),
- MYSQLND_METHOD(mysqlnd_conn_data, simple_command_handle_response),
MYSQLND_METHOD(mysqlnd_conn_data, restart_psession),
MYSQLND_METHOD(mysqlnd_conn_data, end_psession),
MYSQLND_METHOD(mysqlnd_conn_data, send_close),
@@ -3073,13 +2408,14 @@ MYSQLND_CLASS_METHODS_START(mysqlnd_conn_data)
MYSQLND_METHOD(mysqlnd_conn_data, execute_init_commands),
MYSQLND_METHOD(mysqlnd_conn_data, get_updated_connect_flags),
MYSQLND_METHOD(mysqlnd_conn_data, connect_handshake),
- MYSQLND_METHOD(mysqlnd_conn_data, simple_command_send_request),
MYSQLND_METHOD(mysqlnd_conn_data, fetch_auth_plugin_by_name),
MYSQLND_METHOD(mysqlnd_conn_data, set_client_option_2d),
MYSQLND_METHOD(mysqlnd_conn_data, negotiate_client_api_capabilities),
- MYSQLND_METHOD(mysqlnd_conn_data, get_client_api_capabilities)
+ MYSQLND_METHOD(mysqlnd_conn_data, get_client_api_capabilities),
+
+ MYSQLND_METHOD(mysqlnd_conn_data, get_scheme)
MYSQLND_CLASS_METHODS_END;
@@ -3089,7 +2425,7 @@ MYSQLND_METHOD(mysqlnd_conn, clone_object)(MYSQLND * const conn)
{
MYSQLND * ret;
DBG_ENTER("mysqlnd_conn::get_reference");
- ret = MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_object_factory).clone_connection_object(conn);
+ ret = conn->data->object_factory.clone_connection_object(conn);
DBG_RETURN(ret);
}
/* }}} */
@@ -3113,9 +2449,9 @@ MYSQLND_METHOD_PRIVATE(mysqlnd_conn, dtor)(MYSQLND * conn)
/* {{{ mysqlnd_conn_data::close */
static enum_func_status
-MYSQLND_METHOD(mysqlnd_conn, close)(MYSQLND * conn_handle, enum_connection_close_type close_type)
+MYSQLND_METHOD(mysqlnd_conn, close)(MYSQLND * conn_handle, const enum_connection_close_type close_type)
{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_methods, close);
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn), close);
MYSQLND_CONN_DATA * conn = conn_handle->data;
enum_func_status ret = FAIL;
@@ -3123,7 +2459,7 @@ MYSQLND_METHOD(mysqlnd_conn, close)(MYSQLND * conn_handle, enum_connection_close
DBG_INF_FMT("conn=%llu", conn->thread_id);
if (PASS == conn->m->local_tx_start(conn, this_func)) {
- if (CONN_GET_STATE(conn) >= CONN_READY) {
+ if (GET_CONNECTION_STATE(&conn->state) >= CONN_READY) {
static enum_mysqlnd_collected_stats close_type_to_stat_map[MYSQLND_CLOSE_LAST] = {
STAT_CLOSE_EXPLICIT,
STAT_CLOSE_IMPLICIT,
@@ -3138,7 +2474,7 @@ MYSQLND_METHOD(mysqlnd_conn, close)(MYSQLND * conn_handle, enum_connection_close
*/
ret = conn->m->send_close(conn);
- /* do it after free_reference/dtor and we might crash */
+ /* If we do it after free_reference/dtor then we might crash */
conn->m->local_tx_end(conn, this_func, ret);
conn_handle->m->dtor(conn_handle);
@@ -3156,20 +2492,267 @@ MYSQLND_CLASS_METHODS_START(mysqlnd_conn)
MYSQLND_CLASS_METHODS_END;
-/* {{{ mysqlnd_init */
+#include "php_network.h"
+
+/* {{{ mysqlnd_stream_array_to_fd_set */
+MYSQLND **
+mysqlnd_stream_array_check_for_readiness(MYSQLND ** conn_array)
+{
+ unsigned int cnt = 0;
+ MYSQLND **p = conn_array, **p_p;
+ MYSQLND **ret = NULL;
+
+ while (*p) {
+ const enum mysqlnd_connection_state conn_state = GET_CONNECTION_STATE(&((*p)->data->state));
+ if (conn_state <= CONN_READY || conn_state == CONN_QUIT_SENT) {
+ cnt++;
+ }
+ p++;
+ }
+ if (cnt) {
+ MYSQLND **ret_p = ret = ecalloc(cnt + 1, sizeof(MYSQLND *));
+ p_p = p = conn_array;
+ while (*p) {
+ const enum mysqlnd_connection_state conn_state = GET_CONNECTION_STATE(&((*p)->data->state));
+ if (conn_state <= CONN_READY || conn_state == CONN_QUIT_SENT) {
+ *ret_p = *p;
+ *p = NULL;
+ ret_p++;
+ } else {
+ *p_p = *p;
+ p_p++;
+ }
+ p++;
+ }
+ *ret_p = NULL;
+ }
+ return ret;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stream_array_to_fd_set */
+static unsigned int
+mysqlnd_stream_array_to_fd_set(MYSQLND ** conn_array, fd_set * fds, php_socket_t * max_fd)
+{
+ php_socket_t this_fd;
+ php_stream *stream = NULL;
+ unsigned int cnt = 0;
+ MYSQLND **p = conn_array;
+ DBG_ENTER("mysqlnd_stream_array_to_fd_set");
+
+ while (*p) {
+ /* get the fd.
+ * NB: Most other code will NOT use the PHP_STREAM_CAST_INTERNAL flag
+ * when casting. It is only used here so that the buffered data warning
+ * is not displayed.
+ * */
+ stream = (*p)->data->vio->data->m.get_stream((*p)->data->vio);
+ DBG_INF_FMT("conn=%llu stream=%p", (*p)->data->thread_id, stream);
+ if (stream != NULL &&
+ SUCCESS == php_stream_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL, (void*)&this_fd, 1) &&
+ ZEND_VALID_SOCKET(this_fd))
+ {
+
+ PHP_SAFE_FD_SET(this_fd, fds);
+
+ if (this_fd > *max_fd) {
+ *max_fd = this_fd;
+ }
+ ++cnt;
+ }
+ ++p;
+ }
+ DBG_RETURN(cnt ? 1 : 0);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stream_array_from_fd_set */
+static unsigned int
+mysqlnd_stream_array_from_fd_set(MYSQLND ** conn_array, fd_set * fds)
+{
+ php_socket_t this_fd;
+ php_stream *stream = NULL;
+ unsigned int ret = 0;
+ zend_bool disproportion = FALSE;
+ MYSQLND **fwd = conn_array, **bckwd = conn_array;
+ DBG_ENTER("mysqlnd_stream_array_from_fd_set");
+
+ while (*fwd) {
+ stream = (*fwd)->data->vio->data->m.get_stream((*fwd)->data->vio);
+ DBG_INF_FMT("conn=%llu stream=%p", (*fwd)->data->thread_id, stream);
+ if (stream != NULL && SUCCESS == php_stream_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL,
+ (void*)&this_fd, 1) && ZEND_VALID_SOCKET(this_fd)) {
+ if (PHP_SAFE_FD_ISSET(this_fd, fds)) {
+ if (disproportion) {
+ *bckwd = *fwd;
+ }
+ ++bckwd;
+ ++fwd;
+ ++ret;
+ continue;
+ }
+ }
+ disproportion = TRUE;
+ ++fwd;
+ }
+ *bckwd = NULL;/* NULL-terminate the list */
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+#ifndef PHP_WIN32
+#define php_select(m, r, w, e, t) select(m, r, w, e, t)
+#else
+#include "win32/select.h"
+#endif
+
+
+/* {{{ mysqlnd_poll */
+PHPAPI enum_func_status
+mysqlnd_poll(MYSQLND **r_array, MYSQLND **e_array, MYSQLND ***dont_poll, long sec, long usec, int * desc_num)
+{
+ struct timeval tv;
+ struct timeval *tv_p = NULL;
+ fd_set rfds, wfds, efds;
+ php_socket_t max_fd = 0;
+ int retval, sets = 0;
+ int set_count, max_set_count = 0;
+
+ DBG_ENTER("_mysqlnd_poll");
+ if (sec < 0 || usec < 0) {
+ php_error_docref(NULL, E_WARNING, "Negative values passed for sec and/or usec");
+ DBG_RETURN(FAIL);
+ }
+
+ FD_ZERO(&rfds);
+ FD_ZERO(&wfds);
+ FD_ZERO(&efds);
+
+ if (r_array != NULL) {
+ *dont_poll = mysqlnd_stream_array_check_for_readiness(r_array);
+ set_count = mysqlnd_stream_array_to_fd_set(r_array, &rfds, &max_fd);
+ if (set_count > max_set_count) {
+ max_set_count = set_count;
+ }
+ sets += set_count;
+ }
+
+ if (e_array != NULL) {
+ set_count = mysqlnd_stream_array_to_fd_set(e_array, &efds, &max_fd);
+ if (set_count > max_set_count) {
+ max_set_count = set_count;
+ }
+ sets += set_count;
+ }
+
+ if (!sets) {
+ php_error_docref(NULL, E_WARNING, *dont_poll ? "All arrays passed are clear":"No stream arrays were passed");
+ DBG_ERR_FMT(*dont_poll ? "All arrays passed are clear":"No stream arrays were passed");
+ DBG_RETURN(FAIL);
+ }
+
+ PHP_SAFE_MAX_FD(max_fd, max_set_count);
+
+ /* Solaris + BSD do not like microsecond values which are >= 1 sec */
+ if (usec > 999999) {
+ tv.tv_sec = sec + (usec / 1000000);
+ tv.tv_usec = usec % 1000000;
+ } else {
+ tv.tv_sec = sec;
+ tv.tv_usec = usec;
+ }
+
+ tv_p = &tv;
+
+ retval = php_select(max_fd + 1, &rfds, &wfds, &efds, tv_p);
+
+ if (retval == -1) {
+ php_error_docref(NULL, E_WARNING, "unable to select [%d]: %s (max_fd=%d)",
+ errno, strerror(errno), max_fd);
+ DBG_RETURN(FAIL);
+ }
+
+ if (r_array != NULL) {
+ mysqlnd_stream_array_from_fd_set(r_array, &rfds);
+ }
+ if (e_array != NULL) {
+ mysqlnd_stream_array_from_fd_set(e_array, &efds);
+ }
+
+ *desc_num = retval;
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_connect */
+PHPAPI MYSQLND * mysqlnd_connection_connect(MYSQLND * conn_handle,
+ const char * const host,
+ const char * const user,
+ const char * const passwd, unsigned int passwd_len,
+ const char * const db, unsigned int db_len,
+ unsigned int port,
+ const char * const sock_or_pipe,
+ unsigned int mysql_flags,
+ unsigned int client_api_flags
+ )
+{
+ enum_func_status ret = FAIL;
+ zend_bool self_alloced = FALSE;
+ MYSQLND_CSTRING hostname = { host, host? strlen(host) : 0 };
+ MYSQLND_CSTRING username = { user, user? strlen(user) : 0 };
+ MYSQLND_CSTRING password = { passwd, passwd_len };
+ MYSQLND_CSTRING database = { db, db_len };
+ MYSQLND_CSTRING socket_or_pipe = { sock_or_pipe, sock_or_pipe? strlen(sock_or_pipe) : 0 };
+
+ DBG_ENTER("mysqlnd_connect");
+ DBG_INF_FMT("host=%s user=%s db=%s port=%u flags=%u", host? host:"", user? user:"", db? db:"", port, mysql_flags);
+
+ if (!conn_handle) {
+ self_alloced = TRUE;
+ if (!(conn_handle = mysqlnd_connection_init(client_api_flags, FALSE, NULL))) {
+ /* OOM */
+ DBG_RETURN(NULL);
+ }
+ }
+
+ ret = conn_handle->m->connect(conn_handle, hostname, username, password, database, port, socket_or_pipe, mysql_flags);
+
+ if (ret == FAIL) {
+ if (self_alloced) {
+ /*
+ We have alloced, thus there are no references to this
+ object - we are free to kill it!
+ */
+ conn_handle->m->dtor(conn_handle);
+ }
+ DBG_RETURN(NULL);
+ }
+ DBG_RETURN(conn_handle);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_connection_init */
PHPAPI MYSQLND *
-mysqlnd_init(unsigned int flags, zend_bool persistent)
+mysqlnd_connection_init(const size_t client_flags, const zend_bool persistent, MYSQLND_CLASS_METHODS_TYPE(mysqlnd_object_factory) *object_factory)
{
+ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_object_factory) *factory = object_factory? object_factory : &MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_object_factory);
MYSQLND * ret;
- DBG_ENTER("mysqlnd_init");
- ret = MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_object_factory).get_connection(persistent);
+ DBG_ENTER("mysqlnd_connection_init");
+ ret = factory->get_connection(factory, persistent);
if (ret && ret->data) {
- ret->data->m->negotiate_client_api_capabilities(ret->data, flags);
+ ret->data->m->negotiate_client_api_capabilities(ret->data, client_flags);
}
DBG_RETURN(ret);
}
/* }}} */
+
/*
* Local variables:
* tab-width: 4
diff --git a/ext/mysqlnd/mysqlnd_connection.h b/ext/mysqlnd/mysqlnd_connection.h
new file mode 100644
index 0000000000..6d0efb99ab
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_connection.h
@@ -0,0 +1,87 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 7 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2017 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Andrey Hristov <andrey@php.net> |
+ | Ulf Wendel <uw@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef MYSQLND_CONNECTION_H
+#define MYSQLND_CONNECTION_H
+
+PHPAPI extern const char * const mysqlnd_out_of_sync;
+PHPAPI extern const char * const mysqlnd_server_gone;
+PHPAPI extern const char * const mysqlnd_out_of_memory;
+
+
+void mysqlnd_upsert_status_init(MYSQLND_UPSERT_STATUS * const upsert_status);
+
+#define UPSERT_STATUS_RESET(status) (status)->m->reset((status))
+
+#define UPSERT_STATUS_GET_SERVER_STATUS(status) (status)->server_status
+#define UPSERT_STATUS_SET_SERVER_STATUS(status, server_st) (status)->server_status = (server_st)
+
+#define UPSERT_STATUS_GET_WARNINGS(status) (status)->warning_count
+#define UPSERT_STATUS_SET_WARNINGS(status, warnings) (status)->warning_count = (warnings)
+
+#define UPSERT_STATUS_GET_AFFECTED_ROWS(status) (status)->affected_rows
+#define UPSERT_STATUS_SET_AFFECTED_ROWS(status, rows) (status)->affected_rows = (rows)
+#define UPSERT_STATUS_SET_AFFECTED_ROWS_TO_ERROR(status) (status)->m->set_affected_rows_to_error((status))
+
+#define UPSERT_STATUS_GET_LAST_INSERT_ID(status) (status)->last_insert_id
+#define UPSERT_STATUS_SET_LAST_INSERT_ID(status, id) (status)->last_insert_id = (id)
+
+
+/* Error handling */
+#define SET_NEW_MESSAGE(buf, buf_len, message, len, persistent) \
+ {\
+ if ((buf)) { \
+ mnd_pefree((buf), (persistent)); \
+ } \
+ if ((message)) { \
+ (buf) = mnd_pestrndup((message), (len), (persistent)); \
+ } else { \
+ (buf) = NULL; \
+ } \
+ (buf_len) = (len); \
+ }
+
+#define SET_EMPTY_MESSAGE(buf, buf_len, persistent) \
+ {\
+ if ((buf)) { \
+ mnd_pefree((buf), (persistent)); \
+ (buf) = NULL; \
+ } \
+ (buf_len) = 0; \
+ }
+
+
+PHPAPI enum_func_status mysqlnd_error_info_init(MYSQLND_ERROR_INFO * const info, const zend_bool persistent);
+PHPAPI void mysqlnd_error_info_free_contents(MYSQLND_ERROR_INFO * const info);
+
+#define GET_CONNECTION_STATE(state_struct) (state_struct)->m->get((state_struct))
+#define SET_CONNECTION_STATE(state_struct, s) (state_struct)->m->set((state_struct), (s))
+
+PHPAPI void mysqlnd_connection_state_init(struct st_mysqlnd_connection_state * const state);
+
+#endif /* MYSQLND_CONNECTION_H */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/mysqlnd/mysqlnd_debug.c b/ext/mysqlnd/mysqlnd_debug.c
index 9eca4de923..a9372af888 100644
--- a/ext/mysqlnd/mysqlnd_debug.c
+++ b/ext/mysqlnd/mysqlnd_debug.c
@@ -14,7 +14,6 @@
+----------------------------------------------------------------------+
| Authors: Andrey Hristov <andrey@php.net> |
| Ulf Wendel <uw@php.net> |
- | Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
diff --git a/ext/mysqlnd/mysqlnd_debug.h b/ext/mysqlnd/mysqlnd_debug.h
index 1eeab270ec..fa242cc929 100644
--- a/ext/mysqlnd/mysqlnd_debug.h
+++ b/ext/mysqlnd/mysqlnd_debug.h
@@ -14,7 +14,6 @@
+----------------------------------------------------------------------+
| Authors: Andrey Hristov <andrey@php.net> |
| Ulf Wendel <uw@php.net> |
- | Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
@@ -68,6 +67,17 @@ void mysqlnd_debug_trace_plugin_register(void);
PHPAPI MYSQLND_DEBUG * mysqlnd_debug_init(const char * skip_functions[]);
+#define MYSQLND_DEBUG_DUMP_TIME 1
+#define MYSQLND_DEBUG_DUMP_TRACE 2
+#define MYSQLND_DEBUG_DUMP_PID 4
+#define MYSQLND_DEBUG_DUMP_LINE 8
+#define MYSQLND_DEBUG_DUMP_FILE 16
+#define MYSQLND_DEBUG_DUMP_LEVEL 32
+#define MYSQLND_DEBUG_APPEND 64
+#define MYSQLND_DEBUG_FLUSH 128
+#define MYSQLND_DEBUG_TRACE_MEMORY_CALLS 256
+#define MYSQLND_DEBUG_PROFILE_CALLS 512
+
#if defined(__GNUC__) || defined(PHP_WIN32)
#ifdef PHP_WIN32
@@ -120,8 +130,9 @@ PHPAPI MYSQLND_DEBUG * mysqlnd_debug_init(const char * skip_functions[]);
if ((dbg_obj2)) { \
dbg_skip_trace |= !(dbg_obj2)->m->func_enter((dbg_obj2), __LINE__, __FILE__, func_name, strlen(func_name)); \
} \
- if (dbg_skip_trace) \
+ if (dbg_skip_trace) { \
/* EMPTY */ ; /* shut compiler's mouth */ \
+ } \
do { \
if (((dbg_obj1) && (dbg_obj1)->flags & MYSQLND_DEBUG_PROFILE_CALLS) || \
((dbg_obj2) && (dbg_obj2)->flags & MYSQLND_DEBUG_PROFILE_CALLS)) \
diff --git a/ext/mysqlnd/mysqlnd_driver.c b/ext/mysqlnd/mysqlnd_driver.c
index 226aea595d..6db86c96b3 100644
--- a/ext/mysqlnd/mysqlnd_driver.c
+++ b/ext/mysqlnd/mysqlnd_driver.c
@@ -14,17 +14,19 @@
+----------------------------------------------------------------------+
| Authors: Andrey Hristov <andrey@php.net> |
| Ulf Wendel <uw@php.net> |
- | Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
#include "php.h"
#include "mysqlnd.h"
+#include "mysqlnd_vio.h"
+#include "mysqlnd_protocol_frame_codec.h"
#include "mysqlnd_wireprotocol.h"
+#include "mysqlnd_connection.h"
+#include "mysqlnd_ps.h"
+#include "mysqlnd_plugin.h"
#include "mysqlnd_priv.h"
-#include "mysqlnd_result.h"
#include "mysqlnd_statistics.h"
-#include "mysqlnd_charset.h"
#include "mysqlnd_debug.h"
#include "mysqlnd_reverse_api.h"
#include "mysqlnd_ext_plugin.h"
@@ -92,25 +94,9 @@ PHPAPI void mysqlnd_library_init(void)
/* }}} */
-
-/* {{{ mysqlnd_error_list_pdtor */
-static void
-mysqlnd_error_list_pdtor(void * pDest)
-{
- MYSQLND_ERROR_LIST_ELEMENT * element = (MYSQLND_ERROR_LIST_ELEMENT *) pDest;
-
- DBG_ENTER("mysqlnd_error_list_pdtor");
- if (element->error) {
- mnd_pefree(element->error, TRUE);
- }
- DBG_VOID_RETURN;
-}
-/* }}} */
-
-
/* {{{ mysqlnd_object_factory::get_connection */
static MYSQLND *
-MYSQLND_METHOD(mysqlnd_object_factory, get_connection)(zend_bool persistent)
+MYSQLND_METHOD(mysqlnd_object_factory, get_connection)(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_object_factory) *factory, const zend_bool persistent)
{
size_t alloc_size_ret = sizeof(MYSQLND) + mysqlnd_plugin_count() * sizeof(void *);
size_t alloc_size_ret_data = sizeof(MYSQLND_CONN_DATA) + mysqlnd_plugin_count() * sizeof(void *);
@@ -132,26 +118,36 @@ MYSQLND_METHOD(mysqlnd_object_factory, get_connection)(zend_bool persistent)
new_object->m = mysqlnd_conn_get_methods();
data = new_object->data;
- data->error_info = &(data->error_info_impl);
+ if (FAIL == mysqlnd_error_info_init(&data->error_info_impl, persistent)) {
+ new_object->m->dtor(new_object);
+ DBG_RETURN(NULL);
+ }
+ data->error_info = &data->error_info_impl;
+
data->options = &(data->options_impl);
+
+ mysqlnd_upsert_status_init(&data->upsert_status_impl);
data->upsert_status = &(data->upsert_status_impl);
+ UPSERT_STATUS_SET_AFFECTED_ROWS_TO_ERROR(data->upsert_status);
data->persistent = persistent;
data->m = mysqlnd_conn_data_get_methods();
- CONN_SET_STATE(data, CONN_ALLOCED);
+ data->object_factory = *factory;
+
+ mysqlnd_connection_state_init(&data->state);
+
data->m->get_reference(data);
- if (PASS != data->m->init(data)) {
- new_object->m->dtor(new_object);
- DBG_RETURN(NULL);
- }
+ mysqlnd_stats_init(&data->stats, STAT_LAST, persistent);
- data->error_info->error_list = mnd_pecalloc(1, sizeof(zend_llist), persistent);
- if (!data->error_info->error_list) {
+ data->protocol_frame_codec = mysqlnd_pfc_init(persistent, factory, data->stats, data->error_info);
+ data->vio = mysqlnd_vio_init(persistent, factory, data->stats, data->error_info);
+ data->payload_decoder_factory = mysqlnd_protocol_payload_decoder_factory_init(data, persistent);
+ data->command_factory = mysqlnd_command_factory_get();
+
+ if (!data->protocol_frame_codec || !data->vio || !data->payload_decoder_factory || !data->command_factory) {
new_object->m->dtor(new_object);
DBG_RETURN(NULL);
- } else {
- zend_llist_init(data->error_info->error_list, sizeof(MYSQLND_ERROR_LIST_ELEMENT), (llist_dtor_func_t)mysqlnd_error_list_pdtor, persistent);
}
DBG_RETURN(new_object);
@@ -190,7 +186,7 @@ MYSQLND_METHOD(mysqlnd_object_factory, clone_connection_object)(MYSQLND * to_be_
/* {{{ mysqlnd_object_factory::get_prepared_statement */
static MYSQLND_STMT *
-MYSQLND_METHOD(mysqlnd_object_factory, get_prepared_statement)(MYSQLND_CONN_DATA * const conn)
+MYSQLND_METHOD(mysqlnd_object_factory, get_prepared_statement)(MYSQLND_CONN_DATA * const conn, const zend_bool persistent)
{
size_t alloc_size = sizeof(MYSQLND_STMT) + mysqlnd_plugin_count() * sizeof(void *);
MYSQLND_STMT * ret = mnd_pecalloc(1, alloc_size, conn->persistent);
@@ -204,13 +200,19 @@ MYSQLND_METHOD(mysqlnd_object_factory, get_prepared_statement)(MYSQLND_CONN_DATA
ret->m = mysqlnd_stmt_get_methods();
ret->persistent = conn->persistent;
- stmt = ret->data = mnd_pecalloc(1, sizeof(MYSQLND_STMT_DATA), conn->persistent);
+ stmt = ret->data = mnd_pecalloc(1, sizeof(MYSQLND_STMT_DATA), persistent);
DBG_INF_FMT("stmt=%p", stmt);
if (!stmt) {
break;
}
- stmt->persistent = conn->persistent;
- stmt->error_info = &(stmt->error_info_impl);
+ stmt->persistent = persistent;
+
+ if (FAIL == mysqlnd_error_info_init(&stmt->error_info_impl, persistent)) {
+ break;
+ }
+ stmt->error_info = &stmt->error_info_impl;
+
+ mysqlnd_upsert_status_init(&stmt->upsert_status_impl);
stmt->upsert_status = &(stmt->upsert_status_impl);
stmt->state = MYSQLND_STMT_INITTED;
stmt->execute_cmd_buffer.length = 4096;
@@ -220,23 +222,18 @@ MYSQLND_METHOD(mysqlnd_object_factory, get_prepared_statement)(MYSQLND_CONN_DATA
}
stmt->prefetch_rows = MYSQLND_DEFAULT_PREFETCH_ROWS;
+
/*
Mark that we reference the connection, thus it won't be
be destructed till there is open statements. The last statement
or normal query result will close it then.
*/
stmt->conn = conn->m->get_reference(conn);
- stmt->error_info->error_list = mnd_pecalloc(1, sizeof(zend_llist), ret->persistent);
- if (!stmt->error_info->error_list) {
- break;
- }
-
- zend_llist_init(stmt->error_info->error_list, sizeof(MYSQLND_ERROR_LIST_ELEMENT), (llist_dtor_func_t) mysqlnd_error_list_pdtor, conn->persistent);
DBG_RETURN(ret);
} while (0);
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
if (ret) {
ret->m->dtor(ret, TRUE);
ret = NULL;
@@ -246,53 +243,89 @@ MYSQLND_METHOD(mysqlnd_object_factory, get_prepared_statement)(MYSQLND_CONN_DATA
/* }}} */
-/* {{{ mysqlnd_object_factory::get_io_channel */
-PHPAPI MYSQLND_NET *
-MYSQLND_METHOD(mysqlnd_object_factory, get_io_channel)(zend_bool persistent, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info)
+/* {{{ mysqlnd_object_factory::get_pfc */
+static MYSQLND_PFC *
+MYSQLND_METHOD(mysqlnd_object_factory, get_pfc)(const zend_bool persistent, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info)
+{
+ size_t pfc_alloc_size = sizeof(MYSQLND_PFC) + mysqlnd_plugin_count() * sizeof(void *);
+ size_t pfc_data_alloc_size = sizeof(MYSQLND_PFC_DATA) + mysqlnd_plugin_count() * sizeof(void *);
+ MYSQLND_PFC * pfc = mnd_pecalloc(1, pfc_alloc_size, persistent);
+ MYSQLND_PFC_DATA * pfc_data = mnd_pecalloc(1, pfc_data_alloc_size, persistent);
+
+ DBG_ENTER("mysqlnd_object_factory::get_pfc");
+ DBG_INF_FMT("persistent=%u", persistent);
+ if (pfc && pfc_data) {
+ pfc->data = pfc_data;
+ pfc->persistent = pfc->data->persistent = persistent;
+ pfc->data->m = *mysqlnd_pfc_get_methods();
+
+ if (PASS != pfc->data->m.init(pfc, stats, error_info)) {
+ pfc->data->m.dtor(pfc, stats, error_info);
+ pfc = NULL;
+ }
+ } else {
+ if (pfc_data) {
+ mnd_pefree(pfc_data, persistent);
+ pfc_data = NULL;
+ }
+ if (pfc) {
+ mnd_pefree(pfc, persistent);
+ pfc = NULL;
+ }
+ }
+ DBG_RETURN(pfc);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_object_factory::get_vio */
+static MYSQLND_VIO *
+MYSQLND_METHOD(mysqlnd_object_factory, get_vio)(const zend_bool persistent, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info)
{
- size_t net_alloc_size = sizeof(MYSQLND_NET) + mysqlnd_plugin_count() * sizeof(void *);
- size_t net_data_alloc_size = sizeof(MYSQLND_NET_DATA) + mysqlnd_plugin_count() * sizeof(void *);
- MYSQLND_NET * net = mnd_pecalloc(1, net_alloc_size, persistent);
- MYSQLND_NET_DATA * net_data = mnd_pecalloc(1, net_data_alloc_size, persistent);
+ size_t vio_alloc_size = sizeof(MYSQLND_VIO) + mysqlnd_plugin_count() * sizeof(void *);
+ size_t vio_data_alloc_size = sizeof(MYSQLND_VIO_DATA) + mysqlnd_plugin_count() * sizeof(void *);
+ MYSQLND_VIO * vio = mnd_pecalloc(1, vio_alloc_size, persistent);
+ MYSQLND_VIO_DATA * vio_data = mnd_pecalloc(1, vio_data_alloc_size, persistent);
- DBG_ENTER("mysqlnd_object_factory::get_io_channel");
+ DBG_ENTER("mysqlnd_object_factory::get_vio");
DBG_INF_FMT("persistent=%u", persistent);
- if (net && net_data) {
- net->data = net_data;
- net->persistent = net->data->persistent = persistent;
- net->data->m = *mysqlnd_net_get_methods();
-
- if (PASS != net->data->m.init(net, stats, error_info)) {
- net->data->m.dtor(net, stats, error_info);
- net = NULL;
+ if (vio && vio_data) {
+ vio->data = vio_data;
+ vio->persistent = vio->data->persistent = persistent;
+ vio->data->m = *mysqlnd_vio_get_methods();
+
+ if (PASS != vio->data->m.init(vio, stats, error_info)) {
+ vio->data->m.dtor(vio, stats, error_info);
+ vio = NULL;
}
} else {
- if (net_data) {
- mnd_pefree(net_data, persistent);
- net_data = NULL;
+ if (vio_data) {
+ mnd_pefree(vio_data, persistent);
+ vio_data = NULL;
}
- if (net) {
- mnd_pefree(net, persistent);
- net = NULL;
+ if (vio) {
+ mnd_pefree(vio, persistent);
+ vio = NULL;
}
}
- DBG_RETURN(net);
+ DBG_RETURN(vio);
}
/* }}} */
-/* {{{ mysqlnd_object_factory::get_protocol_decoder */
-static MYSQLND_PROTOCOL *
-MYSQLND_METHOD(mysqlnd_object_factory, get_protocol_decoder)(zend_bool persistent)
+/* {{{ mysqlnd_object_factory::get_protocol_payload_decoder_factory */
+static MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY *
+MYSQLND_METHOD(mysqlnd_object_factory, get_protocol_payload_decoder_factory)(MYSQLND_CONN_DATA * conn, const zend_bool persistent)
{
- size_t alloc_size = sizeof(MYSQLND_PROTOCOL) + mysqlnd_plugin_count() * sizeof(void *);
- MYSQLND_PROTOCOL *ret = mnd_pecalloc(1, alloc_size, persistent);
+ size_t alloc_size = sizeof(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY) + mysqlnd_plugin_count() * sizeof(void *);
+ MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY *ret = mnd_pecalloc(1, alloc_size, persistent);
- DBG_ENTER("mysqlnd_object_factory::get_protocol_decoder");
+ DBG_ENTER("mysqlnd_object_factory::get_protocol_payload_decoder_factory");
DBG_INF_FMT("persistent=%u", persistent);
if (ret) {
ret->persistent = persistent;
- ret->m = mysqlnd_mysqlnd_protocol_methods;
+ ret->conn = conn;
+ ret->m = MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_protocol_payload_decoder_factory);
}
DBG_RETURN(ret);
@@ -304,8 +337,9 @@ PHPAPI MYSQLND_CLASS_METHODS_START(mysqlnd_object_factory)
MYSQLND_METHOD(mysqlnd_object_factory, get_connection),
MYSQLND_METHOD(mysqlnd_object_factory, clone_connection_object),
MYSQLND_METHOD(mysqlnd_object_factory, get_prepared_statement),
- MYSQLND_METHOD(mysqlnd_object_factory, get_io_channel),
- MYSQLND_METHOD(mysqlnd_object_factory, get_protocol_decoder)
+ MYSQLND_METHOD(mysqlnd_object_factory, get_pfc),
+ MYSQLND_METHOD(mysqlnd_object_factory, get_vio),
+ MYSQLND_METHOD(mysqlnd_object_factory, get_protocol_payload_decoder_factory)
MYSQLND_CLASS_METHODS_END;
/*
diff --git a/ext/mysqlnd/mysqlnd_enum_n_def.h b/ext/mysqlnd/mysqlnd_enum_n_def.h
index 69d3f33830..8b10f6a711 100644
--- a/ext/mysqlnd/mysqlnd_enum_n_def.h
+++ b/ext/mysqlnd/mysqlnd_enum_n_def.h
@@ -48,6 +48,9 @@
#define MYSQLND_NET_CMD_BUFFER_MIN_SIZE 4096
#define MYSQLND_NET_CMD_BUFFER_MIN_SIZE_STR "4096"
+#define MYSQLND_STMT_ID_LENGTH 4
+
+
#define SERVER_STATUS_IN_TRANS 1 /* Transaction has started */
#define SERVER_STATUS_AUTOCOMMIT 2 /* Server in auto_commit mode */
#define SERVER_MORE_RESULTS_EXISTS 8 /* Multi query - next query exists */
@@ -111,7 +114,29 @@
CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION | \
CLIENT_MULTI_RESULTS | CLIENT_LOCAL_FILES | CLIENT_PLUGIN_AUTH)
-#define MYSQLND_NET_FLAG_USE_COMPRESSION 1
+#define MYSQLND_PROTOCOL_FLAG_USE_COMPRESSION 1
+
+
+/* Client Error codes */
+#define CR_UNKNOWN_ERROR 2000
+#define CR_CONNECTION_ERROR 2002
+#define CR_SERVER_GONE_ERROR 2006
+#define CR_OUT_OF_MEMORY 2008
+#define CR_SERVER_LOST 2013
+#define CR_COMMANDS_OUT_OF_SYNC 2014
+#define CR_CANT_FIND_CHARSET 2019
+#define CR_MALFORMED_PACKET 2027
+#define CR_NOT_IMPLEMENTED 2054
+#define CR_NO_PREPARE_STMT 2030
+#define CR_PARAMS_NOT_BOUND 2031
+#define CR_INVALID_PARAMETER_NO 2034
+#define CR_INVALID_BUFFER_USE 2035
+
+#define MYSQLND_EE_FILENOTFOUND 7890
+
+#define UNKNOWN_SQLSTATE "HY000"
+
+#define MAX_CHARSET_LEN 32
#define TRANS_START_NO_OPT 0
@@ -185,7 +210,7 @@ typedef enum mysqlnd_parse_exec_response_type
MYSQLND_PARSE_EXEC_RESPONSE_EXPLICIT,
} enum_mysqlnd_parse_exec_response_type;
-typedef enum mysqlnd_option
+typedef enum mysqlnd_client_option
{
MYSQL_OPT_CONNECT_TIMEOUT,
MYSQL_OPT_COMPRESS,
@@ -231,9 +256,9 @@ typedef enum mysqlnd_option
MYSQLND_OPT_SSL_PASSPHRASE = 209,
MYSQLND_OPT_MAX_ALLOWED_PACKET = 210,
MYSQLND_OPT_AUTH_PROTOCOL = 211
-} enum_mysqlnd_option;
+} enum_mysqlnd_client_option;
-typedef enum mysqlnd_protocol_type
+typedef enum mysqlnd_session_protocol_type
{
MYSQL_PROTOCOL_DEFAULT = 0,
MYSQL_PROTOCOL_TCP, /* all, supported */
@@ -241,7 +266,7 @@ typedef enum mysqlnd_protocol_type
MYSQL_PROTOCOL_PIPE, /* win32, not-supported */
MYSQL_PROTOCOL_MEMORY, /* win32, not-supported */
MYSQL_PROTOCOL_LAST
-} enum_mysqlnd_protocol_type;
+} enum_mysqlnd_session_protocol_type;
typedef enum mysqlnd_field_types
{
@@ -493,6 +518,8 @@ typedef enum mysqlnd_collected_stats
STAT_MEM_STRNDUP_COUNT,
STAT_MEM_ESTRDUP_COUNT,
STAT_MEM_STRDUP_COUNT,
+ STAT_MEM_EDUP_COUNT,
+ STAT_MEM_DUP_COUNT,
STAT_TEXT_TYPE_FETCHED_NULL,
STAT_TEXT_TYPE_FETCHED_BIT,
STAT_TEXT_TYPE_FETCHED_INT8,
@@ -634,7 +661,11 @@ enum php_mysqlnd_server_command
COM_BINLOG_DUMP_GTID = 30,
COM_RESET_CONNECTION = 31,
COM_STMT_EXECUTE_BATCH = 32,
- COM_END
+ COM_END,
+ /* Here follow own, non-protocol, commands */
+ COM_REAP_RESULT=240, /* own command */
+ COM_ENABLE_SSL, /* own command */
+ COM_HANDSHAKE, /* own command */
};
diff --git a/ext/mysqlnd/mysqlnd_ext_plugin.c b/ext/mysqlnd/mysqlnd_ext_plugin.c
index f3231f5d14..56eb1b3024 100644
--- a/ext/mysqlnd/mysqlnd_ext_plugin.c
+++ b/ext/mysqlnd/mysqlnd_ext_plugin.c
@@ -13,8 +13,8 @@
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Andrey Hristov <andrey@php.net> |
+ | Johannes Schlter <johannes@php.net> |
| Ulf Wendel <uw@php.net> |
- | Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
@@ -23,16 +23,18 @@
#include "mysqlnd_priv.h"
#include "mysqlnd_result.h"
#include "mysqlnd_debug.h"
+#include "mysqlnd_commands.h"
+#include "mysqlnd_ext_plugin.h"
static struct st_mysqlnd_conn_methods * mysqlnd_conn_methods;
static struct st_mysqlnd_conn_data_methods * mysqlnd_conn_data_methods;
static struct st_mysqlnd_stmt_methods * mysqlnd_stmt_methods;
-/* {{{ _mysqlnd_plugin_get_plugin_connection_data */
-PHPAPI void **
-_mysqlnd_plugin_get_plugin_connection_data(const MYSQLND * conn, unsigned int plugin_id)
+/* {{{ mysqlnd_plugin__get_plugin_connection_data */
+static void **
+mysqlnd_plugin__get_plugin_connection_data(const MYSQLND * conn, const unsigned int plugin_id)
{
- DBG_ENTER("_mysqlnd_plugin_get_plugin_connection_data");
+ DBG_ENTER("mysqlnd_plugin__get_plugin_connection_data");
DBG_INF_FMT("plugin_id=%u", plugin_id);
if (!conn || plugin_id >= mysqlnd_plugin_count()) {
return NULL;
@@ -42,11 +44,11 @@ _mysqlnd_plugin_get_plugin_connection_data(const MYSQLND * conn, unsigned int pl
/* }}} */
-/* {{{ _mysqlnd_plugin_get_plugin_connection_data_data */
-PHPAPI void **
-_mysqlnd_plugin_get_plugin_connection_data_data(const MYSQLND_CONN_DATA * conn, unsigned int plugin_id)
+/* {{{ mysqlnd_plugin__get_plugin_connection_data_data */
+static void **
+mysqlnd_plugin__get_plugin_connection_data_data(const MYSQLND_CONN_DATA * conn, const unsigned int plugin_id)
{
- DBG_ENTER("_mysqlnd_plugin_get_plugin_connection_data_data");
+ DBG_ENTER("mysqlnd_plugin__get_plugin_connection_data_data");
DBG_INF_FMT("plugin_id=%u", plugin_id);
if (!conn || plugin_id >= mysqlnd_plugin_count()) {
return NULL;
@@ -56,10 +58,11 @@ _mysqlnd_plugin_get_plugin_connection_data_data(const MYSQLND_CONN_DATA * conn,
/* }}} */
-/* {{{ _mysqlnd_plugin_get_plugin_result_data */
-PHPAPI void ** _mysqlnd_plugin_get_plugin_result_data(const MYSQLND_RES * result, unsigned int plugin_id)
+/* {{{ mysqlnd_plugin__get_plugin_result_data */
+static void **
+mysqlnd_plugin__get_plugin_result_data(const MYSQLND_RES * result, const unsigned int plugin_id)
{
- DBG_ENTER("_mysqlnd_plugin_get_plugin_result_data");
+ DBG_ENTER("mysqlnd_plugin__get_plugin_result_data");
DBG_INF_FMT("plugin_id=%u", plugin_id);
if (!result || plugin_id >= mysqlnd_plugin_count()) {
return NULL;
@@ -69,10 +72,11 @@ PHPAPI void ** _mysqlnd_plugin_get_plugin_result_data(const MYSQLND_RES * result
/* }}} */
-/* {{{ _mysqlnd_plugin_get_plugin_result_unbuffered_data */
-PHPAPI void ** _mysqlnd_plugin_get_plugin_result_unbuffered_data(const MYSQLND_RES_UNBUFFERED * result, unsigned int plugin_id)
+/* {{{ _mysqlnd_plugin__get_plugin_result_unbuffered_data */
+static void **
+mysqlnd_plugin__get_plugin_result_unbuffered_data(const MYSQLND_RES_UNBUFFERED * result, const unsigned int plugin_id)
{
- DBG_ENTER("_mysqlnd_plugin_get_plugin_result_data");
+ DBG_ENTER("mysqlnd_plugin__get_plugin_result_data");
DBG_INF_FMT("plugin_id=%u", plugin_id);
if (!result || plugin_id >= mysqlnd_plugin_count()) {
return NULL;
@@ -82,10 +86,11 @@ PHPAPI void ** _mysqlnd_plugin_get_plugin_result_unbuffered_data(const MYSQLND_R
/* }}} */
-/* {{{ _mysqlnd_plugin_get_plugin_result_buffered_data */
-PHPAPI void ** _mysqlnd_plugin_get_plugin_result_buffered_data_zval(const MYSQLND_RES_BUFFERED_ZVAL * result, unsigned int plugin_id)
+/* {{{ _mysqlnd_plugin__get_plugin_result_buffered_data */
+static void **
+mysqlnd_plugin__get_plugin_result_buffered_data_zval(const MYSQLND_RES_BUFFERED_ZVAL * result, const unsigned int plugin_id)
{
- DBG_ENTER("_mysqlnd_plugin_get_plugin_result_data");
+ DBG_ENTER("_mysqlnd_plugin__get_plugin_result_data");
DBG_INF_FMT("plugin_id=%u", plugin_id);
if (!result || plugin_id >= mysqlnd_plugin_count()) {
return NULL;
@@ -94,10 +99,11 @@ PHPAPI void ** _mysqlnd_plugin_get_plugin_result_buffered_data_zval(const MYSQLN
}
/* }}} */
-/* {{{ _mysqlnd_plugin_get_plugin_result_buffered_data */
-PHPAPI void ** _mysqlnd_plugin_get_plugin_result_buffered_data_c(const MYSQLND_RES_BUFFERED_C * result, unsigned int plugin_id)
+/* {{{ mysqlnd_plugin__get_plugin_result_buffered_data */
+static void **
+mysqlnd_plugin__get_plugin_result_buffered_data_c(const MYSQLND_RES_BUFFERED_C * result, const unsigned int plugin_id)
{
- DBG_ENTER("_mysqlnd_plugin_get_plugin_result_data");
+ DBG_ENTER("mysqlnd_plugin__get_plugin_result_data");
DBG_INF_FMT("plugin_id=%u", plugin_id);
if (!result || plugin_id >= mysqlnd_plugin_count()) {
return NULL;
@@ -107,24 +113,25 @@ PHPAPI void ** _mysqlnd_plugin_get_plugin_result_buffered_data_c(const MYSQLND_R
/* }}} */
-/* {{{ _mysqlnd_plugin_get_plugin_protocol_data */
-PHPAPI void **
-_mysqlnd_plugin_get_plugin_protocol_data(const MYSQLND_PROTOCOL * protocol, unsigned int plugin_id)
+/* {{{ mysqlnd_plugin__get_plugin_protocol_data */
+static void **
+mysqlnd_plugin__get_plugin_protocol_data(const MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * factory, const unsigned int plugin_id)
{
- DBG_ENTER("_mysqlnd_plugin_get_plugin_protocol_data");
+ DBG_ENTER("mysqlnd_plugin__get_plugin_protocol_data");
DBG_INF_FMT("plugin_id=%u", plugin_id);
- if (!protocol || plugin_id >= mysqlnd_plugin_count()) {
+ if (!factory || plugin_id >= mysqlnd_plugin_count()) {
return NULL;
}
- DBG_RETURN((void *)((char *)protocol + sizeof(MYSQLND_PROTOCOL) + plugin_id * sizeof(void *)));
+ DBG_RETURN((void *)((char *)factory + sizeof(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY) + plugin_id * sizeof(void *)));
}
/* }}} */
-/* {{{ _mysqlnd_plugin_get_plugin_stmt_data */
-PHPAPI void ** _mysqlnd_plugin_get_plugin_stmt_data(const MYSQLND_STMT * stmt, unsigned int plugin_id)
+/* {{{ mysqlnd_plugin__get_plugin_stmt_data */
+static void **
+mysqlnd_plugin__get_plugin_stmt_data(const MYSQLND_STMT * stmt, const unsigned int plugin_id)
{
- DBG_ENTER("_mysqlnd_plugin_get_plugin_stmt_data");
+ DBG_ENTER("mysqlnd_plugin__get_plugin_stmt_data");
DBG_INF_FMT("plugin_id=%u", plugin_id);
if (!stmt || plugin_id >= mysqlnd_plugin_count()) {
return NULL;
@@ -134,160 +141,314 @@ PHPAPI void ** _mysqlnd_plugin_get_plugin_stmt_data(const MYSQLND_STMT * stmt, u
/* }}} */
-/* {{{ _mysqlnd_plugin_get_plugin_net_data */
-PHPAPI void ** _mysqlnd_plugin_get_plugin_net_data(const MYSQLND_NET * net, unsigned int plugin_id)
+/* {{{ mysqlnd_plugin__get_plugin_pfc_data */
+static void **
+mysqlnd_plugin__get_plugin_pfc_data(const MYSQLND_PFC * pfc, const unsigned int plugin_id)
{
- DBG_ENTER("_mysqlnd_plugin_get_plugin_net_data");
+ DBG_ENTER("mysqlnd_plugin__get_plugin_pfc_data");
DBG_INF_FMT("plugin_id=%u", plugin_id);
- if (!net || plugin_id >= mysqlnd_plugin_count()) {
+ if (!pfc || plugin_id >= mysqlnd_plugin_count()) {
return NULL;
}
- DBG_RETURN((void *)((char *)net + sizeof(MYSQLND_NET) + plugin_id * sizeof(void *)));
+ DBG_RETURN((void *)((char *)pfc + sizeof(MYSQLND_PFC) + plugin_id * sizeof(void *)));
}
/* }}} */
+/* {{{ _mysqlnd_plugin__get_plugin_vio_data */
+static void **
+mysqlnd_plugin__get_plugin_vio_data(const MYSQLND_VIO * vio, const unsigned int plugin_id)
+{
+ DBG_ENTER("_mysqlnd_plugin__get_plugin_vio_data");
+ DBG_INF_FMT("plugin_id=%u", plugin_id);
+ if (!vio || plugin_id >= mysqlnd_plugin_count()) {
+ return NULL;
+ }
+ DBG_RETURN((void *)((char *)vio + sizeof(MYSQLND_VIO) + plugin_id * sizeof(void *)));
+}
+/* }}} */
-/* {{{ mysqlnd_conn_get_methods */
-PHPAPI struct st_mysqlnd_conn_methods *
-mysqlnd_conn_get_methods()
+struct st_mysqlnd_plugin__plugin_area_getters mysqlnd_plugin_area_getters =
{
- return mysqlnd_conn_methods;
+ mysqlnd_plugin__get_plugin_connection_data,
+ mysqlnd_plugin__get_plugin_connection_data_data,
+ mysqlnd_plugin__get_plugin_result_data,
+ mysqlnd_plugin__get_plugin_result_unbuffered_data,
+ mysqlnd_plugin__get_plugin_result_buffered_data_zval,
+ mysqlnd_plugin__get_plugin_result_buffered_data_c,
+ mysqlnd_plugin__get_plugin_stmt_data,
+ mysqlnd_plugin__get_plugin_protocol_data,
+ mysqlnd_plugin__get_plugin_pfc_data,
+ mysqlnd_plugin__get_plugin_vio_data,
+};
+
+
+
+/* {{{ _mysqlnd_object_factory_get_methods */
+static MYSQLND_CLASS_METHODS_TYPE(mysqlnd_object_factory) *
+_mysqlnd_object_factory_get_methods()
+{
+ return &MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_object_factory);
}
/* }}} */
/* {{{ mysqlnd_conn_set_methods */
-PHPAPI void mysqlnd_conn_set_methods(struct st_mysqlnd_conn_methods *methods)
+static void
+_mysqlnd_object_factory_set_methods(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_object_factory) *methods)
+{
+ MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_object_factory) = *methods;
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_conn_get_methods */
+static MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn) *
+_mysqlnd_conn_get_methods()
+{
+ return mysqlnd_conn_methods;
+}
+/* }}} */
+
+/* {{{ _mysqlnd_conn_set_methods */
+static void
+_mysqlnd_conn_set_methods(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn) *methods)
{
mysqlnd_conn_methods = methods;
}
/* }}} */
-/* {{{ mysqlnd_conn_get_methods */
-PHPAPI struct st_mysqlnd_conn_data_methods *
-mysqlnd_conn_data_get_methods()
+/* {{{ _mysqlnd_conn_data_get_methods */
+static MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data) *
+_mysqlnd_conn_data_get_methods()
{
return mysqlnd_conn_data_methods;
}
/* }}} */
-/* {{{ mysqlnd_conn_set_methods */
-PHPAPI void mysqlnd_conn_data_set_methods(struct st_mysqlnd_conn_data_methods * methods)
+/* {{{ _mysqlnd_conn_data_set_methods */
+static void
+_mysqlnd_conn_data_set_methods(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data) * methods)
{
mysqlnd_conn_data_methods = methods;
}
/* }}} */
-/* {{{ mysqlnd_result_get_methods */
-PHPAPI struct st_mysqlnd_res_methods *
-mysqlnd_result_get_methods()
+/* {{{ _mysqlnd_result_get_methods */
+static MYSQLND_CLASS_METHODS_TYPE(mysqlnd_res) *
+_mysqlnd_result_get_methods()
{
return &MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_res);
}
/* }}} */
-/* {{{ mysqlnd_result_set_methods */
-PHPAPI void
-mysqlnd_result_set_methods(struct st_mysqlnd_res_methods * methods)
+/* {{{ _mysqlnd_result_set_methods */
+static void
+_mysqlnd_result_set_methods(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_res) * methods)
{
MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_res) = *methods;
}
/* }}} */
-/* {{{ mysqlnd_result_unbuffered_get_methods */
-PHPAPI struct st_mysqlnd_result_unbuffered_methods *
-mysqlnd_result_unbuffered_get_methods()
+/* {{{ _mysqlnd_result_unbuffered_get_methods */
+static MYSQLND_CLASS_METHODS_TYPE(mysqlnd_result_unbuffered) *
+_mysqlnd_result_unbuffered_get_methods()
{
return &MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_result_unbuffered);
}
/* }}} */
-/* {{{ mysqlnd_result_unbuffered_set_methods */
-PHPAPI void
-mysqlnd_result_unbuffered_set_methods(struct st_mysqlnd_result_unbuffered_methods * methods)
+/* {{{ _mysqlnd_result_unbuffered_set_methods */
+static void
+_mysqlnd_result_unbuffered_set_methods(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_result_unbuffered) * methods)
{
MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_result_unbuffered) = *methods;
}
/* }}} */
-/* {{{ mysqlnd_result_buffered_get_methods */
-PHPAPI struct st_mysqlnd_result_buffered_methods *
-mysqlnd_result_buffered_get_methods()
+/* {{{ _mysqlnd_result_buffered_get_methods */
+static MYSQLND_CLASS_METHODS_TYPE(mysqlnd_result_buffered) *
+_mysqlnd_result_buffered_get_methods()
{
return &MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_result_buffered);
}
/* }}} */
-/* {{{ mysqlnd_result_buffered_set_methods */
-PHPAPI void
-mysqlnd_result_buffered_set_methods(struct st_mysqlnd_result_buffered_methods * methods)
+/* {{{ _mysqlnd_result_buffered_set_methods */
+static void
+_mysqlnd_result_buffered_set_methods(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_result_buffered) * methods)
{
MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_result_buffered) = *methods;
}
/* }}} */
-/* {{{ mysqlnd_stmt_get_methods */
-PHPAPI struct st_mysqlnd_stmt_methods *
-mysqlnd_stmt_get_methods()
+/* {{{ _mysqlnd_stmt_get_methods */
+static MYSQLND_CLASS_METHODS_TYPE(mysqlnd_stmt) *
+_mysqlnd_stmt_get_methods()
{
return mysqlnd_stmt_methods;
}
/* }}} */
-/* {{{ mysqlnd_stmt_set_methods */
-PHPAPI void
-mysqlnd_stmt_set_methods(struct st_mysqlnd_stmt_methods *methods)
+/* {{{ _mysqlnd_stmt_set_methods */
+static void
+_mysqlnd_stmt_set_methods(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_stmt) *methods)
{
mysqlnd_stmt_methods = methods;
}
/* }}} */
-/* {{{ mysqlnd_protocol_get_methods */
-PHPAPI struct st_mysqlnd_protocol_methods *
-mysqlnd_protocol_get_methods()
+/* {{{ _mysqlnd_protocol_payload_decoder_factory_get_methods */
+static MYSQLND_CLASS_METHODS_TYPE(mysqlnd_protocol_payload_decoder_factory) *
+_mysqlnd_protocol_payload_decoder_factory_get_methods()
{
- return &MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_protocol);
+ return &MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_protocol_payload_decoder_factory);
}
/* }}} */
-/* {{{ mysqlnd_protocol_set_methods */
-PHPAPI void
-mysqlnd_protocol_set_methods(struct st_mysqlnd_protocol_methods * methods)
+/* {{{ _mysqlnd_protocol_payload_decoder_factory_set_methods */
+static void
+_mysqlnd_protocol_payload_decoder_factory_set_methods(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_protocol_payload_decoder_factory) * methods)
{
- MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_protocol) = *methods;
+ MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_protocol_payload_decoder_factory) = *methods;
}
/* }}} */
-/* {{{ mysqlnd_net_get_methods */
-PHPAPI struct st_mysqlnd_net_methods *
-mysqlnd_net_get_methods()
+/* {{{ _mysqlnd_pfc_get_methods */
+static MYSQLND_CLASS_METHODS_TYPE(mysqlnd_protocol_packet_frame_codec) *
+_mysqlnd_pfc_get_methods()
{
- return &MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_net);
+ return &MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_protocol_packet_frame_codec);
}
/* }}} */
-/* {{{ mysqlnd_net_set_methods */
-PHPAPI void
-mysqlnd_net_set_methods(struct st_mysqlnd_net_methods * methods)
+/* {{{ _mysqlnd_pfc_set_methods */
+static void
+_mysqlnd_pfc_set_methods(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_protocol_packet_frame_codec) * methods)
{
- MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_net) = *methods;
+ MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_protocol_packet_frame_codec) = *methods;
}
/* }}} */
+/* {{{ _mysqlnd_vio_get_methods */
+static MYSQLND_CLASS_METHODS_TYPE(mysqlnd_vio) *
+_mysqlnd_vio_get_methods()
+{
+ return &MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_vio);
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_vio_set_methods */
+static void
+_mysqlnd_vio_set_methods(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_vio) * methods)
+{
+ MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_vio) = *methods;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_command_factory_get */
+static func_mysqlnd__command_factory
+_mysqlnd_command_factory_get()
+{
+ return mysqlnd_command_factory;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_command_factory_set */
+static void
+_mysqlnd_command_factory_set(func_mysqlnd__command_factory factory)
+{
+ mysqlnd_command_factory = factory;
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_error_info_get_methods */
+static MYSQLND_CLASS_METHODS_TYPE(mysqlnd_error_info) *
+_mysqlnd_error_info_get_methods()
+{
+ return &MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_error_info);
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_error_info_set_methods */
+static void
+_mysqlnd_error_info_set_methods(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_error_info) *methods)
+{
+ MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_error_info) = *methods;
+}
+/* }}} */
+
+
+struct st_mysqlnd_plugin_methods_xetters mysqlnd_plugin_methods_xetters =
+{
+ {
+ _mysqlnd_object_factory_get_methods,
+ _mysqlnd_object_factory_set_methods
+ },
+ {
+ _mysqlnd_conn_get_methods,
+ _mysqlnd_conn_set_methods,
+ },
+ {
+ _mysqlnd_conn_data_get_methods,
+ _mysqlnd_conn_data_set_methods,
+ },
+ {
+ _mysqlnd_result_get_methods,
+ _mysqlnd_result_set_methods,
+ },
+ {
+ _mysqlnd_result_unbuffered_get_methods,
+ _mysqlnd_result_unbuffered_set_methods,
+ },
+ {
+ _mysqlnd_result_buffered_get_methods,
+ _mysqlnd_result_buffered_set_methods,
+ },
+ {
+ _mysqlnd_stmt_get_methods,
+ _mysqlnd_stmt_set_methods,
+ },
+ {
+ _mysqlnd_protocol_payload_decoder_factory_get_methods,
+ _mysqlnd_protocol_payload_decoder_factory_set_methods,
+ },
+ {
+ _mysqlnd_pfc_get_methods,
+ _mysqlnd_pfc_set_methods,
+ },
+ {
+ _mysqlnd_vio_get_methods,
+ _mysqlnd_vio_set_methods,
+ },
+ {
+ _mysqlnd_error_info_get_methods,
+ _mysqlnd_error_info_set_methods,
+ },
+ {
+ _mysqlnd_command_factory_get,
+ _mysqlnd_command_factory_set,
+ },
+};
+
/*
* Local variables:
* tab-width: 4
diff --git a/ext/mysqlnd/mysqlnd_ext_plugin.h b/ext/mysqlnd/mysqlnd_ext_plugin.h
index 947a8eea12..bebc7196cb 100644
--- a/ext/mysqlnd/mysqlnd_ext_plugin.h
+++ b/ext/mysqlnd/mysqlnd_ext_plugin.h
@@ -13,66 +13,155 @@
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Andrey Hristov <andrey@php.net> |
+ | Johannes Schlter <johannes@php.net> |
| Ulf Wendel <uw@php.net> |
- | Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
#ifndef MYSQLND_EXT_PLUGIN_H
#define MYSQLND_EXT_PLUGIN_H
-PHPAPI void ** _mysqlnd_plugin_get_plugin_connection_data(const MYSQLND * conn, unsigned int plugin_id);
-#define mysqlnd_plugin_get_plugin_connection_data(c, p_id) _mysqlnd_plugin_get_plugin_connection_data((c), (p_id))
-
-PHPAPI void ** _mysqlnd_plugin_get_plugin_connection_data_data(const MYSQLND_CONN_DATA * conn, unsigned int plugin_id);
-#define mysqlnd_plugin_get_plugin_connection_data_data(c, p_id) _mysqlnd_plugin_get_plugin_connection_data_data((c), (p_id))
-
-PHPAPI void ** _mysqlnd_plugin_get_plugin_result_data(const MYSQLND_RES * result, unsigned int plugin_id);
-#define mysqlnd_plugin_get_plugin_result_data(r, p_id) _mysqlnd_plugin_get_plugin_result_data((r), (p_id))
-
-PHPAPI void ** _mysqlnd_plugin_get_plugin_result_unbuffered_data(const MYSQLND_RES_UNBUFFERED * result, unsigned int plugin_id);
-#define mysqlnd_plugin_get_plugin_result_unbuffered_data(r, p_id) _mysqlnd_plugin_get_plugin_result_unbuffered_data((r), (p_id))
-
-PHPAPI void ** _mysqlnd_plugin_get_plugin_result_buffered_data_zval(const MYSQLND_RES_BUFFERED_ZVAL * result, unsigned int plugin_id);
-#define mysqlnd_plugin_get_plugin_result_buffered_data_zval(r, p_id) _mysqlnd_plugin_get_plugin_result_buffered_data_zval((r), (p_id))
-
-PHPAPI void ** _mysqlnd_plugin_get_plugin_result_buffered_data_c(const MYSQLND_RES_BUFFERED_C * result, unsigned int plugin_id);
-#define mysqlnd_plugin_get_plugin_result_buffered_data_c(r, p_id) _mysqlnd_plugin_get_plugin_result_buffered_data_c((r), (p_id))
-
-PHPAPI void ** _mysqlnd_plugin_get_plugin_stmt_data(const MYSQLND_STMT * stmt, unsigned int plugin_id);
-#define mysqlnd_plugin_get_plugin_stmt_data(s, p_id) _mysqlnd_plugin_get_plugin_stmt_data((s), (p_id))
-
-PHPAPI void ** _mysqlnd_plugin_get_plugin_protocol_data(const MYSQLND_PROTOCOL * protocol, unsigned int plugin_id);
-#define mysqlnd_plugin_get_plugin_protocol_data(p, p_id) _mysqlnd_plugin_get_plugin_protocol_data((p), (p_id))
-
-PHPAPI void ** _mysqlnd_plugin_get_plugin_net_data(const MYSQLND_NET * net, unsigned int plugin_id);
-#define mysqlnd_plugin_get_plugin_net_data(n, p_id) _mysqlnd_plugin_get_plugin_net_data((n), (p_id))
-
-
-PHPAPI struct st_mysqlnd_conn_methods * mysqlnd_conn_get_methods();
-PHPAPI void mysqlnd_conn_set_methods(struct st_mysqlnd_conn_methods * methods);
-
-PHPAPI struct st_mysqlnd_conn_data_methods * mysqlnd_conn_data_get_methods();
-PHPAPI void mysqlnd_conn_data_set_methods(struct st_mysqlnd_conn_data_methods * methods);
-
-PHPAPI struct st_mysqlnd_res_methods * mysqlnd_result_get_methods();
-PHPAPI void mysqlnd_result_set_methods(struct st_mysqlnd_res_methods * methods);
-
-PHPAPI struct st_mysqlnd_result_unbuffered_methods * mysqlnd_result_unbuffered_get_methods();
-PHPAPI void mysqlnd_result_unbuffered_set_methods(struct st_mysqlnd_result_unbuffered_methods * methods);
-
-PHPAPI struct st_mysqlnd_result_buffered_methods * mysqlnd_result_buffered_get_methods();
-PHPAPI void mysqlnd_result_buffered_set_methods(struct st_mysqlnd_result_buffered_methods * methods);
-
-PHPAPI struct st_mysqlnd_stmt_methods * mysqlnd_stmt_get_methods();
-PHPAPI void mysqlnd_stmt_set_methods(struct st_mysqlnd_stmt_methods * methods);
-
-PHPAPI struct st_mysqlnd_protocol_methods * mysqlnd_protocol_get_methods();
-PHPAPI void mysqlnd_protocol_set_methods(struct st_mysqlnd_protocol_methods * methods);
-
-PHPAPI struct st_mysqlnd_net_methods * mysqlnd_net_get_methods();
-PHPAPI void mysqlnd_net_set_methods(struct st_mysqlnd_net_methods * methods);
-
+struct st_mysqlnd_plugin__plugin_area_getters
+{
+ void ** (*get_connection_area)(const MYSQLND * conn, const unsigned int plugin_id);
+ void ** (*get_connection_data_area)(const MYSQLND_CONN_DATA * conn, const unsigned int plugin_id);
+ void ** (*get_result_area)(const MYSQLND_RES * result, const unsigned int plugin_id);
+ void ** (*get_unbuffered_area)(const MYSQLND_RES_UNBUFFERED * result, const unsigned int plugin_id);
+ void ** (*get_result_buffered_area)(const MYSQLND_RES_BUFFERED_ZVAL * result, const unsigned int plugin_id);
+ void ** (*get_result_buffered_aread_c)(const MYSQLND_RES_BUFFERED_C * result, const unsigned int plugin_id);
+ void ** (*get_stmt_area)(const MYSQLND_STMT * stmt, const unsigned int plugin_id);
+ void ** (*get_protocol_decoder_area)(const MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * factory, const unsigned int plugin_id);
+ void ** (*get_pfc_area)(const MYSQLND_PFC * pfc, const unsigned int plugin_id);
+ void ** (*get_vio_area)(const MYSQLND_VIO * vio, const unsigned int plugin_id);
+};
+
+extern struct st_mysqlnd_plugin__plugin_area_getters mysqlnd_plugin_area_getters;
+
+#define mysqlnd_plugin_get_plugin_connection_data(c, p_id) mysqlnd_plugin_area_getters.get_connection_area((c), (p_id))
+#define mysqlnd_plugin_get_plugin_connection_data_data(c, p_id) mysqlnd_plugin_area_getters.get_connection_data_area((c), (p_id))
+#define mysqlnd_plugin_get_plugin_result_data(res, p_id) mysqlnd_plugin_area_getters.get_result_area((res), (p_id))
+#define mysqlnd_plugin_get_plugin_result_unbuffered_data(res, p_id) mysqlnd_plugin_area_getters.get_unbuffered_area((res), (p_id))
+#define mysqlnd_plugin_get_plugin_result_buffered_data_zval(res, p_id) mysqlnd_plugin_area_getters.get_result_buffered_area((res), (p_id))
+#define mysqlnd_plugin_get_plugin_result_buffered_data_c(res, p_id) mysqlnd_plugin_area_getters.get_result_buffered_aread_c((res), (p_id))
+#define mysqlnd_plugin_get_plugin_stmt_data(stmt, p_id) mysqlnd_plugin_area_getters.get_stmt_area((stmt), (p_id))
+#define mysqlnd_plugin_get_plugin_protocol_data(proto, p_id) mysqlnd_plugin_area_getters.get_protocol_decoder_area((proto), (p_id))
+#define mysqlnd_plugin_get_plugin_pfc_data(pfc, p_id) mysqlnd_plugin_area_getters.get_pfc_area((pfc), (p_id))
+#define mysqlnd_plugin_get_plugin_vio_data(vio, p_id) mysqlnd_plugin_area_getters.get_pfc_area((vio), (p_id))
+
+
+struct st_mysqlnd_plugin_methods_xetters
+{
+ struct st_mnd_object_factory_xetters
+ {
+ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_object_factory) * (*get)();
+ void (*set)(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_object_factory) *methods);
+ } object_factory;
+
+ struct st_mnd_connection_xetters
+ {
+ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn) * (*get)();
+ void (*set)(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn) *methods);
+ } connection;
+
+ struct st_mnd_connection_data_xetters
+ {
+ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data) * (*get)();
+ void (*set)(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data) *methods);
+ } connection_data;
+
+ struct st_mnd_result_xetters
+ {
+ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_res) * (*get)();
+ void (*set)(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_res) *methods);
+ } result;
+
+ struct st_mnd_unbuffered_result_xetters
+ {
+ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_result_unbuffered) * (*get)();
+ void (*set)(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_result_unbuffered) *methods);
+ } unbuffered_result;
+
+ struct st_mnd_buffered_result_xetters
+ {
+ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_result_buffered)* (*get)();
+ void (*set)(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_result_buffered) *methods);
+ } buffered_result;
+
+ struct st_mnd_stmt_xetters
+ {
+ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_stmt) * (*get)();
+ void (*set)(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_stmt) * methods);
+ } statement;
+
+ struct st_mnd_protocol_xetters
+ {
+ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_protocol_payload_decoder_factory)* (*get)();
+ void (*set)(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_protocol_payload_decoder_factory) *methods);
+ } protocol;
+
+ struct st_mnd_pfc_xetters
+ {
+ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_protocol_packet_frame_codec) * (*get)();
+ void (*set)(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_protocol_packet_frame_codec) * methods);
+ } pfc;
+
+ struct st_mnd_vio_xetters
+ {
+ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_vio) * (*get)();
+ void (*set)(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_vio) * methods);
+ } vio;
+
+ struct st_mnd_error_info_xetters
+ {
+ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_error_info) * (*get)();
+ void (*set)(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_error_info) * methods);
+ } error_info;
+
+ struct st_mnd_command_factory_xetters
+ {
+ func_mysqlnd__command_factory (*get)();
+ void (*set)(func_mysqlnd__command_factory factory);
+ } command_factory;
+};
+
+extern struct st_mysqlnd_plugin_methods_xetters mysqlnd_plugin_methods_xetters;
+
+
+#define mysqlnd_object_factory_get_methods() mysqlnd_plugin_methods_xetters.object_factory.get()
+#define mysqlnd_object_factory_set_methods(m) mysqlnd_plugin_methods_xetters.object_factory.set((m))
+
+#define mysqlnd_conn_get_methods() mysqlnd_plugin_methods_xetters.connection.get()
+#define mysqlnd_conn_set_methods(m) mysqlnd_plugin_methods_xetters.connection.set((m))
+
+#define mysqlnd_conn_data_get_methods() mysqlnd_plugin_methods_xetters.connection_data.get()
+#define mysqlnd_conn_data_set_methods(m) mysqlnd_plugin_methods_xetters.connection_data.set((m))
+
+#define mysqlnd_result_get_methods() mysqlnd_plugin_methods_xetters.result.get()
+#define mysqlnd_result_set_methods(m) mysqlnd_plugin_methods_xetters.result.set((m))
+
+#define mysqlnd_result_unbuffered_get_methods() mysqlnd_plugin_methods_xetters.unbuffered_result.get()
+#define mysqlnd_result_unbuffered_set_methods(m) mysqlnd_plugin_methods_xetters.unbuffered_result.set((m))
+
+#define mysqlnd_result_buffered_get_methods() mysqlnd_plugin_methods_xetters.buffered_result.get()
+#define mysqlnd_result_buffered_set_methods(m) mysqlnd_plugin_methods_xetters.buffered_result.set((m))
+
+#define mysqlnd_stmt_get_methods() mysqlnd_plugin_methods_xetters.statement.get()
+#define mysqlnd_stmt_set_methods(m) mysqlnd_plugin_methods_xetters.statement.set((m))
+
+#define mysqlnd_protocol_get_methods() mysqlnd_plugin_methods_xetters.protocol.get()
+#define mysqlnd_protocol_set_methods(m) mysqlnd_plugin_methods_xetters.protocol.set((m))
+
+#define mysqlnd_pfc_get_methods() mysqlnd_plugin_methods_xetters.pfc.get()
+#define mysqlnd_pfc_set_methods(m) mysqlnd_plugin_methods_xetters.pfc.set((m))
+
+#define mysqlnd_vio_get_methods() mysqlnd_plugin_methods_xetters.vio.get()
+#define mysqlnd_vio_set_methods(m) mysqlnd_plugin_methods_xetters.vio.set((m))
+
+#define mysqlnd_command_factory_get() mysqlnd_plugin_methods_xetters.command_factory.get()
+#define mysqlnd_command_factory_set(m) mysqlnd_plugin_methods_xetters.command_factory.set((m))
+
+#define mysqlnd_error_info_get_methods() mysqlnd_plugin_methods_xetters.error_info.get()
+#define mysqlnd_error_info_set_methods(m) mysqlnd_plugin_methods_xetters.error_info.set((m))
#endif /* MYSQLND_EXT_PLUGIN_H */
diff --git a/ext/mysqlnd/mysqlnd_libmysql_compat.h b/ext/mysqlnd/mysqlnd_libmysql_compat.h
index d6608f36b4..56e94e25f3 100644
--- a/ext/mysqlnd/mysqlnd_libmysql_compat.h
+++ b/ext/mysqlnd/mysqlnd_libmysql_compat.h
@@ -17,7 +17,6 @@
| Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
-
#ifndef MYSQLND_LIBMYSQL_COMPAT_H
#define MYSQLND_LIBMYSQL_COMPAT_H
@@ -58,11 +57,10 @@
#define mysql_field_count(r) mysqlnd_field_count((r))
#define mysql_field_seek(r,o) mysqlnd_field_seek((r), (o))
#define mysql_field_tell(r) mysqlnd_field_tell((r))
-#define mysql_init(a) mysqlnd_init((a))
+#define mysql_init(a) mysqlnd_connection_init((a), false)
#define mysql_insert_id(r) mysqlnd_insert_id((r))
#define mysql_kill(r,n) mysqlnd_kill((r), (n))
#define mysql_list_dbs(c, wild) mysqlnd_list_dbs((c), (wild))
-#define mysql_list_fields(c, tab, wild) mysqlnd_list_fields((c), (tab), (wild))
#define mysql_list_processes(c) mysqlnd_list_processes((c))
#define mysql_list_tables(c, wild) mysqlnd_list_tables((c), (wild))
#define mysql_more_results(r) mysqlnd_more_results((r))
diff --git a/ext/mysqlnd/mysqlnd_loaddata.c b/ext/mysqlnd/mysqlnd_loaddata.c
index 3284258d09..0ad5a4acb4 100644
--- a/ext/mysqlnd/mysqlnd_loaddata.c
+++ b/ext/mysqlnd/mysqlnd_loaddata.c
@@ -17,9 +17,7 @@
| Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
-
#include "php.h"
-#include "php_globals.h"
#include "mysqlnd.h"
#include "mysqlnd_wireprotocol.h"
#include "mysqlnd_priv.h"
@@ -136,12 +134,12 @@ mysqlnd_local_infile_default(MYSQLND_CONN_DATA * conn)
/* }}} */
-static const char *lost_conn = "Lost connection to MySQL server during LOAD DATA of local file";
+static const char *lost_conn = "Lost connection to MySQL server during LOAD DATA of a local file";
/* {{{ mysqlnd_handle_local_infile */
enum_func_status
-mysqlnd_handle_local_infile(MYSQLND_CONN_DATA * conn, const char * filename, zend_bool * is_warning)
+mysqlnd_handle_local_infile(MYSQLND_CONN_DATA * conn, const char * const filename, zend_bool * is_warning)
{
zend_uchar *buf = NULL;
zend_uchar empty_packet[MYSQLND_HEADER_SIZE];
@@ -151,14 +149,15 @@ mysqlnd_handle_local_infile(MYSQLND_CONN_DATA * conn, const char * filename, zen
int bufsize;
size_t ret;
MYSQLND_INFILE infile;
- MYSQLND_NET * net = conn->net;
+ MYSQLND_PFC * net = conn->protocol_frame_codec;
+ MYSQLND_VIO * vio = conn->vio;
DBG_ENTER("mysqlnd_handle_local_infile");
if (!(conn->options->flags & CLIENT_LOCAL_FILES)) {
php_error_docref(NULL, E_WARNING, "LOAD DATA LOCAL INFILE forbidden");
/* write empty packet to server */
- ret = net->data->m.send_ex(net, empty_packet, 0, conn->stats, conn->error_info);
+ ret = net->data->m.send(net, vio, empty_packet, 0, conn->stats, conn->error_info);
*is_warning = TRUE;
goto infile_error;
}
@@ -176,24 +175,24 @@ mysqlnd_handle_local_infile(MYSQLND_CONN_DATA * conn, const char * filename, zen
*is_warning = TRUE;
/* error occurred */
tmp_error_no = infile.local_infile_error(info, tmp_buf, sizeof(tmp_buf));
- SET_CLIENT_ERROR(*conn->error_info, tmp_error_no, UNKNOWN_SQLSTATE, tmp_buf);
+ SET_CLIENT_ERROR(conn->error_info, tmp_error_no, UNKNOWN_SQLSTATE, tmp_buf);
/* write empty packet to server */
- ret = net->data->m.send_ex(net, empty_packet, 0, conn->stats, conn->error_info);
+ ret = net->data->m.send(net, vio, empty_packet, 0, conn->stats, conn->error_info);
goto infile_error;
}
/* read data */
while ((bufsize = infile.local_infile_read (info, buf + MYSQLND_HEADER_SIZE, buflen - MYSQLND_HEADER_SIZE)) > 0) {
- if ((ret = net->data->m.send_ex(net, buf, bufsize, conn->stats, conn->error_info)) == 0) {
+ if ((ret = net->data->m.send(net, vio, buf, bufsize, conn->stats, conn->error_info)) == 0) {
DBG_ERR_FMT("Error during read : %d %s %s", CR_SERVER_LOST, UNKNOWN_SQLSTATE, lost_conn);
- SET_CLIENT_ERROR(*conn->error_info, CR_SERVER_LOST, UNKNOWN_SQLSTATE, lost_conn);
+ SET_CLIENT_ERROR(conn->error_info, CR_SERVER_LOST, UNKNOWN_SQLSTATE, lost_conn);
goto infile_error;
}
}
/* send empty packet for eof */
- if ((ret = net->data->m.send_ex(net, empty_packet, 0, conn->stats, conn->error_info)) == 0) {
- SET_CLIENT_ERROR(*conn->error_info, CR_SERVER_LOST, UNKNOWN_SQLSTATE, lost_conn);
+ if ((ret = net->data->m.send(net, vio, empty_packet, 0, conn->stats, conn->error_info)) == 0) {
+ SET_CLIENT_ERROR(conn->error_info, CR_SERVER_LOST, UNKNOWN_SQLSTATE, lost_conn);
goto infile_error;
}
@@ -204,7 +203,7 @@ mysqlnd_handle_local_infile(MYSQLND_CONN_DATA * conn, const char * filename, zen
*is_warning = TRUE;
DBG_ERR_FMT("Bufsize < 0, warning, %d %s %s", CR_SERVER_LOST, UNKNOWN_SQLSTATE, lost_conn);
tmp_error_no = infile.local_infile_error(info, tmp_buf, sizeof(tmp_buf));
- SET_CLIENT_ERROR(*conn->error_info, tmp_error_no, UNKNOWN_SQLSTATE, tmp_buf);
+ SET_CLIENT_ERROR(conn->error_info, tmp_error_no, UNKNOWN_SQLSTATE, tmp_buf);
goto infile_error;
}
@@ -212,7 +211,13 @@ mysqlnd_handle_local_infile(MYSQLND_CONN_DATA * conn, const char * filename, zen
infile_error:
/* get response from server and update upsert values */
- if (FAIL == conn->m->simple_command_handle_response(conn, PROT_OK_PACKET, FALSE, COM_QUERY, FALSE)) {
+ if (FAIL == conn->payload_decoder_factory->m.send_command_handle_response(
+ conn->payload_decoder_factory,
+ PROT_OK_PACKET, FALSE, COM_QUERY, FALSE,
+ conn->error_info,
+ conn->upsert_status,
+ &conn->last_message,
+ conn->persistent)) {
result = FAIL;
}
diff --git a/ext/mysqlnd/mysqlnd_net.c b/ext/mysqlnd/mysqlnd_net.c
index 19f919443e..0be3935f46 100644
--- a/ext/mysqlnd/mysqlnd_net.c
+++ b/ext/mysqlnd/mysqlnd_net.c
@@ -14,7 +14,6 @@
+----------------------------------------------------------------------+
| Authors: Andrey Hristov <andrey@php.net> |
| Ulf Wendel <uw@php.net> |
- | Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
@@ -132,11 +131,7 @@ MYSQLND_METHOD(mysqlnd_net, open_pipe)(MYSQLND_NET * const net, const char * con
const zend_bool persistent,
MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info)
{
-#if PHP_API_VERSION < 20100412
- unsigned int streams_options = ENFORCE_SAFE_MODE;
-#else
unsigned int streams_options = 0;
-#endif
dtor_func_t origin_dtor;
php_stream * net_stream = NULL;
@@ -172,11 +167,7 @@ MYSQLND_METHOD(mysqlnd_net, open_tcp_or_unix)(MYSQLND_NET * const net, const cha
const zend_bool persistent,
MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info)
{
-#if PHP_API_VERSION < 20100412
- unsigned int streams_options = ENFORCE_SAFE_MODE;
-#else
unsigned int streams_options = 0;
-#endif
unsigned int streams_flags = STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT;
char * hashed_details = NULL;
int hashed_details_len = 0;
@@ -982,11 +973,7 @@ MYSQLND_METHOD(mysqlnd_net, enable_ssl)(MYSQLND_NET * const net)
php_stream_context_set_option(context, "ssl", "allow_self_signed", &verify_peer_zval);
}
}
-#if PHP_API_VERSION >= 20131106
- php_stream_context_set(net_stream, context);
-#else
php_stream_context_set(net_stream, context);
-#endif
if (php_stream_xport_crypto_setup(net_stream, STREAM_CRYPTO_METHOD_TLS_CLIENT, NULL) < 0 ||
php_stream_xport_crypto_enable(net_stream, 1) < 0)
{
@@ -1002,11 +989,7 @@ MYSQLND_METHOD(mysqlnd_net, enable_ssl)(MYSQLND_NET * const net)
of the context, which means usage of already freed memory, bad. Actually we don't need this
context anymore after we have enabled SSL on the connection. Thus it is very simple, we remove it.
*/
-#if PHP_API_VERSION >= 20131106
php_stream_context_set(net_stream, NULL);
-#else
- php_stream_context_set(net_stream, NULL);
-#endif
if (net->data->options.timeout_read) {
struct timeval tv;
diff --git a/ext/mysqlnd/mysqlnd_plugin.c b/ext/mysqlnd/mysqlnd_plugin.c
index 3694797192..4815a856e4 100644
--- a/ext/mysqlnd/mysqlnd_plugin.c
+++ b/ext/mysqlnd/mysqlnd_plugin.c
@@ -14,7 +14,6 @@
+----------------------------------------------------------------------+
| Authors: Andrey Hristov <andrey@php.net> |
| Ulf Wendel <uw@php.net> |
- | Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
@@ -61,6 +60,7 @@ static struct st_mysqlnd_typeii_plugin_example mysqlnd_example_plugin =
}
},
NULL, /* methods */
+ 0
};
diff --git a/ext/mysqlnd/mysqlnd_plugin.h b/ext/mysqlnd/mysqlnd_plugin.h
new file mode 100644
index 0000000000..9f6ec598f0
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_plugin.h
@@ -0,0 +1,41 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 7 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2017 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Andrey Hristov <andrey@php.net> |
+ | Ulf Wendel <uw@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef MYSQLND_PLUGIN_H
+#define MYSQLND_PLUGIN_H
+
+
+void mysqlnd_plugin_subsystem_init(void);
+void mysqlnd_plugin_subsystem_end(void);
+
+void mysqlnd_register_builtin_authentication_plugins(void);
+
+void mysqlnd_example_plugin_register(void);
+
+#endif /* MYSQLND_PLUGIN_H */
+
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/mysqlnd/mysqlnd_priv.h b/ext/mysqlnd/mysqlnd_priv.h
index 9b898bed42..8f40de39bf 100644
--- a/ext/mysqlnd/mysqlnd_priv.h
+++ b/ext/mysqlnd/mysqlnd_priv.h
@@ -14,219 +14,26 @@
+----------------------------------------------------------------------+
| Authors: Andrey Hristov <andrey@php.net> |
| Ulf Wendel <uw@php.net> |
- | Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
#ifndef MYSQLND_PRIV_H
#define MYSQLND_PRIV_H
-
-#ifdef ZTS
-#include "TSRM.h"
-#endif
-
-#define MYSQLND_STR_W_LEN(str) str, (sizeof(str) - 1)
-
-#define MYSQLND_DEBUG_DUMP_TIME 1
-#define MYSQLND_DEBUG_DUMP_TRACE 2
-#define MYSQLND_DEBUG_DUMP_PID 4
-#define MYSQLND_DEBUG_DUMP_LINE 8
-#define MYSQLND_DEBUG_DUMP_FILE 16
-#define MYSQLND_DEBUG_DUMP_LEVEL 32
-#define MYSQLND_DEBUG_APPEND 64
-#define MYSQLND_DEBUG_FLUSH 128
-#define MYSQLND_DEBUG_TRACE_MEMORY_CALLS 256
-#define MYSQLND_DEBUG_PROFILE_CALLS 512
-
-
-/* Client Error codes */
-#define CR_UNKNOWN_ERROR 2000
-#define CR_CONNECTION_ERROR 2002
-#define CR_SERVER_GONE_ERROR 2006
-#define CR_OUT_OF_MEMORY 2008
-#define CR_SERVER_LOST 2013
-#define CR_COMMANDS_OUT_OF_SYNC 2014
-#define CR_CANT_FIND_CHARSET 2019
-#define CR_MALFORMED_PACKET 2027
-#define CR_NOT_IMPLEMENTED 2054
-#define CR_NO_PREPARE_STMT 2030
-#define CR_PARAMS_NOT_BOUND 2031
-#define CR_INVALID_PARAMETER_NO 2034
-#define CR_INVALID_BUFFER_USE 2035
-
-#define MYSQLND_EE_FILENOTFOUND 7890
-
-#define UNKNOWN_SQLSTATE "HY000"
-
-#define MAX_CHARSET_LEN 32
-
-
-#define SET_ERROR_AFF_ROWS(s) (s)->upsert_status->affected_rows = (uint64_t) ~0
-
-/* Error handling */
-#define SET_NEW_MESSAGE(buf, buf_len, message, len, persistent) \
- {\
- if ((buf)) { \
- mnd_pefree((buf), (persistent)); \
- } \
- if ((message)) { \
- (buf) = mnd_pestrndup((message), (len), (persistent)); \
- } else { \
- (buf) = NULL; \
- } \
- (buf_len) = (len); \
- }
-
-#define SET_EMPTY_MESSAGE(buf, buf_len, persistent) \
- {\
- if ((buf)) { \
- mnd_pefree((buf), (persistent)); \
- (buf) = NULL; \
- } \
- (buf_len) = 0; \
- }
-
-
-#define SET_EMPTY_ERROR(error_info) \
- { \
- (error_info).error_no = 0; \
- (error_info).error[0] = '\0'; \
- strlcpy((error_info).sqlstate, "00000", sizeof((error_info).sqlstate)); \
- if ((error_info).error_list) { \
- zend_llist_clean((error_info).error_list); \
- } \
- }
-
-
-#define SET_CLIENT_ERROR(error_info, a, b, c) \
-{ \
- if (0 == (a)) { \
- SET_EMPTY_ERROR((error_info)); \
- } else { \
- (error_info).error_no = (a); \
- strlcpy((error_info).sqlstate, (b), sizeof((error_info).sqlstate)); \
- strlcpy((error_info).error, (c), sizeof((error_info).error)); \
- if ((error_info).error_list) {\
- MYSQLND_ERROR_LIST_ELEMENT error_for_the_list = {0}; \
- \
- error_for_the_list.error_no = (a); \
- strlcpy(error_for_the_list.sqlstate, (b), sizeof(error_for_the_list.sqlstate)); \
- error_for_the_list.error = mnd_pestrdup((c), TRUE); \
- if (error_for_the_list.error) { \
- DBG_INF_FMT("adding error [%s] to the list", error_for_the_list.error); \
- zend_llist_add_element((error_info).error_list, &error_for_the_list); \
- } \
- } \
- } \
-}
-
-
-#define COPY_CLIENT_ERROR(error_info_to, error_info_from) \
- { \
- SET_CLIENT_ERROR((error_info_to), (error_info_from).error_no, (error_info_from).sqlstate, (error_info_from).error); \
- }
-
-
-#define SET_OOM_ERROR(error_info) SET_CLIENT_ERROR((error_info), CR_OUT_OF_MEMORY, UNKNOWN_SQLSTATE, mysqlnd_out_of_memory)
-
-
-#define SET_STMT_ERROR(stmt, a, b, c) SET_CLIENT_ERROR(*(stmt)->error_info, a, b, c)
-
-#define CONN_GET_STATE(c) (c)->m->get_state((c))
-#define CONN_SET_STATE(c, s) (c)->m->set_state((c), (s))
-
-/* PS stuff */
-typedef void (*ps_field_fetch_func)(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_len, zend_uchar ** row);
-struct st_mysqlnd_perm_bind {
- ps_field_fetch_func func;
- /* should be signed int */
- int pack_len;
- unsigned int php_type;
- zend_bool is_possibly_blob;
- zend_bool can_ret_as_str_in_uni;
-};
-
-extern struct st_mysqlnd_perm_bind mysqlnd_ps_fetch_functions[MYSQL_TYPE_LAST + 1];
-
-enum_func_status mysqlnd_stmt_fetch_row_buffered(MYSQLND_RES * result, void * param, unsigned int flags, zend_bool * fetched_anything);
-enum_func_status mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES * result, void * param, unsigned int flags, zend_bool * fetched_anything);
-
-
-PHPAPI extern const char * const mysqlnd_old_passwd;
-PHPAPI extern const char * const mysqlnd_out_of_sync;
-PHPAPI extern const char * const mysqlnd_server_gone;
-PHPAPI extern const char * const mysqlnd_out_of_memory;
-
PHPAPI extern MYSQLND_CLASS_METHOD_TABLE_NAME_FORWARD(mysqlnd_object_factory);
PHPAPI extern MYSQLND_CLASS_METHOD_TABLE_NAME_FORWARD(mysqlnd_conn);
PHPAPI extern MYSQLND_CLASS_METHOD_TABLE_NAME_FORWARD(mysqlnd_conn_data);
PHPAPI extern MYSQLND_CLASS_METHOD_TABLE_NAME_FORWARD(mysqlnd_res);
PHPAPI extern MYSQLND_CLASS_METHOD_TABLE_NAME_FORWARD(mysqlnd_result_unbuffered);
PHPAPI extern MYSQLND_CLASS_METHOD_TABLE_NAME_FORWARD(mysqlnd_result_buffered);
-PHPAPI extern MYSQLND_CLASS_METHOD_TABLE_NAME_FORWARD(mysqlnd_protocol);
-PHPAPI extern MYSQLND_CLASS_METHOD_TABLE_NAME_FORWARD(mysqlnd_net);
-
-enum_func_status mysqlnd_handle_local_infile(MYSQLND_CONN_DATA * conn, const char * filename, zend_bool * is_warning);
-
-
-
-void _mysqlnd_init_ps_subsystem();/* This one is private, mysqlnd_library_init() will call it */
-void _mysqlnd_init_ps_fetch_subsystem();
-
-void ps_fetch_from_1_to_8_bytes(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_len, zend_uchar ** row, unsigned int byte_count);
-
-void mysqlnd_plugin_subsystem_init(void);
-void mysqlnd_plugin_subsystem_end(void);
-
-void mysqlnd_register_builtin_authentication_plugins(void);
-
-void mysqlnd_example_plugin_register(void);
-
-struct st_mysqlnd_packet_greet;
-struct st_mysqlnd_authentication_plugin;
-
-enum_func_status
-mysqlnd_auth_handshake(MYSQLND_CONN_DATA * conn,
- const char * const user,
- const char * const passwd,
- const size_t passwd_len,
- const char * const db,
- const size_t db_len,
- const MYSQLND_OPTIONS * const options,
- zend_ulong mysql_flags,
- unsigned int server_charset_no,
- zend_bool use_full_blown_auth_packet,
- const char * const auth_protocol,
- const zend_uchar * const auth_plugin_data,
- const size_t auth_plugin_data_len,
- char ** switch_to_auth_protocol,
- size_t * switch_to_auth_protocol_len,
- zend_uchar ** switch_to_auth_protocol_data,
- size_t * switch_to_auth_protocol_data_len
- );
-
-enum_func_status
-mysqlnd_auth_change_user(MYSQLND_CONN_DATA * const conn,
- const char * const user,
- const size_t user_len,
- const char * const passwd,
- const size_t passwd_len,
- const char * const db,
- const size_t db_len,
- const zend_bool silent,
- zend_bool use_full_blown_auth_packet,
- const char * const auth_protocol,
- zend_uchar * auth_plugin_data,
- size_t auth_plugin_data_len,
- char ** switch_to_auth_protocol,
- size_t * switch_to_auth_protocol_len,
- zend_uchar ** switch_to_auth_protocol_data,
- size_t * switch_to_auth_protocol_data_len
- );
+PHPAPI extern MYSQLND_CLASS_METHOD_TABLE_NAME_FORWARD(mysqlnd_protocol_payload_decoder_factory);
+PHPAPI extern MYSQLND_CLASS_METHOD_TABLE_NAME_FORWARD(mysqlnd_protocol_packet_frame_codec);
+PHPAPI extern MYSQLND_CLASS_METHOD_TABLE_NAME_FORWARD(mysqlnd_vio);
+PHPAPI extern MYSQLND_CLASS_METHOD_TABLE_NAME_FORWARD(mysqlnd_upsert_status);
+PHPAPI extern MYSQLND_CLASS_METHOD_TABLE_NAME_FORWARD(mysqlnd_error_info);
+enum_func_status mysqlnd_handle_local_infile(MYSQLND_CONN_DATA * conn, const char * const filename, zend_bool * is_warning);
#endif /* MYSQLND_PRIV_H */
-
/*
* Local variables:
* tab-width: 4
diff --git a/ext/mysqlnd/mysqlnd_protocol_frame_codec.c b/ext/mysqlnd/mysqlnd_protocol_frame_codec.c
new file mode 100644
index 0000000000..f8cdcf751c
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_protocol_frame_codec.c
@@ -0,0 +1,508 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 7 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2017 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Andrey Hristov <andrey@php.net> |
+ | Ulf Wendel <uw@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "php.h"
+#include "mysqlnd.h"
+#include "mysqlnd_connection.h"
+#include "mysqlnd_priv.h"
+#include "mysqlnd_read_buffer.h"
+#include "mysqlnd_wireprotocol.h"
+#include "mysqlnd_statistics.h"
+#include "mysqlnd_debug.h"
+#ifdef MYSQLND_COMPRESSION_ENABLED
+#include <zlib.h>
+#endif
+
+
+/* {{{ mysqlnd_pfc::reset */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_pfc, reset)(MYSQLND_PFC * const pfc, MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info)
+{
+ DBG_ENTER("mysqlnd_pfc::reset");
+ pfc->data->packet_no = pfc->data->compressed_envelope_packet_no = 0;
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/* We assume that MYSQLND_HEADER_SIZE is 4 bytes !! */
+#define COPY_HEADER(T,A) do { \
+ *(((char *)(T))) = *(((char *)(A)));\
+ *(((char *)(T))+1) = *(((char *)(A))+1);\
+ *(((char *)(T))+2) = *(((char *)(A))+2);\
+ *(((char *)(T))+3) = *(((char *)(A))+3); } while (0)
+#define STORE_HEADER_SIZE(safe_storage, buffer) COPY_HEADER((safe_storage), (buffer))
+#define RESTORE_HEADER_SIZE(buffer, safe_storage) STORE_HEADER_SIZE((safe_storage), (buffer))
+
+
+/* {{{ mysqlnd_pfc::send */
+/*
+ IMPORTANT : It's expected that buffer has place in the beginning for MYSQLND_HEADER_SIZE !!!!
+ This is done for performance reasons in the caller of this function.
+ Otherwise we will have to do send two TCP packets, or do new alloc and memcpy.
+ Neither are quick, thus the clients of this function are obligated to do
+ what they are asked for.
+
+ `count` is actually the length of the payload data. Thus :
+ count + MYSQLND_HEADER_SIZE = sizeof(buffer) (not the pointer but the actual buffer)
+*/
+static size_t
+MYSQLND_METHOD(mysqlnd_pfc, send)(MYSQLND_PFC * const pfc, MYSQLND_VIO * const vio, zend_uchar * const buffer, const size_t count,
+ MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info)
+{
+ zend_uchar safe_buf[((MYSQLND_HEADER_SIZE) + (sizeof(zend_uchar)) - 1) / (sizeof(zend_uchar))];
+ zend_uchar * safe_storage = safe_buf;
+ size_t bytes_sent, packets_sent = 1;
+ size_t left = count;
+ zend_uchar * p = (zend_uchar *) buffer;
+ zend_uchar * compress_buf = NULL;
+ size_t to_be_sent;
+
+ DBG_ENTER("mysqlnd_pfc::send");
+ DBG_INF_FMT("count=" MYSQLND_SZ_T_SPEC " compression=%u", count, pfc->data->compressed);
+
+ if (pfc->data->compressed == TRUE) {
+ size_t comp_buf_size = MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE + MYSQLND_HEADER_SIZE + MIN(left, MYSQLND_MAX_PACKET_SIZE);
+ DBG_INF_FMT("compress_buf_size="MYSQLND_SZ_T_SPEC, comp_buf_size);
+ compress_buf = mnd_emalloc(comp_buf_size);
+ }
+
+ do {
+ to_be_sent = MIN(left, MYSQLND_MAX_PACKET_SIZE);
+ DBG_INF_FMT("to_be_sent=%u", to_be_sent);
+ DBG_INF_FMT("packets_sent=%u", packets_sent);
+ DBG_INF_FMT("compressed_envelope_packet_no=%u", pfc->data->compressed_envelope_packet_no);
+ DBG_INF_FMT("packet_no=%u", pfc->data->packet_no);
+#ifdef MYSQLND_COMPRESSION_ENABLED
+ if (pfc->data->compressed == TRUE) {
+ /* here we need to compress the data and then write it, first comes the compressed header */
+ size_t tmp_complen = to_be_sent;
+ size_t payload_size;
+ zend_uchar * uncompressed_payload = p; /* should include the header */
+
+ STORE_HEADER_SIZE(safe_storage, uncompressed_payload);
+ int3store(uncompressed_payload, to_be_sent);
+ int1store(uncompressed_payload + 3, pfc->data->packet_no);
+ if (PASS == pfc->data->m.encode((compress_buf + COMPRESSED_HEADER_SIZE + MYSQLND_HEADER_SIZE), &tmp_complen,
+ uncompressed_payload, to_be_sent + MYSQLND_HEADER_SIZE))
+ {
+ int3store(compress_buf + MYSQLND_HEADER_SIZE, to_be_sent + MYSQLND_HEADER_SIZE);
+ payload_size = tmp_complen;
+ } else {
+ int3store(compress_buf + MYSQLND_HEADER_SIZE, 0);
+ memcpy(compress_buf + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE, uncompressed_payload, to_be_sent + MYSQLND_HEADER_SIZE);
+ payload_size = to_be_sent + MYSQLND_HEADER_SIZE;
+ }
+ RESTORE_HEADER_SIZE(uncompressed_payload, safe_storage);
+
+ int3store(compress_buf, payload_size);
+ int1store(compress_buf + 3, pfc->data->packet_no);
+ DBG_INF_FMT("writing "MYSQLND_SZ_T_SPEC" bytes to the network", payload_size + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE);
+ bytes_sent = vio->data->m.network_write(vio, compress_buf, payload_size + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE, conn_stats, error_info);
+ pfc->data->compressed_envelope_packet_no++;
+ #if WHEN_WE_NEED_TO_CHECK_WHETHER_COMPRESSION_WORKS_CORRECTLY
+ if (res == Z_OK) {
+ size_t decompressed_size = left + MYSQLND_HEADER_SIZE;
+ zend_uchar * decompressed_data = mnd_malloc(decompressed_size);
+ int error = pfc->data->m.decode(decompressed_data, decompressed_size,
+ compress_buf + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE, payload_size);
+ if (error == Z_OK) {
+ int i;
+ DBG_INF("success decompressing");
+ for (i = 0 ; i < decompressed_size; i++) {
+ if (i && (i % 30 == 0)) {
+ printf("\n\t\t");
+ }
+ printf("%.2X ", (int)*((char*)&(decompressed_data[i])));
+ DBG_INF_FMT("%.2X ", (int)*((char*)&(decompressed_data[i])));
+ }
+ } else {
+ DBG_INF("error decompressing");
+ }
+ mnd_free(decompressed_data);
+ }
+ #endif /* WHEN_WE_NEED_TO_CHECK_WHETHER_COMPRESSION_WORKS_CORRECTLY */
+ } else
+#endif /* MYSQLND_COMPRESSION_ENABLED */
+ {
+ DBG_INF("no compression");
+ STORE_HEADER_SIZE(safe_storage, p);
+ int3store(p, to_be_sent);
+ int1store(p + 3, pfc->data->packet_no);
+ bytes_sent = vio->data->m.network_write(vio, p, to_be_sent + MYSQLND_HEADER_SIZE, conn_stats, error_info);
+ RESTORE_HEADER_SIZE(p, safe_storage);
+ pfc->data->compressed_envelope_packet_no++;
+ }
+ pfc->data->packet_no++;
+
+ p += to_be_sent;
+ left -= to_be_sent;
+ packets_sent++;
+ /*
+ if left is 0 then there is nothing more to send, but if the last packet was exactly
+ with the size MYSQLND_MAX_PACKET_SIZE we need to send additional packet, which has
+ empty payload. Thus if left == 0 we check for to_be_sent being the max size. If it is
+ indeed it then loop once more, then to_be_sent will become 0, left will stay 0. Empty
+ packet will be sent and this loop will end.
+ */
+ } while (bytes_sent && (left > 0 || to_be_sent == MYSQLND_MAX_PACKET_SIZE));
+
+ DBG_INF_FMT("packet_size="MYSQLND_SZ_T_SPEC" packet_no=%u", left, pfc->data->packet_no);
+
+ MYSQLND_INC_CONN_STATISTIC_W_VALUE3(conn_stats,
+ STAT_BYTES_SENT, count + packets_sent * MYSQLND_HEADER_SIZE,
+ STAT_PROTOCOL_OVERHEAD_OUT, packets_sent * MYSQLND_HEADER_SIZE,
+ STAT_PACKETS_SENT, packets_sent);
+
+ if (compress_buf) {
+ mnd_efree(compress_buf);
+ }
+
+ /* Even for zero size payload we have to send a packet */
+ if (!bytes_sent) {
+ DBG_ERR_FMT("Can't %u send bytes", count);
+ SET_CLIENT_ERROR(error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
+ }
+ DBG_RETURN(bytes_sent);
+}
+/* }}} */
+
+
+#ifdef MYSQLND_COMPRESSION_ENABLED
+
+/* {{{ mysqlnd_pfc::read_compressed_packet_from_stream_and_fill_read_buffer */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_pfc, read_compressed_packet_from_stream_and_fill_read_buffer)
+ (MYSQLND_PFC * pfc, MYSQLND_VIO * vio, size_t net_payload_size, MYSQLND_STATS * conn_stats, MYSQLND_ERROR_INFO * error_info)
+{
+ size_t decompressed_size;
+ enum_func_status retval = PASS;
+ zend_uchar * compressed_data = NULL;
+ zend_uchar comp_header[COMPRESSED_HEADER_SIZE];
+ DBG_ENTER("mysqlnd_pfc::read_compressed_packet_from_stream_and_fill_read_buffer");
+
+ /* Read the compressed header */
+ if (FAIL == vio->data->m.network_read(vio, comp_header, COMPRESSED_HEADER_SIZE, conn_stats, error_info)) {
+ DBG_RETURN(FAIL);
+ }
+ decompressed_size = uint3korr(comp_header);
+
+ /* When decompressed_size is 0, then the data is not compressed, and we have wasted 3 bytes */
+ /* we need to decompress the data */
+
+ if (decompressed_size) {
+ compressed_data = mnd_emalloc(net_payload_size);
+ if (FAIL == vio->data->m.network_read(vio, compressed_data, net_payload_size, conn_stats, error_info)) {
+ retval = FAIL;
+ goto end;
+ }
+ pfc->data->uncompressed_data = mysqlnd_create_read_buffer(decompressed_size);
+ retval = pfc->data->m.decode(pfc->data->uncompressed_data->data, decompressed_size, compressed_data, net_payload_size);
+ if (FAIL == retval) {
+ goto end;
+ }
+ } else {
+ DBG_INF_FMT("The server decided not to compress the data. Our job is easy. Copying %u bytes", net_payload_size);
+ pfc->data->uncompressed_data = mysqlnd_create_read_buffer(net_payload_size);
+ if (FAIL == vio->data->m.network_read(vio, pfc->data->uncompressed_data->data, net_payload_size, conn_stats, error_info)) {
+ retval = FAIL;
+ goto end;
+ }
+ }
+end:
+ if (compressed_data) {
+ mnd_efree(compressed_data);
+ }
+ DBG_RETURN(retval);
+}
+/* }}} */
+#endif /* MYSQLND_COMPRESSION_ENABLED */
+
+
+/* {{{ mysqlnd_pfc::decode */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_pfc, decode)(zend_uchar * uncompressed_data, const size_t uncompressed_data_len,
+ const zend_uchar * const compressed_data, const size_t compressed_data_len)
+{
+#ifdef MYSQLND_COMPRESSION_ENABLED
+ int error;
+ uLongf tmp_complen = uncompressed_data_len;
+ DBG_ENTER("mysqlnd_pfc::decode");
+ error = uncompress(uncompressed_data, &tmp_complen, compressed_data, compressed_data_len);
+
+ DBG_INF_FMT("compressed data: decomp_len=%lu compressed_size="MYSQLND_SZ_T_SPEC, tmp_complen, compressed_data_len);
+ if (error != Z_OK) {
+ DBG_INF_FMT("decompression NOT successful. error=%d Z_OK=%d Z_BUF_ERROR=%d Z_MEM_ERROR=%d", error, Z_OK, Z_BUF_ERROR, Z_MEM_ERROR);
+ }
+ DBG_RETURN(error == Z_OK? PASS:FAIL);
+#else
+ DBG_ENTER("mysqlnd_pfc::decode");
+ DBG_RETURN(FAIL);
+#endif
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_pfc::encode */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_pfc, encode)(zend_uchar * compress_buffer, size_t * compress_buffer_len,
+ const zend_uchar * const uncompressed_data, const size_t uncompressed_data_len)
+{
+#ifdef MYSQLND_COMPRESSION_ENABLED
+ int error;
+ uLongf tmp_complen = *compress_buffer_len;
+ DBG_ENTER("mysqlnd_pfc::encode");
+ error = compress(compress_buffer, &tmp_complen, uncompressed_data, uncompressed_data_len);
+
+ if (error != Z_OK) {
+ DBG_INF_FMT("compression NOT successful. error=%d Z_OK=%d Z_BUF_ERROR=%d Z_MEM_ERROR=%d", error, Z_OK, Z_BUF_ERROR, Z_MEM_ERROR);
+ } else {
+ *compress_buffer_len = tmp_complen;
+ DBG_INF_FMT("compression successful. compressed size=%lu", tmp_complen);
+ }
+
+ DBG_RETURN(error == Z_OK? PASS:FAIL);
+#else
+ DBG_ENTER("mysqlnd_pfc::encode");
+ DBG_RETURN(FAIL);
+#endif
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_pfc::receive */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_pfc, receive)(MYSQLND_PFC * const pfc, MYSQLND_VIO * const vio, zend_uchar * const buffer, const size_t count,
+ MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info)
+{
+ size_t to_read = count;
+ zend_uchar * p = buffer;
+
+ DBG_ENTER("mysqlnd_pfc::receive");
+#ifdef MYSQLND_COMPRESSION_ENABLED
+ if (pfc->data->compressed) {
+ if (pfc->data->uncompressed_data) {
+ size_t to_read_from_buffer = MIN(pfc->data->uncompressed_data->bytes_left(pfc->data->uncompressed_data), to_read);
+ DBG_INF_FMT("reading "MYSQLND_SZ_T_SPEC" from uncompressed_data buffer", to_read_from_buffer);
+ if (to_read_from_buffer) {
+ pfc->data->uncompressed_data->read(pfc->data->uncompressed_data, to_read_from_buffer, (zend_uchar *) p);
+ p += to_read_from_buffer;
+ to_read -= to_read_from_buffer;
+ }
+ DBG_INF_FMT("left "MYSQLND_SZ_T_SPEC" to read", to_read);
+ if (TRUE == pfc->data->uncompressed_data->is_empty(pfc->data->uncompressed_data)) {
+ /* Everything was consumed. This should never happen here, but for security */
+ pfc->data->uncompressed_data->free_buffer(&pfc->data->uncompressed_data);
+ }
+ }
+ if (to_read) {
+ zend_uchar net_header[MYSQLND_HEADER_SIZE];
+ size_t net_payload_size;
+ zend_uchar packet_no;
+
+ if (FAIL == vio->data->m.network_read(vio, net_header, MYSQLND_HEADER_SIZE, conn_stats, error_info)) {
+ DBG_RETURN(FAIL);
+ }
+ net_payload_size = uint3korr(net_header);
+ packet_no = uint1korr(net_header + 3);
+ if (pfc->data->compressed_envelope_packet_no != packet_no) {
+ DBG_ERR_FMT("Transport level: packets out of order. Expected %u received %u. Packet size="MYSQLND_SZ_T_SPEC,
+ pfc->data->compressed_envelope_packet_no, packet_no, net_payload_size);
+
+ php_error(E_WARNING, "Packets out of order. Expected %u received %u. Packet size="MYSQLND_SZ_T_SPEC,
+ pfc->data->compressed_envelope_packet_no, packet_no, net_payload_size);
+ DBG_RETURN(FAIL);
+ }
+ pfc->data->compressed_envelope_packet_no++;
+#ifdef MYSQLND_DUMP_HEADER_N_BODY
+ DBG_INF_FMT("HEADER: hwd_packet_no=%u size=%3u", packet_no, (zend_ulong) net_payload_size);
+#endif
+ /* Now let's read from the wire, decompress it and fill the read buffer */
+ pfc->data->m.read_compressed_packet_from_stream_and_fill_read_buffer(pfc, vio, net_payload_size, conn_stats, error_info);
+
+ /*
+ Now a bit of recursion - read from the read buffer,
+ if the data which we have just read from the wire
+ is not enough, then the recursive call will try to
+ satisfy it until it is satisfied.
+ */
+ DBG_RETURN(pfc->data->m.receive(pfc, vio, p, to_read, conn_stats, error_info));
+ }
+ DBG_RETURN(PASS);
+ }
+#endif /* MYSQLND_COMPRESSION_ENABLED */
+ DBG_RETURN(vio->data->m.network_read(vio, p, to_read, conn_stats, error_info));
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_pfc::set_client_option */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_pfc, set_client_option)(MYSQLND_PFC * const pfc, enum_mysqlnd_client_option option, const char * const value)
+{
+ DBG_ENTER("mysqlnd_pfc::set_client_option");
+ DBG_INF_FMT("option=%u", option);
+ switch (option) {
+ case MYSQL_OPT_COMPRESS:
+ pfc->data->flags |= MYSQLND_PROTOCOL_FLAG_USE_COMPRESSION;
+ break;
+ case MYSQL_SERVER_PUBLIC_KEY: {
+ const zend_bool pers = pfc->persistent;
+ if (pfc->data->sha256_server_public_key) {
+ mnd_pefree(pfc->data->sha256_server_public_key, pers);
+ }
+ pfc->data->sha256_server_public_key = value? mnd_pestrdup(value, pers) : NULL;
+ break;
+ case MYSQLND_OPT_NET_CMD_BUFFER_SIZE:
+ DBG_INF("MYSQLND_OPT_NET_CMD_BUFFER_SIZE");
+ if (*(unsigned int*) value < MYSQLND_NET_CMD_BUFFER_MIN_SIZE) {
+ DBG_RETURN(FAIL);
+ }
+ pfc->cmd_buffer.length = *(unsigned int*) value;
+ DBG_INF_FMT("new_length="MYSQLND_SZ_T_SPEC, pfc->cmd_buffer.length);
+ if (!pfc->cmd_buffer.buffer) {
+ pfc->cmd_buffer.buffer = mnd_pemalloc(pfc->cmd_buffer.length, pfc->persistent);
+ } else {
+ pfc->cmd_buffer.buffer = mnd_perealloc(pfc->cmd_buffer.buffer, pfc->cmd_buffer.length, pfc->persistent);
+ }
+ break;
+ }
+ default:
+ DBG_RETURN(FAIL);
+ }
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_pfc::free_contents */
+static void
+MYSQLND_METHOD(mysqlnd_pfc, free_contents)(MYSQLND_PFC * pfc)
+{
+ DBG_ENTER("mysqlnd_pfc::free_contents");
+
+#ifdef MYSQLND_COMPRESSION_ENABLED
+ if (pfc->data->uncompressed_data) {
+ pfc->data->uncompressed_data->free_buffer(&pfc->data->uncompressed_data);
+ }
+#endif
+ if (pfc->data->sha256_server_public_key) {
+ mnd_pefree(pfc->data->sha256_server_public_key, pfc->persistent);
+ pfc->data->sha256_server_public_key = NULL;
+ }
+
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_pfc::init */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_pfc, init)(MYSQLND_PFC * const pfc, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info)
+{
+ unsigned int buf_size;
+ DBG_ENTER("mysqlnd_pfc::init");
+
+ buf_size = MYSQLND_G(net_cmd_buffer_size); /* this is long, cast to unsigned int*/
+ pfc->data->m.set_client_option(pfc, MYSQLND_OPT_NET_CMD_BUFFER_SIZE, (char *) &buf_size);
+
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_pfc::dtor */
+static void
+MYSQLND_METHOD(mysqlnd_pfc, dtor)(MYSQLND_PFC * const pfc, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info)
+{
+ DBG_ENTER("mysqlnd_pfc::dtor");
+ if (pfc) {
+ pfc->data->m.free_contents(pfc);
+
+ if (pfc->cmd_buffer.buffer) {
+ DBG_INF("Freeing cmd buffer");
+ mnd_pefree(pfc->cmd_buffer.buffer, pfc->persistent);
+ pfc->cmd_buffer.buffer = NULL;
+ }
+
+ mnd_pefree(pfc->data, pfc->data->persistent);
+ mnd_pefree(pfc, pfc->persistent);
+ }
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+MYSQLND_CLASS_METHODS_START(mysqlnd_protocol_packet_frame_codec)
+ MYSQLND_METHOD(mysqlnd_pfc, init),
+ MYSQLND_METHOD(mysqlnd_pfc, dtor),
+ MYSQLND_METHOD(mysqlnd_pfc, reset),
+
+ MYSQLND_METHOD(mysqlnd_pfc, set_client_option),
+
+ MYSQLND_METHOD(mysqlnd_pfc, decode),
+ MYSQLND_METHOD(mysqlnd_pfc, encode),
+
+ MYSQLND_METHOD(mysqlnd_pfc, send),
+ MYSQLND_METHOD(mysqlnd_pfc, receive),
+
+#ifdef MYSQLND_COMPRESSION_ENABLED
+ MYSQLND_METHOD(mysqlnd_pfc, read_compressed_packet_from_stream_and_fill_read_buffer),
+#else
+ NULL,
+#endif
+
+ MYSQLND_METHOD(mysqlnd_pfc, free_contents),
+MYSQLND_CLASS_METHODS_END;
+
+
+/* {{{ mysqlnd_pfc_init */
+PHPAPI MYSQLND_PFC *
+mysqlnd_pfc_init(const zend_bool persistent, MYSQLND_CLASS_METHODS_TYPE(mysqlnd_object_factory) *object_factory, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info)
+{
+ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_object_factory) *factory = object_factory? object_factory : &MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_object_factory);
+ MYSQLND_PFC * pfc;
+ DBG_ENTER("mysqlnd_pfc_init");
+ pfc = factory->get_protocol_frame_codec(persistent, stats, error_info);
+ DBG_RETURN(pfc);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_pfc_free */
+PHPAPI void
+mysqlnd_pfc_free(MYSQLND_PFC * const pfc, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info)
+{
+ DBG_ENTER("mysqlnd_pfc_free");
+ if (pfc) {
+ pfc->data->m.dtor(pfc, stats, error_info);
+ }
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/mysqlnd/mysqlnd_protocol_frame_codec.h b/ext/mysqlnd/mysqlnd_protocol_frame_codec.h
new file mode 100644
index 0000000000..bb632c6ba9
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_protocol_frame_codec.h
@@ -0,0 +1,35 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 7 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2017 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Andrey Hristov <andrey@php.net> |
+ | Ulf Wendel <uw@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef MYSQLND_PROTOCOL_FRAME_CODEC_H
+#define MYSQLND_PROTOCOL_FRAME_CODEC_H
+
+PHPAPI MYSQLND_PFC * mysqlnd_pfc_init(const zend_bool persistent, MYSQLND_CLASS_METHODS_TYPE(mysqlnd_object_factory) *object_factory, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info);
+PHPAPI void mysqlnd_pfc_free(MYSQLND_PFC * const pfc, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info);
+
+#endif /* MYSQLND_PROTOCOL_FRAME_CODEC_H */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/mysqlnd/mysqlnd_ps.c b/ext/mysqlnd/mysqlnd_ps.c
index 52a06d7615..41c024ab16 100644
--- a/ext/mysqlnd/mysqlnd_ps.c
+++ b/ext/mysqlnd/mysqlnd_ps.c
@@ -14,14 +14,15 @@
+----------------------------------------------------------------------+
| Authors: Andrey Hristov <andrey@php.net> |
| Ulf Wendel <uw@php.net> |
- | Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
#include "php.h"
#include "mysqlnd.h"
#include "mysqlnd_wireprotocol.h"
+#include "mysqlnd_connection.h"
#include "mysqlnd_priv.h"
+#include "mysqlnd_ps.h"
#include "mysqlnd_result.h"
#include "mysqlnd_result_meta.h"
#include "mysqlnd_statistics.h"
@@ -29,9 +30,6 @@
#include "mysqlnd_block_alloc.h"
#include "mysqlnd_ext_plugin.h"
-#define MYSQLND_SILENT
-
-
const char * const mysqlnd_not_bound_as_blob = "Can't send long data for non-string/non-binary data types";
const char * const mysqlnd_stmt_not_prepared = "Statement not prepared";
@@ -40,25 +38,23 @@ enum_func_status mysqlnd_stmt_execute_generate_request(MYSQLND_STMT * const s, z
enum_func_status mysqlnd_stmt_execute_batch_generate_request(MYSQLND_STMT * const s, zend_uchar ** request, size_t *request_len, zend_bool * free_buffer);
static void mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const stmt);
-static void mysqlnd_stmt_separate_one_result_bind(MYSQLND_STMT * const stmt, unsigned int param_no);
+static void mysqlnd_stmt_separate_one_result_bind(MYSQLND_STMT * const stmt, const unsigned int param_no);
/* {{{ mysqlnd_stmt::store_result */
static MYSQLND_RES *
MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
enum_func_status ret;
- MYSQLND_CONN_DATA * conn;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
+ MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
MYSQLND_RES * result;
DBG_ENTER("mysqlnd_stmt::store_result");
- if (!stmt || !stmt->conn || !stmt->result) {
+ if (!stmt || !conn || !stmt->result) {
DBG_RETURN(NULL);
}
DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
- conn = stmt->conn;
-
/* be compliant with libmysql - NULL will turn */
if (!stmt->field_count) {
DBG_RETURN(NULL);
@@ -70,18 +66,16 @@ MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const s)
}
/* Nothing to store for UPSERT/LOAD DATA*/
- if (CONN_GET_STATE(conn) != CONN_FETCHING_DATA ||
- stmt->state != MYSQLND_STMT_WAITING_USE_OR_STORE)
+ if (GET_CONNECTION_STATE(&conn->state) != CONN_FETCHING_DATA || stmt->state != MYSQLND_STMT_WAITING_USE_OR_STORE)
{
- SET_CLIENT_ERROR(*conn->error_info, CR_COMMANDS_OUT_OF_SYNC,
- UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
+ SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
DBG_RETURN(NULL);
}
stmt->default_rset_handler = s->m->store_result;
- SET_EMPTY_ERROR(*stmt->error_info);
- SET_EMPTY_ERROR(*conn->error_info);
+ SET_EMPTY_ERROR(stmt->error_info);
+ SET_EMPTY_ERROR(conn->error_info);
MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_PS_BUFFERED_SETS);
result = stmt->result;
@@ -90,7 +84,7 @@ MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const s)
result->stored_data = (MYSQLND_RES_BUFFERED *) mysqlnd_result_buffered_zval_init(result->field_count, TRUE, result->persistent);
if (!result->stored_data) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
DBG_RETURN(NULL);
}
@@ -99,19 +93,18 @@ MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const s)
result->stored_data->m.fetch_row = mysqlnd_stmt_fetch_row_buffered;
if (PASS == ret) {
- /* Overflow ? */
if (result->stored_data->type == MYSQLND_BUFFERED_TYPE_ZVAL) {
MYSQLND_RES_BUFFERED_ZVAL * set = (MYSQLND_RES_BUFFERED_ZVAL *) result->stored_data;
if (result->stored_data->row_count) {
/* don't try to allocate more than possible - mnd_XXalloc expects size_t, and it can have narrower range than uint64_t */
if (result->stored_data->row_count * result->meta->field_count * sizeof(zval *) > SIZE_MAX) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
DBG_RETURN(NULL);
}
/* if pecalloc is used valgrind barks gcc version 4.3.1 20080507 (prerelease) [gcc-4_3-branch revision 135036] (SUSE Linux) */
set->data = mnd_emalloc((size_t)(result->stored_data->row_count * result->meta->field_count * sizeof(zval)));
if (!set->data) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
DBG_RETURN(NULL);
}
memset(set->data, 0, (size_t)(result->stored_data->row_count * result->meta->field_count * sizeof(zval)));
@@ -123,11 +116,11 @@ MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const s)
}
/* libmysql API docs say it should be so for SELECT statements */
- stmt->upsert_status->affected_rows = stmt->result->stored_data->row_count;
+ UPSERT_STATUS_SET_AFFECTED_ROWS(stmt->upsert_status, stmt->result->stored_data->row_count);
stmt->state = MYSQLND_STMT_USE_OR_STORE_CALLED;
} else {
- COPY_CLIENT_ERROR(*conn->error_info, result->stored_data->error_info);
+ COPY_CLIENT_ERROR(conn->error_info, result->stored_data->error_info);
stmt->result->m.free_result_contents(stmt->result);
mnd_pefree(stmt->result, stmt->result->persistent);
stmt->result = NULL;
@@ -143,18 +136,16 @@ MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const s)
static MYSQLND_RES *
MYSQLND_METHOD(mysqlnd_stmt, get_result)(MYSQLND_STMT * const s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
- MYSQLND_CONN_DATA * conn;
- MYSQLND_RES *result;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
+ MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
+ MYSQLND_RES * result;
DBG_ENTER("mysqlnd_stmt::get_result");
- if (!stmt || !stmt->conn || !stmt->result) {
+ if (!stmt || !conn || !stmt->result) {
DBG_RETURN(NULL);
}
DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
- conn = stmt->conn;
-
/* be compliant with libmysql - NULL will turn */
if (!stmt->field_count) {
DBG_RETURN(NULL);
@@ -166,35 +157,34 @@ MYSQLND_METHOD(mysqlnd_stmt, get_result)(MYSQLND_STMT * const s)
}
/* Nothing to store for UPSERT/LOAD DATA*/
- if (CONN_GET_STATE(conn) != CONN_FETCHING_DATA || stmt->state != MYSQLND_STMT_WAITING_USE_OR_STORE) {
- SET_CLIENT_ERROR(*conn->error_info, CR_COMMANDS_OUT_OF_SYNC,
- UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
+ if (GET_CONNECTION_STATE(&conn->state) != CONN_FETCHING_DATA || stmt->state != MYSQLND_STMT_WAITING_USE_OR_STORE) {
+ SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
DBG_RETURN(NULL);
}
- SET_EMPTY_ERROR(*stmt->error_info);
- SET_EMPTY_ERROR(*conn->error_info);
+ SET_EMPTY_ERROR(stmt->error_info);
+ SET_EMPTY_ERROR(conn->error_info);
MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_BUFFERED_SETS);
do {
result = conn->m->result_init(stmt->result->field_count, stmt->persistent);
if (!result) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
break;
}
result->meta = stmt->result->meta->m->clone_metadata(stmt->result->meta, FALSE);
if (!result->meta) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
break;
}
if ((result = result->m.store_result(result, conn, MYSQLND_STORE_PS | MYSQLND_STORE_NO_COPY))) {
- stmt->upsert_status->affected_rows = result->stored_data->row_count;
+ UPSERT_STATUS_SET_AFFECTED_ROWS(stmt->upsert_status, result->stored_data->row_count);
stmt->state = MYSQLND_STMT_PREPARED;
result->type = MYSQLND_RES_PS_BUF;
} else {
- COPY_CLIENT_ERROR(*stmt->error_info, *conn->error_info);
+ COPY_CLIENT_ERROR(stmt->error_info, *conn->error_info);
stmt->state = MYSQLND_STMT_PREPARED;
break;
}
@@ -213,12 +203,11 @@ MYSQLND_METHOD(mysqlnd_stmt, get_result)(MYSQLND_STMT * const s)
static zend_bool
MYSQLND_METHOD(mysqlnd_stmt, more_results)(const MYSQLND_STMT * s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
+ MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
DBG_ENTER("mysqlnd_stmt::more_results");
/* (conn->state == CONN_NEXT_RESULT_PENDING) too */
- DBG_RETURN((stmt && stmt->conn && (stmt->conn->m->get_server_status(stmt->conn) & SERVER_MORE_RESULTS_EXISTS))?
- TRUE:
- FALSE);
+ DBG_RETURN((stmt && conn && (conn->m->get_server_status(conn) & SERVER_MORE_RESULTS_EXISTS))? TRUE: FALSE);
}
/* }}} */
@@ -227,21 +216,20 @@ MYSQLND_METHOD(mysqlnd_stmt, more_results)(const MYSQLND_STMT * s)
static enum_func_status
MYSQLND_METHOD(mysqlnd_stmt, next_result)(MYSQLND_STMT * s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
- MYSQLND_CONN_DATA * conn;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
+ MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
DBG_ENTER("mysqlnd_stmt::next_result");
- if (!stmt || !stmt->conn || !stmt->result) {
+ if (!stmt || !conn || !stmt->result) {
DBG_RETURN(FAIL);
}
- conn = stmt->conn;
DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
- if (CONN_GET_STATE(conn) != CONN_NEXT_RESULT_PENDING || !(conn->upsert_status->server_status & SERVER_MORE_RESULTS_EXISTS)) {
+ if (GET_CONNECTION_STATE(&conn->state) != CONN_NEXT_RESULT_PENDING || !(UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status) & SERVER_MORE_RESULTS_EXISTS)) {
DBG_RETURN(FAIL);
}
- DBG_INF_FMT("server_status=%u cursor=%u", stmt->upsert_status->server_status, stmt->upsert_status->server_status & SERVER_STATUS_CURSOR_EXISTS);
+ DBG_INF_FMT("server_status=%u cursor=%u", UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status), UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status) & SERVER_STATUS_CURSOR_EXISTS);
/* Free space for next result */
s->m->free_stmt_result(s);
@@ -257,27 +245,28 @@ MYSQLND_METHOD(mysqlnd_stmt, next_result)(MYSQLND_STMT * s)
static enum_func_status
mysqlnd_stmt_skip_metadata(MYSQLND_STMT * s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
+ MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
/* Follows parameter metadata, we have just to skip it, as libmysql does */
unsigned int i = 0;
enum_func_status ret = FAIL;
MYSQLND_PACKET_RES_FIELD * field_packet;
DBG_ENTER("mysqlnd_stmt_skip_metadata");
- if (!stmt || !stmt->conn || !stmt->conn->protocol) {
+ if (!stmt || !conn) {
DBG_RETURN(FAIL);
}
DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
- field_packet = stmt->conn->protocol->m.get_result_field_packet(stmt->conn->protocol, FALSE);
+ field_packet = conn->payload_decoder_factory->m.get_result_field_packet(conn->payload_decoder_factory, FALSE);
if (!field_packet) {
- SET_OOM_ERROR(*stmt->error_info);
- SET_OOM_ERROR(*stmt->conn->error_info);
+ SET_OOM_ERROR(stmt->error_info);
+ SET_OOM_ERROR(conn->error_info);
} else {
ret = PASS;
field_packet->skip_parsing = TRUE;
for (;i < stmt->param_count; i++) {
- if (FAIL == PACKET_READ(field_packet, stmt->conn)) {
+ if (FAIL == PACKET_READ(field_packet)) {
ret = FAIL;
break;
}
@@ -294,38 +283,39 @@ mysqlnd_stmt_skip_metadata(MYSQLND_STMT * s)
static enum_func_status
mysqlnd_stmt_read_prepare_response(MYSQLND_STMT * s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
+ MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
MYSQLND_PACKET_PREPARE_RESPONSE * prepare_resp;
enum_func_status ret = FAIL;
DBG_ENTER("mysqlnd_stmt_read_prepare_response");
- if (!stmt || !stmt->conn || !stmt->conn->protocol) {
+ if (!stmt || !conn) {
DBG_RETURN(FAIL);
}
DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
- prepare_resp = stmt->conn->protocol->m.get_prepare_response_packet(stmt->conn->protocol, FALSE);
+ prepare_resp = conn->payload_decoder_factory->m.get_prepare_response_packet(conn->payload_decoder_factory, FALSE);
if (!prepare_resp) {
- SET_OOM_ERROR(*stmt->error_info);
- SET_OOM_ERROR(*stmt->conn->error_info);
+ SET_OOM_ERROR(stmt->error_info);
+ SET_OOM_ERROR(conn->error_info);
goto done;
}
- if (FAIL == PACKET_READ(prepare_resp, stmt->conn)) {
+ if (FAIL == PACKET_READ(prepare_resp)) {
goto done;
}
if (0xFF == prepare_resp->error_code) {
- COPY_CLIENT_ERROR(*stmt->error_info, prepare_resp->error_info);
- COPY_CLIENT_ERROR(*stmt->conn->error_info, prepare_resp->error_info);
+ COPY_CLIENT_ERROR(stmt->error_info, prepare_resp->error_info);
+ COPY_CLIENT_ERROR(conn->error_info, prepare_resp->error_info);
goto done;
}
ret = PASS;
stmt->stmt_id = prepare_resp->stmt_id;
- stmt->warning_count = stmt->conn->upsert_status->warning_count = prepare_resp->warning_count;
- stmt->field_count = stmt->conn->field_count = prepare_resp->field_count;
+ UPSERT_STATUS_SET_WARNINGS(conn->upsert_status, prepare_resp->warning_count);
+ UPSERT_STATUS_SET_AFFECTED_ROWS(stmt->upsert_status, 0); /* be like libmysql */
+ stmt->field_count = conn->field_count = prepare_resp->field_count;
stmt->param_count = prepare_resp->param_count;
- stmt->upsert_status->affected_rows = 0; /* be like libmysql */
done:
PACKET_FREE(prepare_resp);
@@ -338,31 +328,37 @@ done:
static enum_func_status
mysqlnd_stmt_prepare_read_eof(MYSQLND_STMT * s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
+ MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
MYSQLND_PACKET_EOF * fields_eof;
enum_func_status ret = FAIL;
DBG_ENTER("mysqlnd_stmt_prepare_read_eof");
- if (!stmt || !stmt->conn || !stmt->conn->protocol) {
+ if (!stmt || !conn) {
DBG_RETURN(FAIL);
}
DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
- fields_eof = stmt->conn->protocol->m.get_eof_packet(stmt->conn->protocol, FALSE);
+ fields_eof = conn->payload_decoder_factory->m.get_eof_packet(conn->payload_decoder_factory, FALSE);
if (!fields_eof) {
- SET_OOM_ERROR(*stmt->error_info);
- SET_OOM_ERROR(*stmt->conn->error_info);
+ SET_OOM_ERROR(stmt->error_info);
+ SET_OOM_ERROR(conn->error_info);
} else {
- if (FAIL == (ret = PACKET_READ(fields_eof, stmt->conn))) {
+ if (FAIL == (ret = PACKET_READ(fields_eof))) {
if (stmt->result) {
stmt->result->m.free_result_contents(stmt->result);
mnd_pefree(stmt->result, stmt->result->persistent);
+ /* XXX: This will crash, because we will null also the methods.
+ But seems it happens in extreme cases or doesn't. Should be fixed by exporting a function
+ (from mysqlnd_driver.c?) to do the reset.
+ This bad handling is also in mysqlnd_result.c
+ */
memset(stmt, 0, sizeof(MYSQLND_STMT_DATA));
stmt->state = MYSQLND_STMT_INITTED;
}
} else {
- stmt->upsert_status->server_status = fields_eof->server_status;
- stmt->upsert_status->warning_count = fields_eof->warning_count;
+ UPSERT_STATUS_SET_SERVER_STATUS(stmt->upsert_status, fields_eof->server_status);
+ UPSERT_STATUS_SET_WARNINGS(stmt->upsert_status, fields_eof->warning_count);
stmt->state = MYSQLND_STMT_PREPARED;
}
PACKET_FREE(fields_eof);
@@ -375,24 +371,25 @@ mysqlnd_stmt_prepare_read_eof(MYSQLND_STMT * s)
/* {{{ mysqlnd_stmt::prepare */
static enum_func_status
-MYSQLND_METHOD(mysqlnd_stmt, prepare)(MYSQLND_STMT * const s, const char * const query, unsigned int query_len)
+MYSQLND_METHOD(mysqlnd_stmt, prepare)(MYSQLND_STMT * const s, const char * const query, const size_t query_len)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
+ MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
MYSQLND_STMT * s_to_prepare = s;
MYSQLND_STMT_DATA * stmt_to_prepare = stmt;
DBG_ENTER("mysqlnd_stmt::prepare");
- if (!stmt || !stmt->conn) {
+ if (!stmt || !conn) {
DBG_RETURN(FAIL);
}
DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
DBG_INF_FMT("query=%s", query);
- SET_ERROR_AFF_ROWS(stmt);
- SET_ERROR_AFF_ROWS(stmt->conn);
+ UPSERT_STATUS_SET_AFFECTED_ROWS_TO_ERROR(stmt->upsert_status);
+ UPSERT_STATUS_SET_AFFECTED_ROWS_TO_ERROR(conn->upsert_status);
- SET_EMPTY_ERROR(*stmt->error_info);
- SET_EMPTY_ERROR(*stmt->conn->error_info);
+ SET_EMPTY_ERROR(stmt->error_info);
+ SET_EMPTY_ERROR(conn->error_info);
if (stmt->state > MYSQLND_STMT_INITTED) {
/* See if we have to clean the wire */
@@ -409,16 +406,27 @@ MYSQLND_METHOD(mysqlnd_stmt, prepare)(MYSQLND_STMT * const s, const char * const
Create a new test statement, which we will prepare, but if anything
fails, we will scrap it.
*/
- s_to_prepare = stmt->conn->m->stmt_init(stmt->conn);
+ s_to_prepare = conn->m->stmt_init(conn);
if (!s_to_prepare) {
goto fail;
}
stmt_to_prepare = s_to_prepare->data;
}
- if (FAIL == stmt_to_prepare->conn->m->simple_command(stmt_to_prepare->conn, COM_STMT_PREPARE, (const zend_uchar *) query, query_len, PROT_LAST, FALSE, TRUE) ||
- FAIL == mysqlnd_stmt_read_prepare_response(s_to_prepare))
{
+ enum_func_status ret = FAIL;
+ const MYSQLND_CSTRING query_string = {query, query_len};
+ struct st_mysqlnd_protocol_command * command = conn->command_factory(COM_STMT_PREPARE, conn, query_string);
+ if (command) {
+ ret = command->run(command);
+ command->free_command(command);
+ }
+ if (FAIL == ret) {
+ goto fail;
+ }
+ }
+
+ if (FAIL == mysqlnd_stmt_read_prepare_response(s_to_prepare)) {
goto fail;
}
@@ -436,19 +444,19 @@ MYSQLND_METHOD(mysqlnd_stmt, prepare)(MYSQLND_STMT * const s, const char * const
no metadata at prepare.
*/
if (stmt_to_prepare->field_count) {
- MYSQLND_RES * result = stmt->conn->m->result_init(stmt_to_prepare->field_count, stmt_to_prepare->persistent);
+ MYSQLND_RES * result = conn->m->result_init(stmt_to_prepare->field_count, stmt_to_prepare->persistent);
if (!result) {
- SET_OOM_ERROR(*stmt->conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
goto fail;
}
/* Allocate the result now as it is needed for the reading of metadata */
stmt_to_prepare->result = result;
- result->conn = stmt_to_prepare->conn->m->get_reference(stmt_to_prepare->conn);
+ result->conn = conn->m->get_reference(conn);
result->type = MYSQLND_RES_PS_BUF;
- if (FAIL == result->m.read_result_metadata(result, stmt_to_prepare->conn) ||
+ if (FAIL == result->m.read_result_metadata(result, conn) ||
FAIL == mysqlnd_stmt_prepare_read_eof(s_to_prepare))
{
goto fail;
@@ -490,23 +498,22 @@ fail:
static enum_func_status
mysqlnd_stmt_execute_parse_response(MYSQLND_STMT * const s, enum_mysqlnd_parse_exec_response_type type)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
+ MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
enum_func_status ret;
- MYSQLND_CONN_DATA * conn;
DBG_ENTER("mysqlnd_stmt_execute_parse_response");
- if (!stmt || !stmt->conn) {
+ if (!stmt || !conn) {
DBG_RETURN(FAIL);
}
- conn = stmt->conn;
- CONN_SET_STATE(conn, CONN_QUERY_SENT);
+ SET_CONNECTION_STATE(&conn->state, CONN_QUERY_SENT);
- ret = mysqlnd_query_read_result_set_header(stmt->conn, s);
+ ret = conn->m->query_read_result_set_header(conn, s);
if (ret == FAIL) {
- COPY_CLIENT_ERROR(*stmt->error_info, *conn->error_info);
- memset(stmt->upsert_status, 0, sizeof(*stmt->upsert_status));
- stmt->upsert_status->affected_rows = conn->upsert_status->affected_rows;
- if (CONN_GET_STATE(conn) == CONN_QUIT_SENT) {
+ COPY_CLIENT_ERROR(stmt->error_info, *conn->error_info);
+ UPSERT_STATUS_RESET(stmt->upsert_status);
+ UPSERT_STATUS_SET_AFFECTED_ROWS(stmt->upsert_status, UPSERT_STATUS_GET_AFFECTED_ROWS(conn->upsert_status));
+ if (GET_CONNECTION_STATE(&conn->state) == CONN_QUIT_SENT) {
/* close the statement here, the connection has been closed */
}
stmt->state = MYSQLND_STMT_PREPARED;
@@ -519,9 +526,13 @@ mysqlnd_stmt_execute_parse_response(MYSQLND_STMT * const s, enum_mysqlnd_parse_e
value is > LONG_MAX or < LONG_MIN, there is string conversion and we have
to resend the types. Next execution will also need to resend the type.
*/
- SET_EMPTY_ERROR(*stmt->error_info);
- SET_EMPTY_ERROR(*stmt->conn->error_info);
- *stmt->upsert_status = *conn->upsert_status; /* copy status */
+ SET_EMPTY_ERROR(stmt->error_info);
+ SET_EMPTY_ERROR(conn->error_info);
+ UPSERT_STATUS_SET_WARNINGS(stmt->upsert_status, UPSERT_STATUS_GET_WARNINGS(conn->upsert_status));
+ UPSERT_STATUS_SET_AFFECTED_ROWS(stmt->upsert_status, UPSERT_STATUS_GET_AFFECTED_ROWS(conn->upsert_status));
+ UPSERT_STATUS_SET_SERVER_STATUS(stmt->upsert_status, UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status));
+ UPSERT_STATUS_SET_LAST_INSERT_ID(stmt->upsert_status, UPSERT_STATUS_GET_LAST_INSERT_ID(conn->upsert_status));
+
stmt->state = MYSQLND_STMT_EXECUTED;
if (conn->last_query_type == QUERY_UPSERT || conn->last_query_type == QUERY_LOAD_LOCAL) {
DBG_INF("PASS");
@@ -534,7 +545,7 @@ mysqlnd_stmt_execute_parse_response(MYSQLND_STMT * const s, enum_mysqlnd_parse_e
For SHOW we don't create (bypasses PS in server)
a result set at prepare and thus a connection was missing
*/
- stmt->result->conn = stmt->conn->m->get_reference(stmt->conn);
+ stmt->result->conn = conn->m->get_reference(conn);
}
/* Update stmt->field_count as SHOW sets it to 0 at prepare */
@@ -551,13 +562,13 @@ mysqlnd_stmt_execute_parse_response(MYSQLND_STMT * const s, enum_mysqlnd_parse_e
use_result() or store_result() and we should be able to scrap the
data on the line, if he just decides to close the statement.
*/
- DBG_INF_FMT("server_status=%u cursor=%u", stmt->upsert_status->server_status,
- stmt->upsert_status->server_status & SERVER_STATUS_CURSOR_EXISTS);
+ DBG_INF_FMT("server_status=%u cursor=%u", UPSERT_STATUS_GET_SERVER_STATUS(stmt->upsert_status),
+ UPSERT_STATUS_GET_SERVER_STATUS(stmt->upsert_status) & SERVER_STATUS_CURSOR_EXISTS);
- if (stmt->upsert_status->server_status & SERVER_STATUS_CURSOR_EXISTS) {
+ if (UPSERT_STATUS_GET_SERVER_STATUS(stmt->upsert_status) & SERVER_STATUS_CURSOR_EXISTS) {
DBG_INF("cursor exists");
stmt->cursor_exists = TRUE;
- CONN_SET_STATE(conn, CONN_READY);
+ SET_CONNECTION_STATE(&conn->state, CONN_READY);
/* Only cursor read */
stmt->default_rset_handler = s->m->use_result;
DBG_INF("use_result");
@@ -585,7 +596,7 @@ mysqlnd_stmt_execute_parse_response(MYSQLND_STMT * const s, enum_mysqlnd_parse_e
}
}
#ifndef MYSQLND_DONT_SKIP_OUT_PARAMS_RESULTSET
- if (stmt->upsert_status->server_status & SERVER_PS_OUT_PARAMS) {
+ if (UPSERT_STATUS_GET_SERVER_STATUS(stmt->upsert_status) & SERVER_PS_OUT_PARAMS) {
s->m->free_stmt_content(s);
DBG_INF("PS OUT Variable RSet, skipping");
/* OUT params result set. Skip for now to retain compatibility */
@@ -593,10 +604,10 @@ mysqlnd_stmt_execute_parse_response(MYSQLND_STMT * const s, enum_mysqlnd_parse_e
}
#endif
- DBG_INF_FMT("server_status=%u cursor=%u", stmt->upsert_status->server_status, stmt->upsert_status->server_status & SERVER_STATUS_CURSOR_EXISTS);
+ DBG_INF_FMT("server_status=%u cursor=%u", UPSERT_STATUS_GET_SERVER_STATUS(stmt->upsert_status), UPSERT_STATUS_GET_SERVER_STATUS(stmt->upsert_status) & SERVER_STATUS_CURSOR_EXISTS);
- if (ret == PASS && conn->last_query_type == QUERY_UPSERT && stmt->upsert_status->affected_rows) {
- MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn->stats, STAT_ROWS_AFFECTED_PS, stmt->upsert_status->affected_rows);
+ if (ret == PASS && conn->last_query_type == QUERY_UPSERT && UPSERT_STATUS_GET_AFFECTED_ROWS(stmt->upsert_status)) {
+ MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn->stats, STAT_ROWS_AFFECTED_PS, UPSERT_STATUS_GET_AFFECTED_ROWS(stmt->upsert_status));
}
DBG_INF(ret == PASS? "PASS":"FAIL");
@@ -622,24 +633,23 @@ MYSQLND_METHOD(mysqlnd_stmt, execute)(MYSQLND_STMT * const s)
/* {{{ mysqlnd_stmt::send_execute */
static enum_func_status
-MYSQLND_METHOD(mysqlnd_stmt, send_execute)(MYSQLND_STMT * const s, enum_mysqlnd_send_execute_type type, zval * read_cb, zval * err_cb)
+MYSQLND_METHOD(mysqlnd_stmt, send_execute)(MYSQLND_STMT * const s, const enum_mysqlnd_send_execute_type type, zval * read_cb, zval * err_cb)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
+ MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
enum_func_status ret;
- MYSQLND_CONN_DATA * conn;
zend_uchar *request = NULL;
size_t request_len;
zend_bool free_request;
DBG_ENTER("mysqlnd_stmt::send_execute");
- if (!stmt || !stmt->conn) {
+ if (!stmt || !conn) {
DBG_RETURN(FAIL);
}
- conn = stmt->conn;
DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
- SET_ERROR_AFF_ROWS(stmt);
- SET_ERROR_AFF_ROWS(stmt->conn);
+ UPSERT_STATUS_SET_AFFECTED_ROWS_TO_ERROR(stmt->upsert_status);
+ UPSERT_STATUS_SET_AFFECTED_ROWS_TO_ERROR(conn->upsert_status);
if (stmt->result && stmt->state >= MYSQLND_STMT_PREPARED && stmt->field_count) {
/*
@@ -687,9 +697,7 @@ MYSQLND_METHOD(mysqlnd_stmt, send_execute)(MYSQLND_STMT * const s, enum_mysqlnd_
stmt->state = MYSQLND_STMT_PREPARED;
} else if (stmt->state < MYSQLND_STMT_PREPARED) {
/* Only initted - error */
- SET_CLIENT_ERROR(*conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE,
- mysqlnd_out_of_sync);
- SET_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
+ SET_CLIENT_ERROR(stmt->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
DBG_INF("FAIL");
DBG_RETURN(FAIL);
}
@@ -697,8 +705,7 @@ MYSQLND_METHOD(mysqlnd_stmt, send_execute)(MYSQLND_STMT * const s, enum_mysqlnd_
if (stmt->param_count) {
unsigned int i, not_bound = 0;
if (!stmt->param_bind) {
- SET_STMT_ERROR(stmt, CR_PARAMS_NOT_BOUND, UNKNOWN_SQLSTATE,
- "No data supplied for parameters in prepared statement");
+ SET_CLIENT_ERROR(stmt->error_info, CR_PARAMS_NOT_BOUND, UNKNOWN_SQLSTATE, "No data supplied for parameters in prepared statement");
DBG_INF("FAIL");
DBG_RETURN(FAIL);
}
@@ -711,7 +718,7 @@ MYSQLND_METHOD(mysqlnd_stmt, send_execute)(MYSQLND_STMT * const s, enum_mysqlnd_
char * msg;
mnd_sprintf(&msg, 0, "No data supplied for %u parameter%s in prepared statement",
not_bound, not_bound>1 ?"s":"");
- SET_STMT_ERROR(stmt, CR_PARAMS_NOT_BOUND, UNKNOWN_SQLSTATE, msg);
+ SET_CLIENT_ERROR(stmt->error_info, CR_PARAMS_NOT_BOUND, UNKNOWN_SQLSTATE, msg);
if (msg) {
mnd_sprintf_free(msg);
}
@@ -721,12 +728,15 @@ MYSQLND_METHOD(mysqlnd_stmt, send_execute)(MYSQLND_STMT * const s, enum_mysqlnd_
}
ret = s->m->generate_execute_request(s, &request, &request_len, &free_request);
if (ret == PASS) {
- /* support for buffer types should be added here ! */
- ret = stmt->conn->m->simple_command(stmt->conn, COM_STMT_EXECUTE, request, request_len,
- PROT_LAST /* we will handle the response packet*/,
- FALSE, FALSE);
+ const MYSQLND_CSTRING payload = {(const char*) request, request_len};
+ struct st_mysqlnd_protocol_command * command = conn->command_factory(COM_STMT_EXECUTE, conn, payload);
+ ret = FAIL;
+ if (command) {
+ ret = command->run(command);
+ command->free_command(command);
+ }
} else {
- SET_STMT_ERROR(stmt, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "Couldn't generate the request. Possibly OOM.");
+ SET_CLIENT_ERROR(stmt->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "Couldn't generate the request. Possibly OOM.");
}
if (free_request) {
@@ -734,7 +744,7 @@ MYSQLND_METHOD(mysqlnd_stmt, send_execute)(MYSQLND_STMT * const s, enum_mysqlnd_
}
if (ret == FAIL) {
- COPY_CLIENT_ERROR(*stmt->error_info, *conn->error_info);
+ COPY_CLIENT_ERROR(stmt->error_info, *conn->error_info);
DBG_INF("FAIL");
DBG_RETURN(FAIL);
}
@@ -747,10 +757,10 @@ MYSQLND_METHOD(mysqlnd_stmt, send_execute)(MYSQLND_STMT * const s, enum_mysqlnd_
/* {{{ mysqlnd_stmt_fetch_row_buffered */
enum_func_status
-mysqlnd_stmt_fetch_row_buffered(MYSQLND_RES * result, void * param, unsigned int flags, zend_bool * fetched_anything)
+mysqlnd_stmt_fetch_row_buffered(MYSQLND_RES * result, void * param, const unsigned int flags, zend_bool * fetched_anything)
{
MYSQLND_STMT * s = (MYSQLND_STMT *) param;
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
const MYSQLND_RES_METADATA * const meta = result->meta;
unsigned int field_count = meta->field_count;
@@ -848,12 +858,13 @@ mysqlnd_stmt_fetch_row_buffered(MYSQLND_RES * result, void * param, unsigned int
/* {{{ mysqlnd_stmt_fetch_row_unbuffered */
enum_func_status
-mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES * result, void * param, unsigned int flags, zend_bool * fetched_anything)
+mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES * result, void * param, const unsigned int flags, zend_bool * fetched_anything)
{
enum_func_status ret;
MYSQLND_STMT * s = (MYSQLND_STMT *) param;
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
MYSQLND_PACKET_ROW * row_packet;
+ MYSQLND_CONN_DATA * conn = result->conn;
const MYSQLND_RES_METADATA * const meta = result->meta;
DBG_ENTER("mysqlnd_stmt_fetch_row_unbuffered");
@@ -865,9 +876,8 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES * result, void * param, unsigned i
DBG_INF("EOF already reached");
DBG_RETURN(PASS);
}
- if (CONN_GET_STATE(result->conn) != CONN_FETCHING_DATA) {
- SET_CLIENT_ERROR(*result->conn->error_info, CR_COMMANDS_OUT_OF_SYNC,
- UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
+ if (GET_CONNECTION_STATE(&conn->state) != CONN_FETCHING_DATA) {
+ SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
DBG_ERR("command out of sync");
DBG_RETURN(FAIL);
}
@@ -882,11 +892,11 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES * result, void * param, unsigned i
If we skip rows (stmt == NULL || stmt->result_bind == NULL) we have to
result->unbuf->m.free_last_data() before it. The function returns always true.
*/
- if (PASS == (ret = PACKET_READ(row_packet, result->conn)) && !row_packet->eof) {
+ if (PASS == (ret = PACKET_READ(row_packet)) && !row_packet->eof) {
unsigned int i, field_count = result->field_count;
if (!row_packet->skip_extraction) {
- result->unbuf->m.free_last_data(result->unbuf, result->conn? result->conn->stats : NULL);
+ result->unbuf->m.free_last_data(result->unbuf, conn->stats);
result->unbuf->last_row_data = row_packet->fields;
result->unbuf->last_row_buffer = row_packet->row_buffer;
@@ -897,8 +907,8 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES * result, void * param, unsigned i
result->unbuf->last_row_data,
row_packet->field_count,
row_packet->fields_metadata,
- result->conn->options->int_and_float_native,
- result->conn->stats))
+ conn->options->int_and_float_native,
+ conn->stats))
{
DBG_RETURN(FAIL);
}
@@ -917,8 +927,7 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES * result, void * param, unsigned i
zval_dtor(result);
#endif
if (!Z_ISNULL_P(data)) {
- if ((Z_TYPE_P(data) == IS_STRING) &&
- (meta->fields[i].max_length < (zend_ulong) Z_STRLEN_P(data))) {
+ if ((Z_TYPE_P(data) == IS_STRING) && (meta->fields[i].max_length < (zend_ulong) Z_STRLEN_P(data))){
meta->fields[i].max_length = Z_STRLEN_P(data);
}
ZVAL_COPY_VALUE(result, data);
@@ -929,7 +938,7 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES * result, void * param, unsigned i
}
}
}
- MYSQLND_INC_CONN_STATISTIC(stmt->conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT_PS_UNBUF);
+ MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT_PS_UNBUF);
} else {
DBG_INF("skipping extraction");
/*
@@ -938,7 +947,8 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES * result, void * param, unsigned i
the bound variables. Thus we need to do part of what it does or Zend will
report leaks.
*/
- row_packet->row_buffer->free_chunk(row_packet->row_buffer);
+ row_packet->result_set_memory_pool->free_chunk(
+ row_packet->result_set_memory_pool, row_packet->row_buffer);
row_packet->row_buffer = NULL;
}
@@ -946,26 +956,27 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES * result, void * param, unsigned i
*fetched_anything = TRUE;
} else if (ret == FAIL) {
if (row_packet->error_info.error_no) {
- COPY_CLIENT_ERROR(*stmt->conn->error_info, row_packet->error_info);
- COPY_CLIENT_ERROR(*stmt->error_info, row_packet->error_info);
+ COPY_CLIENT_ERROR(conn->error_info, row_packet->error_info);
+ COPY_CLIENT_ERROR(stmt->error_info, row_packet->error_info);
}
- CONN_SET_STATE(result->conn, CONN_READY);
+ SET_CONNECTION_STATE(&conn->state, CONN_READY);
result->unbuf->eof_reached = TRUE; /* so next time we won't get an error */
} else if (row_packet->eof) {
DBG_INF("EOF");
/* Mark the connection as usable again */
result->unbuf->eof_reached = TRUE;
- memset(result->conn->upsert_status, 0, sizeof(*result->conn->upsert_status));
- result->conn->upsert_status->warning_count = row_packet->warning_count;
- result->conn->upsert_status->server_status = row_packet->server_status;
+ UPSERT_STATUS_RESET(conn->upsert_status);
+ UPSERT_STATUS_SET_WARNINGS(conn->upsert_status, row_packet->warning_count);
+ UPSERT_STATUS_SET_SERVER_STATUS(conn->upsert_status, row_packet->server_status);
+
/*
result->row_packet will be cleaned when
destroying the result object
*/
- if (result->conn->upsert_status->server_status & SERVER_MORE_RESULTS_EXISTS) {
- CONN_SET_STATE(result->conn, CONN_NEXT_RESULT_PENDING);
+ if (UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status) & SERVER_MORE_RESULTS_EXISTS) {
+ SET_CONNECTION_STATE(&conn->state, CONN_NEXT_RESULT_PENDING);
} else {
- CONN_SET_STATE(result->conn, CONN_READY);
+ SET_CONNECTION_STATE(&conn->state, CONN_READY);
}
}
@@ -979,32 +990,29 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES * result, void * param, unsigned i
static MYSQLND_RES *
MYSQLND_METHOD(mysqlnd_stmt, use_result)(MYSQLND_STMT * s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
+ MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
MYSQLND_RES * result;
- MYSQLND_CONN_DATA * conn;
DBG_ENTER("mysqlnd_stmt::use_result");
- if (!stmt || !stmt->conn || !stmt->result) {
+ if (!stmt || !conn || !stmt->result) {
DBG_RETURN(NULL);
}
DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
- conn = stmt->conn;
-
if (!stmt->field_count ||
- (!stmt->cursor_exists && CONN_GET_STATE(conn) != CONN_FETCHING_DATA) ||
- (stmt->cursor_exists && CONN_GET_STATE(conn) != CONN_READY) ||
+ (!stmt->cursor_exists && GET_CONNECTION_STATE(&conn->state) != CONN_FETCHING_DATA) ||
+ (stmt->cursor_exists && GET_CONNECTION_STATE(&conn->state) != CONN_READY) ||
(stmt->state != MYSQLND_STMT_WAITING_USE_OR_STORE))
{
- SET_CLIENT_ERROR(*conn->error_info, CR_COMMANDS_OUT_OF_SYNC,
- UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
+ SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
DBG_ERR("command out of sync");
DBG_RETURN(NULL);
}
- SET_EMPTY_ERROR(*stmt->error_info);
+ SET_EMPTY_ERROR(stmt->error_info);
- MYSQLND_INC_CONN_STATISTIC(stmt->conn->stats, STAT_PS_UNBUFFERED_SETS);
+ MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_PS_UNBUFFERED_SETS);
result = stmt->result;
result->m.use_result(stmt->result, TRUE);
@@ -1018,16 +1026,15 @@ MYSQLND_METHOD(mysqlnd_stmt, use_result)(MYSQLND_STMT * s)
/* }}} */
-#define STMT_ID_LENGTH 4
-
/* {{{ mysqlnd_fetch_row_cursor */
enum_func_status
-mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES * result, void * param, unsigned int flags, zend_bool * fetched_anything)
+mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES * result, void * param, const unsigned int flags, zend_bool * fetched_anything)
{
enum_func_status ret;
MYSQLND_STMT * s = (MYSQLND_STMT *) param;
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
- zend_uchar buf[STMT_ID_LENGTH /* statement id */ + 4 /* number of rows to fetch */];
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
+ MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
+ zend_uchar buf[MYSQLND_STMT_ID_LENGTH /* statement id */ + 4 /* number of rows to fetch */];
MYSQLND_PACKET_ROW * row_packet;
DBG_ENTER("mysqlnd_fetch_stmt_row_cursor");
@@ -1036,13 +1043,11 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES * result, void * param, unsigned int f
DBG_ERR("no statement");
DBG_RETURN(FAIL);
}
-
DBG_INF_FMT("stmt=%lu flags=%u", stmt->stmt_id, flags);
if (stmt->state < MYSQLND_STMT_USER_FETCHING) {
/* Only initted - error */
- SET_CLIENT_ERROR(*stmt->conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE,
- mysqlnd_out_of_sync);
+ SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
DBG_ERR("command out of sync");
DBG_RETURN(FAIL);
}
@@ -1050,28 +1055,38 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES * result, void * param, unsigned int f
DBG_RETURN(FAIL);
}
- SET_EMPTY_ERROR(*stmt->error_info);
- SET_EMPTY_ERROR(*stmt->conn->error_info);
+ SET_EMPTY_ERROR(stmt->error_info);
+ SET_EMPTY_ERROR(conn->error_info);
int4store(buf, stmt->stmt_id);
- int4store(buf + STMT_ID_LENGTH, 1); /* for now fetch only one row */
+ int4store(buf + MYSQLND_STMT_ID_LENGTH, 1); /* for now fetch only one row */
+
+ {
+ const MYSQLND_CSTRING payload = {(const char*) buf, sizeof(buf)};
+ struct st_mysqlnd_protocol_command * command = conn->command_factory(COM_STMT_FETCH, conn, payload);
+ ret = FAIL;
+ if (command) {
+ ret = command->run(command);
+ command->free_command(command);
+ if (ret == FAIL) {
+ COPY_CLIENT_ERROR(stmt->error_info, *conn->error_info);
+ }
+ }
+ if (FAIL == ret) {
+ DBG_RETURN(FAIL);
+ }
- if (FAIL == stmt->conn->m->simple_command(stmt->conn, COM_STMT_FETCH, buf, sizeof(buf),
- PROT_LAST /* we will handle the response packet*/,
- FALSE, TRUE)) {
- COPY_CLIENT_ERROR(*stmt->error_info, *stmt->conn->error_info);
- DBG_RETURN(FAIL);
}
row_packet->skip_extraction = stmt->result_bind? FALSE:TRUE;
- memset(stmt->upsert_status, 0, sizeof(*stmt->upsert_status));
- if (PASS == (ret = PACKET_READ(row_packet, result->conn)) && !row_packet->eof) {
+ UPSERT_STATUS_RESET(stmt->upsert_status);
+ if (PASS == (ret = PACKET_READ(row_packet)) && !row_packet->eof) {
const MYSQLND_RES_METADATA * const meta = result->meta;
unsigned int i, field_count = result->field_count;
if (!row_packet->skip_extraction) {
- result->unbuf->m.free_last_data(result->unbuf, result->conn? result->conn->stats : NULL);
+ result->unbuf->m.free_last_data(result->unbuf, conn->stats);
result->unbuf->last_row_data = row_packet->fields;
result->unbuf->last_row_buffer = row_packet->row_buffer;
@@ -1082,8 +1097,8 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES * result, void * param, unsigned int f
result->unbuf->last_row_data,
row_packet->field_count,
row_packet->fields_metadata,
- result->conn->options->int_and_float_native,
- result->conn->stats))
+ conn->options->int_and_float_native,
+ conn->stats))
{
DBG_RETURN(FAIL);
}
@@ -1127,38 +1142,36 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES * result, void * param, unsigned int f
the bound variables. Thus we need to do part of what it does or Zend will
report leaks.
*/
- row_packet->row_buffer->free_chunk(row_packet->row_buffer);
+ row_packet->result_set_memory_pool->free_chunk(
+ row_packet->result_set_memory_pool, row_packet->row_buffer);
row_packet->row_buffer = NULL;
}
/* We asked for one row, the next one should be EOF, eat it */
- ret = PACKET_READ(row_packet, result->conn);
+ ret = PACKET_READ(row_packet);
if (row_packet->row_buffer) {
- row_packet->row_buffer->free_chunk(row_packet->row_buffer);
+ row_packet->result_set_memory_pool->free_chunk(
+ row_packet->result_set_memory_pool, row_packet->row_buffer);
row_packet->row_buffer = NULL;
}
- MYSQLND_INC_CONN_STATISTIC(stmt->conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT_PS_CURSOR);
+ MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT_PS_CURSOR);
result->unbuf->row_count++;
*fetched_anything = TRUE;
} else {
*fetched_anything = FALSE;
+ UPSERT_STATUS_SET_WARNINGS(stmt->upsert_status, row_packet->warning_count);
+ UPSERT_STATUS_SET_WARNINGS(conn->upsert_status, row_packet->warning_count);
- stmt->upsert_status->warning_count =
- stmt->conn->upsert_status->warning_count =
- row_packet->warning_count;
-
- stmt->upsert_status->server_status =
- stmt->conn->upsert_status->server_status =
- row_packet->server_status;
+ UPSERT_STATUS_SET_SERVER_STATUS(stmt->upsert_status, row_packet->server_status);
+ UPSERT_STATUS_SET_SERVER_STATUS(conn->upsert_status, row_packet->server_status);
result->unbuf->eof_reached = row_packet->eof;
}
- stmt->upsert_status->warning_count =
- stmt->conn->upsert_status->warning_count =
- row_packet->warning_count;
- stmt->upsert_status->server_status =
- stmt->conn->upsert_status->server_status =
- row_packet->server_status;
+ UPSERT_STATUS_SET_WARNINGS(stmt->upsert_status, row_packet->warning_count);
+ UPSERT_STATUS_SET_WARNINGS(conn->upsert_status, row_packet->warning_count);
+
+ UPSERT_STATUS_SET_SERVER_STATUS(stmt->upsert_status, row_packet->server_status);
+ UPSERT_STATUS_SET_SERVER_STATUS(conn->upsert_status, row_packet->server_status);
DBG_INF_FMT("ret=%s fetched=%u server_status=%u warnings=%u eof=%u",
ret == PASS? "PASS":"FAIL", *fetched_anything,
@@ -1173,7 +1186,8 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES * result, void * param, unsigned int f
static enum_func_status
MYSQLND_METHOD(mysqlnd_stmt, fetch)(MYSQLND_STMT * const s, zend_bool * const fetched_anything)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
+ MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
enum_func_status ret;
DBG_ENTER("mysqlnd_stmt::fetch");
if (!stmt || !stmt->conn) {
@@ -1181,10 +1195,8 @@ MYSQLND_METHOD(mysqlnd_stmt, fetch)(MYSQLND_STMT * const s, zend_bool * const fe
}
DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
- if (!stmt->result ||
- stmt->state < MYSQLND_STMT_WAITING_USE_OR_STORE) {
- SET_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
-
+ if (!stmt->result || stmt->state < MYSQLND_STMT_WAITING_USE_OR_STORE) {
+ SET_CLIENT_ERROR(stmt->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
DBG_ERR("command out of sync");
DBG_RETURN(FAIL);
} else if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) {
@@ -1194,8 +1206,8 @@ MYSQLND_METHOD(mysqlnd_stmt, fetch)(MYSQLND_STMT * const s, zend_bool * const fe
}
stmt->state = MYSQLND_STMT_USER_FETCHING;
- SET_EMPTY_ERROR(*stmt->error_info);
- SET_EMPTY_ERROR(*stmt->conn->error_info);
+ SET_EMPTY_ERROR(stmt->error_info);
+ SET_EMPTY_ERROR(conn->error_info);
DBG_INF_FMT("result_bind=%p separated_once=%u", &stmt->result_bind, stmt->result_zvals_separated_once);
/*
@@ -1229,18 +1241,18 @@ MYSQLND_METHOD(mysqlnd_stmt, fetch)(MYSQLND_STMT * const s, zend_bool * const fe
static enum_func_status
MYSQLND_METHOD(mysqlnd_stmt, reset)(MYSQLND_STMT * const s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
enum_func_status ret = PASS;
- zend_uchar cmd_buf[STMT_ID_LENGTH /* statement id */];
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
+ MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
DBG_ENTER("mysqlnd_stmt::reset");
- if (!stmt || !stmt->conn) {
+ if (!stmt || !conn) {
DBG_RETURN(FAIL);
}
DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
- SET_EMPTY_ERROR(*stmt->error_info);
- SET_EMPTY_ERROR(*stmt->conn->error_info);
+ SET_EMPTY_ERROR(stmt->error_info);
+ SET_EMPTY_ERROR(conn->error_info);
if (stmt->stmt_id) {
MYSQLND_CONN_DATA * conn = stmt->conn;
@@ -1263,12 +1275,18 @@ MYSQLND_METHOD(mysqlnd_stmt, reset)(MYSQLND_STMT * const s)
be separated before that.
*/
- int4store(cmd_buf, stmt->stmt_id);
- if (CONN_GET_STATE(conn) == CONN_READY &&
- FAIL == (ret = conn->m->simple_command(conn, COM_STMT_RESET, cmd_buf,
- sizeof(cmd_buf), PROT_OK_PACKET,
- FALSE, TRUE))) {
- COPY_CLIENT_ERROR(*stmt->error_info, *conn->error_info);
+ if (GET_CONNECTION_STATE(&conn->state) == CONN_READY) {
+ size_t stmt_id = stmt->stmt_id;
+ struct st_mysqlnd_protocol_command * command = stmt->conn->command_factory(COM_STMT_RESET, stmt->conn, stmt_id);
+ ret = FAIL;
+ if (command) {
+ ret = command->run(command);
+ command->free_command(command);
+
+ if (ret == FAIL) {
+ COPY_CLIENT_ERROR(stmt->error_info, *conn->error_info);
+ }
+ }
}
*stmt->upsert_status = *conn->upsert_status;
}
@@ -1282,11 +1300,12 @@ MYSQLND_METHOD(mysqlnd_stmt, reset)(MYSQLND_STMT * const s)
static enum_func_status
MYSQLND_METHOD(mysqlnd_stmt, flush)(MYSQLND_STMT * const s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
enum_func_status ret = PASS;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
+ MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
DBG_ENTER("mysqlnd_stmt::flush");
- if (!stmt || !stmt->conn) {
+ if (!stmt || !conn) {
DBG_RETURN(FAIL);
}
DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
@@ -1319,76 +1338,72 @@ MYSQLND_METHOD(mysqlnd_stmt, flush)(MYSQLND_STMT * const s)
/* {{{ mysqlnd_stmt::send_long_data */
static enum_func_status
MYSQLND_METHOD(mysqlnd_stmt, send_long_data)(MYSQLND_STMT * const s, unsigned int param_no,
- const char * const data, zend_ulong length)
+ const char * const data, zend_ulong data_length)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
enum_func_status ret = FAIL;
- MYSQLND_CONN_DATA * conn;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
+ MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
zend_uchar * cmd_buf;
- enum php_mysqlnd_server_command cmd = COM_STMT_SEND_LONG_DATA;
DBG_ENTER("mysqlnd_stmt::send_long_data");
- if (!stmt || !stmt->conn) {
+ if (!stmt || !conn) {
DBG_RETURN(FAIL);
}
- DBG_INF_FMT("stmt=%lu param_no=%u data_len=%lu", stmt->stmt_id, param_no, length);
+ DBG_INF_FMT("stmt=%lu param_no=%u data_len=%lu", stmt->stmt_id, param_no, data_length);
- conn = stmt->conn;
-
- SET_EMPTY_ERROR(*stmt->error_info);
- SET_EMPTY_ERROR(*stmt->conn->error_info);
+ SET_EMPTY_ERROR(stmt->error_info);
+ SET_EMPTY_ERROR(conn->error_info);
if (stmt->state < MYSQLND_STMT_PREPARED) {
- SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
+ SET_CLIENT_ERROR(stmt->error_info, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
DBG_ERR("not prepared");
DBG_RETURN(FAIL);
}
if (!stmt->param_bind) {
- SET_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
+ SET_CLIENT_ERROR(stmt->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
DBG_ERR("command out of sync");
DBG_RETURN(FAIL);
}
if (param_no >= stmt->param_count) {
- SET_STMT_ERROR(stmt, CR_INVALID_PARAMETER_NO, UNKNOWN_SQLSTATE, "Invalid parameter number");
+ SET_CLIENT_ERROR(stmt->error_info, CR_INVALID_PARAMETER_NO, UNKNOWN_SQLSTATE, "Invalid parameter number");
DBG_ERR("invalid param_no");
DBG_RETURN(FAIL);
}
if (stmt->param_bind[param_no].type != MYSQL_TYPE_LONG_BLOB) {
- SET_STMT_ERROR(stmt, CR_INVALID_BUFFER_USE, UNKNOWN_SQLSTATE, mysqlnd_not_bound_as_blob);
+ SET_CLIENT_ERROR(stmt->error_info, CR_INVALID_BUFFER_USE, UNKNOWN_SQLSTATE, mysqlnd_not_bound_as_blob);
DBG_ERR("param_no is not of a blob type");
DBG_RETURN(FAIL);
}
- /*
- XXX: Unfortunately we have to allocate additional buffer to be able the
- additional data, which is like a header inside the payload.
- This should be optimised, but it will be a pervasive change, so
- conn->m->simple_command() will accept not a buffer, but actually MYSQLND_STRING*
- terminated by NULL, to send. If the strings are not big, we can collapse them
- on the buffer every connection has, but otherwise we will just send them
- one by one to the wire.
- */
-
- if (CONN_GET_STATE(conn) == CONN_READY) {
- size_t packet_len;
- cmd_buf = mnd_emalloc(packet_len = STMT_ID_LENGTH + 2 + length);
+ if (GET_CONNECTION_STATE(&conn->state) == CONN_READY) {
+ const size_t packet_len = MYSQLND_STMT_ID_LENGTH + 2 + data_length;
+ cmd_buf = mnd_emalloc(packet_len);
if (cmd_buf) {
stmt->param_bind[param_no].flags |= MYSQLND_PARAM_BIND_BLOB_USED;
int4store(cmd_buf, stmt->stmt_id);
- int2store(cmd_buf + STMT_ID_LENGTH, param_no);
- memcpy(cmd_buf + STMT_ID_LENGTH + 2, data, length);
+ int2store(cmd_buf + MYSQLND_STMT_ID_LENGTH, param_no);
+ memcpy(cmd_buf + MYSQLND_STMT_ID_LENGTH + 2, data, data_length);
- /* COM_STMT_SEND_LONG_DATA doesn't send an OK packet*/
- ret = conn->m->simple_command(conn, cmd, cmd_buf, packet_len, PROT_LAST , FALSE, TRUE);
- mnd_efree(cmd_buf);
- if (FAIL == ret) {
- COPY_CLIENT_ERROR(*stmt->error_info, *conn->error_info);
+ /* COM_STMT_SEND_LONG_DATA doesn't acknowledge with an OK packet */
+ {
+ const MYSQLND_CSTRING payload = {(const char *) cmd_buf, packet_len};
+ struct st_mysqlnd_protocol_command * command = conn->command_factory(COM_STMT_SEND_LONG_DATA, conn, payload);
+ ret = FAIL;
+ if (command) {
+ ret = command->run(command);
+ command->free_command(command);
+ if (ret == FAIL) {
+ COPY_CLIENT_ERROR(stmt->error_info, *conn->error_info);
+ }
+ }
}
+
+ mnd_efree(cmd_buf);
} else {
ret = FAIL;
- SET_OOM_ERROR(*stmt->error_info);
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(stmt->error_info);
+ SET_OOM_ERROR(conn->error_info);
}
/*
Cover protocol error: COM_STMT_SEND_LONG_DATA was designed to be quick and not
@@ -1410,12 +1425,12 @@ MYSQLND_METHOD(mysqlnd_stmt, send_long_data)(MYSQLND_STMT * const s, unsigned in
#if HAVE_USLEEP && !defined(PHP_WIN32)
usleep(120000);
#endif
- if ((packet_len = conn->net->m.consume_uneaten_data(conn->net, cmd))) {
+ if ((packet_len = conn->protocol_frame_codec->m.consume_uneaten_data(conn->protocol_frame_codec, COM_STMT_SEND_LONG_DATA))) {
php_error_docref(NULL, E_WARNING, "There was an error "
"while sending long data. Probably max_allowed_packet_size "
"is smaller than the data. You have to increase it or send "
"smaller chunks of data. Answer was "MYSQLND_SZ_T_SPEC" bytes long.", packet_len);
- SET_STMT_ERROR(stmt, CR_CONNECTION_ERROR, UNKNOWN_SQLSTATE,
+ SET_CLIENT_ERROR(stmt->error_info, CR_CONNECTION_ERROR, UNKNOWN_SQLSTATE,
"Server responded to COM_STMT_SEND_LONG_DATA.");
ret = FAIL;
}
@@ -1432,15 +1447,17 @@ MYSQLND_METHOD(mysqlnd_stmt, send_long_data)(MYSQLND_STMT * const s, unsigned in
static enum_func_status
MYSQLND_METHOD(mysqlnd_stmt, bind_parameters)(MYSQLND_STMT * const s, MYSQLND_PARAM_BIND * const param_bind)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
+ MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
+
DBG_ENTER("mysqlnd_stmt::bind_param");
- if (!stmt || !stmt->conn) {
+ if (!stmt || !conn) {
DBG_RETURN(FAIL);
}
DBG_INF_FMT("stmt=%lu param_count=%u", stmt->stmt_id, stmt->param_count);
if (stmt->state < MYSQLND_STMT_PREPARED) {
- SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
+ SET_CLIENT_ERROR(stmt->error_info, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
DBG_ERR("not prepared");
if (param_bind) {
s->m->free_parameter_bind(s, param_bind);
@@ -1448,14 +1465,14 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_parameters)(MYSQLND_STMT * const s, MYSQLND_PA
DBG_RETURN(FAIL);
}
- SET_EMPTY_ERROR(*stmt->error_info);
- SET_EMPTY_ERROR(*stmt->conn->error_info);
+ SET_EMPTY_ERROR(stmt->error_info);
+ SET_EMPTY_ERROR(conn->error_info);
if (stmt->param_count) {
unsigned int i = 0;
if (!param_bind) {
- SET_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, "Re-binding (still) not supported");
+ SET_CLIENT_ERROR(stmt->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, "Re-binding (still) not supported");
DBG_ERR("Re-binding (still) not supported");
DBG_RETURN(FAIL);
} else if (stmt->param_bind) {
@@ -1501,26 +1518,28 @@ static enum_func_status
MYSQLND_METHOD(mysqlnd_stmt, bind_one_parameter)(MYSQLND_STMT * const s, unsigned int param_no,
zval * const zv, zend_uchar type)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
+ MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
+
DBG_ENTER("mysqlnd_stmt::bind_one_parameter");
- if (!stmt || !stmt->conn) {
+ if (!stmt || !conn) {
DBG_RETURN(FAIL);
}
DBG_INF_FMT("stmt=%lu param_no=%u param_count=%u type=%u", stmt->stmt_id, param_no, stmt->param_count, type);
if (stmt->state < MYSQLND_STMT_PREPARED) {
- SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
+ SET_CLIENT_ERROR(stmt->error_info, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
DBG_ERR("not prepared");
DBG_RETURN(FAIL);
}
if (param_no >= stmt->param_count) {
- SET_STMT_ERROR(stmt, CR_INVALID_PARAMETER_NO, UNKNOWN_SQLSTATE, "Invalid parameter number");
+ SET_CLIENT_ERROR(stmt->error_info, CR_INVALID_PARAMETER_NO, UNKNOWN_SQLSTATE, "Invalid parameter number");
DBG_ERR("invalid param_no");
DBG_RETURN(FAIL);
}
- SET_EMPTY_ERROR(*stmt->error_info);
- SET_EMPTY_ERROR(*stmt->conn->error_info);
+ SET_EMPTY_ERROR(stmt->error_info);
+ SET_EMPTY_ERROR(conn->error_info);
if (stmt->param_count) {
if (!stmt->param_bind) {
@@ -1555,21 +1574,23 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_one_parameter)(MYSQLND_STMT * const s, unsigne
static enum_func_status
MYSQLND_METHOD(mysqlnd_stmt, refresh_bind_param)(MYSQLND_STMT * const s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
+ MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
+
DBG_ENTER("mysqlnd_stmt::refresh_bind_param");
- if (!stmt || !stmt->conn) {
+ if (!stmt || !conn) {
DBG_RETURN(FAIL);
}
DBG_INF_FMT("stmt=%lu param_count=%u", stmt->stmt_id, stmt->param_count);
if (stmt->state < MYSQLND_STMT_PREPARED) {
- SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
+ SET_CLIENT_ERROR(stmt->error_info, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
DBG_ERR("not prepared");
DBG_RETURN(FAIL);
}
- SET_EMPTY_ERROR(*stmt->error_info);
- SET_EMPTY_ERROR(*stmt->conn->error_info);
+ SET_EMPTY_ERROR(stmt->error_info);
+ SET_EMPTY_ERROR(conn->error_info);
if (stmt->param_count) {
stmt->send_types_to_server = 1;
@@ -1584,15 +1605,17 @@ static enum_func_status
MYSQLND_METHOD(mysqlnd_stmt, bind_result)(MYSQLND_STMT * const s,
MYSQLND_RESULT_BIND * const result_bind)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
+ MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
+
DBG_ENTER("mysqlnd_stmt::bind_result");
- if (!stmt || !stmt->conn) {
+ if (!stmt || !conn) {
DBG_RETURN(FAIL);
}
DBG_INF_FMT("stmt=%lu field_count=%u", stmt->stmt_id, stmt->field_count);
if (stmt->state < MYSQLND_STMT_PREPARED) {
- SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
+ SET_CLIENT_ERROR(stmt->error_info, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
if (result_bind) {
s->m->free_result_bind(s, result_bind);
}
@@ -1600,8 +1623,8 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_result)(MYSQLND_STMT * const s,
DBG_RETURN(FAIL);
}
- SET_EMPTY_ERROR(*stmt->error_info);
- SET_EMPTY_ERROR(*stmt->conn->error_info);
+ SET_EMPTY_ERROR(stmt->error_info);
+ SET_EMPTY_ERROR(conn->error_info);
if (stmt->field_count) {
unsigned int i = 0;
@@ -1640,27 +1663,29 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_result)(MYSQLND_STMT * const s,
static enum_func_status
MYSQLND_METHOD(mysqlnd_stmt, bind_one_result)(MYSQLND_STMT * const s, unsigned int param_no)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
+ MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
+
DBG_ENTER("mysqlnd_stmt::bind_result");
- if (!stmt || !stmt->conn) {
+ if (!stmt || !conn) {
DBG_RETURN(FAIL);
}
DBG_INF_FMT("stmt=%lu field_count=%u", stmt->stmt_id, stmt->field_count);
if (stmt->state < MYSQLND_STMT_PREPARED) {
- SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
+ SET_CLIENT_ERROR(stmt->error_info, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
DBG_ERR("not prepared");
DBG_RETURN(FAIL);
}
if (param_no >= stmt->field_count) {
- SET_STMT_ERROR(stmt, CR_INVALID_PARAMETER_NO, UNKNOWN_SQLSTATE, "Invalid parameter number");
+ SET_CLIENT_ERROR(stmt->error_info, CR_INVALID_PARAMETER_NO, UNKNOWN_SQLSTATE, "Invalid parameter number");
DBG_ERR("invalid param_no");
DBG_RETURN(FAIL);
}
- SET_EMPTY_ERROR(*stmt->error_info);
- SET_EMPTY_ERROR(*stmt->conn->error_info);
+ SET_EMPTY_ERROR(stmt->error_info);
+ SET_EMPTY_ERROR(conn->error_info);
if (stmt->field_count) {
mysqlnd_stmt_separate_one_result_bind(s, param_no);
@@ -1691,8 +1716,8 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_one_result)(MYSQLND_STMT * const s, unsigned i
static uint64_t
MYSQLND_METHOD(mysqlnd_stmt, insert_id)(const MYSQLND_STMT * const s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
- return stmt? stmt->upsert_status->last_insert_id : 0;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
+ return stmt? UPSERT_STATUS_GET_LAST_INSERT_ID(stmt->upsert_status) : 0;
}
/* }}} */
@@ -1701,8 +1726,8 @@ MYSQLND_METHOD(mysqlnd_stmt, insert_id)(const MYSQLND_STMT * const s)
static uint64_t
MYSQLND_METHOD(mysqlnd_stmt, affected_rows)(const MYSQLND_STMT * const s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
- return stmt? stmt->upsert_status->affected_rows : 0;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
+ return stmt? UPSERT_STATUS_GET_AFFECTED_ROWS(stmt->upsert_status) : 0;
}
/* }}} */
@@ -1711,7 +1736,7 @@ MYSQLND_METHOD(mysqlnd_stmt, affected_rows)(const MYSQLND_STMT * const s)
static uint64_t
MYSQLND_METHOD(mysqlnd_stmt, num_rows)(const MYSQLND_STMT * const s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
return stmt && stmt->result? mysqlnd_num_rows(stmt->result):0;
}
/* }}} */
@@ -1721,8 +1746,8 @@ MYSQLND_METHOD(mysqlnd_stmt, num_rows)(const MYSQLND_STMT * const s)
static unsigned int
MYSQLND_METHOD(mysqlnd_stmt, warning_count)(const MYSQLND_STMT * const s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
- return stmt? stmt->upsert_status->warning_count : 0;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
+ return stmt? UPSERT_STATUS_GET_WARNINGS(stmt->upsert_status) : 0;
}
/* }}} */
@@ -1731,8 +1756,8 @@ MYSQLND_METHOD(mysqlnd_stmt, warning_count)(const MYSQLND_STMT * const s)
static unsigned int
MYSQLND_METHOD(mysqlnd_stmt, server_status)(const MYSQLND_STMT * const s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
- return stmt? stmt->upsert_status->server_status : 0;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
+ return stmt? UPSERT_STATUS_GET_SERVER_STATUS(stmt->upsert_status) : 0;
}
/* }}} */
@@ -1741,7 +1766,7 @@ MYSQLND_METHOD(mysqlnd_stmt, server_status)(const MYSQLND_STMT * const s)
static unsigned int
MYSQLND_METHOD(mysqlnd_stmt, field_count)(const MYSQLND_STMT * const s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
return stmt? stmt->field_count : 0;
}
/* }}} */
@@ -1751,7 +1776,7 @@ MYSQLND_METHOD(mysqlnd_stmt, field_count)(const MYSQLND_STMT * const s)
static unsigned int
MYSQLND_METHOD(mysqlnd_stmt, param_count)(const MYSQLND_STMT * const s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
return stmt? stmt->param_count : 0;
}
/* }}} */
@@ -1761,7 +1786,7 @@ MYSQLND_METHOD(mysqlnd_stmt, param_count)(const MYSQLND_STMT * const s)
static unsigned int
MYSQLND_METHOD(mysqlnd_stmt, errno)(const MYSQLND_STMT * const s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
return stmt? stmt->error_info->error_no : 0;
}
/* }}} */
@@ -1771,7 +1796,7 @@ MYSQLND_METHOD(mysqlnd_stmt, errno)(const MYSQLND_STMT * const s)
static const char *
MYSQLND_METHOD(mysqlnd_stmt, error)(const MYSQLND_STMT * const s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
return stmt? stmt->error_info->error : 0;
}
/* }}} */
@@ -1781,7 +1806,7 @@ MYSQLND_METHOD(mysqlnd_stmt, error)(const MYSQLND_STMT * const s)
static const char *
MYSQLND_METHOD(mysqlnd_stmt, sqlstate)(const MYSQLND_STMT * const s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
return stmt && stmt->error_info->sqlstate[0] ? stmt->error_info->sqlstate:MYSQLND_SQLSTATE_NULL;
}
/* }}} */
@@ -1791,7 +1816,7 @@ MYSQLND_METHOD(mysqlnd_stmt, sqlstate)(const MYSQLND_STMT * const s)
static enum_func_status
MYSQLND_METHOD(mysqlnd_stmt, data_seek)(const MYSQLND_STMT * const s, uint64_t row)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
return stmt && stmt->result? stmt->result->m.seek_data(stmt->result, row) : FAIL;
}
/* }}} */
@@ -1801,7 +1826,7 @@ MYSQLND_METHOD(mysqlnd_stmt, data_seek)(const MYSQLND_STMT * const s, uint64_t r
static MYSQLND_RES *
MYSQLND_METHOD(mysqlnd_stmt, param_metadata)(MYSQLND_STMT * const s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
if (!stmt || !stmt->param_count) {
return NULL;
}
@@ -1814,57 +1839,57 @@ MYSQLND_METHOD(mysqlnd_stmt, param_metadata)(MYSQLND_STMT * const s)
static MYSQLND_RES *
MYSQLND_METHOD(mysqlnd_stmt, result_metadata)(MYSQLND_STMT * const s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
- MYSQLND_RES *result;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
+ MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
+ MYSQLND_RES * result_meta = NULL;
DBG_ENTER("mysqlnd_stmt::result_metadata");
- if (!stmt) {
+ if (!stmt || ! conn) {
DBG_RETURN(NULL);
}
DBG_INF_FMT("stmt=%u field_count=%u", stmt->stmt_id, stmt->field_count);
- if (!stmt->field_count || !stmt->conn || !stmt->result || !stmt->result->meta) {
+ if (!stmt->field_count || !stmt->result || !stmt->result->meta) {
DBG_INF("NULL");
DBG_RETURN(NULL);
}
if (stmt->update_max_length && stmt->result->stored_data) {
/* stored result, we have to update the max_length before we clone the meta data :( */
- stmt->result->stored_data->m.initialize_result_set_rest(stmt->result->stored_data, stmt->result->meta, stmt->conn->stats,
- stmt->conn->options->int_and_float_native);
+ stmt->result->stored_data->m.initialize_result_set_rest(stmt->result->stored_data,
+ stmt->result->meta,
+ conn->stats,
+ conn->options->int_and_float_native);
}
/*
TODO: This implementation is kind of a hack,
find a better way to do it. In different functions I have put
fuses to check for result->m.fetch_row() being NULL. This should
be handled in a better way.
-
- In the meantime we don't need a zval cache reference for this fake
- result set, so we don't get one.
*/
do {
- result = stmt->conn->m->result_init(stmt->field_count, stmt->persistent);
- if (!result) {
+ result_meta = conn->m->result_init(stmt->field_count, stmt->persistent);
+ if (!result_meta) {
break;
}
- result->type = MYSQLND_RES_NORMAL;
- result->unbuf = mysqlnd_result_unbuffered_init(stmt->field_count, TRUE, result->persistent);
- if (!result->unbuf) {
+ result_meta->type = MYSQLND_RES_NORMAL;
+ result_meta->unbuf = mysqlnd_result_unbuffered_init(stmt->field_count, TRUE, result_meta->persistent);
+ if (!result_meta->unbuf) {
break;
}
- result->unbuf->eof_reached = TRUE;
- result->meta = stmt->result->meta->m->clone_metadata(stmt->result->meta, FALSE);
- if (!result->meta) {
+ result_meta->unbuf->eof_reached = TRUE;
+ result_meta->meta = stmt->result->meta->m->clone_metadata(stmt->result->meta, FALSE);
+ if (!result_meta->meta) {
break;
}
- DBG_INF_FMT("result=%p", result);
- DBG_RETURN(result);
+ DBG_INF_FMT("result_meta=%p", result_meta);
+ DBG_RETURN(result_meta);
} while (0);
- SET_OOM_ERROR(*stmt->conn->error_info);
- if (result) {
- result->m.free_result(result, TRUE);
+ SET_OOM_ERROR(conn->error_info);
+ if (result_meta) {
+ result_meta->m.free_result(result_meta, TRUE);
}
DBG_RETURN(NULL);
}
@@ -1877,7 +1902,7 @@ MYSQLND_METHOD(mysqlnd_stmt, attr_set)(MYSQLND_STMT * const s,
enum mysqlnd_stmt_attr attr_type,
const void * const value)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
DBG_ENTER("mysqlnd_stmt::attr_set");
if (!stmt) {
DBG_RETURN(FAIL);
@@ -1897,7 +1922,7 @@ MYSQLND_METHOD(mysqlnd_stmt, attr_set)(MYSQLND_STMT * const s,
case STMT_ATTR_CURSOR_TYPE: {
unsigned int ival = *(unsigned int *) value;
if (ival > (zend_ulong) CURSOR_TYPE_READ_ONLY) {
- SET_STMT_ERROR(stmt, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "Not implemented");
+ SET_CLIENT_ERROR(stmt->error_info, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "Not implemented");
DBG_INF("FAIL");
DBG_RETURN(FAIL);
}
@@ -1909,7 +1934,7 @@ MYSQLND_METHOD(mysqlnd_stmt, attr_set)(MYSQLND_STMT * const s,
if (ival == 0) {
ival = MYSQLND_DEFAULT_PREFETCH_ROWS;
} else if (ival > 1) {
- SET_STMT_ERROR(stmt, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "Not implemented");
+ SET_CLIENT_ERROR(stmt->error_info, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "Not implemented");
DBG_INF("FAIL");
DBG_RETURN(FAIL);
}
@@ -1917,7 +1942,7 @@ MYSQLND_METHOD(mysqlnd_stmt, attr_set)(MYSQLND_STMT * const s,
break;
}
default:
- SET_STMT_ERROR(stmt, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "Not implemented");
+ SET_CLIENT_ERROR(stmt->error_info, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "Not implemented");
DBG_RETURN(FAIL);
}
DBG_INF("PASS");
@@ -1932,7 +1957,7 @@ MYSQLND_METHOD(mysqlnd_stmt, attr_get)(const MYSQLND_STMT * const s,
enum mysqlnd_stmt_attr attr_type,
void * const value)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
DBG_ENTER("mysqlnd_stmt::attr_set");
if (!stmt) {
DBG_RETURN(FAIL);
@@ -1963,9 +1988,11 @@ MYSQLND_METHOD(mysqlnd_stmt, attr_get)(const MYSQLND_STMT * const s,
static enum_func_status
MYSQLND_METHOD(mysqlnd_stmt, free_result)(MYSQLND_STMT * const s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
+ MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
+
DBG_ENTER("mysqlnd_stmt::free_result");
- if (!stmt || !stmt->conn) {
+ if (!stmt || !conn) {
DBG_RETURN(FAIL);
}
DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
@@ -2005,8 +2032,8 @@ MYSQLND_METHOD(mysqlnd_stmt, free_result)(MYSQLND_STMT * const s)
stmt->state = MYSQLND_STMT_PREPARED;
}
- if (CONN_GET_STATE(stmt->conn) != CONN_QUIT_SENT) {
- CONN_SET_STATE(stmt->conn, CONN_READY);
+ if (GET_CONNECTION_STATE(&conn->state) != CONN_QUIT_SENT) {
+ SET_CONNECTION_STATE(&conn->state, CONN_READY);
}
DBG_RETURN(PASS);
@@ -2018,7 +2045,7 @@ MYSQLND_METHOD(mysqlnd_stmt, free_result)(MYSQLND_STMT * const s)
static void
mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
unsigned int i;
DBG_ENTER("mysqlnd_stmt_separate_result_bind");
@@ -2039,8 +2066,7 @@ mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const s)
for (i = 0; i < stmt->field_count; i++) {
/* Let's try with no cache */
if (stmt->result_bind[i].bound == TRUE) {
- DBG_INF_FMT("%u has refcount=%u", i,
- Z_REFCOUNTED(stmt->result_bind[i].zv)? Z_REFCOUNT(stmt->result_bind[i].zv) : 0);
+ DBG_INF_FMT("%u has refcount=%u", i, Z_REFCOUNTED(stmt->result_bind[i].zv)? Z_REFCOUNT(stmt->result_bind[i].zv) : 0);
zval_ptr_dtor(&stmt->result_bind[i].zv);
}
}
@@ -2055,9 +2081,9 @@ mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const s)
/* {{{ mysqlnd_stmt_separate_one_result_bind */
static void
-mysqlnd_stmt_separate_one_result_bind(MYSQLND_STMT * const s, unsigned int param_no)
+mysqlnd_stmt_separate_one_result_bind(MYSQLND_STMT * const s, const unsigned int param_no)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
DBG_ENTER("mysqlnd_stmt_separate_one_result_bind");
if (!stmt) {
DBG_VOID_RETURN;
@@ -2075,9 +2101,7 @@ mysqlnd_stmt_separate_one_result_bind(MYSQLND_STMT * const s, unsigned int param
*/
/* Let's try with no cache */
if (stmt->result_bind[param_no].bound == TRUE) {
- DBG_INF_FMT("%u has refcount=%u", param_no,
- Z_REFCOUNTED(stmt->result_bind[param_no].zv)?
- Z_REFCOUNT(stmt->result_bind[param_no].zv) : 0);
+ DBG_INF_FMT("%u has refcount=%u", param_no, Z_REFCOUNTED(stmt->result_bind[param_no].zv)? Z_REFCOUNT(stmt->result_bind[param_no].zv) : 0);
zval_ptr_dtor(&stmt->result_bind[param_no].zv);
}
@@ -2090,7 +2114,7 @@ mysqlnd_stmt_separate_one_result_bind(MYSQLND_STMT * const s, unsigned int param
static void
MYSQLND_METHOD(mysqlnd_stmt, free_stmt_result)(MYSQLND_STMT * const s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
DBG_ENTER("mysqlnd_stmt::free_stmt_result");
if (!stmt) {
DBG_VOID_RETURN;
@@ -2121,7 +2145,7 @@ MYSQLND_METHOD(mysqlnd_stmt, free_stmt_result)(MYSQLND_STMT * const s)
static void
MYSQLND_METHOD(mysqlnd_stmt, free_stmt_content)(MYSQLND_STMT * const s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
DBG_ENTER("mysqlnd_stmt::free_stmt_content");
if (!stmt) {
DBG_VOID_RETURN;
@@ -2153,25 +2177,22 @@ MYSQLND_METHOD(mysqlnd_stmt, free_stmt_content)(MYSQLND_STMT * const s)
/* }}} */
-/* {{{ mysqlnd_stmt::net_close */
+/* {{{ mysqlnd_stmt::close_on_server */
static enum_func_status
-MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, net_close)(MYSQLND_STMT * const s, zend_bool implicit)
+MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, close_on_server)(MYSQLND_STMT * const s, zend_bool implicit)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
- MYSQLND_CONN_DATA * conn;
- zend_uchar cmd_buf[STMT_ID_LENGTH /* statement id */];
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
+ MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
enum_mysqlnd_collected_stats statistic = STAT_LAST;
- DBG_ENTER("mysqlnd_stmt::net_close");
- if (!stmt || !stmt->conn) {
+ DBG_ENTER("mysqlnd_stmt::close_on_server");
+ if (!stmt || !conn) {
DBG_RETURN(FAIL);
}
DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
- conn = stmt->conn;
-
- SET_EMPTY_ERROR(*stmt->error_info);
- SET_EMPTY_ERROR(*stmt->conn->error_info);
+ SET_EMPTY_ERROR(stmt->error_info);
+ SET_EMPTY_ERROR(conn->error_info);
/*
If the user decided to close the statement right after execute()
@@ -2199,13 +2220,21 @@ MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, net_close)(MYSQLND_STMT * const s, zend_boo
MYSQLND_INC_GLOBAL_STATISTIC(implicit == TRUE? STAT_FREE_RESULT_IMPLICIT:
STAT_FREE_RESULT_EXPLICIT);
- int4store(cmd_buf, stmt->stmt_id);
- if (CONN_GET_STATE(conn) == CONN_READY &&
- FAIL == conn->m->simple_command(conn, COM_STMT_CLOSE, cmd_buf, sizeof(cmd_buf),
- PROT_LAST /* COM_STMT_CLOSE doesn't send an OK packet*/,
- FALSE, TRUE)) {
- COPY_CLIENT_ERROR(*stmt->error_info, *conn->error_info);
- DBG_RETURN(FAIL);
+ if (GET_CONNECTION_STATE(&conn->state) == CONN_READY) {
+ enum_func_status ret = FAIL;
+ size_t stmt_id = stmt->stmt_id;
+ struct st_mysqlnd_protocol_command * command = conn->command_factory(COM_STMT_CLOSE, conn, stmt_id);
+ if (command) {
+ ret = command->run(command);
+ command->free_command(command);
+
+ if (ret == FAIL) {
+ COPY_CLIENT_ERROR(stmt->error_info, *conn->error_info);
+ }
+ }
+ if (ret == FAIL) {
+ DBG_RETURN(FAIL);
+ }
}
}
switch (stmt->execute_count) {
@@ -2229,8 +2258,8 @@ MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, net_close)(MYSQLND_STMT * const s, zend_boo
s->m->free_stmt_content(s);
- if (stmt->conn) {
- stmt->conn->m->free_reference(stmt->conn);
+ if (conn) {
+ conn->m->free_reference(conn);
stmt->conn = NULL;
}
@@ -2253,7 +2282,7 @@ MYSQLND_METHOD(mysqlnd_stmt, dtor)(MYSQLND_STMT * const s, zend_bool implicit)
MYSQLND_INC_GLOBAL_STATISTIC(implicit == TRUE? STAT_STMT_CLOSE_IMPLICIT:
STAT_STMT_CLOSE_EXPLICIT);
- ret = s->m->net_close(s, implicit);
+ ret = s->m->close_on_server(s, implicit);
mnd_pefree(stmt, persistent);
}
mnd_pefree(s, persistent);
@@ -2268,7 +2297,7 @@ MYSQLND_METHOD(mysqlnd_stmt, dtor)(MYSQLND_STMT * const s, zend_bool implicit)
static MYSQLND_PARAM_BIND *
MYSQLND_METHOD(mysqlnd_stmt, alloc_param_bind)(MYSQLND_STMT * const s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
DBG_ENTER("mysqlnd_stmt::alloc_param_bind");
if (!stmt) {
DBG_RETURN(NULL);
@@ -2282,7 +2311,7 @@ MYSQLND_METHOD(mysqlnd_stmt, alloc_param_bind)(MYSQLND_STMT * const s)
static MYSQLND_RESULT_BIND *
MYSQLND_METHOD(mysqlnd_stmt, alloc_result_bind)(MYSQLND_STMT * const s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
DBG_ENTER("mysqlnd_stmt::alloc_result_bind");
if (!stmt) {
DBG_RETURN(NULL);
@@ -2296,7 +2325,7 @@ MYSQLND_METHOD(mysqlnd_stmt, alloc_result_bind)(MYSQLND_STMT * const s)
PHPAPI void
MYSQLND_METHOD(mysqlnd_stmt, free_parameter_bind)(MYSQLND_STMT * const s, MYSQLND_PARAM_BIND * param_bind)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
if (stmt) {
mnd_pefree(param_bind, stmt->persistent);
}
@@ -2308,7 +2337,7 @@ MYSQLND_METHOD(mysqlnd_stmt, free_parameter_bind)(MYSQLND_STMT * const s, MYSQLN
PHPAPI void
MYSQLND_METHOD(mysqlnd_stmt, free_result_bind)(MYSQLND_STMT * const s, MYSQLND_RESULT_BIND * result_bind)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
if (stmt) {
mnd_pefree(result_bind, stmt->persistent);
}
@@ -2329,7 +2358,7 @@ MYSQLND_CLASS_METHODS_START(mysqlnd_stmt)
MYSQLND_METHOD(mysqlnd_stmt, free_result),
MYSQLND_METHOD(mysqlnd_stmt, data_seek),
MYSQLND_METHOD(mysqlnd_stmt, reset),
- MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, net_close),
+ MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, close_on_server),
MYSQLND_METHOD(mysqlnd_stmt, dtor),
MYSQLND_METHOD(mysqlnd_stmt, fetch),
@@ -2372,18 +2401,6 @@ MYSQLND_CLASS_METHODS_START(mysqlnd_stmt)
MYSQLND_CLASS_METHODS_END;
-/* {{{ _mysqlnd_stmt_init */
-MYSQLND_STMT *
-_mysqlnd_stmt_init(MYSQLND_CONN_DATA * const conn)
-{
- MYSQLND_STMT * ret;
- DBG_ENTER("_mysqlnd_stmt_init");
- ret = MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_object_factory).get_prepared_statement(conn);
- DBG_RETURN(ret);
-}
-/* }}} */
-
-
/* {{{ _mysqlnd_init_ps_subsystem */
void _mysqlnd_init_ps_subsystem()
{
diff --git a/ext/mysqlnd/mysqlnd_ps.h b/ext/mysqlnd/mysqlnd_ps.h
new file mode 100644
index 0000000000..73facf2dce
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_ps.h
@@ -0,0 +1,54 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 7 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2017 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Andrey Hristov <andrey@php.net> |
+ | Ulf Wendel <uw@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef MYSQLND_PS_H
+#define MYSQLND_PS_H
+
+/* PS stuff */
+typedef void (*ps_field_fetch_func)(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row);
+
+struct st_mysqlnd_perm_bind {
+ ps_field_fetch_func func;
+ /* should be signed int */
+ int pack_len;
+ unsigned int php_type;
+ zend_bool is_possibly_blob;
+ zend_bool can_ret_as_str_in_uni;
+};
+
+extern struct st_mysqlnd_perm_bind mysqlnd_ps_fetch_functions[MYSQL_TYPE_LAST + 1];
+
+enum_func_status mysqlnd_stmt_fetch_row_buffered(MYSQLND_RES * result, void * param, const unsigned int flags, zend_bool * fetched_anything);
+enum_func_status mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES * result, void * param, const unsigned int flags, zend_bool * fetched_anything);
+
+void _mysqlnd_init_ps_subsystem();/* This one is private, mysqlnd_library_init() will call it */
+void _mysqlnd_init_ps_fetch_subsystem();
+
+void ps_fetch_from_1_to_8_bytes(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row, unsigned int byte_count);
+
+#endif /* MYSQLND_PS_H */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/mysqlnd/mysqlnd_ps_codec.c b/ext/mysqlnd/mysqlnd_ps_codec.c
index e1bb948822..c61bb30edb 100644
--- a/ext/mysqlnd/mysqlnd_ps_codec.c
+++ b/ext/mysqlnd/mysqlnd_ps_codec.c
@@ -14,18 +14,17 @@
+----------------------------------------------------------------------+
| Authors: Andrey Hristov <andrey@php.net> |
| Ulf Wendel <uw@php.net> |
- | Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
#include "php.h"
#include "mysqlnd.h"
#include "mysqlnd_wireprotocol.h"
+#include "mysqlnd_connection.h"
+#include "mysqlnd_ps.h"
#include "mysqlnd_priv.h"
#include "mysqlnd_debug.h"
-#include "ext/mysqlnd/mysql_float_to_double.h"
-
-#define MYSQLND_SILENT
+#include "mysql_float_to_double.h"
enum mysqlnd_timestamp_type
@@ -54,8 +53,8 @@ struct st_mysqlnd_perm_bind mysqlnd_ps_fetch_functions[MYSQL_TYPE_LAST + 1];
/* {{{ ps_fetch_from_1_to_8_bytes */
void
-ps_fetch_from_1_to_8_bytes(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_len,
- zend_uchar ** row, unsigned int byte_count)
+ps_fetch_from_1_to_8_bytes(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len,
+ const zend_uchar ** row, unsigned int byte_count)
{
char tmp[22];
size_t tmp_len = 0;
@@ -128,7 +127,7 @@ ps_fetch_from_1_to_8_bytes(zval * zv, const MYSQLND_FIELD * const field, unsigne
/* {{{ ps_fetch_null */
static void
-ps_fetch_null(zval *zv, const MYSQLND_FIELD * const field, unsigned int pack_len, zend_uchar ** row)
+ps_fetch_null(zval *zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row)
{
ZVAL_NULL(zv);
}
@@ -137,7 +136,7 @@ ps_fetch_null(zval *zv, const MYSQLND_FIELD * const field, unsigned int pack_len
/* {{{ ps_fetch_int8 */
static void
-ps_fetch_int8(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_len, zend_uchar ** row)
+ps_fetch_int8(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row)
{
ps_fetch_from_1_to_8_bytes(zv, field, pack_len, row, 1);
}
@@ -146,7 +145,7 @@ ps_fetch_int8(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_le
/* {{{ ps_fetch_int16 */
static void
-ps_fetch_int16(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_len, zend_uchar ** row)
+ps_fetch_int16(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row)
{
ps_fetch_from_1_to_8_bytes(zv, field, pack_len, row, 2);
}
@@ -155,7 +154,7 @@ ps_fetch_int16(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_l
/* {{{ ps_fetch_int32 */
static void
-ps_fetch_int32(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_len, zend_uchar ** row)
+ps_fetch_int32(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row)
{
ps_fetch_from_1_to_8_bytes(zv, field, pack_len, row, 4);
}
@@ -164,7 +163,7 @@ ps_fetch_int32(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_l
/* {{{ ps_fetch_int64 */
static void
-ps_fetch_int64(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_len, zend_uchar ** row)
+ps_fetch_int64(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row)
{
ps_fetch_from_1_to_8_bytes(zv, field, pack_len, row, 8);
}
@@ -173,7 +172,7 @@ ps_fetch_int64(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_l
/* {{{ ps_fetch_float */
static void
-ps_fetch_float(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_len, zend_uchar ** row)
+ps_fetch_float(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row)
{
float fval;
double dval;
@@ -186,7 +185,7 @@ ps_fetch_float(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_l
# define NOT_FIXED_DEC 31
#endif
- dval = mysql_float_to_double(fval, (field->decimals >= NOT_FIXED_DEC) ? -1 : field->decimals);
+ dval = mysql_float_to_double(fval, (field->decimals >= NOT_FIXED_DEC) ? -1 : (int)field->decimals);
ZVAL_DOUBLE(zv, dval);
DBG_VOID_RETURN;
@@ -196,7 +195,7 @@ ps_fetch_float(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_l
/* {{{ ps_fetch_double */
static void
-ps_fetch_double(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_len, zend_uchar ** row)
+ps_fetch_double(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row)
{
double value;
DBG_ENTER("ps_fetch_double");
@@ -211,7 +210,7 @@ ps_fetch_double(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_
/* {{{ ps_fetch_time */
static void
-ps_fetch_time(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_len, zend_uchar ** row)
+ps_fetch_time(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row)
{
struct st_mysqlnd_time t;
zend_ulong length; /* First byte encodes the length*/
@@ -219,7 +218,7 @@ ps_fetch_time(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_le
DBG_ENTER("ps_fetch_time");
if ((length = php_mysqlnd_net_field_length(row))) {
- zend_uchar * to= *row;
+ const zend_uchar * to = *row;
t.time_type = MYSQLND_TIMESTAMP_TIME;
t.neg = (zend_bool) to[0];
@@ -254,7 +253,7 @@ ps_fetch_time(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_le
/* {{{ ps_fetch_date */
static void
-ps_fetch_date(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_len, zend_uchar ** row)
+ps_fetch_date(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row)
{
struct st_mysqlnd_time t = {0};
zend_ulong length; /* First byte encodes the length*/
@@ -262,10 +261,10 @@ ps_fetch_date(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_le
DBG_ENTER("ps_fetch_date");
if ((length = php_mysqlnd_net_field_length(row))) {
- zend_uchar *to= *row;
+ const zend_uchar * to = *row;
- t.time_type= MYSQLND_TIMESTAMP_DATE;
- t.neg= 0;
+ t.time_type = MYSQLND_TIMESTAMP_DATE;
+ t.neg = 0;
t.second_part = t.hour = t.minute = t.second = 0;
@@ -291,7 +290,7 @@ ps_fetch_date(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_le
/* {{{ ps_fetch_datetime */
static void
-ps_fetch_datetime(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_len, zend_uchar ** row)
+ps_fetch_datetime(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row)
{
struct st_mysqlnd_time t;
zend_ulong length; /* First byte encodes the length*/
@@ -299,10 +298,10 @@ ps_fetch_datetime(zval * zv, const MYSQLND_FIELD * const field, unsigned int pac
DBG_ENTER("ps_fetch_datetime");
if ((length = php_mysqlnd_net_field_length(row))) {
- zend_uchar * to = *row;
+ const zend_uchar * to = *row;
t.time_type = MYSQLND_TIMESTAMP_DATETIME;
- t.neg = 0;
+ t.neg = 0;
t.year = (unsigned int) sint2korr(to);
t.month = (unsigned int) to[2];
@@ -335,7 +334,7 @@ ps_fetch_datetime(zval * zv, const MYSQLND_FIELD * const field, unsigned int pac
/* {{{ ps_fetch_string */
static void
-ps_fetch_string(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_len, zend_uchar ** row)
+ps_fetch_string(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row)
{
/*
For now just copy, before we make it possible
@@ -355,9 +354,9 @@ ps_fetch_string(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_
/* {{{ ps_fetch_bit */
static void
-ps_fetch_bit(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_len, zend_uchar ** row)
+ps_fetch_bit(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row)
{
- zend_ulong length = php_mysqlnd_net_field_length(row);
+ const zend_ulong length = php_mysqlnd_net_field_length(row);
ps_fetch_from_1_to_8_bytes(zv, field, pack_len, row, length);
}
/* }}} */
@@ -551,7 +550,7 @@ mysqlnd_stmt_execute_check_n_enlarge_buffer(zend_uchar **buf, zend_uchar **p, si
size_t left = (*buf_len - (*p - *buf));
if (left < (needed_bytes + overalloc)) {
- size_t offset = *p - *buf;
+ const size_t offset = *p - *buf;
zend_uchar *tmp_buf;
*buf_len = offset + needed_bytes + overalloc;
tmp_buf = mnd_emalloc(*buf_len);
@@ -578,7 +577,7 @@ mysqlnd_stmt_execute_prepare_param_types(MYSQLND_STMT_DATA * stmt, zval ** copie
unsigned int i;
DBG_ENTER("mysqlnd_stmt_execute_prepare_param_types");
for (i = 0; i < stmt->param_count; i++) {
- short current_type = stmt->param_bind[i].type;
+ const short current_type = stmt->param_bind[i].type;
zval *parameter = &stmt->param_bind[i].zv;
ZVAL_DEREF(parameter);
@@ -587,7 +586,7 @@ mysqlnd_stmt_execute_prepare_param_types(MYSQLND_STMT_DATA * stmt, zval ** copie
if (Z_TYPE_P(parameter) != IS_LONG &&
PASS != mysqlnd_stmt_copy_it(copies_param, parameter, stmt->param_count, i))
{
- SET_OOM_ERROR(*stmt->error_info);
+ SET_OOM_ERROR(stmt->error_info);
goto end;
}
/*
@@ -695,7 +694,7 @@ mysqlnd_stmt_execute_calculate_param_values_size(MYSQLND_STMT_DATA * stmt, zval
/* Double binding of the same zval, make a copy */
if (!*copies_param || Z_ISUNDEF((*copies_param)[i])) {
if (PASS != mysqlnd_stmt_copy_it(copies_param, the_var, stmt->param_count, i)) {
- SET_OOM_ERROR(*stmt->error_info);
+ SET_OOM_ERROR(stmt->error_info);
goto end;
}
}
@@ -710,7 +709,7 @@ mysqlnd_stmt_execute_calculate_param_values_size(MYSQLND_STMT_DATA * stmt, zval
if (Z_TYPE_P(the_var) != IS_DOUBLE) {
if (!*copies_param || Z_ISUNDEF((*copies_param)[i])) {
if (PASS != mysqlnd_stmt_copy_it(copies_param, the_var, stmt->param_count, i)) {
- SET_OOM_ERROR(*stmt->error_info);
+ SET_OOM_ERROR(stmt->error_info);
goto end;
}
}
@@ -745,7 +744,7 @@ use_string:
if (Z_TYPE_P(the_var) != IS_STRING) {
if (!*copies_param || Z_ISUNDEF((*copies_param)[i])) {
if (PASS != mysqlnd_stmt_copy_it(copies_param, the_var, stmt->param_count, i)) {
- SET_OOM_ERROR(*stmt->error_info);
+ SET_OOM_ERROR(stmt->error_info);
goto end;
}
}
@@ -810,7 +809,7 @@ mysqlnd_stmt_execute_store_param_values(MYSQLND_STMT_DATA * stmt, zval * copies,
case MYSQL_TYPE_VAR_STRING:
send_string:
{
- size_t len = Z_STRLEN_P(data);
+ const size_t len = Z_STRLEN_P(data);
/* to is after p. The latter hasn't been moved */
*p = php_mysqlnd_net_store_length(*p, len);
memcpy(*p, Z_STRVAL_P(data), len);
@@ -845,7 +844,7 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar
{
unsigned int null_count = (stmt->param_count + 7) / 8;
if (FAIL == mysqlnd_stmt_execute_check_n_enlarge_buffer(buf, p, buf_len, provided_buffer, null_count)) {
- SET_OOM_ERROR(*stmt->error_info);
+ SET_OOM_ERROR(stmt->error_info);
goto end;
}
/* put `null` bytes */
@@ -871,7 +870,7 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar
if (stmt->send_types_to_server) {
if (FAIL == mysqlnd_stmt_execute_check_n_enlarge_buffer(buf, p, buf_len, provided_buffer, stmt->param_count * 2)) {
- SET_OOM_ERROR(*stmt->error_info);
+ SET_OOM_ERROR(stmt->error_info);
goto end;
}
mysqlnd_stmt_execute_store_types(stmt, copies, p);
@@ -887,7 +886,7 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar
/* 2.2 Enlarge the buffer, if needed */
if (FAIL == mysqlnd_stmt_execute_check_n_enlarge_buffer(buf, p, buf_len, provided_buffer, data_size)) {
- SET_OOM_ERROR(*stmt->error_info);
+ SET_OOM_ERROR(stmt->error_info);
goto end;
}
diff --git a/ext/mysqlnd/mysqlnd_read_buffer.c b/ext/mysqlnd/mysqlnd_read_buffer.c
new file mode 100644
index 0000000000..4319eaa75e
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_read_buffer.c
@@ -0,0 +1,96 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 7 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2017 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Andrey Hristov <andrey@php.net> |
+ | Ulf Wendel <uw@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "php.h"
+#include "mysqlnd.h"
+#include "mysqlnd_debug.h"
+#include "mysqlnd_read_buffer.h"
+
+
+/* {{{ mysqlnd_read_buffer_is_empty */
+static zend_bool
+mysqlnd_read_buffer_is_empty(const MYSQLND_READ_BUFFER * const buffer)
+{
+ return buffer->len? FALSE:TRUE;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_read_buffer_read */
+static void
+mysqlnd_read_buffer_read(MYSQLND_READ_BUFFER * buffer, const size_t count, zend_uchar * dest)
+{
+ if (buffer->len >= count) {
+ memcpy(dest, buffer->data + buffer->offset, count);
+ buffer->offset += count;
+ buffer->len -= count;
+ }
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_read_buffer_bytes_left */
+static size_t
+mysqlnd_read_buffer_bytes_left(const MYSQLND_READ_BUFFER * const buffer)
+{
+ return buffer->len;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_read_buffer_free */
+static void
+mysqlnd_read_buffer_free(MYSQLND_READ_BUFFER ** buffer)
+{
+ DBG_ENTER("mysqlnd_read_buffer_free");
+ if (*buffer) {
+ mnd_efree((*buffer)->data);
+ mnd_efree(*buffer);
+ *buffer = NULL;
+ }
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_create_read_buffer */
+PHPAPI MYSQLND_READ_BUFFER *
+mysqlnd_create_read_buffer(const size_t count)
+{
+ MYSQLND_READ_BUFFER * ret = mnd_emalloc(sizeof(MYSQLND_READ_BUFFER));
+ DBG_ENTER("mysqlnd_create_read_buffer");
+ ret->is_empty = mysqlnd_read_buffer_is_empty;
+ ret->read = mysqlnd_read_buffer_read;
+ ret->bytes_left = mysqlnd_read_buffer_bytes_left;
+ ret->free_buffer = mysqlnd_read_buffer_free;
+ ret->data = mnd_emalloc(count);
+ ret->size = ret->len = count;
+ ret->offset = 0;
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/mysqlnd/mysqlnd_read_buffer.h b/ext/mysqlnd/mysqlnd_read_buffer.h
new file mode 100644
index 0000000000..488fe03210
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_read_buffer.h
@@ -0,0 +1,25 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 7 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2017 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Andrey Hristov <andrey@php.net> |
+ | Ulf Wendel <uw@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef MYSQLND_READ_BUFFER_H
+#define MYSQLND_READ_BUFFER_H
+
+PHPAPI MYSQLND_READ_BUFFER * mysqlnd_create_read_buffer(const size_t count);
+
+#endif /* MYSQLND_READ_BUFFER_H */
diff --git a/ext/mysqlnd/mysqlnd_result.c b/ext/mysqlnd/mysqlnd_result.c
index 2184c00b8d..ea4d3b26c4 100644
--- a/ext/mysqlnd/mysqlnd_result.c
+++ b/ext/mysqlnd/mysqlnd_result.c
@@ -14,7 +14,6 @@
+----------------------------------------------------------------------+
| Authors: Andrey Hristov <andrey@php.net> |
| Ulf Wendel <uw@php.net> |
- | Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
@@ -22,6 +21,7 @@
#include "mysqlnd.h"
#include "mysqlnd_wireprotocol.h"
#include "mysqlnd_block_alloc.h"
+#include "mysqlnd_connection.h"
#include "mysqlnd_priv.h"
#include "mysqlnd_result.h"
#include "mysqlnd_result_meta.h"
@@ -29,18 +29,16 @@
#include "mysqlnd_debug.h"
#include "mysqlnd_ext_plugin.h"
-#define MYSQLND_SILENT
-
/* {{{ mysqlnd_result_buffered_zval::initialize_result_set_rest */
static enum_func_status
-MYSQLND_METHOD(mysqlnd_result_buffered_zval, initialize_result_set_rest)(MYSQLND_RES_BUFFERED * const result, MYSQLND_RES_METADATA * const meta,
- MYSQLND_STATS * stats, zend_bool int_and_float_native)
+MYSQLND_METHOD(mysqlnd_result_buffered_zval, initialize_result_set_rest)(MYSQLND_RES_BUFFERED * const result,
+ MYSQLND_RES_METADATA * const meta,
+ MYSQLND_STATS * stats,
+ zend_bool int_and_float_native)
{
- unsigned int i;
enum_func_status ret = PASS;
const unsigned int field_count = meta->field_count;
const uint64_t row_count = result->row_count;
- enum_func_status rc;
zval *data_begin = ((MYSQLND_RES_BUFFERED_ZVAL *) result)->data;
zval *data_cursor = data_begin;
@@ -52,25 +50,27 @@ MYSQLND_METHOD(mysqlnd_result_buffered_zval, initialize_result_set_rest)(MYSQLND
}
while ((data_cursor - data_begin) < (int)(row_count * field_count)) {
if (Z_ISUNDEF(data_cursor[0])) {
- rc = result->m.row_decoder(result->row_buffers[(data_cursor - data_begin) / field_count],
- data_cursor,
- field_count,
- meta->fields,
- int_and_float_native,
- stats);
+ unsigned int i;
+ const size_t current_row_num = (data_cursor - data_begin) / field_count;
+ enum_func_status rc = result->m.row_decoder(result->row_buffers[current_row_num],
+ data_cursor,
+ field_count,
+ meta->fields,
+ int_and_float_native,
+ stats);
if (rc != PASS) {
ret = FAIL;
break;
}
- result->initialized_rows++;
- for (i = 0; i < field_count; i++) {
+ ++result->initialized_rows;
+ for (i = 0; i < field_count; ++i) {
/*
NULL fields are 0 length, 0 is not more than 0
String of zero size, definitely can't be the next max_length.
Thus for NULL and zero-length we are quite efficient.
*/
if (Z_TYPE(data_cursor[i]) == IS_STRING) {
- zend_ulong len = Z_STRLEN(data_cursor[i]);
+ const size_t len = Z_STRLEN(data_cursor[i]);
if (meta->fields[i].max_length < len) {
meta->fields[i].max_length = len;
}
@@ -86,8 +86,10 @@ MYSQLND_METHOD(mysqlnd_result_buffered_zval, initialize_result_set_rest)(MYSQLND
/* {{{ mysqlnd_result_buffered_c::initialize_result_set_rest */
static enum_func_status
-MYSQLND_METHOD(mysqlnd_result_buffered_c, initialize_result_set_rest)(MYSQLND_RES_BUFFERED * const result, MYSQLND_RES_METADATA * const meta,
- MYSQLND_STATS * stats, zend_bool int_and_float_native)
+MYSQLND_METHOD(mysqlnd_result_buffered_c, initialize_result_set_rest)(MYSQLND_RES_BUFFERED * const result,
+ MYSQLND_RES_METADATA * const meta,
+ MYSQLND_STATS * stats,
+ zend_bool int_and_float_native)
{
unsigned int i;
enum_func_status ret = PASS;
@@ -125,7 +127,7 @@ MYSQLND_METHOD(mysqlnd_result_buffered_c, initialize_result_set_rest)(MYSQLND_RE
Thus for NULL and zero-length we are quite efficient.
*/
if (Z_TYPE(current_row[i]) == IS_STRING) {
- zend_ulong len = Z_STRLEN(current_row[i]);
+ const size_t len = Z_STRLEN(current_row[i]);
if (meta->fields[i].max_length < len) {
meta->fields[i].max_length = len;
}
@@ -164,7 +166,8 @@ MYSQLND_METHOD(mysqlnd_result_unbuffered, free_last_data)(MYSQLND_RES_UNBUFFERED
if (unbuf->last_row_buffer) {
DBG_INF("Freeing last row buffer");
/* Nothing points to this buffer now, free it */
- unbuf->last_row_buffer->free_chunk(unbuf->last_row_buffer);
+ unbuf->result_set_memory_pool->free_chunk(
+ unbuf->result_set_memory_pool, unbuf->last_row_buffer);
unbuf->last_row_buffer = NULL;
}
@@ -213,7 +216,7 @@ MYSQLND_METHOD(mysqlnd_result_buffered_zval, free_result)(MYSQLND_RES_BUFFERED_Z
set->data = NULL; /* prevent double free if following loop is interrupted */
if (data) {
- unsigned int field_count = set->field_count;
+ const unsigned int field_count = set->field_count;
int64_t row;
for (row = set->row_count - 1; row >= 0; row--) {
@@ -251,19 +254,23 @@ static void
MYSQLND_METHOD(mysqlnd_result_buffered, free_result)(MYSQLND_RES_BUFFERED * const set)
{
int64_t row;
+ MYSQLND_MEMORY_POOL * pool;
DBG_ENTER("mysqlnd_result_buffered::free_result");
DBG_INF_FMT("Freeing "MYSQLND_LLU_SPEC" row(s)", set->row_count);
+ mysqlnd_error_info_free_contents(&set->error_info);
+
if (set->type == MYSQLND_BUFFERED_TYPE_ZVAL) {
MYSQLND_METHOD(mysqlnd_result_buffered_zval, free_result)((MYSQLND_RES_BUFFERED_ZVAL *) set);
} if (set->type == MYSQLND_BUFFERED_TYPE_C) {
MYSQLND_METHOD(mysqlnd_result_buffered_c, free_result)((MYSQLND_RES_BUFFERED_C *) set);
}
+ pool = set->result_set_memory_pool;
for (row = set->row_count - 1; row >= 0; row--) {
MYSQLND_MEMORY_POOL_CHUNK *current_buffer = set->row_buffers[row];
- current_buffer->free_chunk(current_buffer);
+ pool->free_chunk(pool, current_buffer);
}
if (set->lengths) {
@@ -281,7 +288,6 @@ MYSQLND_METHOD(mysqlnd_result_buffered, free_result)(MYSQLND_RES_BUFFERED * cons
set->result_set_memory_pool = NULL;
}
-
set->row_count = 0;
mnd_pefree(set, set->persistent);
@@ -306,7 +312,6 @@ MYSQLND_METHOD(mysqlnd_res, free_result_buffers)(MYSQLND_RES * result)
result->stored_data = NULL;
}
-
DBG_VOID_RETURN;
}
/* }}} */
@@ -370,7 +375,7 @@ MYSQLND_METHOD(mysqlnd_res, read_result_metadata)(MYSQLND_RES * result, MYSQLND_
result->meta = result->m.result_meta_init(result->field_count, result->persistent);
if (!result->meta) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
DBG_RETURN(FAIL);
}
@@ -400,26 +405,27 @@ MYSQLND_METHOD(mysqlnd_res, read_result_metadata)(MYSQLND_RES * result, MYSQLND_
enum_func_status
mysqlnd_query_read_result_set_header(MYSQLND_CONN_DATA * conn, MYSQLND_STMT * s)
{
- MYSQLND_STMT_DATA * stmt = s ? s->data:NULL;
enum_func_status ret;
+ MYSQLND_STMT_DATA * stmt = s ? s->data : NULL;
MYSQLND_PACKET_RSET_HEADER * rset_header = NULL;
MYSQLND_PACKET_EOF * fields_eof = NULL;
+ const zend_bool persistent = conn->persistent;
DBG_ENTER("mysqlnd_query_read_result_set_header");
DBG_INF_FMT("stmt=%lu", stmt? stmt->stmt_id:0);
ret = FAIL;
do {
- rset_header = conn->protocol->m.get_rset_header_packet(conn->protocol, FALSE);
+ rset_header = conn->payload_decoder_factory->m.get_rset_header_packet(conn->payload_decoder_factory, FALSE);
if (!rset_header) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
ret = FAIL;
break;
}
- SET_ERROR_AFF_ROWS(conn);
+ UPSERT_STATUS_SET_AFFECTED_ROWS_TO_ERROR(conn->upsert_status);
- if (FAIL == (ret = PACKET_READ(rset_header, conn))) {
+ if (FAIL == (ret = PACKET_READ(rset_header))) {
php_error_docref(NULL, E_WARNING, "Error reading result set's header");
break;
}
@@ -434,16 +440,16 @@ mysqlnd_query_read_result_set_header(MYSQLND_CONN_DATA * conn, MYSQLND_STMT * s)
a multi-statement or a stored procedure, so it should be
safe to unconditionally turn off the flag here.
*/
- conn->upsert_status->server_status &= ~SERVER_MORE_RESULTS_EXISTS;
+ UPSERT_STATUS_SET_SERVER_STATUS(conn->upsert_status, UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status) & ~SERVER_MORE_RESULTS_EXISTS);
/*
This will copy the error code and the messages, as they
are buffers in the struct
*/
- COPY_CLIENT_ERROR(*conn->error_info, rset_header->error_info);
+ COPY_CLIENT_ERROR(conn->error_info, rset_header->error_info);
ret = FAIL;
DBG_ERR_FMT("error=%s", rset_header->error_info.error);
/* Return back from CONN_QUERY_SENT */
- CONN_SET_STATE(conn, CONN_READY);
+ SET_CONNECTION_STATE(&conn->state, CONN_READY);
break;
}
conn->error_info->error_no = 0;
@@ -454,9 +460,9 @@ mysqlnd_query_read_result_set_header(MYSQLND_CONN_DATA * conn, MYSQLND_STMT * s)
DBG_INF("LOAD DATA");
conn->last_query_type = QUERY_LOAD_LOCAL;
conn->field_count = 0; /* overwrite previous value, or the last value could be used and lead to bug#53503 */
- CONN_SET_STATE(conn, CONN_SENDING_LOAD_DATA);
- ret = mysqlnd_handle_local_infile(conn, rset_header->info_or_local_file, &is_warning);
- CONN_SET_STATE(conn, (ret == PASS || is_warning == TRUE)? CONN_READY:CONN_QUIT_SENT);
+ SET_CONNECTION_STATE(&conn->state, CONN_SENDING_LOAD_DATA);
+ ret = mysqlnd_handle_local_infile(conn, rset_header->info_or_local_file.s, &is_warning);
+ SET_CONNECTION_STATE(&conn->state, (ret == PASS || is_warning == TRUE)? CONN_READY:CONN_QUIT_SENT);
MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_NON_RSET_QUERY);
break;
}
@@ -464,19 +470,19 @@ mysqlnd_query_read_result_set_header(MYSQLND_CONN_DATA * conn, MYSQLND_STMT * s)
DBG_INF("UPSERT");
conn->last_query_type = QUERY_UPSERT;
conn->field_count = rset_header->field_count;
- memset(conn->upsert_status, 0, sizeof(*conn->upsert_status));
- conn->upsert_status->warning_count = rset_header->warning_count;
- conn->upsert_status->server_status = rset_header->server_status;
- conn->upsert_status->affected_rows = rset_header->affected_rows;
- conn->upsert_status->last_insert_id = rset_header->last_insert_id;
- SET_NEW_MESSAGE(conn->last_message, conn->last_message_len,
- rset_header->info_or_local_file, rset_header->info_or_local_file_len,
- conn->persistent);
+ UPSERT_STATUS_RESET(conn->upsert_status);
+ UPSERT_STATUS_SET_WARNINGS(conn->upsert_status, rset_header->warning_count);
+ UPSERT_STATUS_SET_SERVER_STATUS(conn->upsert_status, rset_header->server_status);
+ UPSERT_STATUS_SET_AFFECTED_ROWS(conn->upsert_status, rset_header->affected_rows);
+ UPSERT_STATUS_SET_LAST_INSERT_ID(conn->upsert_status, rset_header->last_insert_id);
+ SET_NEW_MESSAGE(conn->last_message.s, conn->last_message.l,
+ rset_header->info_or_local_file.s, rset_header->info_or_local_file.l,
+ persistent);
/* Result set can follow UPSERT statement, check server_status */
- if (conn->upsert_status->server_status & SERVER_MORE_RESULTS_EXISTS) {
- CONN_SET_STATE(conn, CONN_NEXT_RESULT_PENDING);
+ if (UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status) & SERVER_MORE_RESULTS_EXISTS) {
+ SET_CONNECTION_STATE(&conn->state, CONN_NEXT_RESULT_PENDING);
} else {
- CONN_SET_STATE(conn, CONN_READY);
+ SET_CONNECTION_STATE(&conn->state, CONN_READY);
}
ret = PASS;
MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_NON_RSET_QUERY);
@@ -486,19 +492,19 @@ mysqlnd_query_read_result_set_header(MYSQLND_CONN_DATA * conn, MYSQLND_STMT * s)
enum_mysqlnd_collected_stats statistic = STAT_LAST;
DBG_INF("Result set pending");
- SET_EMPTY_MESSAGE(conn->last_message, conn->last_message_len, conn->persistent);
+ SET_EMPTY_MESSAGE(conn->last_message.s, conn->last_message.l, persistent);
MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_RSET_QUERY);
- memset(conn->upsert_status, 0, sizeof(*conn->upsert_status));
+ UPSERT_STATUS_RESET(conn->upsert_status);
/* restore after zeroing */
- SET_ERROR_AFF_ROWS(conn);
+ UPSERT_STATUS_SET_AFFECTED_ROWS_TO_ERROR(conn->upsert_status);
conn->last_query_type = QUERY_SELECT;
- CONN_SET_STATE(conn, CONN_FETCHING_DATA);
+ SET_CONNECTION_STATE(&conn->state, CONN_FETCHING_DATA);
/* PS has already allocated it */
conn->field_count = rset_header->field_count;
if (!stmt) {
- result = conn->current_result = conn->m->result_init(rset_header->field_count, conn->persistent);
+ result = conn->current_result = conn->m->result_init(rset_header->field_count, persistent);
} else {
if (!stmt->result) {
DBG_INF("This is 'SHOW'/'EXPLAIN'-like query.");
@@ -525,7 +531,7 @@ mysqlnd_query_read_result_set_header(MYSQLND_CONN_DATA * conn, MYSQLND_STMT * s)
result = stmt->result;
}
if (!result) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
ret = FAIL;
break;
}
@@ -541,13 +547,13 @@ mysqlnd_query_read_result_set_header(MYSQLND_CONN_DATA * conn, MYSQLND_STMT * s)
}
/* Check for SERVER_STATUS_MORE_RESULTS if needed */
- fields_eof = conn->protocol->m.get_eof_packet(conn->protocol, FALSE);
+ fields_eof = conn->payload_decoder_factory->m.get_eof_packet(conn->payload_decoder_factory, FALSE);
if (!fields_eof) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
ret = FAIL;
break;
}
- if (FAIL == (ret = PACKET_READ(fields_eof, conn))) {
+ if (FAIL == (ret = PACKET_READ(fields_eof))) {
DBG_ERR("Error occurred while reading the EOF packet");
result->m.free_result_contents(result);
mnd_efree(result);
@@ -555,12 +561,17 @@ mysqlnd_query_read_result_set_header(MYSQLND_CONN_DATA * conn, MYSQLND_STMT * s)
conn->current_result = NULL;
} else {
stmt->result = NULL;
+ /* XXX: This will crash, because we will null also the methods.
+ But seems it happens in extreme cases or doesn't. Should be fixed by exporting a function
+ (from mysqlnd_driver.c?) to do the reset.
+ This is done also in mysqlnd_ps.c
+ */
memset(stmt, 0, sizeof(*stmt));
stmt->state = MYSQLND_STMT_INITTED;
}
} else {
DBG_INF_FMT("warnings=%u server_status=%u", fields_eof->warning_count, fields_eof->server_status);
- conn->upsert_status->warning_count = fields_eof->warning_count;
+ UPSERT_STATUS_SET_WARNINGS(conn->upsert_status, fields_eof->warning_count);
/*
If SERVER_MORE_RESULTS_EXISTS is set then this is either MULTI_QUERY or a CALL()
The first packet after sending the query/com_execute has the bit set only
@@ -568,7 +579,7 @@ mysqlnd_query_read_result_set_header(MYSQLND_CONN_DATA * conn, MYSQLND_STMT * s)
will include many result sets. What actually matters are the bits set at the end
of every result set (the EOF packet).
*/
- conn->upsert_status->server_status = fields_eof->server_status;
+ UPSERT_STATUS_SET_SERVER_STATUS(conn->upsert_status, fields_eof->server_status);
if (fields_eof->server_status & SERVER_QUERY_NO_GOOD_INDEX_USED) {
statistic = STAT_BAD_INDEX_USED;
} else if (fields_eof->server_status & SERVER_QUERY_NO_INDEX_USED) {
@@ -598,7 +609,7 @@ mysqlnd_query_read_result_set_header(MYSQLND_CONN_DATA * conn, MYSQLND_STMT * s)
of PHP, to be called as separate function. But let's have it for
completeness.
*/
-static zend_ulong *
+static const size_t *
MYSQLND_METHOD(mysqlnd_result_buffered_zval, fetch_lengths)(MYSQLND_RES_BUFFERED * const result)
{
const MYSQLND_RES_BUFFERED_ZVAL * set = (MYSQLND_RES_BUFFERED_ZVAL *) result;
@@ -630,7 +641,7 @@ MYSQLND_METHOD(mysqlnd_result_buffered_zval, fetch_lengths)(MYSQLND_RES_BUFFERED
of PHP, to be called as separate function. But let's have it for
completeness.
*/
-static zend_ulong *
+static const size_t *
MYSQLND_METHOD(mysqlnd_result_buffered_c, fetch_lengths)(MYSQLND_RES_BUFFERED * const result)
{
const MYSQLND_RES_BUFFERED_C * set = (MYSQLND_RES_BUFFERED_C *) result;
@@ -647,7 +658,7 @@ MYSQLND_METHOD(mysqlnd_result_buffered_c, fetch_lengths)(MYSQLND_RES_BUFFERED *
/* {{{ mysqlnd_result_unbuffered::fetch_lengths */
-static zend_ulong *
+static const size_t *
MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_lengths)(MYSQLND_RES_UNBUFFERED * const result)
{
/* simulate output of libmysql */
@@ -657,10 +668,10 @@ MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_lengths)(MYSQLND_RES_UNBUFFERED
/* {{{ mysqlnd_res::fetch_lengths */
-static zend_ulong *
+static const size_t *
MYSQLND_METHOD(mysqlnd_res, fetch_lengths)(MYSQLND_RES * const result)
{
- zend_ulong * ret;
+ const size_t * ret;
DBG_ENTER("mysqlnd_res::fetch_lengths");
ret = result->stored_data && result->stored_data->m.fetch_lengths ?
result->stored_data->m.fetch_lengths(result->stored_data) :
@@ -680,7 +691,8 @@ MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row_c)(MYSQLND_RES * result, voi
enum_func_status ret;
MYSQLND_ROW_C *row = (MYSQLND_ROW_C *) param;
MYSQLND_PACKET_ROW *row_packet = result->unbuf->row_packet;
- const MYSQLND_RES_METADATA * const meta = result->meta;
+ MYSQLND_RES_METADATA * const meta = result->meta;
+ MYSQLND_CONN_DATA * const conn = result->conn;
DBG_ENTER("mysqlnd_result_unbuffered::fetch_row_c");
@@ -689,8 +701,8 @@ MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row_c)(MYSQLND_RES * result, voi
/* No more rows obviously */
DBG_RETURN(PASS);
}
- if (CONN_GET_STATE(result->conn) != CONN_FETCHING_DATA) {
- SET_CLIENT_ERROR(*result->conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
+ if (!conn || GET_CONNECTION_STATE(&conn->state) != CONN_FETCHING_DATA) {
+ SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
DBG_RETURN(FAIL);
}
if (!row_packet) {
@@ -704,15 +716,15 @@ MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row_c)(MYSQLND_RES * result, voi
If we skip rows (row == NULL) we have to
result->m.unbuffered_free_last_data() before it. The function returns always true.
*/
- if (PASS == (ret = PACKET_READ(row_packet, result->conn)) && !row_packet->eof) {
- result->unbuf->m.free_last_data(result->unbuf, result->conn? result->conn->stats : NULL);
+ if (PASS == (ret = PACKET_READ(row_packet)) && !row_packet->eof) {
+ result->unbuf->m.free_last_data(result->unbuf, conn->stats);
result->unbuf->last_row_data = row_packet->fields;
result->unbuf->last_row_buffer = row_packet->row_buffer;
row_packet->fields = NULL;
row_packet->row_buffer = NULL;
- MYSQLND_INC_CONN_STATISTIC(result->conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_UNBUF);
+ MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_UNBUF);
if (!row_packet->skip_extraction) {
unsigned int i, field_count = meta->field_count;
@@ -721,8 +733,8 @@ MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row_c)(MYSQLND_RES * result, voi
result->unbuf->last_row_data,
field_count,
row_packet->fields_metadata,
- result->conn->options->int_and_float_native,
- result->conn->stats);
+ conn->options->int_and_float_native,
+ conn->stats);
if (PASS != rc) {
DBG_RETURN(FAIL);
}
@@ -730,11 +742,11 @@ MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row_c)(MYSQLND_RES * result, voi
*row = mnd_malloc(field_count * sizeof(char *));
if (*row) {
MYSQLND_FIELD * field = meta->fields;
- zend_ulong * lengths = result->unbuf->lengths;
+ size_t * lengths = result->unbuf->lengths;
for (i = 0; i < field_count; i++, field++) {
zval * data = &result->unbuf->last_row_data[i];
- unsigned int len = (Z_TYPE_P(data) == IS_STRING)? Z_STRLEN_P(data) : 0;
+ const size_t len = (Z_TYPE_P(data) == IS_STRING)? Z_STRLEN_P(data) : 0;
/* BEGIN difference between normal normal fetch and _c */
if (Z_TYPE_P(data) != IS_NULL) {
@@ -754,7 +766,7 @@ MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row_c)(MYSQLND_RES * result, voi
}
}
} else {
- SET_OOM_ERROR(*result->conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
}
}
}
@@ -762,28 +774,29 @@ MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row_c)(MYSQLND_RES * result, voi
*fetched_anything = TRUE;
} else if (ret == FAIL) {
if (row_packet->error_info.error_no) {
- COPY_CLIENT_ERROR(*result->conn->error_info, row_packet->error_info);
+ COPY_CLIENT_ERROR(conn->error_info, row_packet->error_info);
DBG_ERR_FMT("errorno=%u error=%s", row_packet->error_info.error_no, row_packet->error_info.error);
}
- CONN_SET_STATE(result->conn, CONN_READY);
+ SET_CONNECTION_STATE(&conn->state, CONN_READY);
result->unbuf->eof_reached = TRUE; /* so next time we won't get an error */
} else if (row_packet->eof) {
/* Mark the connection as usable again */
DBG_INF_FMT("warnings=%u server_status=%u", row_packet->warning_count, row_packet->server_status);
result->unbuf->eof_reached = TRUE;
- memset(result->conn->upsert_status, 0, sizeof(*result->conn->upsert_status));
- result->conn->upsert_status->warning_count = row_packet->warning_count;
- result->conn->upsert_status->server_status = row_packet->server_status;
+
+ UPSERT_STATUS_RESET(conn->upsert_status);
+ UPSERT_STATUS_SET_WARNINGS(conn->upsert_status, row_packet->warning_count);
+ UPSERT_STATUS_SET_SERVER_STATUS(conn->upsert_status, row_packet->server_status);
/*
result->row_packet will be cleaned when
destroying the result object
*/
- if (result->conn->upsert_status->server_status & SERVER_MORE_RESULTS_EXISTS) {
- CONN_SET_STATE(result->conn, CONN_NEXT_RESULT_PENDING);
+ if (UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status) & SERVER_MORE_RESULTS_EXISTS) {
+ SET_CONNECTION_STATE(&conn->state, CONN_NEXT_RESULT_PENDING);
} else {
- CONN_SET_STATE(result->conn, CONN_READY);
+ SET_CONNECTION_STATE(&conn->state, CONN_READY);
}
- result->unbuf->m.free_last_data(result->unbuf, result->conn? result->conn->stats : NULL);
+ result->unbuf->m.free_last_data(result->unbuf, conn->stats);
}
DBG_INF_FMT("ret=%s fetched=%u", ret == PASS? "PASS":"FAIL", *fetched_anything);
@@ -800,6 +813,7 @@ MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row)(MYSQLND_RES * result, void
zval *row = (zval *) param;
MYSQLND_PACKET_ROW *row_packet = result->unbuf->row_packet;
const MYSQLND_RES_METADATA * const meta = result->meta;
+ MYSQLND_CONN_DATA * const conn = result->conn;
DBG_ENTER("mysqlnd_result_unbuffered::fetch_row");
@@ -808,8 +822,8 @@ MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row)(MYSQLND_RES * result, void
/* No more rows obviously */
DBG_RETURN(PASS);
}
- if (CONN_GET_STATE(result->conn) != CONN_FETCHING_DATA) {
- SET_CLIENT_ERROR(*result->conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
+ if (GET_CONNECTION_STATE(&conn->state) != CONN_FETCHING_DATA) {
+ SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
DBG_RETURN(FAIL);
}
if (!row_packet) {
@@ -823,36 +837,36 @@ MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row)(MYSQLND_RES * result, void
If we skip rows (row == NULL) we have to
result->m.unbuffered_free_last_data() before it. The function returns always true.
*/
- if (PASS == (ret = PACKET_READ(row_packet, result->conn)) && !row_packet->eof) {
- result->unbuf->m.free_last_data(result->unbuf, result->conn? result->conn->stats : NULL);
+ if (PASS == (ret = PACKET_READ(row_packet)) && !row_packet->eof) {
+ result->unbuf->m.free_last_data(result->unbuf, conn->stats);
result->unbuf->last_row_data = row_packet->fields;
result->unbuf->last_row_buffer = row_packet->row_buffer;
row_packet->fields = NULL;
row_packet->row_buffer = NULL;
- MYSQLND_INC_CONN_STATISTIC(result->conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_UNBUF);
+ MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_UNBUF);
if (!row_packet->skip_extraction) {
unsigned int i, field_count = meta->field_count;
enum_func_status rc = result->unbuf->m.row_decoder(result->unbuf->last_row_buffer,
- result->unbuf->last_row_data,
- field_count,
- row_packet->fields_metadata,
- result->conn->options->int_and_float_native,
- result->conn->stats);
+ result->unbuf->last_row_data,
+ field_count,
+ row_packet->fields_metadata,
+ conn->options->int_and_float_native,
+ conn->stats);
if (PASS != rc) {
DBG_RETURN(FAIL);
}
{
HashTable * row_ht = Z_ARRVAL_P(row);
MYSQLND_FIELD * field = meta->fields;
- zend_ulong * lengths = result->unbuf->lengths;
+ size_t * lengths = result->unbuf->lengths;
for (i = 0; i < field_count; i++, field++) {
zval * data = &result->unbuf->last_row_data[i];
- unsigned int len = (Z_TYPE_P(data) == IS_STRING)? Z_STRLEN_P(data) : 0;
+ const size_t len = (Z_TYPE_P(data) == IS_STRING)? Z_STRLEN_P(data) : 0;
if (flags & MYSQLND_FETCH_NUM) {
Z_TRY_ADDREF_P(data);
@@ -868,9 +882,9 @@ MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row)(MYSQLND_RES * result, void
*/
Z_TRY_ADDREF_P(data);
if (meta->zend_hash_keys[i].is_numeric == FALSE) {
- zend_hash_update(Z_ARRVAL_P(row), meta->fields[i].sname, data);
+ zend_hash_update(row_ht, meta->fields[i].sname, data);
} else {
- zend_hash_index_update(Z_ARRVAL_P(row), meta->zend_hash_keys[i].key, data);
+ zend_hash_index_update(row_ht, meta->zend_hash_keys[i].key, data);
}
}
@@ -888,28 +902,29 @@ MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row)(MYSQLND_RES * result, void
*fetched_anything = TRUE;
} else if (ret == FAIL) {
if (row_packet->error_info.error_no) {
- COPY_CLIENT_ERROR(*result->conn->error_info, row_packet->error_info);
+ COPY_CLIENT_ERROR(conn->error_info, row_packet->error_info);
DBG_ERR_FMT("errorno=%u error=%s", row_packet->error_info.error_no, row_packet->error_info.error);
}
- CONN_SET_STATE(result->conn, CONN_READY);
+ SET_CONNECTION_STATE(&conn->state, CONN_READY);
result->unbuf->eof_reached = TRUE; /* so next time we won't get an error */
} else if (row_packet->eof) {
/* Mark the connection as usable again */
DBG_INF_FMT("warnings=%u server_status=%u", row_packet->warning_count, row_packet->server_status);
result->unbuf->eof_reached = TRUE;
- memset(result->conn->upsert_status, 0, sizeof(*result->conn->upsert_status));
- result->conn->upsert_status->warning_count = row_packet->warning_count;
- result->conn->upsert_status->server_status = row_packet->server_status;
+
+ UPSERT_STATUS_RESET(conn->upsert_status);
+ UPSERT_STATUS_SET_WARNINGS(conn->upsert_status, row_packet->warning_count);
+ UPSERT_STATUS_SET_SERVER_STATUS(conn->upsert_status, row_packet->server_status);
/*
result->row_packet will be cleaned when
destroying the result object
*/
- if (result->conn->upsert_status->server_status & SERVER_MORE_RESULTS_EXISTS) {
- CONN_SET_STATE(result->conn, CONN_NEXT_RESULT_PENDING);
+ if (UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status) & SERVER_MORE_RESULTS_EXISTS) {
+ SET_CONNECTION_STATE(&conn->state, CONN_NEXT_RESULT_PENDING);
} else {
- CONN_SET_STATE(result->conn, CONN_READY);
+ SET_CONNECTION_STATE(&conn->state, CONN_READY);
}
- result->unbuf->m.free_last_data(result->unbuf, result->conn? result->conn->stats : NULL);
+ result->unbuf->m.free_last_data(result->unbuf, conn->stats);
}
DBG_INF_FMT("ret=%s fetched=%u", ret == PASS? "PASS":"FAIL", *fetched_anything);
@@ -920,11 +935,12 @@ MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row)(MYSQLND_RES * result, void
/* {{{ mysqlnd_res::use_result */
static MYSQLND_RES *
-MYSQLND_METHOD(mysqlnd_res, use_result)(MYSQLND_RES * const result, zend_bool ps)
+MYSQLND_METHOD(mysqlnd_res, use_result)(MYSQLND_RES * const result, const zend_bool ps)
{
+ MYSQLND_CONN_DATA * const conn = result->conn;
DBG_ENTER("mysqlnd_res::use_result");
- SET_EMPTY_ERROR(*result->conn->error_info);
+ SET_EMPTY_ERROR(conn->error_info);
if (ps == FALSE) {
result->type = MYSQLND_RES_NORMAL;
@@ -943,20 +959,22 @@ MYSQLND_METHOD(mysqlnd_res, use_result)(MYSQLND_RES * const result, zend_bool ps
this to be not NULL.
*/
/* FALSE = non-persistent */
- result->unbuf->row_packet = result->conn->protocol->m.get_row_packet(result->conn->protocol, FALSE);
- if (!result->unbuf->row_packet) {
- goto oom;
+ {
+ struct st_mysqlnd_packet_row * row_packet = conn->payload_decoder_factory->m.get_row_packet(conn->payload_decoder_factory, FALSE);
+ if (!row_packet) {
+ goto oom;
+ }
+ row_packet->result_set_memory_pool = result->unbuf->result_set_memory_pool;
+ row_packet->field_count = result->field_count;
+ row_packet->binary_protocol = ps;
+ row_packet->fields_metadata = result->meta->fields;
+
+ result->unbuf->row_packet = row_packet;
}
- result->unbuf->row_packet->result_set_memory_pool = result->unbuf->result_set_memory_pool;
- result->unbuf->row_packet->field_count = result->field_count;
- result->unbuf->row_packet->binary_protocol = ps;
- result->unbuf->row_packet->fields_metadata = result->meta->fields;
- result->unbuf->row_packet->bit_fields_count = result->meta->bit_fields_count;
- result->unbuf->row_packet->bit_fields_total_len = result->meta->bit_fields_total_len;
DBG_RETURN(result);
oom:
- SET_OOM_ERROR(*result->conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
DBG_RETURN(NULL);
}
/* }}} */
@@ -966,10 +984,11 @@ oom:
static enum_func_status
MYSQLND_METHOD(mysqlnd_result_buffered, fetch_row_c)(MYSQLND_RES * result, void * param, unsigned int flags, zend_bool * fetched_anything)
{
+ enum_func_status ret = FAIL;
MYSQLND_ROW_C * row = (MYSQLND_ROW_C *) param;
const MYSQLND_RES_METADATA * const meta = result->meta;
unsigned int field_count = meta->field_count;
- enum_func_status ret = FAIL;
+ MYSQLND_CONN_DATA * const conn = result->conn;
DBG_ENTER("mysqlnd_result_buffered::fetch_row_c");
if (result->stored_data->type == MYSQLND_BUFFERED_TYPE_ZVAL) {
@@ -988,20 +1007,20 @@ MYSQLND_METHOD(mysqlnd_result_buffered, fetch_row_c)(MYSQLND_RES * result, void
current_row,
field_count,
meta->fields,
- result->conn->options->int_and_float_native,
- result->conn->stats);
+ conn->options->int_and_float_native,
+ conn->stats);
if (rc != PASS) {
DBG_RETURN(FAIL);
}
- set->initialized_rows++;
- for (i = 0; i < field_count; i++) {
+ ++set->initialized_rows;
+ for (i = 0; i < field_count; ++i) {
/*
NULL fields are 0 length, 0 is not more than 0
String of zero size, definitely can't be the next max_length.
Thus for NULL and zero-length we are quite efficient.
*/
if (Z_TYPE(current_row[i]) == IS_STRING) {
- zend_ulong len = Z_STRLEN(current_row[i]);
+ const size_t len = Z_STRLEN(current_row[i]);
if (meta->fields[i].max_length < len) {
meta->fields[i].max_length = len;
}
@@ -1013,7 +1032,7 @@ MYSQLND_METHOD(mysqlnd_result_buffered, fetch_row_c)(MYSQLND_RES * result, void
/* there is no conn handle in this function thus we can't set OOM in error_info */
*row = mnd_malloc(field_count * sizeof(char *));
if (*row) {
- for (i = 0; i < field_count; i++) {
+ for (i = 0; i < field_count; ++i) {
zval * data = &current_row[i];
set->lengths[i] = (Z_TYPE_P(data) == IS_STRING)? Z_STRLEN_P(data) : 0;
@@ -1028,7 +1047,7 @@ MYSQLND_METHOD(mysqlnd_result_buffered, fetch_row_c)(MYSQLND_RES * result, void
set->data_cursor += field_count;
MYSQLND_INC_GLOBAL_STATISTIC(STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_BUF);
} else {
- SET_OOM_ERROR(*result->conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
}
/* END difference between normal normal fetch and _c */
@@ -1058,41 +1077,40 @@ MYSQLND_METHOD(mysqlnd_result_buffered, fetch_row_c)(MYSQLND_RES * result, void
static enum_func_status
MYSQLND_METHOD(mysqlnd_result_buffered_zval, fetch_row)(MYSQLND_RES * result, void * param, const unsigned int flags, zend_bool * fetched_anything)
{
+ enum_func_status ret = FAIL;
zval * row = (zval *) param;
const MYSQLND_RES_METADATA * const meta = result->meta;
- unsigned int field_count = meta->field_count;
- enum_func_status ret = FAIL;
+ const unsigned int field_count = meta->field_count;
MYSQLND_RES_BUFFERED_ZVAL * set = (MYSQLND_RES_BUFFERED_ZVAL *) result->stored_data;
+ MYSQLND_CONN_DATA * const conn = result->conn;
DBG_ENTER("mysqlnd_result_buffered_zval::fetch_row");
/* If we haven't read everything */
- if (set->data_cursor &&
- (set->data_cursor - set->data) < (set->row_count * field_count))
- {
+ if (set->data_cursor && (set->data_cursor - set->data) < (set->row_count * field_count)) {
unsigned int i;
zval *current_row = set->data_cursor;
if (Z_ISUNDEF(current_row[0])) {
- uint64_t row_num = (set->data_cursor - set->data) / field_count;
+ const size_t row_num = (set->data_cursor - set->data) / field_count;
enum_func_status rc = set->m.row_decoder(set->row_buffers[row_num],
- current_row,
- field_count,
- meta->fields,
- result->conn->options->int_and_float_native,
- result->conn->stats);
+ current_row,
+ field_count,
+ meta->fields,
+ conn->options->int_and_float_native,
+ conn->stats);
if (rc != PASS) {
DBG_RETURN(FAIL);
}
- set->initialized_rows++;
- for (i = 0; i < field_count; i++) {
+ ++set->initialized_rows;
+ for (i = 0; i < field_count; ++i) {
/*
NULL fields are 0 length, 0 is not more than 0
String of zero size, definitely can't be the next max_length.
Thus for NULL and zero-length we are quite efficient.
*/
if (Z_TYPE(current_row[i]) == IS_STRING) {
- zend_ulong len = Z_STRLEN(current_row[i]);
+ const size_t len = Z_STRLEN(current_row[i]);
if (meta->fields[i].max_length < len) {
meta->fields[i].max_length = len;
}
@@ -1100,7 +1118,7 @@ MYSQLND_METHOD(mysqlnd_result_buffered_zval, fetch_row)(MYSQLND_RES * result, vo
}
}
- for (i = 0; i < field_count; i++) {
+ for (i = 0; i < field_count; ++i) {
zval * data = &current_row[i];
set->lengths[i] = (Z_TYPE_P(data) == IS_STRING)? Z_STRLEN_P(data) : 0;
@@ -1145,10 +1163,11 @@ MYSQLND_METHOD(mysqlnd_result_buffered_zval, fetch_row)(MYSQLND_RES * result, vo
static enum_func_status
MYSQLND_METHOD(mysqlnd_result_buffered_c, fetch_row)(MYSQLND_RES * result, void * param, const unsigned int flags, zend_bool * fetched_anything)
{
+ enum_func_status ret = FAIL;
zval * row = (zval *) param;
const MYSQLND_RES_METADATA * const meta = result->meta;
- unsigned int field_count = meta->field_count;
- enum_func_status ret = FAIL;
+ const unsigned int field_count = meta->field_count;
+ MYSQLND_CONN_DATA * const conn = result->conn;
MYSQLND_RES_BUFFERED_C * set = (MYSQLND_RES_BUFFERED_C *) result->stored_data;
@@ -1156,38 +1175,38 @@ MYSQLND_METHOD(mysqlnd_result_buffered_c, fetch_row)(MYSQLND_RES * result, void
/* If we haven't read everything */
if (set->current_row < set->row_count) {
- zval *current_row;
enum_func_status rc;
+ zval * current_row;
unsigned int i;
current_row = mnd_emalloc(field_count * sizeof(zval));
if (!current_row) {
- SET_OOM_ERROR(*result->conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
DBG_RETURN(FAIL);
}
rc = result->stored_data->m.row_decoder(result->stored_data->row_buffers[set->current_row],
- current_row,
- field_count,
- meta->fields,
- result->conn->options->int_and_float_native,
- result->conn->stats);
+ current_row,
+ field_count,
+ meta->fields,
+ conn->options->int_and_float_native,
+ conn->stats);
if (rc != PASS) {
DBG_RETURN(FAIL);
}
if (!(set->initialized[set->current_row >> 3] & (1 << (set->current_row & 7)))) {
set->initialized[set->current_row >> 3] |= (1 << (set->current_row & 7)); /* mark initialized */
- set->initialized_rows++;
+ ++set->initialized_rows;
- for (i = 0; i < field_count; i++) {
+ for (i = 0; i < field_count; ++i) {
/*
NULL fields are 0 length, 0 is not more than 0
String of zero size, definitely can't be the next max_length.
Thus for NULL and zero-length we are quite efficient.
*/
if (Z_TYPE(current_row[i]) == IS_STRING) {
- zend_ulong len = Z_STRLEN(current_row[i]);
+ const size_t len = Z_STRLEN(current_row[i]);
if (meta->fields[i].max_length < len) {
meta->fields[i].max_length = len;
}
@@ -1195,7 +1214,7 @@ MYSQLND_METHOD(mysqlnd_result_buffered_c, fetch_row)(MYSQLND_RES * result, void
}
}
- for (i = 0; i < field_count; i++) {
+ for (i = 0; i < field_count; ++i) {
zval * data = &current_row[i];
set->lengths[i] = (Z_TYPE_P(data) == IS_STRING)? Z_STRLEN_P(data) : 0;
@@ -1228,7 +1247,7 @@ MYSQLND_METHOD(mysqlnd_result_buffered_c, fetch_row)(MYSQLND_RES * result, void
zval_ptr_dtor(data);
}
mnd_efree(current_row);
- set->current_row++;
+ ++set->current_row;
MYSQLND_INC_GLOBAL_STATISTIC(STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_BUF);
*fetched_anything = TRUE;
ret = PASS;
@@ -1271,14 +1290,11 @@ MYSQLND_METHOD(mysqlnd_res, store_result_fetch_data)(MYSQLND_CONN_DATA * const c
zend_bool binary_protocol)
{
enum_func_status ret;
- MYSQLND_PACKET_ROW * row_packet = NULL;
unsigned int next_extend = STORE_RESULT_PREALLOCATED_SET_IF_NOT_EMPTY, free_rows = 1;
- MYSQLND_RES_BUFFERED *set;
+ MYSQLND_RES_BUFFERED * set = result->stored_data;
+ MYSQLND_PACKET_ROW * row_packet = NULL;
DBG_ENTER("mysqlnd_res::store_result_fetch_data");
-
- set = result->stored_data;
-
if (!set || !row_buffers) {
ret = FAIL;
goto end;
@@ -1286,30 +1302,28 @@ MYSQLND_METHOD(mysqlnd_res, store_result_fetch_data)(MYSQLND_CONN_DATA * const c
if (free_rows) {
*row_buffers = mnd_pemalloc((size_t)(free_rows * sizeof(MYSQLND_MEMORY_POOL_CHUNK *)), 0);
if (!*row_buffers) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
ret = FAIL;
goto end;
}
}
- set->references = 1;
-
/* non-persistent */
- row_packet = conn->protocol->m.get_row_packet(conn->protocol, FALSE);
+ row_packet = conn->payload_decoder_factory->m.get_row_packet(conn->payload_decoder_factory, FALSE);
if (!row_packet) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
ret = FAIL;
goto end;
}
+ set->references = 1;
+
row_packet->result_set_memory_pool = result->stored_data->result_set_memory_pool;
row_packet->field_count = meta->field_count;
row_packet->binary_protocol = binary_protocol;
row_packet->fields_metadata = meta->fields;
- row_packet->bit_fields_count = meta->bit_fields_count;
- row_packet->bit_fields_total_len = meta->bit_fields_total_len;
row_packet->skip_extraction = TRUE; /* let php_mysqlnd_rowp_read() not allocate row_packet->fields, we will do it */
- while (FAIL != (ret = PACKET_READ(row_packet, conn)) && !row_packet->eof) {
+ while (FAIL != (ret = PACKET_READ(row_packet)) && !row_packet->eof) {
if (!free_rows) {
uint64_t total_allocated_rows = free_rows = next_extend = next_extend * 11 / 10; /* extend with 10% */
MYSQLND_MEMORY_POOL_CHUNK ** new_row_buffers;
@@ -1317,13 +1331,13 @@ MYSQLND_METHOD(mysqlnd_res, store_result_fetch_data)(MYSQLND_CONN_DATA * const c
/* don't try to allocate more than possible - mnd_XXalloc expects size_t, and it can have narrower range than uint64_t */
if (total_allocated_rows * sizeof(MYSQLND_MEMORY_POOL_CHUNK *) > SIZE_MAX) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
ret = FAIL;
goto end;
}
new_row_buffers = mnd_perealloc(*row_buffers, (size_t)(total_allocated_rows * sizeof(MYSQLND_MEMORY_POOL_CHUNK *)), 0);
if (!new_row_buffers) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
ret = FAIL;
goto end;
}
@@ -1353,35 +1367,38 @@ MYSQLND_METHOD(mysqlnd_res, store_result_fetch_data)(MYSQLND_CONN_DATA * const c
/* Finally clean */
if (row_packet->eof) {
- memset(conn->upsert_status, 0, sizeof(*conn->upsert_status));
- conn->upsert_status->warning_count = row_packet->warning_count;
- conn->upsert_status->server_status = row_packet->server_status;
+ UPSERT_STATUS_RESET(conn->upsert_status);
+ UPSERT_STATUS_SET_WARNINGS(conn->upsert_status, row_packet->warning_count);
+ UPSERT_STATUS_SET_SERVER_STATUS(conn->upsert_status, row_packet->server_status);
}
/* save some memory */
if (free_rows) {
/* don't try to allocate more than possible - mnd_XXalloc expects size_t, and it can have narrower range than uint64_t */
if (set->row_count * sizeof(MYSQLND_MEMORY_POOL_CHUNK *) > SIZE_MAX) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
ret = FAIL;
goto end;
}
*row_buffers = mnd_perealloc(*row_buffers, (size_t) (set->row_count * sizeof(MYSQLND_MEMORY_POOL_CHUNK *)), 0);
}
- if (conn->upsert_status->server_status & SERVER_MORE_RESULTS_EXISTS) {
- CONN_SET_STATE(conn, CONN_NEXT_RESULT_PENDING);
+ if (UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status) & SERVER_MORE_RESULTS_EXISTS) {
+ SET_CONNECTION_STATE(&conn->state, CONN_NEXT_RESULT_PENDING);
} else {
- CONN_SET_STATE(conn, CONN_READY);
+ SET_CONNECTION_STATE(&conn->state, CONN_READY);
}
if (ret == FAIL) {
- COPY_CLIENT_ERROR(set->error_info, row_packet->error_info);
+ COPY_CLIENT_ERROR(&set->error_info, row_packet->error_info);
} else {
/* libmysql's documentation says it should be so for SELECT statements */
- conn->upsert_status->affected_rows = set->row_count;
+ UPSERT_STATUS_SET_AFFECTED_ROWS(conn->upsert_status, set->row_count);
}
DBG_INF_FMT("ret=%s row_count=%u warnings=%u server_status=%u",
- ret == PASS? "PASS":"FAIL", (uint) set->row_count, conn->upsert_status->warning_count, conn->upsert_status->server_status);
+ ret == PASS? "PASS":"FAIL",
+ (uint) set->row_count,
+ UPSERT_STATUS_GET_WARNINGS(conn->upsert_status),
+ UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status));
end:
PACKET_FREE(row_packet);
DBG_INF_FMT("rows=%llu", (unsigned long long)result->stored_data->row_count);
@@ -1406,19 +1423,19 @@ MYSQLND_METHOD(mysqlnd_res, store_result)(MYSQLND_RES * result,
result->conn = conn->m->get_reference(conn);
result->type = MYSQLND_RES_NORMAL;
- CONN_SET_STATE(conn, CONN_FETCHING_DATA);
+ SET_CONNECTION_STATE(&conn->state, CONN_FETCHING_DATA);
if (flags & MYSQLND_STORE_NO_COPY) {
result->stored_data = (MYSQLND_RES_BUFFERED *) mysqlnd_result_buffered_zval_init(result->field_count, flags & MYSQLND_STORE_PS, result->persistent);
if (!result->stored_data) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
DBG_RETURN(NULL);
}
row_buffers = &result->stored_data->row_buffers;
} else if (flags & MYSQLND_STORE_COPY) {
result->stored_data = (MYSQLND_RES_BUFFERED *) mysqlnd_result_buffered_c_init(result->field_count, flags & MYSQLND_STORE_PS, result->persistent);
if (!result->stored_data) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
DBG_RETURN(NULL);
}
row_buffers = &result->stored_data->row_buffers;
@@ -1427,26 +1444,26 @@ MYSQLND_METHOD(mysqlnd_res, store_result)(MYSQLND_RES * result,
if (FAIL == ret) {
if (result->stored_data) {
- COPY_CLIENT_ERROR(*conn->error_info, result->stored_data->error_info);
+ COPY_CLIENT_ERROR(conn->error_info, result->stored_data->error_info);
} else {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
}
DBG_RETURN(NULL);
} else {
- /* Overflow ? */
if (flags & MYSQLND_STORE_NO_COPY) {
- MYSQLND_RES_METADATA * meta = result->meta;
+ const MYSQLND_RES_METADATA * const meta = result->meta;
MYSQLND_RES_BUFFERED_ZVAL * set = (MYSQLND_RES_BUFFERED_ZVAL *) result->stored_data;
+
if (set->row_count) {
/* don't try to allocate more than possible - mnd_XXalloc expects size_t, and it can have narrower range than uint64_t */
if (set->row_count * meta->field_count * sizeof(zval *) > SIZE_MAX) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
DBG_RETURN(NULL);
}
/* if pecalloc is used valgrind barks gcc version 4.3.1 20080507 (prerelease) [gcc-4_3-branch revision 135036] (SUSE Linux) */
set->data = mnd_emalloc((size_t)(set->row_count * meta->field_count * sizeof(zval)));
if (!set->data) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
DBG_RETURN(NULL);
}
memset(set->data, 0, (size_t)(set->row_count * meta->field_count * sizeof(zval)));
@@ -1456,12 +1473,12 @@ MYSQLND_METHOD(mysqlnd_res, store_result)(MYSQLND_RES * result,
} else if (flags & MYSQLND_STORE_COPY) {
MYSQLND_RES_BUFFERED_C * set = (MYSQLND_RES_BUFFERED_C *) result->stored_data;
set->current_row = 0;
- set->initialized = mnd_pecalloc((set->row_count / 8) + 1, sizeof(zend_uchar), set->persistent); /* +1 for safety */
+ set->initialized = mnd_pecalloc((unsigned int) ((set->row_count / 8) + 1), sizeof(zend_uchar), set->persistent); /* +1 for safety */
}
}
/* libmysql's documentation says it should be so for SELECT statements */
- conn->upsert_status->affected_rows = result->stored_data->row_count;
+ UPSERT_STATUS_SET_AFFECTED_ROWS(conn->upsert_status, result->stored_data->row_count);
DBG_RETURN(result);
}
@@ -1481,9 +1498,10 @@ MYSQLND_METHOD(mysqlnd_res, skip_result)(MYSQLND_RES * const result)
fetch_row function isn't actually set (NULL), thus we have to skip these.
*/
if (result->unbuf && !result->unbuf->eof_reached) {
+ MYSQLND_CONN_DATA * const conn = result->conn;
DBG_INF("skipping result");
/* We have to fetch all data to clean the line */
- MYSQLND_INC_CONN_STATISTIC(result->conn->stats,
+ MYSQLND_INC_CONN_STATISTIC(conn->stats,
result->type == MYSQLND_RES_NORMAL? STAT_FLUSHED_NORMAL_SETS:
STAT_FLUSHED_PS_SETS);
@@ -1498,7 +1516,7 @@ MYSQLND_METHOD(mysqlnd_res, skip_result)(MYSQLND_RES * const result)
/* {{{ mysqlnd_res::free_result */
static enum_func_status
-MYSQLND_METHOD(mysqlnd_res, free_result)(MYSQLND_RES * result, zend_bool implicit)
+MYSQLND_METHOD(mysqlnd_res, free_result)(MYSQLND_RES * result, const zend_bool implicit)
{
DBG_ENTER("mysqlnd_res::free_result");
@@ -1565,7 +1583,7 @@ static uint64_t
MYSQLND_METHOD(mysqlnd_result_unbuffered, num_rows)(const MYSQLND_RES_UNBUFFERED * const result)
{
/* Be compatible with libmysql. We count row_count, but will return 0 */
- return result->eof_reached? result->row_count:0;
+ return result->eof_reached? result->row_count : 0;
}
/* }}} */
@@ -1617,10 +1635,13 @@ MYSQLND_METHOD(mysqlnd_res, fetch_field)(MYSQLND_RES * const result)
not during mysqli_fetch_field() time.
*/
if (result->stored_data && (result->stored_data->initialized_rows < result->stored_data->row_count)) {
+ const MYSQLND_CONN_DATA * const conn = result->conn;
DBG_INF_FMT("We have decode the whole result set to be able to satisfy this meta request");
/* we have to initialize the rest to get the updated max length */
- if (PASS != result->stored_data->m.initialize_result_set_rest(result->stored_data, result->meta, result->conn->stats,
- result->conn->options->int_and_float_native))
+ if (PASS != result->stored_data->m.initialize_result_set_rest(result->stored_data,
+ result->meta,
+ conn->stats,
+ conn->options->int_and_float_native))
{
break;
}
@@ -1651,10 +1672,13 @@ MYSQLND_METHOD(mysqlnd_res, fetch_field_direct)(MYSQLND_RES * const result, cons
not during mysqli_fetch_field_direct() time.
*/
if (result->stored_data && (result->stored_data->initialized_rows < result->stored_data->row_count)) {
+ const MYSQLND_CONN_DATA * const conn = result->conn;
DBG_INF_FMT("We have decode the whole result set to be able to satisfy this meta request");
/* we have to initialized the rest to get the updated max length */
- if (PASS != result->stored_data->m.initialize_result_set_rest(result->stored_data, result->meta, result->conn->stats,
- result->conn->options->int_and_float_native))
+ if (PASS != result->stored_data->m.initialize_result_set_rest(result->stored_data,
+ result->meta,
+ conn->stats,
+ conn->options->int_and_float_native))
{
break;
}
@@ -1676,9 +1700,12 @@ MYSQLND_METHOD(mysqlnd_res, fetch_fields)(MYSQLND_RES * const result)
do {
if (result->meta) {
if (result->stored_data && (result->stored_data->initialized_rows < result->stored_data->row_count)) {
+ const MYSQLND_CONN_DATA * const conn = result->conn;
/* we have to initialize the rest to get the updated max length */
- if (PASS != result->stored_data->m.initialize_result_set_rest(result->stored_data, result->meta, result->conn->stats,
- result->conn->options->int_and_float_native))
+ if (PASS != result->stored_data->m.initialize_result_set_rest(result->stored_data,
+ result->meta,
+ conn->stats,
+ conn->options->int_and_float_native))
{
break;
}
@@ -1783,7 +1810,7 @@ MYSQLND_METHOD(mysqlnd_res, fetch_all)(MYSQLND_RES * result, const unsigned int
if ((!result->unbuf && !set)) {
php_error_docref(NULL, E_WARNING, "fetch_all can be used only with buffered sets");
if (result->conn) {
- SET_CLIENT_ERROR(*result->conn->error_info, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "fetch_all can be used only with buffered sets");
+ SET_CLIENT_ERROR(result->conn->error_info, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "fetch_all can be used only with buffered sets");
}
RETVAL_NULL();
DBG_VOID_RETURN;
@@ -1893,9 +1920,9 @@ MYSQLND_CLASS_METHODS_END;
/* {{{ mysqlnd_result_init */
PHPAPI MYSQLND_RES *
-mysqlnd_result_init(unsigned int field_count, zend_bool persistent)
+mysqlnd_result_init(const unsigned int field_count, const zend_bool persistent)
{
- size_t alloc_size = sizeof(MYSQLND_RES) + mysqlnd_plugin_count() * sizeof(void *);
+ const size_t alloc_size = sizeof(MYSQLND_RES) + mysqlnd_plugin_count() * sizeof(void *);
MYSQLND_RES * ret = mnd_pecalloc(1, alloc_size, persistent);
DBG_ENTER("mysqlnd_result_init");
@@ -1915,9 +1942,9 @@ mysqlnd_result_init(unsigned int field_count, zend_bool persistent)
/* {{{ mysqlnd_result_unbuffered_init */
PHPAPI MYSQLND_RES_UNBUFFERED *
-mysqlnd_result_unbuffered_init(unsigned int field_count, zend_bool ps, zend_bool persistent)
+mysqlnd_result_unbuffered_init(const unsigned int field_count, const zend_bool ps, const zend_bool persistent)
{
- size_t alloc_size = sizeof(MYSQLND_RES_UNBUFFERED) + mysqlnd_plugin_count() * sizeof(void *);
+ const size_t alloc_size = sizeof(MYSQLND_RES_UNBUFFERED) + mysqlnd_plugin_count() * sizeof(void *);
MYSQLND_RES_UNBUFFERED * ret = mnd_pecalloc(1, alloc_size, persistent);
DBG_ENTER("mysqlnd_result_unbuffered_init");
@@ -1925,8 +1952,7 @@ mysqlnd_result_unbuffered_init(unsigned int field_count, zend_bool ps, zend_bool
if (!ret) {
DBG_RETURN(NULL);
}
-
- if (!(ret->lengths = mnd_pecalloc(field_count, sizeof(zend_ulong), persistent))) {
+ if (!(ret->lengths = mnd_pecalloc(field_count, sizeof(size_t), persistent))) {
mnd_pefree(ret, persistent);
DBG_RETURN(NULL);
}
@@ -1956,9 +1982,9 @@ mysqlnd_result_unbuffered_init(unsigned int field_count, zend_bool ps, zend_bool
/* {{{ mysqlnd_result_buffered_zval_init */
PHPAPI MYSQLND_RES_BUFFERED_ZVAL *
-mysqlnd_result_buffered_zval_init(unsigned int field_count, zend_bool ps, zend_bool persistent)
+mysqlnd_result_buffered_zval_init(const unsigned int field_count, const zend_bool ps, const zend_bool persistent)
{
- size_t alloc_size = sizeof(MYSQLND_RES_BUFFERED_ZVAL) + mysqlnd_plugin_count() * sizeof(void *);
+ const size_t alloc_size = sizeof(MYSQLND_RES_BUFFERED_ZVAL) + mysqlnd_plugin_count() * sizeof(void *);
MYSQLND_RES_BUFFERED_ZVAL * ret = mnd_pecalloc(1, alloc_size, persistent);
DBG_ENTER("mysqlnd_result_buffered_zval_init");
@@ -1966,7 +1992,11 @@ mysqlnd_result_buffered_zval_init(unsigned int field_count, zend_bool ps, zend_b
if (!ret) {
DBG_RETURN(NULL);
}
- if (!(ret->lengths = mnd_pecalloc(field_count, sizeof(zend_ulong), persistent))) {
+ if (FAIL == mysqlnd_error_info_init(&ret->error_info, persistent)) {
+ mnd_pefree(ret, persistent);
+ DBG_RETURN(NULL);
+ }
+ if (!(ret->lengths = mnd_pecalloc(field_count, sizeof(size_t), persistent))) {
mnd_pefree(ret, persistent);
DBG_RETURN(NULL);
}
@@ -1999,9 +2029,9 @@ mysqlnd_result_buffered_zval_init(unsigned int field_count, zend_bool ps, zend_b
/* {{{ mysqlnd_result_buffered_c_init */
PHPAPI MYSQLND_RES_BUFFERED_C *
-mysqlnd_result_buffered_c_init(unsigned int field_count, zend_bool ps, zend_bool persistent)
+mysqlnd_result_buffered_c_init(const unsigned int field_count, const zend_bool ps, const zend_bool persistent)
{
- size_t alloc_size = sizeof(MYSQLND_RES_BUFFERED_C) + mysqlnd_plugin_count() * sizeof(void *);
+ const size_t alloc_size = sizeof(MYSQLND_RES_BUFFERED_C) + mysqlnd_plugin_count() * sizeof(void *);
MYSQLND_RES_BUFFERED_C * ret = mnd_pecalloc(1, alloc_size, persistent);
DBG_ENTER("mysqlnd_result_buffered_c_init");
@@ -2009,7 +2039,11 @@ mysqlnd_result_buffered_c_init(unsigned int field_count, zend_bool ps, zend_bool
if (!ret) {
DBG_RETURN(NULL);
}
- if (!(ret->lengths = mnd_pecalloc(field_count, sizeof(zend_ulong), persistent))) {
+ if (FAIL == mysqlnd_error_info_init(&ret->error_info, persistent)) {
+ mnd_pefree(ret, persistent);
+ DBG_RETURN(NULL);
+ }
+ if (!(ret->lengths = mnd_pecalloc(field_count, sizeof(size_t), persistent))) {
mnd_pefree(ret, persistent);
DBG_RETURN(NULL);
}
diff --git a/ext/mysqlnd/mysqlnd_result.h b/ext/mysqlnd/mysqlnd_result.h
index ae929b3a90..24ab81f6b2 100644
--- a/ext/mysqlnd/mysqlnd_result.h
+++ b/ext/mysqlnd/mysqlnd_result.h
@@ -14,17 +14,16 @@
+----------------------------------------------------------------------+
| Authors: Andrey Hristov <andrey@php.net> |
| Ulf Wendel <uw@php.net> |
- | Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
#ifndef MYSQLND_RESULT_H
#define MYSQLND_RESULT_H
-PHPAPI MYSQLND_RES * mysqlnd_result_init(unsigned int field_count, zend_bool persistent);
-PHPAPI MYSQLND_RES_UNBUFFERED * mysqlnd_result_unbuffered_init(unsigned int field_count, zend_bool ps, zend_bool persistent);
-PHPAPI MYSQLND_RES_BUFFERED_ZVAL * mysqlnd_result_buffered_zval_init(unsigned int field_count, zend_bool ps, zend_bool persistent);
-PHPAPI MYSQLND_RES_BUFFERED_C * mysqlnd_result_buffered_c_init(unsigned int field_count, zend_bool ps, zend_bool persistent);
+PHPAPI MYSQLND_RES * mysqlnd_result_init(const unsigned int field_count, const zend_bool persistent);
+PHPAPI MYSQLND_RES_UNBUFFERED * mysqlnd_result_unbuffered_init(const unsigned int field_count, const zend_bool ps, const zend_bool persistent);
+PHPAPI MYSQLND_RES_BUFFERED_ZVAL * mysqlnd_result_buffered_zval_init(const unsigned int field_count, const zend_bool ps, const zend_bool persistent);
+PHPAPI MYSQLND_RES_BUFFERED_C * mysqlnd_result_buffered_c_init(const unsigned int field_count, const zend_bool ps, const zend_bool persistent);
enum_func_status mysqlnd_query_read_result_set_header(MYSQLND_CONN_DATA * conn, MYSQLND_STMT * stmt);
diff --git a/ext/mysqlnd/mysqlnd_result_meta.c b/ext/mysqlnd/mysqlnd_result_meta.c
index d50e1f4c72..101758c466 100644
--- a/ext/mysqlnd/mysqlnd_result_meta.c
+++ b/ext/mysqlnd/mysqlnd_result_meta.c
@@ -14,12 +14,13 @@
+----------------------------------------------------------------------+
| Authors: Andrey Hristov <andrey@php.net> |
| Ulf Wendel <uw@php.net> |
- | Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
#include "php.h"
#include "mysqlnd.h"
+#include "mysqlnd_connection.h"
+#include "mysqlnd_ps.h"
#include "mysqlnd_priv.h"
#include "mysqlnd_result.h"
#include "mysqlnd_wireprotocol.h"
@@ -56,9 +57,9 @@ MYSQLND_METHOD(mysqlnd_res_meta, read_metadata)(MYSQLND_RES_METADATA * const met
DBG_ENTER("mysqlnd_res_meta::read_metadata");
- field_packet = conn->protocol->m.get_result_field_packet(conn->protocol, FALSE);
+ field_packet = conn->payload_decoder_factory->m.get_result_field_packet(conn->payload_decoder_factory, FALSE);
if (!field_packet) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
DBG_RETURN(FAIL);
}
field_packet->persistent_alloc = meta->persistent;
@@ -72,66 +73,23 @@ MYSQLND_METHOD(mysqlnd_res_meta, read_metadata)(MYSQLND_RES_METADATA * const met
}
field_packet->metadata = &(meta->fields[i]);
- if (FAIL == PACKET_READ(field_packet, conn)) {
+ if (FAIL == PACKET_READ(field_packet)) {
PACKET_FREE(field_packet);
DBG_RETURN(FAIL);
}
if (field_packet->error_info.error_no) {
- COPY_CLIENT_ERROR(*conn->error_info, field_packet->error_info);
+ COPY_CLIENT_ERROR(conn->error_info, field_packet->error_info);
/* Return back from CONN_QUERY_SENT */
PACKET_FREE(field_packet);
DBG_RETURN(FAIL);
}
- if (field_packet->stupid_list_fields_eof == TRUE) {
- meta->field_count = i;
- break;
- }
-
if (mysqlnd_ps_fetch_functions[meta->fields[i].type].func == NULL) {
- DBG_ERR_FMT("Unknown type %u sent by the server. Please send a report to the developers",
- meta->fields[i].type);
- php_error_docref(NULL, E_WARNING,
- "Unknown type %u sent by the server. "
- "Please send a report to the developers",
- meta->fields[i].type);
+ DBG_ERR_FMT("Unknown type %u sent by the server. Please send a report to the developers", meta->fields[i].type);
+ php_error_docref(NULL, E_WARNING, "Unknown type %u sent by the server. Please send a report to the developers", meta->fields[i].type);
PACKET_FREE(field_packet);
DBG_RETURN(FAIL);
}
- if (meta->fields[i].type == MYSQL_TYPE_BIT) {
- size_t field_len;
- DBG_INF("BIT");
- ++meta->bit_fields_count;
- /* .length is in bits */
- field_len = meta->fields[i].length / 8;
- /*
- If there is rest, add one byte :
- 8 bits = 1 byte but 9 bits = 2 bytes
- */
- if (meta->fields[i].length % 8) {
- ++field_len;
- }
- switch (field_len) {
- case 8:
- case 7:
- case 6:
- case 5:
- meta->bit_fields_total_len += 20;/* 21 digis, no sign*/
- break;
- case 4:
- meta->bit_fields_total_len += 10;/* 2 000 000 000*/
- break;
- case 3:
- meta->bit_fields_total_len += 8;/* 12 000 000*/
- break;
- case 2:
- meta->bit_fields_total_len += 5;/* 32 500 */
- break;
- case 1:
- meta->bit_fields_total_len += 3;/* 120 */
- break;
- }
- }
/* For BC we have to check whether the key is numeric and use it like this */
if ((meta->zend_hash_keys[i].is_numeric = ZEND_HANDLE_NUMERIC(field_packet->metadata->sname, idx))) {
@@ -179,7 +137,7 @@ MYSQLND_METHOD(mysqlnd_res_meta, free)(MYSQLND_RES_METADATA * meta)
/* {{{ mysqlnd_res::clone_metadata */
static MYSQLND_RES_METADATA *
-MYSQLND_METHOD(mysqlnd_res_meta, clone_metadata)(const MYSQLND_RES_METADATA * const meta, zend_bool persistent)
+MYSQLND_METHOD(mysqlnd_res_meta, clone_metadata)(const MYSQLND_RES_METADATA * const meta, const zend_bool persistent)
{
unsigned int i;
/* +1 is to have empty marker at the end */
diff --git a/ext/mysqlnd/mysqlnd_result_meta.h b/ext/mysqlnd/mysqlnd_result_meta.h
index 4bab5f0eb1..de6674388c 100644
--- a/ext/mysqlnd/mysqlnd_result_meta.h
+++ b/ext/mysqlnd/mysqlnd_result_meta.h
@@ -13,8 +13,8 @@
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Andrey Hristov <andrey@php.net> |
+ | Johannes Schlter <johannes@php.net> |
| Ulf Wendel <uw@php.net> |
- | Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
diff --git a/ext/mysqlnd/mysqlnd_reverse_api.c b/ext/mysqlnd/mysqlnd_reverse_api.c
index d58ed16721..95d4f70ea6 100644
--- a/ext/mysqlnd/mysqlnd_reverse_api.c
+++ b/ext/mysqlnd/mysqlnd_reverse_api.c
@@ -13,8 +13,8 @@
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Andrey Hristov <andrey@php.net> |
+ | Johannes Schlter <johannes@php.net> |
| Ulf Wendel <uw@php.net> |
- | Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
diff --git a/ext/mysqlnd/mysqlnd_statistics.c b/ext/mysqlnd/mysqlnd_statistics.c
index bdd5c814ef..10677983a6 100644
--- a/ext/mysqlnd/mysqlnd_statistics.c
+++ b/ext/mysqlnd/mysqlnd_statistics.c
@@ -25,9 +25,7 @@
#include "mysqlnd_debug.h"
-/* {{{ mysqlnd_stats_values_names
- */
-
+/* {{{ mysqlnd_stats_values_names */
const MYSQLND_STRING mysqlnd_stats_values_names[STAT_LAST] =
{
{ MYSQLND_STR_W_LEN("bytes_sent") },
@@ -113,8 +111,10 @@ const MYSQLND_STRING mysqlnd_stats_values_names[STAT_LAST] =
{ MYSQLND_STR_W_LEN("mem_free_amount") },
{ MYSQLND_STR_W_LEN("mem_estrndup_count") },
{ MYSQLND_STR_W_LEN("mem_strndup_count") },
- { MYSQLND_STR_W_LEN("mem_estndup_count") },
+ { MYSQLND_STR_W_LEN("mem_estrdup_count") },
{ MYSQLND_STR_W_LEN("mem_strdup_count") },
+ { MYSQLND_STR_W_LEN("mem_edupl_count") },
+ { MYSQLND_STR_W_LEN("mem_dupl_count") },
{ MYSQLND_STR_W_LEN("proto_text_fetched_null") },
{ MYSQLND_STR_W_LEN("proto_text_fetched_bit") },
{ MYSQLND_STR_W_LEN("proto_text_fetched_tinyint") },
@@ -212,25 +212,9 @@ mysqlnd_fill_stats_hash(const MYSQLND_STATS * const stats, const MYSQLND_STRING
/* }}} */
-/* {{{ _mysqlnd_get_client_stats */
-PHPAPI void
-_mysqlnd_get_client_stats(zval *return_value ZEND_FILE_LINE_DC)
-{
- MYSQLND_STATS stats, *stats_ptr = mysqlnd_global_stats;
- DBG_ENTER("_mysqlnd_get_client_stats");
- if (!stats_ptr) {
- memset(&stats, 0, sizeof(stats));
- stats_ptr = &stats;
- }
- mysqlnd_fill_stats_hash(stats_ptr, mysqlnd_stats_values_names, return_value ZEND_FILE_LINE_CC);
- DBG_VOID_RETURN;
-}
-/* }}} */
-
-
/* {{{ mysqlnd_stats_init */
PHPAPI void
-mysqlnd_stats_init(MYSQLND_STATS ** stats, size_t statistic_count, int persistent)
+mysqlnd_stats_init(MYSQLND_STATS ** stats, const size_t statistic_count, const zend_bool persistent)
{
*stats = pecalloc(1, sizeof(MYSQLND_STATS), persistent);
if (*stats == NULL) {
@@ -249,7 +233,7 @@ mysqlnd_stats_init(MYSQLND_STATS ** stats, size_t statistic_count, int persisten
/* {{{ mysqlnd_stats_end */
PHPAPI void
-mysqlnd_stats_end(MYSQLND_STATS * stats, int persistent)
+mysqlnd_stats_end(MYSQLND_STATS * stats, const zend_bool persistent)
{
#ifdef ZTS
tsrm_mutex_free(stats->LOCK_access);
@@ -295,6 +279,25 @@ mysqlnd_stats_reset_triggers(MYSQLND_STATS * const stats)
/* }}} */
+/************ MYSQLND specific code **********/
+
+/* {{{ _mysqlnd_get_client_stats */
+PHPAPI void
+_mysqlnd_get_client_stats(MYSQLND_STATS * stats_ptr, zval *return_value ZEND_FILE_LINE_DC)
+{
+ MYSQLND_STATS stats;
+ DBG_ENTER("_mysqlnd_get_client_stats");
+ if (!stats_ptr) {
+ memset(&stats, 0, sizeof(stats));
+ stats_ptr = &stats;
+ }
+ mysqlnd_fill_stats_hash(stats_ptr, mysqlnd_stats_values_names, return_value ZEND_FILE_LINE_CC);
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+
/*
* Local variables:
* tab-width: 4
diff --git a/ext/mysqlnd/mysqlnd_statistics.h b/ext/mysqlnd/mysqlnd_statistics.h
index 042ef3a766..fc61959c9b 100644
--- a/ext/mysqlnd/mysqlnd_statistics.h
+++ b/ext/mysqlnd/mysqlnd_statistics.h
@@ -14,18 +14,12 @@
+----------------------------------------------------------------------+
| Authors: Andrey Hristov <andrey@php.net> |
| Ulf Wendel <uw@php.net> |
- | Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
#ifndef MYSQLND_STATISTICS_H
#define MYSQLND_STATISTICS_H
-
-PHPAPI extern MYSQLND_STATS * mysqlnd_global_stats;
-
-extern const MYSQLND_STRING mysqlnd_stats_values_names[];
-
#ifdef ZTS
#define MYSQLND_STATS_LOCK(stats) tsrm_mutex_lock((stats)->LOCK_access)
#define MYSQLND_STATS_UNLOCK(stats) tsrm_mutex_unlock((stats)->LOCK_access)
@@ -116,50 +110,11 @@ extern const MYSQLND_STRING mysqlnd_stats_values_names[];
-#ifndef MYSQLND_CORE_STATISTICS_DISABLED
-
-#define MYSQLND_INC_GLOBAL_STATISTIC(statistic) \
- MYSQLND_INC_STATISTIC(MYSQLND_G(collect_statistics), mysqlnd_global_stats, (statistic))
-
-#define MYSQLND_DEC_CONN_STATISTIC(conn_stats, statistic) \
- MYSQLND_DEC_STATISTIC(MYSQLND_G(collect_statistics), mysqlnd_global_stats, (statistic))
-
-#define MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(statistic1, value1, statistic2, value2) \
- MYSQLND_INC_STATISTIC_W_VALUE2(MYSQLND_G(collect_statistics), mysqlnd_global_stats, (statistic1), (value1), (statistic2), (value2))
-
-#define MYSQLND_INC_CONN_STATISTIC(conn_stats, statistic) \
- MYSQLND_INC_STATISTIC(MYSQLND_G(collect_statistics), mysqlnd_global_stats, (statistic)); \
- MYSQLND_INC_STATISTIC(MYSQLND_G(collect_statistics), (conn_stats), (statistic));
-
-#define MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn_stats, statistic, value) \
- MYSQLND_INC_STATISTIC_W_VALUE(MYSQLND_G(collect_statistics), mysqlnd_global_stats, (statistic), (value)); \
- MYSQLND_INC_STATISTIC_W_VALUE(MYSQLND_G(collect_statistics), (conn_stats), (statistic), (value));
-
-#define MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn_stats, statistic1, value1, statistic2, value2) \
- MYSQLND_INC_STATISTIC_W_VALUE2(MYSQLND_G(collect_statistics), mysqlnd_global_stats, (statistic1), (value1), (statistic2), (value2)); \
- MYSQLND_INC_STATISTIC_W_VALUE2(MYSQLND_G(collect_statistics), (conn_stats), (statistic1), (value1), (statistic2), (value2));
-
-#define MYSQLND_INC_CONN_STATISTIC_W_VALUE3(conn_stats, statistic1, value1, statistic2, value2, statistic3, value3) \
- MYSQLND_INC_STATISTIC_W_VALUE3(MYSQLND_G(collect_statistics), mysqlnd_global_stats, (statistic1), (value1), (statistic2), (value2), (statistic3), (value3)); \
- MYSQLND_INC_STATISTIC_W_VALUE3(MYSQLND_G(collect_statistics), (conn_stats), (statistic1), (value1), (statistic2), (value2), (statistic3), (value3));
-
-#else
-
-#define MYSQLND_INC_GLOBAL_STATISTIC(statistic)
-#define MYSQLND_DEC_CONN_STATISTIC(conn_stats, statistic)
-#define MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(statistic1, value1, statistic2, value2)
-#define MYSQLND_INC_CONN_STATISTIC(conn_stats, statistic)
-#define MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn_stats, statistic, value)
-#define MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn_stats, statistic1, value1, statistic2, value2)
-#define MYSQLND_INC_CONN_STATISTIC_W_VALUE3(conn_stats, statistic1, value1, statistic2, value2, statistic3, value3)
-
-#endif /* MYSQLND_CORE_STATISTICS_DISABLED */
+PHPAPI void mysqlnd_stats_init(MYSQLND_STATS ** stats, const size_t statistic_count, const zend_bool persistent);
+PHPAPI void mysqlnd_stats_end(MYSQLND_STATS * stats, const zend_bool persistent);
PHPAPI void mysqlnd_fill_stats_hash(const MYSQLND_STATS * const stats, const MYSQLND_STRING * names, zval *return_value ZEND_FILE_LINE_DC);
-PHPAPI void mysqlnd_stats_init(MYSQLND_STATS ** stats, size_t statistic_count, int persistent);
-PHPAPI void mysqlnd_stats_end(MYSQLND_STATS * stats, int persistent);
-
PHPAPI mysqlnd_stat_trigger mysqlnd_stats_set_trigger(MYSQLND_STATS * const stats, enum_mysqlnd_collected_stats stat, mysqlnd_stat_trigger trigger);
PHPAPI mysqlnd_stat_trigger mysqlnd_stats_reset_triggers(MYSQLND_STATS * const stats);
diff --git a/ext/mysqlnd/mysqlnd_structs.h b/ext/mysqlnd/mysqlnd_structs.h
index 9784655479..230ac573d5 100644
--- a/ext/mysqlnd/mysqlnd_structs.h
+++ b/ext/mysqlnd/mysqlnd_structs.h
@@ -14,7 +14,6 @@
+----------------------------------------------------------------------+
| Authors: Andrey Hristov <andrey@php.net> |
| Ulf Wendel <uw@php.net> |
- | Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
@@ -26,11 +25,28 @@
#define MYSQLND_TYPEDEFED_METHODS
#define MYSQLND_CLASS_METHOD_TABLE_NAME(class) mysqlnd_##class##_methods
-#define MYSQLND_CLASS_METHOD_TABLE_NAME_FORWARD(class) struct st_##class##_methods MYSQLND_CLASS_METHOD_TABLE_NAME(class)
+#define MYSQLND_CLASS_METHODS_TYPE(class) struct st_##class##_methods
+#define MYSQLND_CLASS_METHOD_TABLE_NAME_FORWARD(class) MYSQLND_CLASS_METHODS_TYPE(class) MYSQLND_CLASS_METHOD_TABLE_NAME(class)
#define MYSQLND_CLASS_METHODS_START(class) MYSQLND_CLASS_METHOD_TABLE_NAME_FORWARD(class) = {
#define MYSQLND_CLASS_METHODS_END }
+#define MYSQLND_CLASS_METHODS_INSTANCE_NAME(class) mysqlnd_##class##_methods_ptr
+#define MYSQLND_CLASS_METHODS_INSTANCE_DECLARE(class) extern const MYSQLND_CLASS_METHODS_TYPE(class) * MYSQLND_CLASS_METHODS_INSTANCE_NAME(class)
+#define MYSQLND_CLASS_METHODS_INSTANCE_DEFINE(class) const MYSQLND_CLASS_METHODS_TYPE(class) * MYSQLND_CLASS_METHODS_INSTANCE_NAME(class) = & MYSQLND_CLASS_METHOD_TABLE_NAME(class)
+
+typedef struct st_mysqlnd_string
+{
+ char *s;
+ size_t l;
+} MYSQLND_STRING;
+
+typedef struct st_mysqlnd_const_string
+{
+ const char *s;
+ size_t l;
+} MYSQLND_CSTRING;
+
typedef struct st_mysqlnd_memory_pool MYSQLND_MEMORY_POOL;
typedef struct st_mysqlnd_memory_pool_chunk MYSQLND_MEMORY_POOL_CHUNK;
@@ -42,20 +58,18 @@ typedef struct st_mysqlnd_memory_pool_chunk_llist MYSQLND_MEMORY_POOL_CHUNK_LLIS
struct st_mysqlnd_memory_pool
{
zend_uchar *arena;
- unsigned int refcount;
unsigned int arena_size;
unsigned int free_size;
MYSQLND_MEMORY_POOL_CHUNK* (*get_chunk)(MYSQLND_MEMORY_POOL * pool, unsigned int size);
+ enum_func_status (*resize_chunk)(MYSQLND_MEMORY_POOL * pool, MYSQLND_MEMORY_POOL_CHUNK * chunk, unsigned int size);
+ void (*free_chunk)(MYSQLND_MEMORY_POOL * pool, MYSQLND_MEMORY_POOL_CHUNK * chunk);
};
struct st_mysqlnd_memory_pool_chunk
{
size_t app;
- MYSQLND_MEMORY_POOL *pool;
zend_uchar *ptr;
- enum_func_status (*resize_chunk)(MYSQLND_MEMORY_POOL_CHUNK * chunk, unsigned int size);
- void (*free_chunk)(MYSQLND_MEMORY_POOL_CHUNK * chunk);
unsigned int size;
zend_bool from_pool;
};
@@ -77,7 +91,7 @@ typedef struct st_mysqlnd_field
const char *org_table; /* Org table name, if table was an alias */
const char *db; /* Database for table */
const char *catalog; /* Catalog for table */
- char *def; /* Default value (set by mysql_list_fields) */
+ char *def; /* Default value */
zend_ulong length; /* Width of column (create length) */
zend_ulong max_length; /* Max width for selected set */
unsigned int name_length;
@@ -96,22 +110,53 @@ typedef struct st_mysqlnd_field
} MYSQLND_FIELD;
-typedef struct st_mysqlnd_upsert_result
+typedef struct st_mysqlnd_upsert_status MYSQLND_UPSERT_STATUS;
+typedef void (*func_mysqlnd_upsert_status__reset)(MYSQLND_UPSERT_STATUS * const upsert_status);
+typedef void (*func_mysqlnd_upsert_status__set_affected_rows_to_error)(MYSQLND_UPSERT_STATUS * const upsert_status);
+
+MYSQLND_CLASS_METHODS_TYPE(mysqlnd_upsert_status)
+{
+ func_mysqlnd_upsert_status__reset reset;
+ func_mysqlnd_upsert_status__set_affected_rows_to_error set_affected_rows_to_error;
+};
+
+struct st_mysqlnd_upsert_status
{
unsigned int warning_count;
unsigned int server_status;
uint64_t affected_rows;
uint64_t last_insert_id;
-} MYSQLND_UPSERT_STATUS;
+ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_upsert_status) *m;
+};
+
+#define SET_EMPTY_ERROR(info) (info)->m->reset((info))
+#define SET_CLIENT_ERROR(info, err_no, sqlstate, error) (err_no)? (info)->m->set_client_error((info), (err_no), (sqlstate), (error)) : (info)->m->reset((info))
+#define SET_OOM_ERROR(info) SET_CLIENT_ERROR((info), CR_OUT_OF_MEMORY, UNKNOWN_SQLSTATE, mysqlnd_out_of_memory)
+#define COPY_CLIENT_ERROR(dest, source) SET_CLIENT_ERROR((dest), (source).error_no, (source).sqlstate, (source).error)
+
+
+typedef struct st_mysqlnd_error_info MYSQLND_ERROR_INFO;
+typedef void (*func_mysqlnd_error_info__reset)(MYSQLND_ERROR_INFO * const info);
+typedef void (*func_mysqlnd_error_info__set_client_error)(MYSQLND_ERROR_INFO * const info, const unsigned int err_no, const char * const sqlstate, const char * const error);
+
+
+MYSQLND_CLASS_METHODS_TYPE(mysqlnd_error_info)
+{
+ func_mysqlnd_error_info__reset reset;
+ func_mysqlnd_error_info__set_client_error set_client_error;
+};
-typedef struct st_mysqlnd_error_info
+struct st_mysqlnd_error_info
{
char error[MYSQLND_ERRMSG_SIZE+1];
char sqlstate[MYSQLND_SQLSTATE_LENGTH + 1];
unsigned int error_no;
zend_llist * error_list;
-} MYSQLND_ERROR_INFO;
+
+ zend_bool persistent;
+ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_error_info) *m;
+};
typedef struct st_mysqlnd_error_list_element
@@ -154,7 +199,7 @@ typedef struct st_mysqlnd_infile
void (*local_infile_end)(void *ptr);
} MYSQLND_INFILE;
-typedef struct st_mysqlnd_options
+typedef struct st_mysqlnd_session_options
{
ulong flags;
@@ -173,11 +218,11 @@ typedef struct st_mysqlnd_options
in the memory which can crash external code. Feel free to reuse these.
*/
HashTable * connect_attr;
+ char * unused1;
+ char * unused2;
char * unused3;
- char * unused4;
- char * unused5;
- enum_mysqlnd_protocol_type protocol;
+ enum_mysqlnd_session_protocol_type protocol;
char *charset_name;
/* maximum allowed packet size for communication */
@@ -186,9 +231,10 @@ typedef struct st_mysqlnd_options
#ifdef MYSQLND_STRING_TO_INT_CONVERSION
zend_bool int_and_float_native;
#endif
-} MYSQLND_OPTIONS;
+} MYSQLND_SESSION_OPTIONS;
-typedef struct st_mysqlnd_net_options
+
+typedef struct st_mysqlnd_vio_options
{
/* timeouts */
unsigned int timeout_connect;
@@ -211,23 +257,17 @@ typedef struct st_mysqlnd_net_options
#define MYSQLND_SSL_PEER_DEFAULT_ACTION MYSQLND_SSL_PEER_VERIFY
} ssl_verify_peer;
- uint64_t flags;
-
- char * sha256_server_public_key;
-
- char * unused1;
- char * unused2;
- char * unused3;
- char * unused4;
-} MYSQLND_NET_OPTIONS;
+} MYSQLND_VIO_OPTIONS;
typedef struct st_mysqlnd_connection MYSQLND;
typedef struct st_mysqlnd_connection_data MYSQLND_CONN_DATA;
-typedef struct st_mysqlnd_net MYSQLND_NET;
-typedef struct st_mysqlnd_net_data MYSQLND_NET_DATA;
-typedef struct st_mysqlnd_protocol MYSQLND_PROTOCOL;
+typedef struct st_mysqlnd_protocol_frame_codec MYSQLND_PFC;
+typedef struct st_mysqlnd_protocol_frame_codec_data MYSQLND_PFC_DATA;
+typedef struct st_mysqlnd_vio MYSQLND_VIO;
+typedef struct st_mysqlnd_vio_data MYSQLND_VIO_DATA;
+typedef struct st_mysqlnd_protocol_payload_decoder_factory MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY;
typedef struct st_mysqlnd_res MYSQLND_RES;
typedef char** MYSQLND_ROW_C; /* return data as array of strings */
typedef struct st_mysqlnd_stmt_data MYSQLND_STMT_DATA;
@@ -271,158 +311,87 @@ struct st_mysqlnd_stats
};
-typedef struct st_mysqlnd_read_buffer {
- zend_uchar * data;
- size_t offset;
- size_t size;
- size_t len;
- zend_bool (*is_empty)(struct st_mysqlnd_read_buffer *);
- void (*read)(struct st_mysqlnd_read_buffer *, size_t count, zend_uchar * dest);
- size_t (*bytes_left)(struct st_mysqlnd_read_buffer *);
- void (*free_buffer)(struct st_mysqlnd_read_buffer **);
-} MYSQLND_READ_BUFFER;
+typedef enum_func_status (*func_mysqlnd_vio__init)(MYSQLND_VIO * const vio, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info);
+typedef void (*func_mysqlnd_vio__dtor)(MYSQLND_VIO * const vio, MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info);
+typedef enum_func_status (*func_mysqlnd_vio__connect)(MYSQLND_VIO * const vio, const MYSQLND_CSTRING scheme, const zend_bool persistent, MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info);
+typedef void (*func_mysqlnd_vio__close_stream)(MYSQLND_VIO * const vio, MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info);
+typedef php_stream * (*func_mysqlnd_vio__open_stream)(MYSQLND_VIO * const vio, const MYSQLND_CSTRING scheme, const zend_bool persistent, MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info);
+typedef php_stream * (*func_mysqlnd_vio__get_stream)(const MYSQLND_VIO * const vio);
+typedef enum_func_status (*func_mysqlnd_vio__set_stream)(MYSQLND_VIO * const vio, php_stream * vio_stream);
+typedef zend_bool (*func_mysqlnd_vio__has_valid_stream)(const MYSQLND_VIO * const vio);
+typedef func_mysqlnd_vio__open_stream (*func_mysqlnd_vio__get_open_stream)(MYSQLND_VIO * const vio, const MYSQLND_CSTRING scheme, MYSQLND_ERROR_INFO * const error_info);
-typedef enum_func_status (*func_mysqlnd_net__set_client_option)(MYSQLND_NET * const net, enum_mysqlnd_option option, const char * const value);
-typedef enum_func_status (*func_mysqlnd_net__decode)(zend_uchar * uncompressed_data, const size_t uncompressed_data_len, const zend_uchar * const compressed_data, const size_t compressed_data_len);
-typedef enum_func_status (*func_mysqlnd_net__encode)(zend_uchar * compress_buffer, size_t * compress_buffer_len, const zend_uchar * const uncompressed_data, const size_t uncompressed_data_len);
-typedef size_t (*func_mysqlnd_net__consume_uneaten_data)(MYSQLND_NET * const net, enum php_mysqlnd_server_command cmd);
-typedef void (*func_mysqlnd_net__free_contents)(MYSQLND_NET * net);
-typedef enum_func_status (*func_mysqlnd_net__enable_ssl)(MYSQLND_NET * const net);
-typedef enum_func_status (*func_mysqlnd_net__disable_ssl)(MYSQLND_NET * const net);
-typedef enum_func_status (*func_mysqlnd_net__network_read_ex)(MYSQLND_NET * const net, zend_uchar * const buffer, const size_t count, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info);
-typedef size_t (*func_mysqlnd_net__network_write_ex)(MYSQLND_NET * const net, const zend_uchar * const buf, const size_t count, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info);
-typedef size_t (*func_mysqlnd_net__send_ex)(MYSQLND_NET * const net, zend_uchar * const buffer, const size_t count, MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info);
-typedef enum_func_status (*func_mysqlnd_net__receive_ex)(MYSQLND_NET * const net, zend_uchar * const buffer, const size_t count, MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info);
-typedef enum_func_status (*func_mysqlnd_net__init)(MYSQLND_NET * const net, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info);
-typedef void (*func_mysqlnd_net__dtor)(MYSQLND_NET * const net, MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info);
-typedef enum_func_status (*func_mysqlnd_net__connect_ex)(MYSQLND_NET * const net, const char * const scheme, const size_t scheme_len, const zend_bool persistent, MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info);
-typedef void (*func_mysqlnd_net__close_stream)(MYSQLND_NET * const net, MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info);
-typedef php_stream * (*func_mysqlnd_net__open_stream)(MYSQLND_NET * const net, const char * const scheme, const size_t scheme_len, const zend_bool persistent, MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info);
-typedef php_stream * (*func_mysqlnd_net__get_stream)(const MYSQLND_NET * const net);
-typedef php_stream * (*func_mysqlnd_net__set_stream)(MYSQLND_NET * const net, php_stream * net_stream);
-typedef func_mysqlnd_net__open_stream (*func_mysqlnd_net__get_open_stream)(MYSQLND_NET * const net, const char * const scheme, const size_t scheme_len, MYSQLND_ERROR_INFO * const error_info);
-typedef void (*func_mysqlnd_net__post_connect_set_opt)(MYSQLND_NET * const net, const char * const scheme, const size_t scheme_len, MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info);
-typedef enum_func_status (*func_mysqlnd_net__read_compressed_packet_from_stream_and_fill_read_buffer)(MYSQLND_NET * net, size_t net_payload_size, MYSQLND_STATS * conn_stats, MYSQLND_ERROR_INFO * error_info);
-
-struct st_mysqlnd_net_methods
-{
- func_mysqlnd_net__init init;
- func_mysqlnd_net__dtor dtor;
- func_mysqlnd_net__connect_ex connect_ex;
- func_mysqlnd_net__close_stream close_stream;
- func_mysqlnd_net__open_stream open_pipe;
- func_mysqlnd_net__open_stream open_tcp_or_unix;
-
- func_mysqlnd_net__get_stream get_stream;
- func_mysqlnd_net__set_stream set_stream;
- func_mysqlnd_net__get_open_stream get_open_stream;
-
- func_mysqlnd_net__post_connect_set_opt post_connect_set_opt;
-
- func_mysqlnd_net__set_client_option set_client_option;
- func_mysqlnd_net__decode decode;
- func_mysqlnd_net__encode encode;
- func_mysqlnd_net__consume_uneaten_data consume_uneaten_data;
- func_mysqlnd_net__free_contents free_contents;
- func_mysqlnd_net__enable_ssl enable_ssl;
- func_mysqlnd_net__disable_ssl disable_ssl;
-
- func_mysqlnd_net__network_read_ex network_read_ex;
- func_mysqlnd_net__network_write_ex network_write_ex;
- func_mysqlnd_net__send_ex send_ex;
- func_mysqlnd_net__receive_ex receive_ex;
-
- func_mysqlnd_net__read_compressed_packet_from_stream_and_fill_read_buffer read_compressed_packet_from_stream_and_fill_read_buffer;
+typedef enum_func_status (*func_mysqlnd_vio__set_client_option)(MYSQLND_VIO * const vio, enum_mysqlnd_client_option option, const char * const value);
+typedef void (*func_mysqlnd_vio__post_connect_set_opt)(MYSQLND_VIO * const vio, const MYSQLND_CSTRING scheme, MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info);
- void * unused1;
- void * unused2;
- void * unused3;
- void * unused4;
- void * unused5;
-};
+typedef enum_func_status (*func_mysqlnd_vio__enable_ssl)(MYSQLND_VIO * const vio);
+typedef enum_func_status (*func_mysqlnd_vio__disable_ssl)(MYSQLND_VIO * const vio);
+typedef enum_func_status (*func_mysqlnd_vio__network_read)(MYSQLND_VIO * const vio, zend_uchar * const buffer, const size_t count, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info);
+typedef size_t (*func_mysqlnd_vio__network_write)(MYSQLND_VIO * const vio, const zend_uchar * const buf, const size_t count, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info);
+typedef size_t (*func_mysqlnd_vio__consume_uneaten_data)(MYSQLND_VIO * const vio, enum php_mysqlnd_server_command cmd);
-struct st_mysqlnd_packet_greet;
-struct st_mysqlnd_packet_greet;
-struct st_mysqlnd_packet_auth;
-struct st_mysqlnd_packet_ok;
-struct st_mysqlnd_packet_command;
-struct st_mysqlnd_packet_eof;
-struct st_mysqlnd_packet_rset_header;
-struct st_mysqlnd_packet_res_field;
-struct st_mysqlnd_packet_row;
-struct st_mysqlnd_packet_stats;
-struct st_mysqlnd_packet_prepare_response;
-struct st_mysqlnd_packet_chg_user_resp;
-struct st_mysqlnd_packet_auth_pam;
-struct st_mysqlnd_packet_sha256_pk_request;
-struct st_mysqlnd_packet_sha256_pk_request_response;
+typedef void (*func_mysqlnd_vio__free_contents)(MYSQLND_VIO * vio);
-typedef struct st_mysqlnd_packet_greet * (*func_mysqlnd_protocol__get_greet_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent);
-typedef struct st_mysqlnd_packet_auth * (*func_mysqlnd_protocol__get_auth_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent);
-typedef struct st_mysqlnd_packet_auth_response *(*func_mysqlnd_protocol__get_auth_response_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent);
-typedef struct st_mysqlnd_packet_change_auth_response * (*func_mysqlnd_protocol__get_change_auth_response_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent);
-typedef struct st_mysqlnd_packet_ok * (*func_mysqlnd_protocol__get_ok_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent);
-typedef struct st_mysqlnd_packet_command * (*func_mysqlnd_protocol__get_command_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent);
-typedef struct st_mysqlnd_packet_eof * (*func_mysqlnd_protocol__get_eof_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent);
-typedef struct st_mysqlnd_packet_rset_header * (*func_mysqlnd_protocol__get_rset_header_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent);
-typedef struct st_mysqlnd_packet_res_field * (*func_mysqlnd_protocol__get_result_field_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent);
-typedef struct st_mysqlnd_packet_row * (*func_mysqlnd_protocol__get_row_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent);
-typedef struct st_mysqlnd_packet_stats * (*func_mysqlnd_protocol__get_stats_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent);
-typedef struct st_mysqlnd_packet_prepare_response *(*func_mysqlnd_protocol__get_prepare_response_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent);
-typedef struct st_mysqlnd_packet_chg_user_resp*(*func_mysqlnd_protocol__get_change_user_response_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent);
-typedef struct st_mysqlnd_packet_sha256_pk_request *(*func_mysqlnd_protocol__get_sha256_pk_request_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent);
-typedef struct st_mysqlnd_packet_sha256_pk_request_response *(*func_mysqlnd_protocol__get_sha256_pk_request_response_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent);
-
-struct st_mysqlnd_protocol_methods
+
+MYSQLND_CLASS_METHODS_TYPE(mysqlnd_vio)
{
- func_mysqlnd_protocol__get_greet_packet get_greet_packet;
- func_mysqlnd_protocol__get_auth_packet get_auth_packet;
- func_mysqlnd_protocol__get_auth_response_packet get_auth_response_packet;
- func_mysqlnd_protocol__get_change_auth_response_packet get_change_auth_response_packet;
- func_mysqlnd_protocol__get_ok_packet get_ok_packet;
- func_mysqlnd_protocol__get_command_packet get_command_packet;
- func_mysqlnd_protocol__get_eof_packet get_eof_packet;
- func_mysqlnd_protocol__get_rset_header_packet get_rset_header_packet;
- func_mysqlnd_protocol__get_result_field_packet get_result_field_packet;
- func_mysqlnd_protocol__get_row_packet get_row_packet;
- func_mysqlnd_protocol__get_stats_packet get_stats_packet;
- func_mysqlnd_protocol__get_prepare_response_packet get_prepare_response_packet;
- func_mysqlnd_protocol__get_change_user_response_packet get_change_user_response_packet;
- func_mysqlnd_protocol__get_sha256_pk_request_packet get_sha256_pk_request_packet;
- func_mysqlnd_protocol__get_sha256_pk_request_response_packet get_sha256_pk_request_response_packet;
+ func_mysqlnd_vio__init init;
+ func_mysqlnd_vio__dtor dtor;
+ func_mysqlnd_vio__connect connect;
- void * unused1;
- void * unused2;
- void * unused3;
+ func_mysqlnd_vio__close_stream close_stream;
+ func_mysqlnd_vio__open_stream open_pipe;
+ func_mysqlnd_vio__open_stream open_tcp_or_unix;
+
+ func_mysqlnd_vio__get_stream get_stream;
+ func_mysqlnd_vio__set_stream set_stream;
+ func_mysqlnd_vio__has_valid_stream has_valid_stream;
+ func_mysqlnd_vio__get_open_stream get_open_stream;
+
+ func_mysqlnd_vio__set_client_option set_client_option;
+ func_mysqlnd_vio__post_connect_set_opt post_connect_set_opt;
+
+ func_mysqlnd_vio__enable_ssl enable_ssl;
+ func_mysqlnd_vio__disable_ssl disable_ssl;
+
+ func_mysqlnd_vio__network_read network_read;
+ func_mysqlnd_vio__network_write network_write;
+
+ func_mysqlnd_vio__consume_uneaten_data consume_uneaten_data;
+
+ func_mysqlnd_vio__free_contents free_contents;
};
-typedef MYSQLND * (*func_mysqlnd_object_factory__get_connection)(zend_bool persistent);
+MYSQLND_CLASS_METHODS_TYPE(mysqlnd_object_factory);
+
+typedef MYSQLND * (*func_mysqlnd_object_factory__get_connection)(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_object_factory) * factory, const zend_bool persistent);
typedef MYSQLND * (*func_mysqlnd_object_factory__clone_connection_object)(MYSQLND * conn);
-typedef MYSQLND_STMT * (*func_mysqlnd_object_factory__get_prepared_statement)(MYSQLND_CONN_DATA * conn);
-typedef MYSQLND_NET * (*func_mysqlnd_object_factory__get_io_channel)(zend_bool persistent, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info);
-typedef MYSQLND_PROTOCOL * (*func_mysqlnd_object_factory__get_protocol_decoder)(zend_bool persistent);
+typedef MYSQLND_STMT * (*func_mysqlnd_object_factory__get_prepared_statement)(MYSQLND_CONN_DATA * conn, const zend_bool persistent);
+typedef MYSQLND_PFC * (*func_mysqlnd_object_factory__get_pfc)(const zend_bool persistent, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info);
+typedef MYSQLND_VIO * (*func_mysqlnd_object_factory__get_vio)(const zend_bool persistent, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info);
+typedef MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * (*func_mysqlnd_object_factory__get_protocol_payload_decoder_factory)(MYSQLND_CONN_DATA * conn, const zend_bool persistent);
-struct st_mysqlnd_object_factory_methods
+MYSQLND_CLASS_METHODS_TYPE(mysqlnd_object_factory)
{
func_mysqlnd_object_factory__get_connection get_connection;
func_mysqlnd_object_factory__clone_connection_object clone_connection_object;
func_mysqlnd_object_factory__get_prepared_statement get_prepared_statement;
- func_mysqlnd_object_factory__get_io_channel get_io_channel;
- func_mysqlnd_object_factory__get_protocol_decoder get_protocol_decoder;
+ func_mysqlnd_object_factory__get_pfc get_protocol_frame_codec;
+ func_mysqlnd_object_factory__get_vio get_vio;
+ func_mysqlnd_object_factory__get_protocol_payload_decoder_factory get_protocol_payload_decoder_factory;
};
-typedef enum_func_status (*func_mysqlnd_conn_data__init)(MYSQLND_CONN_DATA * conn);
-typedef enum_func_status (*func_mysqlnd_conn_data__connect)(MYSQLND_CONN_DATA * conn, const char * host, const char * user, const char * passwd, unsigned int passwd_len, const char * db, unsigned int db_len, unsigned int port, const char * socket_or_pipe, unsigned int mysql_flags);
-typedef zend_ulong (*func_mysqlnd_conn_data__escape_string)(MYSQLND_CONN_DATA * const conn, char *newstr, const char *escapestr, size_t escapestr_len);
+typedef enum_func_status (*func_mysqlnd_conn_data__connect)(MYSQLND_CONN_DATA * conn, MYSQLND_CSTRING hostname, MYSQLND_CSTRING username, MYSQLND_CSTRING password, MYSQLND_CSTRING database, unsigned int port, MYSQLND_CSTRING socket_or_pipe, unsigned int mysql_flags);
+typedef zend_ulong (*func_mysqlnd_conn_data__escape_string)(MYSQLND_CONN_DATA * const conn, char *newstr, const char *escapestr, size_t escapestr_len);
typedef enum_func_status (*func_mysqlnd_conn_data__set_charset)(MYSQLND_CONN_DATA * const conn, const char * const charset);
-typedef enum_func_status (*func_mysqlnd_conn_data__query)(MYSQLND_CONN_DATA * conn, const char * query, unsigned int query_len);
-typedef enum_func_status (*func_mysqlnd_conn_data__send_query)(MYSQLND_CONN_DATA * conn, const char *query, unsigned int query_len, enum_mysqlnd_send_query_type type, zval *read_cb, zval *err_cb);
+typedef enum_func_status (*func_mysqlnd_conn_data__query)(MYSQLND_CONN_DATA * conn, const char * const query, const size_t query_len);
+typedef enum_func_status (*func_mysqlnd_conn_data__send_query)(MYSQLND_CONN_DATA * conn, const char * const query, const size_t query_len, enum_mysqlnd_send_query_type type, zval *read_cb, zval *err_cb);
typedef enum_func_status (*func_mysqlnd_conn_data__reap_query)(MYSQLND_CONN_DATA * conn, enum_mysqlnd_reap_result_type type);
typedef MYSQLND_RES * (*func_mysqlnd_conn_data__use_result)(MYSQLND_CONN_DATA * const conn, const unsigned int flags);
typedef MYSQLND_RES * (*func_mysqlnd_conn_data__store_result)(MYSQLND_CONN_DATA * const conn, const unsigned int flags);
@@ -436,7 +405,7 @@ typedef enum_func_status (*func_mysqlnd_conn_data__refresh_server)(MYSQLND_CONN_
typedef enum_func_status (*func_mysqlnd_conn_data__ping)(MYSQLND_CONN_DATA * const conn);
typedef enum_func_status (*func_mysqlnd_conn_data__kill_connection)(MYSQLND_CONN_DATA * conn, unsigned int pid);
-typedef enum_func_status (*func_mysqlnd_conn_data__select_db)(MYSQLND_CONN_DATA * const conn, const char * const db, unsigned int db_len);
+typedef enum_func_status (*func_mysqlnd_conn_data__select_db)(MYSQLND_CONN_DATA * const conn, const char * const db, const size_t db_len);
typedef enum_func_status (*func_mysqlnd_conn_data__server_dump_debug_information)(MYSQLND_CONN_DATA * const conn);
typedef enum_func_status (*func_mysqlnd_conn_data__change_user)(MYSQLND_CONN_DATA * const conn, const char * user, const char * passwd, const char * db, zend_bool silent, size_t passwd_len);
@@ -446,15 +415,14 @@ typedef const char * (*func_mysqlnd_conn_data__get_sqlstate)(const MYSQLND_CONN
typedef uint64_t (*func_mysqlnd_conn_data__get_thread_id)(const MYSQLND_CONN_DATA * const conn);
typedef void (*func_mysqlnd_conn_data__get_statistics)(const MYSQLND_CONN_DATA * const conn, zval *return_value ZEND_FILE_LINE_DC);
-typedef zend_ulong (*func_mysqlnd_conn_data__get_server_version)(const MYSQLND_CONN_DATA * const conn);
+typedef zend_ulong (*func_mysqlnd_conn_data__get_server_version)(const MYSQLND_CONN_DATA * const conn);
typedef const char * (*func_mysqlnd_conn_data__get_server_information)(const MYSQLND_CONN_DATA * const conn);
typedef enum_func_status (*func_mysqlnd_conn_data__get_server_statistics)(MYSQLND_CONN_DATA * conn, zend_string **message);
typedef const char * (*func_mysqlnd_conn_data__get_host_information)(const MYSQLND_CONN_DATA * const conn);
typedef unsigned int (*func_mysqlnd_conn_data__get_protocol_information)(const MYSQLND_CONN_DATA * const conn);
typedef const char * (*func_mysqlnd_conn_data__get_last_message)(const MYSQLND_CONN_DATA * const conn);
typedef const char * (*func_mysqlnd_conn_data__charset_name)(const MYSQLND_CONN_DATA * const conn);
-typedef MYSQLND_RES * (*func_mysqlnd_conn_data__list_fields)(MYSQLND_CONN_DATA * conn, const char * table, const char * achtung_wild);
-typedef MYSQLND_RES * (*func_mysqlnd_conn_data__list_method)(MYSQLND_CONN_DATA * conn, const char * query, const char * achtung_wild, char *par1);
+typedef MYSQLND_RES * (*func_mysqlnd_conn_data__list_method)(MYSQLND_CONN_DATA * conn, const char * const query, const char * const achtung_wild, const char * const par1);
typedef uint64_t (*func_mysqlnd_conn_data__get_last_insert_id)(const MYSQLND_CONN_DATA * const conn);
typedef uint64_t (*func_mysqlnd_conn_data__get_affected_rows)(const MYSQLND_CONN_DATA * const conn);
@@ -464,7 +432,7 @@ typedef unsigned int (*func_mysqlnd_conn_data__get_field_count)(const MYSQLND_C
typedef unsigned int (*func_mysqlnd_conn_data__get_server_status)(const MYSQLND_CONN_DATA * const conn);
typedef enum_func_status (*func_mysqlnd_conn_data__set_server_option)(MYSQLND_CONN_DATA * const conn, enum_mysqlnd_server_option option);
-typedef enum_func_status (*func_mysqlnd_conn_data__set_client_option)(MYSQLND_CONN_DATA * const conn, enum_mysqlnd_option option, const char * const value);
+typedef enum_func_status (*func_mysqlnd_conn_data__set_client_option)(MYSQLND_CONN_DATA * const conn, enum_mysqlnd_client_option option, const char * const value);
typedef void (*func_mysqlnd_conn_data__free_contents)(MYSQLND_CONN_DATA * conn);/* private */
typedef void (*func_mysqlnd_conn_data__free_options)(MYSQLND_CONN_DATA * conn); /* private */
typedef void (*func_mysqlnd_conn_data__dtor)(MYSQLND_CONN_DATA * conn); /* private */
@@ -473,11 +441,9 @@ typedef enum_func_status (*func_mysqlnd_conn_data__query_read_result_set_header)
typedef MYSQLND_CONN_DATA * (*func_mysqlnd_conn_data__get_reference)(MYSQLND_CONN_DATA * const conn);
typedef enum_func_status (*func_mysqlnd_conn_data__free_reference)(MYSQLND_CONN_DATA * const conn);
-typedef enum mysqlnd_connection_state (*func_mysqlnd_conn_data__get_state)(const MYSQLND_CONN_DATA * const conn);
-typedef void (*func_mysqlnd_conn_data__set_state)(MYSQLND_CONN_DATA * const conn, enum mysqlnd_connection_state new_state);
-typedef enum_func_status (*func_mysqlnd_conn_data__simple_command)(MYSQLND_CONN_DATA * conn, enum php_mysqlnd_server_command command, const zend_uchar * const arg, size_t arg_len, enum mysqlnd_packet_type ok_packet, zend_bool silent, zend_bool ignore_upsert_status);
-typedef enum_func_status (*func_mysqlnd_conn_data__simple_command_handle_response)(MYSQLND_CONN_DATA * conn, enum mysqlnd_packet_type ok_packet, zend_bool silent, enum php_mysqlnd_server_command command, zend_bool ignore_upsert_status);
+typedef enum_func_status (*func_mysqlnd_conn_data__send_command_do_request)(MYSQLND_CONN_DATA * const conn, const enum php_mysqlnd_server_command command, const zend_uchar * const arg, const size_t arg_len, const zend_bool silent, const zend_bool ignore_upsert_status);
+typedef enum_func_status (*func_mysqlnd_conn_data__send_command_handle_response)(MYSQLND_CONN_DATA * const conn, const enum mysqlnd_packet_type ok_packet, const zend_bool silent, const enum php_mysqlnd_server_command command, const zend_bool ignore_upsert_status);
typedef enum_func_status (*func_mysqlnd_conn_data__restart_psession)(MYSQLND_CONN_DATA * conn);
typedef enum_func_status (*func_mysqlnd_conn_data__end_psession)(MYSQLND_CONN_DATA * conn);
@@ -496,24 +462,25 @@ typedef void (*func_mysqlnd_conn_data__tx_cor_options_to_string)(const MYSQLN
typedef enum_func_status (*func_mysqlnd_conn_data__tx_savepoint)(MYSQLND_CONN_DATA * conn, const char * const name);
typedef enum_func_status (*func_mysqlnd_conn_data__tx_savepoint_release)(MYSQLND_CONN_DATA * conn, const char * const name);
-typedef enum_func_status (*func_mysqlnd_conn_data__local_tx_start)(MYSQLND_CONN_DATA * conn, size_t this_func);
-typedef enum_func_status (*func_mysqlnd_conn_data__local_tx_end)(MYSQLND_CONN_DATA * conn, size_t this_func, enum_func_status status);
+typedef enum_func_status (*func_mysqlnd_conn_data__local_tx_start)(MYSQLND_CONN_DATA * conn, const size_t this_func);
+typedef enum_func_status (*func_mysqlnd_conn_data__local_tx_end)(MYSQLND_CONN_DATA * conn, const size_t this_func, const enum_func_status status);
typedef enum_func_status (*func_mysqlnd_conn_data__execute_init_commands)(MYSQLND_CONN_DATA * conn);
typedef unsigned int (*func_mysqlnd_conn_data__get_updated_connect_flags)(MYSQLND_CONN_DATA * conn, unsigned int mysql_flags);
-typedef enum_func_status (*func_mysqlnd_conn_data__connect_handshake)(MYSQLND_CONN_DATA * conn, const char * const host, const char * const user, const char * const passwd, const unsigned int passwd_len, const char * const db, const unsigned int db_len, const unsigned int mysql_flags);
-typedef enum_func_status (*func_mysqlnd_conn_data__simple_command_send_request)(MYSQLND_CONN_DATA * conn, enum php_mysqlnd_server_command command, const zend_uchar * const arg, size_t arg_len, zend_bool silent, zend_bool ignore_upsert_status);
+typedef enum_func_status (*func_mysqlnd_conn_data__connect_handshake)(MYSQLND_CONN_DATA * conn, const MYSQLND_CSTRING * const scheme, const MYSQLND_CSTRING * const username, const MYSQLND_CSTRING * const password, const MYSQLND_CSTRING * const database, const unsigned int mysql_flags);
typedef struct st_mysqlnd_authentication_plugin * (*func_mysqlnd_conn_data__fetch_auth_plugin_by_name)(const char * const requested_protocol);
-typedef enum_func_status (*func_mysqlnd_conn_data__set_client_option_2d)(MYSQLND_CONN_DATA * const conn, enum mysqlnd_option option, const char * const key, const char * const value);
+typedef enum_func_status (*func_mysqlnd_conn_data__set_client_option_2d)(MYSQLND_CONN_DATA * const conn, const enum_mysqlnd_client_option option, const char * const key, const char * const value);
-typedef unsigned int (*func_mysqlnd_conn_data__negotiate_client_api_capabilities)(MYSQLND_CONN_DATA * const conn, const unsigned int flags);
-typedef unsigned int (*func_mysqlnd_conn_data__get_client_api_capabilities)(const MYSQLND_CONN_DATA * const conn);
+typedef size_t (*func_mysqlnd_conn_data__negotiate_client_api_capabilities)(MYSQLND_CONN_DATA * const conn, const size_t flags);
+typedef size_t (*func_mysqlnd_conn_data__get_client_api_capabilities)(const MYSQLND_CONN_DATA * const conn);
+typedef MYSQLND_STRING (*func_mysqlnd_conn_data__get_scheme)(MYSQLND_CONN_DATA * conn, MYSQLND_CSTRING hostname, MYSQLND_CSTRING *socket_or_pipe, unsigned int port, zend_bool * unix_socket, zend_bool * named_pipe);
-struct st_mysqlnd_conn_data_methods
+
+
+MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data)
{
- func_mysqlnd_conn_data__init init;
func_mysqlnd_conn_data__connect connect;
func_mysqlnd_conn_data__escape_string escape_string;
func_mysqlnd_conn_data__set_charset set_charset;
@@ -549,7 +516,6 @@ struct st_mysqlnd_conn_data_methods
func_mysqlnd_conn_data__get_protocol_information get_protocol_information;
func_mysqlnd_conn_data__get_last_message get_last_message;
func_mysqlnd_conn_data__charset_name charset_name;
- func_mysqlnd_conn_data__list_fields list_fields;
func_mysqlnd_conn_data__list_method list_method;
func_mysqlnd_conn_data__get_last_insert_id get_last_insert_id;
@@ -570,11 +536,6 @@ struct st_mysqlnd_conn_data_methods
func_mysqlnd_conn_data__get_reference get_reference;
func_mysqlnd_conn_data__free_reference free_reference;
- func_mysqlnd_conn_data__get_state get_state;
- func_mysqlnd_conn_data__set_state set_state;
-
- func_mysqlnd_conn_data__simple_command simple_command;
- func_mysqlnd_conn_data__simple_command_handle_response simple_command_handle_response;
func_mysqlnd_conn_data__restart_psession restart_psession;
func_mysqlnd_conn_data__end_psession end_psession;
@@ -598,22 +559,23 @@ struct st_mysqlnd_conn_data_methods
func_mysqlnd_conn_data__execute_init_commands execute_init_commands;
func_mysqlnd_conn_data__get_updated_connect_flags get_updated_connect_flags;
func_mysqlnd_conn_data__connect_handshake connect_handshake;
- func_mysqlnd_conn_data__simple_command_send_request simple_command_send_request;
func_mysqlnd_conn_data__fetch_auth_plugin_by_name fetch_auth_plugin_by_name;
func_mysqlnd_conn_data__set_client_option_2d set_client_option_2d;
func_mysqlnd_conn_data__negotiate_client_api_capabilities negotiate_client_api_capabilities;
func_mysqlnd_conn_data__get_client_api_capabilities get_client_api_capabilities;
+
+ func_mysqlnd_conn_data__get_scheme get_scheme;
};
-typedef enum_func_status (*func_mysqlnd_data__connect)(MYSQLND * conn, const char * host, const char * user, const char * passwd, unsigned int passwd_len, const char * db, unsigned int db_len, unsigned int port, const char * socket_or_pipe, unsigned int mysql_flags);
+typedef enum_func_status (*func_mysqlnd_data__connect)(MYSQLND * conn, const MYSQLND_CSTRING hostname, const MYSQLND_CSTRING username, const MYSQLND_CSTRING password, const MYSQLND_CSTRING database, unsigned int port, const MYSQLND_CSTRING socket_or_pipe, unsigned int mysql_flags);
typedef MYSQLND * (*func_mysqlnd_conn__clone_object)(MYSQLND * const conn);
typedef void (*func_mysqlnd_conn__dtor)(MYSQLND * conn);
-typedef enum_func_status (*func_mysqlnd_conn__close)(MYSQLND * conn, enum_connection_close_type close_type);
+typedef enum_func_status (*func_mysqlnd_conn__close)(MYSQLND * conn, const enum_connection_close_type close_type);
-struct st_mysqlnd_conn_methods
+MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn)
{
func_mysqlnd_data__connect connect;
func_mysqlnd_conn__clone_object clone_object;
@@ -621,13 +583,14 @@ struct st_mysqlnd_conn_methods
func_mysqlnd_conn__close close;
};
+
/* for decoding - binary or text protocol */
typedef enum_func_status (*func_mysqlnd_res__row_decoder)(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval * fields,
unsigned int field_count, const MYSQLND_FIELD * fields_metadata,
zend_bool as_int_or_float, MYSQLND_STATS * stats);
-typedef MYSQLND_RES * (*func_mysqlnd_res__use_result)(MYSQLND_RES * const result, zend_bool ps_protocol);
+typedef MYSQLND_RES * (*func_mysqlnd_res__use_result)(MYSQLND_RES * const result, const zend_bool ps_protocol);
typedef MYSQLND_RES * (*func_mysqlnd_res__store_result)(MYSQLND_RES * result, MYSQLND_CONN_DATA * const conn, const unsigned int flags);
typedef void (*func_mysqlnd_res__fetch_into)(MYSQLND_RES *result, const unsigned int flags, zval *return_value, enum_mysqlnd_extension ext ZEND_FILE_LINE_DC);
typedef MYSQLND_ROW_C (*func_mysqlnd_res__fetch_row_c)(MYSQLND_RES *result);
@@ -644,11 +607,11 @@ typedef const MYSQLND_FIELD *(*func_mysqlnd_res__fetch_field_direct)(MYSQLND_RES
typedef const MYSQLND_FIELD *(*func_mysqlnd_res__fetch_fields)(MYSQLND_RES * const result);
typedef enum_func_status (*func_mysqlnd_res__read_result_metadata)(MYSQLND_RES * result, MYSQLND_CONN_DATA * conn);
-typedef zend_ulong * (*func_mysqlnd_res__fetch_lengths)(MYSQLND_RES * const result);
+typedef const size_t * (*func_mysqlnd_res__fetch_lengths)(MYSQLND_RES * const result);
typedef enum_func_status (*func_mysqlnd_res__store_result_fetch_data)(MYSQLND_CONN_DATA * const conn, MYSQLND_RES * result, MYSQLND_RES_METADATA * meta, MYSQLND_MEMORY_POOL_CHUNK *** row_buffers, zend_bool binary_protocol);
typedef void (*func_mysqlnd_res__free_result_buffers)(MYSQLND_RES * result); /* private */
-typedef enum_func_status (*func_mysqlnd_res__free_result)(MYSQLND_RES * result, zend_bool implicit);
+typedef enum_func_status (*func_mysqlnd_res__free_result)(MYSQLND_RES * result, const zend_bool implicit);
typedef void (*func_mysqlnd_res__free_result_internal)(MYSQLND_RES *result);
typedef void (*func_mysqlnd_res__free_result_contents)(MYSQLND_RES *result);
typedef void (*func_mysqlnd_res__free_buffered_data)(MYSQLND_RES *result);
@@ -657,7 +620,7 @@ typedef void (*func_mysqlnd_res__unbuffered_free_last_data)(MYSQLND_RES *resu
typedef MYSQLND_RES_METADATA * (*func_mysqlnd_res__result_meta_init)(unsigned int field_count, zend_bool persistent);
-struct st_mysqlnd_res_methods
+MYSQLND_CLASS_METHODS_TYPE(mysqlnd_res)
{
mysqlnd_fetch_row_func fetch_row;
@@ -694,12 +657,12 @@ struct st_mysqlnd_res_methods
};
-typedef uint64_t (*func_mysqlnd_result_unbuffered__num_rows)(const MYSQLND_RES_UNBUFFERED * const result);
-typedef zend_ulong * (*func_mysqlnd_result_unbuffered__fetch_lengths)(MYSQLND_RES_UNBUFFERED * const result);
-typedef void (*func_mysqlnd_result_unbuffered__free_last_data)(MYSQLND_RES_UNBUFFERED * result, MYSQLND_STATS * const global_stats);
-typedef void (*func_mysqlnd_result_unbuffered__free_result)(MYSQLND_RES_UNBUFFERED * const result, MYSQLND_STATS * const global_stats);
+typedef uint64_t (*func_mysqlnd_result_unbuffered__num_rows)(const MYSQLND_RES_UNBUFFERED * const result);
+typedef const size_t * (*func_mysqlnd_result_unbuffered__fetch_lengths)(MYSQLND_RES_UNBUFFERED * const result);
+typedef void (*func_mysqlnd_result_unbuffered__free_last_data)(MYSQLND_RES_UNBUFFERED * result, MYSQLND_STATS * const global_stats);
+typedef void (*func_mysqlnd_result_unbuffered__free_result)(MYSQLND_RES_UNBUFFERED * const result, MYSQLND_STATS * const global_stats);
-struct st_mysqlnd_result_unbuffered_methods
+MYSQLND_CLASS_METHODS_TYPE(mysqlnd_result_unbuffered)
{
mysqlnd_fetch_row_func fetch_row;
func_mysqlnd_res__row_decoder row_decoder;
@@ -712,11 +675,11 @@ struct st_mysqlnd_result_unbuffered_methods
typedef uint64_t (*func_mysqlnd_result_buffered__num_rows)(const MYSQLND_RES_BUFFERED * const result);
typedef enum_func_status (*func_mysqlnd_result_buffered__initialize_result_set_rest)(MYSQLND_RES_BUFFERED * const result, MYSQLND_RES_METADATA * const meta,
MYSQLND_STATS * stats, zend_bool int_and_float_native);
-typedef zend_ulong * (*func_mysqlnd_result_buffered__fetch_lengths)(MYSQLND_RES_BUFFERED * const result);
+typedef const size_t * (*func_mysqlnd_result_buffered__fetch_lengths)(MYSQLND_RES_BUFFERED * const result);
typedef enum_func_status (*func_mysqlnd_result_buffered__data_seek)(MYSQLND_RES_BUFFERED * const result, const uint64_t row);
typedef void (*func_mysqlnd_result_buffered__free_result)(MYSQLND_RES_BUFFERED * const result);
-struct st_mysqlnd_result_buffered_methods
+MYSQLND_CLASS_METHODS_TYPE(mysqlnd_result_buffered)
{
mysqlnd_fetch_row_func fetch_row;
func_mysqlnd_res__row_decoder row_decoder;
@@ -734,10 +697,10 @@ typedef const MYSQLND_FIELD * (*func_mysqlnd_res_meta__fetch_fields)(MYSQLND_RES
typedef MYSQLND_FIELD_OFFSET (*func_mysqlnd_res_meta__field_tell)(const MYSQLND_RES_METADATA * const meta);
typedef MYSQLND_FIELD_OFFSET (*func_mysqlnd_res_meta__field_seek)(MYSQLND_RES_METADATA * const meta, const MYSQLND_FIELD_OFFSET field_offset);
typedef enum_func_status (*func_mysqlnd_res_meta__read_metadata)(MYSQLND_RES_METADATA * const meta, MYSQLND_CONN_DATA * conn);
-typedef MYSQLND_RES_METADATA * (*func_mysqlnd_res_meta__clone_metadata)(const MYSQLND_RES_METADATA * const meta, zend_bool persistent);
+typedef MYSQLND_RES_METADATA * (*func_mysqlnd_res_meta__clone_metadata)(const MYSQLND_RES_METADATA * const meta, const zend_bool persistent);
typedef void (*func_mysqlnd_res_meta__free_metadata)(MYSQLND_RES_METADATA * meta);
-struct st_mysqlnd_res_meta_methods
+MYSQLND_CLASS_METHODS_TYPE(mysqlnd_res_meta)
{
func_mysqlnd_res_meta__fetch_field fetch_field;
func_mysqlnd_res_meta__fetch_field_direct fetch_field_direct;
@@ -750,8 +713,8 @@ struct st_mysqlnd_res_meta_methods
};
-typedef enum_func_status (*func_mysqlnd_stmt__prepare)(MYSQLND_STMT * const stmt, const char * const query, unsigned int query_len);
-typedef enum_func_status (*func_mysqlnd_stmt__send_execute)(MYSQLND_STMT * const s, enum_mysqlnd_send_execute_type type, zval * read_cb, zval * err_cb);
+typedef enum_func_status (*func_mysqlnd_stmt__prepare)(MYSQLND_STMT * const stmt, const char * const query, const size_t query_len);
+typedef enum_func_status (*func_mysqlnd_stmt__send_execute)(MYSQLND_STMT * const s, const enum_mysqlnd_send_execute_type type, zval * read_cb, zval * err_cb);
typedef enum_func_status (*func_mysqlnd_stmt__execute)(MYSQLND_STMT * const stmt);
typedef MYSQLND_RES * (*func_mysqlnd_stmt__use_result)(MYSQLND_STMT * const stmt);
typedef MYSQLND_RES * (*func_mysqlnd_stmt__store_result)(MYSQLND_STMT * const stmt);
@@ -761,7 +724,7 @@ typedef enum_func_status (*func_mysqlnd_stmt__next_result)(MYSQLND_STMT * const
typedef enum_func_status (*func_mysqlnd_stmt__free_result)(MYSQLND_STMT * const stmt);
typedef enum_func_status (*func_mysqlnd_stmt__seek_data)(const MYSQLND_STMT * const stmt, uint64_t row);
typedef enum_func_status (*func_mysqlnd_stmt__reset)(MYSQLND_STMT * const stmt);
-typedef enum_func_status (*func_mysqlnd_stmt__net_close)(MYSQLND_STMT * const stmt, zend_bool implicit); /* private */
+typedef enum_func_status (*func_mysqlnd_stmt__close_on_server)(MYSQLND_STMT * const stmt, zend_bool implicit); /* private */
typedef enum_func_status (*func_mysqlnd_stmt__dtor)(MYSQLND_STMT * const stmt, zend_bool implicit); /* use this for mysqlnd_stmt_close */
typedef enum_func_status (*func_mysqlnd_stmt__fetch)(MYSQLND_STMT * const stmt, zend_bool * const fetched_anything);
typedef enum_func_status (*func_mysqlnd_stmt__bind_parameters)(MYSQLND_STMT * const stmt, MYSQLND_PARAM_BIND * const param_bind);
@@ -794,7 +757,7 @@ typedef void (*func_mysqlnd_stmt__free_stmt_content)(MYSQLND_STMT * const s)
typedef enum_func_status (*func_mysqlnd_stmt__flush)(MYSQLND_STMT * const stmt);
typedef void (*func_mysqlnd_stmt__free_stmt_result)(MYSQLND_STMT * const s);
-struct st_mysqlnd_stmt_methods
+MYSQLND_CLASS_METHODS_TYPE(mysqlnd_stmt)
{
func_mysqlnd_stmt__prepare prepare;
func_mysqlnd_stmt__send_execute send_execute;
@@ -807,7 +770,7 @@ struct st_mysqlnd_stmt_methods
func_mysqlnd_stmt__free_result free_result;
func_mysqlnd_stmt__seek_data seek_data;
func_mysqlnd_stmt__reset reset;
- func_mysqlnd_stmt__net_close net_close;
+ func_mysqlnd_stmt__close_on_server close_on_server;
func_mysqlnd_stmt__dtor dtor;
func_mysqlnd_stmt__fetch fetch;
@@ -854,103 +817,96 @@ struct st_mysqlnd_stmt_methods
};
-struct st_mysqlnd_net_data
+struct st_mysqlnd_vio_data
{
php_stream *stream;
- zend_bool compressed;
zend_bool ssl;
+ MYSQLND_VIO_OPTIONS options;
#ifdef MYSQLND_DO_WIRE_CHECK_BEFORE_COMMAND
zend_uchar last_command;
#else
zend_uchar unused_pad1;
#endif
- MYSQLND_NET_OPTIONS options;
-
- unsigned int refcount;
zend_bool persistent;
- struct st_mysqlnd_net_methods m;
+ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_vio) m;
};
-struct st_mysqlnd_net
+struct st_mysqlnd_vio
{
- /* cmd buffer */
- MYSQLND_CMD_BUFFER cmd_buffer;
+ struct st_mysqlnd_vio_data * data;
- struct st_mysqlnd_net_data * data;
+ zend_bool persistent;
+};
-#ifdef MYSQLND_COMPRESSION_ENABLED
- MYSQLND_READ_BUFFER * uncompressed_data;
-#else
- void * unused_pad1;
-#endif
- zend_bool persistent;
- /* sequence for simple checking of correct packets */
- zend_uchar packet_no;
- zend_uchar compressed_envelope_packet_no;
+struct st_mysqlnd_protocol_command
+{
+ enum_func_status (*run)(void *cmd);
+ void (*free_command)(void * cmd);
};
+typedef struct st_mysqlnd_protocol_command * (*func_mysqlnd__command_factory)(enum php_mysqlnd_server_command command, ...);
+
+
-struct st_mysqlnd_protocol
+typedef struct st_mysqlnd_connection_state MYSQLND_CONNECTION_STATE;
+typedef enum mysqlnd_connection_state (*func_mysqlnd_connection_state__get)(const MYSQLND_CONNECTION_STATE * const state_struct);
+typedef void (*func_mysqlnd_connection_state__set)(MYSQLND_CONNECTION_STATE * const state_struct, const enum mysqlnd_connection_state state);
+
+
+MYSQLND_CLASS_METHODS_TYPE(mysqlnd_connection_state)
{
- zend_bool persistent;
- struct st_mysqlnd_protocol_methods m;
+ func_mysqlnd_connection_state__get get;
+ func_mysqlnd_connection_state__set set;
};
+struct st_mysqlnd_connection_state
+{
+ enum mysqlnd_connection_state state;
+
+ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_connection_state) *m;
+};
struct st_mysqlnd_connection_data
{
/* Operation related */
- MYSQLND_NET * net;
- MYSQLND_PROTOCOL * protocol;
+ MYSQLND_PFC * protocol_frame_codec;
+ MYSQLND_VIO * vio;
+ MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * payload_decoder_factory;
/* Information related */
- char *host;
- unsigned int host_len;
- char *unix_socket;
- unsigned int unix_socket_len;
- char *user;
- unsigned int user_len;
- char *passwd;
- unsigned int passwd_len;
- char *scheme;
- unsigned int scheme_len;
+ MYSQLND_STRING hostname;
+ MYSQLND_STRING unix_socket;
+ MYSQLND_STRING username;
+ MYSQLND_STRING password;
+ MYSQLND_STRING scheme;
uint64_t thread_id;
char *server_version;
char *host_info;
- zend_uchar *auth_plugin_data;
- size_t auth_plugin_data_len;
+ MYSQLND_STRING authentication_plugin_data;
const MYSQLND_CHARSET *charset;
const MYSQLND_CHARSET *greet_charset;
- char *connect_or_select_db;
- unsigned int connect_or_select_db_len;
+ MYSQLND_STRING connect_or_select_db;
MYSQLND_INFILE infile;
unsigned int protocol_version;
- zend_ulong max_packet_size;
unsigned int port;
- zend_ulong client_flag;
- zend_ulong server_capabilities;
+ zend_ulong server_capabilities;
/* For UPSERT queries */
MYSQLND_UPSERT_STATUS * upsert_status;
MYSQLND_UPSERT_STATUS upsert_status_impl;
- char *last_message;
- unsigned int last_message_len;
+ MYSQLND_STRING last_message;
/* If error packet, we use these */
MYSQLND_ERROR_INFO * error_info;
MYSQLND_ERROR_INFO error_info_impl;
- /*
- To prevent queries during unbuffered fetches. Also to
- mark the connection as destroyed for garbage collection.
- */
- enum mysqlnd_connection_state state;
- enum_mysqlnd_query_type last_query_type;
+ MYSQLND_CONNECTION_STATE state;
+ enum_mysqlnd_query_type last_query_type;
/* Temporary storage between query and (use|store)_result() call */
MYSQLND_RES *current_result;
@@ -967,20 +923,23 @@ struct st_mysqlnd_connection_data
unsigned int field_count;
/* options */
- MYSQLND_OPTIONS * options;
- MYSQLND_OPTIONS options_impl;
+ MYSQLND_SESSION_OPTIONS * options;
+ MYSQLND_SESSION_OPTIONS options_impl;
/* stats */
MYSQLND_STATS * stats;
- unsigned int client_api_capabilities;
+ size_t client_api_capabilities;
zval async_read_cb;
zval async_err_cb;
zend_bool in_async_read_cb;
zend_bool in_async_err_cb;
- struct st_mysqlnd_conn_data_methods * m;
+ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_object_factory) object_factory;
+ func_mysqlnd__command_factory command_factory;
+
+ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data) * m;
/* persistent connection */
zend_bool persistent;
@@ -991,10 +950,192 @@ struct st_mysqlnd_connection
{
MYSQLND_CONN_DATA * data;
zend_bool persistent;
- struct st_mysqlnd_conn_methods * m;
+ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn) * m;
+};
+
+
+
+struct st_mysqlnd_packet_greet;
+struct st_mysqlnd_packet_greet;
+struct st_mysqlnd_packet_auth;
+struct st_mysqlnd_packet_ok;
+struct st_mysqlnd_packet_command;
+struct st_mysqlnd_packet_eof;
+struct st_mysqlnd_packet_rset_header;
+struct st_mysqlnd_packet_res_field;
+struct st_mysqlnd_packet_row;
+struct st_mysqlnd_packet_stats;
+struct st_mysqlnd_packet_prepare_response;
+struct st_mysqlnd_packet_chg_user_resp;
+struct st_mysqlnd_packet_auth_pam;
+struct st_mysqlnd_packet_sha256_pk_request;
+struct st_mysqlnd_packet_sha256_pk_request_response;
+
+typedef struct st_mysqlnd_packet_greet * (*func_mysqlnd_protocol_payload_decoder_factory__get_greet_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent);
+typedef struct st_mysqlnd_packet_auth * (*func_mysqlnd_protocol_payload_decoder_factory__get_auth_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent);
+typedef struct st_mysqlnd_packet_auth_response *(*func_mysqlnd_protocol_payload_decoder_factory__get_auth_response_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent);
+typedef struct st_mysqlnd_packet_change_auth_response * (*func_mysqlnd_protocol_payload_decoder_factory__get_change_auth_response_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent);
+typedef struct st_mysqlnd_packet_ok * (*func_mysqlnd_protocol_payload_decoder_factory__get_ok_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent);
+typedef struct st_mysqlnd_packet_command * (*func_mysqlnd_protocol_payload_decoder_factory__get_command_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent);
+typedef struct st_mysqlnd_packet_eof * (*func_mysqlnd_protocol_payload_decoder_factory__get_eof_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent);
+typedef struct st_mysqlnd_packet_rset_header * (*func_mysqlnd_protocol_payload_decoder_factory__get_rset_header_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent);
+typedef struct st_mysqlnd_packet_res_field * (*func_mysqlnd_protocol_payload_decoder_factory__get_result_field_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent);
+typedef struct st_mysqlnd_packet_row * (*func_mysqlnd_protocol_payload_decoder_factory__get_row_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent);
+typedef struct st_mysqlnd_packet_stats * (*func_mysqlnd_protocol_payload_decoder_factory__get_stats_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent);
+typedef struct st_mysqlnd_packet_prepare_response *(*func_mysqlnd_protocol_payload_decoder_factory__get_prepare_response_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent);
+typedef struct st_mysqlnd_packet_chg_user_resp*(*func_mysqlnd_protocol_payload_decoder_factory__get_change_user_response_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent);
+typedef struct st_mysqlnd_packet_sha256_pk_request *(*func_mysqlnd_protocol_payload_decoder_factory__get_sha256_pk_request_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent);
+typedef struct st_mysqlnd_packet_sha256_pk_request_response *(*func_mysqlnd_protocol_payload_decoder_factory__get_sha256_pk_request_response_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent);
+
+typedef enum_func_status (*func_mysqlnd_protocol_payload_decoder_factory__send_command)(
+ MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * payload_decoder_factory,
+ const enum php_mysqlnd_server_command command,
+ const zend_uchar * const arg, const size_t arg_len,
+ const zend_bool silent,
+
+ MYSQLND_CONNECTION_STATE * connection_state,
+ MYSQLND_ERROR_INFO * error_info,
+ MYSQLND_UPSERT_STATUS * upsert_status,
+ MYSQLND_STATS * stats,
+ func_mysqlnd_conn_data__send_close send_close,
+ void * send_close_ctx);
+
+typedef enum_func_status (*func_mysqlnd_protocol_payload_decoder_factory__send_command_handle_OK)(
+ MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const payload_decoder_factory,
+ MYSQLND_ERROR_INFO * const error_info,
+ MYSQLND_UPSERT_STATUS * const upsert_status,
+ const zend_bool ignore_upsert_status, /* actually used only by LOAD DATA. COM_QUERY and COM_EXECUTE handle the responses themselves */
+ MYSQLND_STRING * const last_message,
+ const zend_bool last_message_persistent);
+
+typedef enum_func_status (*func_mysqlnd_protocol_payload_decoder_factory__send_command_handle_EOF)(
+ MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const payload_decoder_factory,
+ MYSQLND_ERROR_INFO * const error_info,
+ MYSQLND_UPSERT_STATUS * const upsert_status);
+
+typedef enum_func_status (*func_mysqlnd_protocol_payload_decoder_factory__send_command_handle_response)(
+ MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * payload_decoder_factory,
+ const enum mysqlnd_packet_type ok_packet,
+ const zend_bool silent,
+ const enum php_mysqlnd_server_command command,
+ const zend_bool ignore_upsert_status, /* actually used only by LOAD DATA. COM_QUERY and COM_EXECUTE handle the responses themselves */
+
+ MYSQLND_ERROR_INFO * error_info,
+ MYSQLND_UPSERT_STATUS * upsert_status,
+ MYSQLND_STRING * last_message,
+ zend_bool last_message_persistent);
+
+
+MYSQLND_CLASS_METHODS_TYPE(mysqlnd_protocol_payload_decoder_factory)
+{
+ func_mysqlnd_protocol_payload_decoder_factory__get_greet_packet get_greet_packet;
+ func_mysqlnd_protocol_payload_decoder_factory__get_auth_packet get_auth_packet;
+ func_mysqlnd_protocol_payload_decoder_factory__get_auth_response_packet get_auth_response_packet;
+ func_mysqlnd_protocol_payload_decoder_factory__get_change_auth_response_packet get_change_auth_response_packet;
+ func_mysqlnd_protocol_payload_decoder_factory__get_ok_packet get_ok_packet;
+ func_mysqlnd_protocol_payload_decoder_factory__get_command_packet get_command_packet;
+ func_mysqlnd_protocol_payload_decoder_factory__get_eof_packet get_eof_packet;
+ func_mysqlnd_protocol_payload_decoder_factory__get_rset_header_packet get_rset_header_packet;
+ func_mysqlnd_protocol_payload_decoder_factory__get_result_field_packet get_result_field_packet;
+ func_mysqlnd_protocol_payload_decoder_factory__get_row_packet get_row_packet;
+ func_mysqlnd_protocol_payload_decoder_factory__get_stats_packet get_stats_packet;
+ func_mysqlnd_protocol_payload_decoder_factory__get_prepare_response_packet get_prepare_response_packet;
+ func_mysqlnd_protocol_payload_decoder_factory__get_change_user_response_packet get_change_user_response_packet;
+ func_mysqlnd_protocol_payload_decoder_factory__get_sha256_pk_request_packet get_sha256_pk_request_packet;
+ func_mysqlnd_protocol_payload_decoder_factory__get_sha256_pk_request_response_packet get_sha256_pk_request_response_packet;
+
+ func_mysqlnd_protocol_payload_decoder_factory__send_command send_command;
+ func_mysqlnd_protocol_payload_decoder_factory__send_command_handle_response send_command_handle_response;
+ func_mysqlnd_protocol_payload_decoder_factory__send_command_handle_OK send_command_handle_OK;
+ func_mysqlnd_protocol_payload_decoder_factory__send_command_handle_EOF send_command_handle_EOF;
+};
+
+struct st_mysqlnd_protocol_payload_decoder_factory
+{
+ MYSQLND_CONN_DATA * conn;
+ zend_bool persistent;
+ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_protocol_payload_decoder_factory) m;
};
+typedef struct st_mysqlnd_read_buffer {
+ zend_uchar * data;
+ size_t offset;
+ size_t size;
+ size_t len;
+ zend_bool (*is_empty)(const struct st_mysqlnd_read_buffer *);
+ void (*read)(struct st_mysqlnd_read_buffer *, size_t count, zend_uchar * dest);
+ size_t (*bytes_left)(const struct st_mysqlnd_read_buffer *);
+ void (*free_buffer)(struct st_mysqlnd_read_buffer **);
+} MYSQLND_READ_BUFFER;
+
+
+
+typedef enum_func_status (*func_mysqlnd_pfc__init)(MYSQLND_PFC * const pfc, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info);
+typedef void (*func_mysqlnd_pfc__dtor)(MYSQLND_PFC * const pfc, MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info);
+typedef enum_func_status (*func_mysqlnd_pfc__reset)(MYSQLND_PFC * const pfc, MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info);
+typedef enum_func_status (*func_mysqlnd_pfc__set_client_option)(MYSQLND_PFC * const pfc, enum_mysqlnd_client_option option, const char * const value);
+typedef enum_func_status (*func_mysqlnd_pfc__decode)(zend_uchar * uncompressed_data, const size_t uncompressed_data_len, const zend_uchar * const compressed_data, const size_t compressed_data_len);
+typedef enum_func_status (*func_mysqlnd_pfc__encode)(zend_uchar * compress_buffer, size_t * compress_buffer_len, const zend_uchar * const uncompressed_data, const size_t uncompressed_data_len);
+typedef size_t (*func_mysqlnd_pfc__send)(MYSQLND_PFC * const pfc, MYSQLND_VIO * const vio, zend_uchar * const buffer, const size_t count, MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info);
+typedef enum_func_status (*func_mysqlnd_pfc__receive)(MYSQLND_PFC * const pfc, MYSQLND_VIO * const vio, zend_uchar * const buffer, const size_t count, MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info);
+typedef enum_func_status (*func_mysqlnd_pfc__read_compressed_packet_from_stream_and_fill_read_buffer)(MYSQLND_PFC * pfc, MYSQLND_VIO * const vio, size_t net_payload_size, MYSQLND_STATS * conn_stats, MYSQLND_ERROR_INFO * error_info);
+typedef void (*func_mysqlnd_pfc__free_contents)(MYSQLND_PFC * pfc);
+
+MYSQLND_CLASS_METHODS_TYPE(mysqlnd_protocol_packet_frame_codec)
+{
+ func_mysqlnd_pfc__init init;
+ func_mysqlnd_pfc__dtor dtor;
+ func_mysqlnd_pfc__reset reset;
+ func_mysqlnd_pfc__set_client_option set_client_option;
+
+ func_mysqlnd_pfc__decode decode;
+ func_mysqlnd_pfc__encode encode;
+
+ func_mysqlnd_pfc__send send;
+ func_mysqlnd_pfc__receive receive;
+
+ func_mysqlnd_pfc__read_compressed_packet_from_stream_and_fill_read_buffer read_compressed_packet_from_stream_and_fill_read_buffer;
+
+ func_mysqlnd_pfc__free_contents free_contents;
+};
+
+
+struct st_mysqlnd_protocol_frame_codec_data
+{
+ php_stream *stream;
+ zend_bool compressed;
+ zend_bool ssl;
+ uint64_t flags;
+ char * sha256_server_public_key;
+
+#ifdef MYSQLND_COMPRESSION_ENABLED
+ MYSQLND_READ_BUFFER * uncompressed_data;
+#else
+ void * unused_pad1;
+#endif
+
+ /* sequence for simple checking of correct packets */
+ zend_uchar packet_no;
+ zend_uchar compressed_envelope_packet_no;
+
+ zend_bool persistent;
+
+ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_protocol_packet_frame_codec) m;
+};
+
+
+struct st_mysqlnd_protocol_frame_codec
+{
+ MYSQLND_CMD_BUFFER cmd_buffer;
+
+ struct st_mysqlnd_protocol_frame_codec_data * data;
+
+ zend_bool persistent;
+};
+
+
+
struct mysqlnd_field_hash_key
{
zend_bool is_numeric;
@@ -1007,11 +1148,7 @@ struct st_mysqlnd_result_metadata
MYSQLND_FIELD *fields;
struct mysqlnd_field_hash_key *zend_hash_keys;
- struct st_mysqlnd_res_meta_methods * m;
-
- size_t bit_fields_total_len; /* trailing \0 not counted */
- /* We need this to make fast allocs in rowp_read */
- unsigned int bit_fields_count;
+ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_res_meta) * m;
unsigned int current_field;
unsigned int field_count;
@@ -1026,7 +1163,7 @@ struct st_mysqlnd_result_metadata
uint64_t initialized_rows; \
\
/* Column lengths of current row - both buffered and unbuffered. For buffered results it duplicates the data found in **data */ \
- zend_ulong *lengths; \
+ size_t *lengths; \
\
MYSQLND_MEMORY_POOL *result_set_memory_pool; \
\
@@ -1037,7 +1174,7 @@ struct st_mysqlnd_result_metadata
unsigned int field_count; \
zend_bool ps; \
zend_bool persistent; \
- struct st_mysqlnd_result_buffered_methods m; \
+ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_result_buffered) m; \
enum mysqlnd_buffered_type type; \
void * unused1; \
void * unused2; \
@@ -1070,7 +1207,7 @@ struct st_mysqlnd_buffered_result_c
struct st_mysqlnd_unbuffered_result
{
- struct st_mysqlnd_result_unbuffered_methods m;
+ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_result_unbuffered) m;
uint64_t row_count;
/* For unbuffered (both normal and PS) */
@@ -1081,7 +1218,7 @@ struct st_mysqlnd_unbuffered_result
Column lengths of current row - both buffered and unbuffered.
For buffered results it duplicates the data found in **data
*/
- zend_ulong *lengths;
+ size_t *lengths;
MYSQLND_MEMORY_POOL *result_set_memory_pool;
@@ -1111,8 +1248,7 @@ struct st_mysqlnd_res
MYSQLND_RES_UNBUFFERED *unbuf;
zend_bool persistent;
-
- struct st_mysqlnd_res_methods m;
+ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_res) m;
};
@@ -1136,7 +1272,6 @@ struct st_mysqlnd_stmt_data
zend_ulong stmt_id;
zend_ulong flags;/* cursor is set here */
enum_mysqlnd_stmt_state state;
- unsigned int warning_count;
MYSQLND_RES *result;
unsigned int field_count;
unsigned int param_count;
@@ -1171,18 +1306,11 @@ struct st_mysqlnd_stmt_data
struct st_mysqlnd_stmt
{
MYSQLND_STMT_DATA * data;
- struct st_mysqlnd_stmt_methods *m;
+ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_stmt) * m;
zend_bool persistent;
};
-typedef struct st_mysqlnd_string
-{
- char *s;
- size_t l;
-} MYSQLND_STRING;
-
-
struct st_mysqlnd_plugin_header
{
unsigned int plugin_api_version;
@@ -1223,8 +1351,8 @@ typedef zend_uchar * (*func_auth_plugin__get_auth_data)(struct st_mysqlnd_authen
size_t * auth_data_len,
MYSQLND_CONN_DATA * conn, const char * const user, const char * const passwd,
const size_t passwd_len, zend_uchar * auth_plugin_data, size_t auth_plugin_data_len,
- const MYSQLND_OPTIONS * const options,
- const MYSQLND_NET_OPTIONS * const net_options, zend_ulong mysql_flags
+ const MYSQLND_SESSION_OPTIONS * const session_options,
+ const MYSQLND_PFC_DATA * const pfc_data, zend_ulong mysql_flags
);
struct st_mysqlnd_authentication_plugin
@@ -1235,5 +1363,4 @@ struct st_mysqlnd_authentication_plugin
} methods;
};
-
#endif /* MYSQLND_STRUCTS_H */
diff --git a/ext/mysqlnd/mysqlnd_vio.c b/ext/mysqlnd/mysqlnd_vio.c
new file mode 100644
index 0000000000..33de52c6be
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_vio.c
@@ -0,0 +1,805 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 7 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2017 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Andrey Hristov <andrey@php.net> |
+ | Ulf Wendel <uw@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "php.h"
+#include "mysqlnd.h"
+#include "mysqlnd_priv.h"
+#include "mysqlnd_statistics.h"
+#include "mysqlnd_debug.h"
+#include "mysqlnd_ext_plugin.h"
+#include "php_network.h"
+
+#ifndef PHP_WIN32
+#include <netinet/tcp.h>
+#else
+#include <winsock.h>
+#endif
+
+
+/* {{{ mysqlnd_set_sock_no_delay */
+static int
+mysqlnd_set_sock_no_delay(php_stream * stream)
+{
+ int socketd = ((php_netstream_data_t*)stream->abstract)->socket;
+ int ret = SUCCESS;
+ int flag = 1;
+ int result = setsockopt(socketd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int));
+
+ DBG_ENTER("mysqlnd_set_sock_no_delay");
+
+ if (result == -1) {
+ ret = FAILURE;
+ }
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_set_sock_keepalive */
+static int
+mysqlnd_set_sock_keepalive(php_stream * stream)
+{
+ int socketd = ((php_netstream_data_t*)stream->abstract)->socket;
+ int ret = SUCCESS;
+ int flag = 1;
+ int result = setsockopt(socketd, SOL_SOCKET, SO_KEEPALIVE, (char *) &flag, sizeof(int));
+
+ DBG_ENTER("mysqlnd_set_sock_keepalive");
+
+ if (result == -1) {
+ ret = FAILURE;
+ }
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_vio::network_read */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_vio, network_read)(MYSQLND_VIO * const vio, zend_uchar * const buffer, const size_t count,
+ MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info)
+{
+ enum_func_status return_value = PASS;
+ php_stream * net_stream = vio->data->m.get_stream(vio);
+ size_t old_chunk_size = net_stream->chunk_size;
+ size_t to_read = count, ret;
+ zend_uchar * p = buffer;
+
+ DBG_ENTER("mysqlnd_vio::network_read");
+ DBG_INF_FMT("count="MYSQLND_SZ_T_SPEC, count);
+
+ net_stream->chunk_size = MIN(to_read, vio->data->options.net_read_buffer_size);
+ while (to_read) {
+ if (!(ret = php_stream_read(net_stream, (char *) p, to_read))) {
+ DBG_ERR_FMT("Error while reading header from socket");
+ return_value = FAIL;
+ break;
+ }
+ p += ret;
+ to_read -= ret;
+ }
+ MYSQLND_INC_CONN_STATISTIC_W_VALUE(stats, STAT_BYTES_RECEIVED, count - to_read);
+ net_stream->chunk_size = old_chunk_size;
+ DBG_RETURN(return_value);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_vio::network_write */
+static size_t
+MYSQLND_METHOD(mysqlnd_vio, network_write)(MYSQLND_VIO * const vio, const zend_uchar * const buffer, const size_t count,
+ MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info)
+{
+ size_t ret;
+ DBG_ENTER("mysqlnd_vio::network_write");
+ DBG_INF_FMT("sending %u bytes", count);
+ ret = php_stream_write(vio->data->m.get_stream(vio), (char *)buffer, count);
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_vio::open_pipe */
+static php_stream *
+MYSQLND_METHOD(mysqlnd_vio, open_pipe)(MYSQLND_VIO * const vio, const MYSQLND_CSTRING scheme, const zend_bool persistent,
+ MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info)
+{
+ unsigned int streams_options = 0;
+ dtor_func_t origin_dtor;
+ php_stream * net_stream = NULL;
+
+ DBG_ENTER("mysqlnd_vio::open_pipe");
+ if (persistent) {
+ streams_options |= STREAM_OPEN_PERSISTENT;
+ }
+ streams_options |= IGNORE_URL;
+ net_stream = php_stream_open_wrapper(scheme.s + sizeof("pipe://") - 1, "r+", streams_options, NULL);
+ if (!net_stream) {
+ SET_CLIENT_ERROR(error_info, CR_CONNECTION_ERROR, UNKNOWN_SQLSTATE, "Unknown errror while connecting");
+ DBG_RETURN(NULL);
+ }
+ /*
+ Streams are not meant for C extensions! Thus we need a hack. Every connected stream will
+ be registered as resource (in EG(regular_list). So far, so good. However, it won't be
+ unregistered until the script ends. So, we need to take care of that.
+ */
+ origin_dtor = EG(regular_list).pDestructor;
+ EG(regular_list).pDestructor = NULL;
+ zend_hash_index_del(&EG(regular_list), net_stream->res->handle); /* ToDO: should it be res->handle, do streams register with addref ?*/
+ EG(regular_list).pDestructor = origin_dtor;
+ net_stream->res = NULL;
+
+ DBG_RETURN(net_stream);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_vio::open_tcp_or_unix */
+static php_stream *
+MYSQLND_METHOD(mysqlnd_vio, open_tcp_or_unix)(MYSQLND_VIO * const vio, const MYSQLND_CSTRING scheme, const zend_bool persistent,
+ MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info)
+{
+ unsigned int streams_options = 0;
+ unsigned int streams_flags = STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT;
+ char * hashed_details = NULL;
+ int hashed_details_len = 0;
+ zend_string *errstr = NULL;
+ int errcode = 0;
+ struct timeval tv;
+ dtor_func_t origin_dtor;
+ php_stream * net_stream = NULL;
+
+ DBG_ENTER("mysqlnd_vio::open_tcp_or_unix");
+
+ vio->data->stream = NULL;
+
+ if (persistent) {
+ hashed_details_len = mnd_sprintf(&hashed_details, 0, "%p", vio);
+ DBG_INF_FMT("hashed_details=%s", hashed_details);
+ }
+
+ if (vio->data->options.timeout_connect) {
+ tv.tv_sec = vio->data->options.timeout_connect;
+ tv.tv_usec = 0;
+ }
+
+ DBG_INF_FMT("calling php_stream_xport_create");
+ net_stream = php_stream_xport_create(scheme.s, scheme.l, streams_options, streams_flags,
+ hashed_details, (vio->data->options.timeout_connect) ? &tv : NULL,
+ NULL /*ctx*/, &errstr, &errcode);
+ if (errstr || !net_stream) {
+ DBG_ERR("Error");
+ if (hashed_details) {
+ mnd_sprintf_free(hashed_details);
+ }
+ errcode = CR_CONNECTION_ERROR;
+ SET_CLIENT_ERROR(error_info,
+ CR_CONNECTION_ERROR,
+ UNKNOWN_SQLSTATE,
+ errstr? ZSTR_VAL(errstr):"Unknown error while connecting");
+ if (errstr) {
+ zend_string_release(errstr);
+ }
+ DBG_RETURN(NULL);
+ }
+ if (hashed_details) {
+ /*
+ If persistent, the streams register it in EG(persistent_list).
+ This is unwanted. ext/mysql or ext/mysqli are responsible to clean,
+ whatever they have to.
+ */
+ zend_resource *le;
+
+ if ((le = zend_hash_str_find_ptr(&EG(persistent_list), hashed_details, hashed_details_len))) {
+ origin_dtor = EG(persistent_list).pDestructor;
+ /*
+ in_free will let streams code skip destructing - big HACK,
+ but STREAMS suck big time regarding persistent streams.
+ Just not compatible for extensions that need persistency.
+ */
+ EG(persistent_list).pDestructor = NULL;
+ zend_hash_str_del(&EG(persistent_list), hashed_details, hashed_details_len);
+ EG(persistent_list).pDestructor = origin_dtor;
+ pefree(le, 1);
+ }
+#if ZEND_DEBUG
+ /* Shut-up the streams, they don't know what they are doing */
+ net_stream->__exposed = 1;
+#endif
+ mnd_sprintf_free(hashed_details);
+ }
+
+ /*
+ Streams are not meant for C extensions! Thus we need a hack. Every connected stream will
+ be registered as resource (in EG(regular_list). So far, so good. However, it won't be
+ unregistered until the script ends. So, we need to take care of that.
+ */
+ origin_dtor = EG(regular_list).pDestructor;
+ EG(regular_list).pDestructor = NULL;
+ zend_hash_index_del(&EG(regular_list), net_stream->res->handle); /* ToDO: should it be res->handle, do streams register with addref ?*/
+ efree(net_stream->res);
+ net_stream->res = NULL;
+ EG(regular_list).pDestructor = origin_dtor;
+ DBG_RETURN(net_stream);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_vio::post_connect_set_opt */
+static void
+MYSQLND_METHOD(mysqlnd_vio, post_connect_set_opt)(MYSQLND_VIO * const vio, const MYSQLND_CSTRING scheme,
+ MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info)
+{
+ php_stream * net_stream = vio->data->m.get_stream(vio);
+ DBG_ENTER("mysqlnd_vio::post_connect_set_opt");
+ if (net_stream) {
+ if (vio->data->options.timeout_read) {
+ struct timeval tv;
+ DBG_INF_FMT("setting %u as PHP_STREAM_OPTION_READ_TIMEOUT", vio->data->options.timeout_read);
+ tv.tv_sec = vio->data->options.timeout_read;
+ tv.tv_usec = 0;
+ php_stream_set_option(net_stream, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &tv);
+ }
+
+ if (!memcmp(scheme.s, "tcp://", sizeof("tcp://") - 1)) {
+ /* TCP -> Set TCP_NODELAY */
+ mysqlnd_set_sock_no_delay(net_stream);
+ /* TCP -> Set SO_KEEPALIVE */
+ mysqlnd_set_sock_keepalive(net_stream);
+ }
+ }
+
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_vio::get_open_stream */
+static func_mysqlnd_vio__open_stream
+MYSQLND_METHOD(mysqlnd_vio, get_open_stream)(MYSQLND_VIO * const vio, const MYSQLND_CSTRING scheme,
+ MYSQLND_ERROR_INFO * const error_info)
+{
+ func_mysqlnd_vio__open_stream ret = NULL;
+ DBG_ENTER("mysqlnd_vio::get_open_stream");
+ if (scheme.l > (sizeof("pipe://") - 1) && !memcmp(scheme.s, "pipe://", sizeof("pipe://") - 1)) {
+ ret = vio->data->m.open_pipe;
+ } else if ((scheme.l > (sizeof("tcp://") - 1) && !memcmp(scheme.s, "tcp://", sizeof("tcp://") - 1))
+ ||
+ (scheme.l > (sizeof("unix://") - 1) && !memcmp(scheme.s, "unix://", sizeof("unix://") - 1)))
+ {
+ ret = vio->data->m.open_tcp_or_unix;
+ }
+
+ if (!ret) {
+ SET_CLIENT_ERROR(error_info, CR_CONNECTION_ERROR, UNKNOWN_SQLSTATE, "No handler for this scheme");
+ }
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_vio::connect */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_vio, connect)(MYSQLND_VIO * const vio, const MYSQLND_CSTRING scheme, const zend_bool persistent,
+ MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info)
+{
+ enum_func_status ret = FAIL;
+ func_mysqlnd_vio__open_stream open_stream = NULL;
+ DBG_ENTER("mysqlnd_vio::connect");
+
+ vio->data->m.close_stream(vio, conn_stats, error_info);
+
+ open_stream = vio->data->m.get_open_stream(vio, scheme, error_info);
+ if (open_stream) {
+ php_stream * net_stream = open_stream(vio, scheme, persistent, conn_stats, error_info);
+ if (net_stream && PASS == vio->data->m.set_stream(vio, net_stream)) {
+ vio->data->m.post_connect_set_opt(vio, scheme, conn_stats, error_info);
+ ret = PASS;
+ }
+ }
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_vio::set_client_option */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_vio, set_client_option)(MYSQLND_VIO * const net, enum_mysqlnd_client_option option, const char * const value)
+{
+ DBG_ENTER("mysqlnd_vio::set_client_option");
+ DBG_INF_FMT("option=%u", option);
+ switch (option) {
+ case MYSQLND_OPT_NET_READ_BUFFER_SIZE:
+ DBG_INF("MYSQLND_OPT_NET_READ_BUFFER_SIZE");
+ net->data->options.net_read_buffer_size = *(unsigned int*) value;
+ DBG_INF_FMT("new_length="MYSQLND_SZ_T_SPEC, net->data->options.net_read_buffer_size);
+ break;
+ case MYSQL_OPT_CONNECT_TIMEOUT:
+ DBG_INF("MYSQL_OPT_CONNECT_TIMEOUT");
+ net->data->options.timeout_connect = *(unsigned int*) value;
+ break;
+ case MYSQLND_OPT_SSL_KEY:
+ {
+ zend_bool pers = net->persistent;
+ if (net->data->options.ssl_key) {
+ mnd_pefree(net->data->options.ssl_key, pers);
+ }
+ net->data->options.ssl_key = value? mnd_pestrdup(value, pers) : NULL;
+ break;
+ }
+ case MYSQLND_OPT_SSL_CERT:
+ {
+ zend_bool pers = net->persistent;
+ if (net->data->options.ssl_cert) {
+ mnd_pefree(net->data->options.ssl_cert, pers);
+ }
+ net->data->options.ssl_cert = value? mnd_pestrdup(value, pers) : NULL;
+ break;
+ }
+ case MYSQLND_OPT_SSL_CA:
+ {
+ zend_bool pers = net->persistent;
+ if (net->data->options.ssl_ca) {
+ mnd_pefree(net->data->options.ssl_ca, pers);
+ }
+ net->data->options.ssl_ca = value? mnd_pestrdup(value, pers) : NULL;
+ break;
+ }
+ case MYSQLND_OPT_SSL_CAPATH:
+ {
+ zend_bool pers = net->persistent;
+ if (net->data->options.ssl_capath) {
+ mnd_pefree(net->data->options.ssl_capath, pers);
+ }
+ net->data->options.ssl_capath = value? mnd_pestrdup(value, pers) : NULL;
+ break;
+ }
+ case MYSQLND_OPT_SSL_CIPHER:
+ {
+ zend_bool pers = net->persistent;
+ if (net->data->options.ssl_cipher) {
+ mnd_pefree(net->data->options.ssl_cipher, pers);
+ }
+ net->data->options.ssl_cipher = value? mnd_pestrdup(value, pers) : NULL;
+ break;
+ }
+ case MYSQLND_OPT_SSL_PASSPHRASE:
+ {
+ zend_bool pers = net->persistent;
+ if (net->data->options.ssl_passphrase) {
+ mnd_pefree(net->data->options.ssl_passphrase, pers);
+ }
+ net->data->options.ssl_passphrase = value? mnd_pestrdup(value, pers) : NULL;
+ break;
+ }
+ case MYSQL_OPT_SSL_VERIFY_SERVER_CERT:
+ {
+ enum mysqlnd_ssl_peer val = *((enum mysqlnd_ssl_peer *)value);
+ switch (val) {
+ case MYSQLND_SSL_PEER_VERIFY:
+ DBG_INF("MYSQLND_SSL_PEER_VERIFY");
+ break;
+ case MYSQLND_SSL_PEER_DONT_VERIFY:
+ DBG_INF("MYSQLND_SSL_PEER_DONT_VERIFY");
+ break;
+ case MYSQLND_SSL_PEER_DEFAULT:
+ DBG_INF("MYSQLND_SSL_PEER_DEFAULT");
+ val = MYSQLND_SSL_PEER_DEFAULT;
+ break;
+ default:
+ DBG_INF("default = MYSQLND_SSL_PEER_DEFAULT_ACTION");
+ val = MYSQLND_SSL_PEER_DEFAULT;
+ break;
+ }
+ net->data->options.ssl_verify_peer = val;
+ break;
+ }
+ case MYSQL_OPT_READ_TIMEOUT:
+ net->data->options.timeout_read = *(unsigned int*) value;
+ break;
+#ifdef WHEN_SUPPORTED_BY_MYSQLI
+ case MYSQL_OPT_WRITE_TIMEOUT:
+ net->data->options.timeout_write = *(unsigned int*) value;
+ break;
+#endif
+ default:
+ DBG_RETURN(FAIL);
+ }
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_vio::consume_uneaten_data */
+size_t
+MYSQLND_METHOD(mysqlnd_vio, consume_uneaten_data)(MYSQLND_VIO * const net, enum php_mysqlnd_server_command cmd)
+{
+#ifdef MYSQLND_DO_WIRE_CHECK_BEFORE_COMMAND
+ /*
+ Switch to non-blocking mode and try to consume something from
+ the line, if possible, then continue. This saves us from looking for
+ the actual place where out-of-order packets have been sent.
+ If someone is completely sure that everything is fine, he can switch it
+ off.
+ */
+ char tmp_buf[256];
+ size_t skipped_bytes = 0;
+ int opt = PHP_STREAM_OPTION_BLOCKING;
+ php_stream * net_stream = net->data->get_stream(net);
+ int was_blocked = net_stream->ops->set_option(net_stream, opt, 0, NULL);
+
+ DBG_ENTER("mysqlnd_vio::consume_uneaten_data");
+
+ if (PHP_STREAM_OPTION_RETURN_ERR != was_blocked) {
+ /* Do a read of 1 byte */
+ int bytes_consumed;
+
+ do {
+ skipped_bytes += (bytes_consumed = php_stream_read(net_stream, tmp_buf, sizeof(tmp_buf)));
+ } while (bytes_consumed == sizeof(tmp_buf));
+
+ if (was_blocked) {
+ net_stream->ops->set_option(net_stream, opt, 1, NULL);
+ }
+
+ if (bytes_consumed) {
+ DBG_ERR_FMT("Skipped %u bytes. Last command hasn't consumed all the output from the server",
+ bytes_consumed, mysqlnd_command_to_text[net->last_command]);
+ php_error_docref(NULL, E_WARNING, "Skipped %u bytes. Last command %s hasn't "
+ "consumed all the output from the server",
+ bytes_consumed, mysqlnd_command_to_text[net->last_command]);
+ }
+ }
+ net->last_command = cmd;
+
+ DBG_RETURN(skipped_bytes);
+#else
+ return 0;
+#endif
+}
+/* }}} */
+
+/*
+ in libmyusql, if cert and !key then key=cert
+*/
+/* {{{ mysqlnd_vio::enable_ssl */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_vio, enable_ssl)(MYSQLND_VIO * const net)
+{
+#ifdef MYSQLND_SSL_SUPPORTED
+ php_stream_context * context = php_stream_context_alloc();
+ php_stream * net_stream = net->data->m.get_stream(net);
+ zend_bool any_flag = FALSE;
+
+ DBG_ENTER("mysqlnd_vio::enable_ssl");
+ if (!context) {
+ DBG_RETURN(FAIL);
+ }
+
+ if (net->data->options.ssl_key) {
+ zval key_zval;
+ ZVAL_STRING(&key_zval, net->data->options.ssl_key);
+ php_stream_context_set_option(context, "ssl", "local_pk", &key_zval);
+ zval_ptr_dtor(&key_zval);
+ any_flag = TRUE;
+ }
+ if (net->data->options.ssl_cert) {
+ zval cert_zval;
+ ZVAL_STRING(&cert_zval, net->data->options.ssl_cert);
+ php_stream_context_set_option(context, "ssl", "local_cert", &cert_zval);
+ if (!net->data->options.ssl_key) {
+ php_stream_context_set_option(context, "ssl", "local_pk", &cert_zval);
+ }
+ zval_ptr_dtor(&cert_zval);
+ any_flag = TRUE;
+ }
+ if (net->data->options.ssl_ca) {
+ zval cafile_zval;
+ ZVAL_STRING(&cafile_zval, net->data->options.ssl_ca);
+ php_stream_context_set_option(context, "ssl", "cafile", &cafile_zval);
+ any_flag = TRUE;
+ }
+ if (net->data->options.ssl_capath) {
+ zval capath_zval;
+ ZVAL_STRING(&capath_zval, net->data->options.ssl_capath);
+ php_stream_context_set_option(context, "ssl", "capath", &capath_zval);
+ zval_ptr_dtor(&capath_zval);
+ any_flag = TRUE;
+ }
+ if (net->data->options.ssl_passphrase) {
+ zval passphrase_zval;
+ ZVAL_STRING(&passphrase_zval, net->data->options.ssl_passphrase);
+ php_stream_context_set_option(context, "ssl", "passphrase", &passphrase_zval);
+ zval_ptr_dtor(&passphrase_zval);
+ any_flag = TRUE;
+ }
+ if (net->data->options.ssl_cipher) {
+ zval cipher_zval;
+ ZVAL_STRING(&cipher_zval, net->data->options.ssl_cipher);
+ php_stream_context_set_option(context, "ssl", "ciphers", &cipher_zval);
+ zval_ptr_dtor(&cipher_zval);
+ any_flag = TRUE;
+ }
+ {
+ zval verify_peer_zval;
+ zend_bool verify;
+
+ if (net->data->options.ssl_verify_peer == MYSQLND_SSL_PEER_DEFAULT) {
+ net->data->options.ssl_verify_peer = any_flag? MYSQLND_SSL_PEER_DEFAULT_ACTION:MYSQLND_SSL_PEER_DONT_VERIFY;
+ }
+
+ verify = net->data->options.ssl_verify_peer == MYSQLND_SSL_PEER_VERIFY? TRUE:FALSE;
+
+ DBG_INF_FMT("VERIFY=%d", verify);
+ ZVAL_BOOL(&verify_peer_zval, verify);
+ php_stream_context_set_option(context, "ssl", "verify_peer", &verify_peer_zval);
+ php_stream_context_set_option(context, "ssl", "verify_peer_name", &verify_peer_zval);
+ if (net->data->options.ssl_verify_peer == MYSQLND_SSL_PEER_DONT_VERIFY) {
+ ZVAL_TRUE(&verify_peer_zval);
+ php_stream_context_set_option(context, "ssl", "allow_self_signed", &verify_peer_zval);
+ }
+ }
+ php_stream_context_set(net_stream, context);
+ if (php_stream_xport_crypto_setup(net_stream, STREAM_CRYPTO_METHOD_TLS_CLIENT, NULL) < 0 ||
+ php_stream_xport_crypto_enable(net_stream, 1) < 0)
+ {
+ DBG_ERR("Cannot connect to MySQL by using SSL");
+ php_error_docref(NULL, E_WARNING, "Cannot connect to MySQL by using SSL");
+ DBG_RETURN(FAIL);
+ }
+ net->data->ssl = TRUE;
+ /*
+ get rid of the context. we are persistent and if this is a real pconn used by mysql/mysqli,
+ then the context would not survive cleaning of EG(regular_list), where it is registered, as a
+ resource. What happens is that after this destruction any use of the network will mean usage
+ of the context, which means usage of already freed memory, bad. Actually we don't need this
+ context anymore after we have enabled SSL on the connection. Thus it is very simple, we remove it.
+ */
+ php_stream_context_set(net_stream, NULL);
+
+ if (net->data->options.timeout_read) {
+ struct timeval tv;
+ DBG_INF_FMT("setting %u as PHP_STREAM_OPTION_READ_TIMEOUT", net->data->options.timeout_read);
+ tv.tv_sec = net->data->options.timeout_read;
+ tv.tv_usec = 0;
+ php_stream_set_option(net_stream, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &tv);
+ }
+
+ DBG_RETURN(PASS);
+#else
+ DBG_ENTER("mysqlnd_vio::enable_ssl");
+ DBG_INF("MYSQLND_SSL_SUPPORTED is not defined");
+ DBG_RETURN(PASS);
+#endif
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_vio::disable_ssl */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_vio, disable_ssl)(MYSQLND_VIO * const vio)
+{
+ DBG_ENTER("mysqlnd_vio::disable_ssl");
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_vio::free_contents */
+static void
+MYSQLND_METHOD(mysqlnd_vio, free_contents)(MYSQLND_VIO * net)
+{
+ zend_bool pers = net->persistent;
+ DBG_ENTER("mysqlnd_vio::free_contents");
+
+ if (net->data->options.ssl_key) {
+ mnd_pefree(net->data->options.ssl_key, pers);
+ net->data->options.ssl_key = NULL;
+ }
+ if (net->data->options.ssl_cert) {
+ mnd_pefree(net->data->options.ssl_cert, pers);
+ net->data->options.ssl_cert = NULL;
+ }
+ if (net->data->options.ssl_ca) {
+ mnd_pefree(net->data->options.ssl_ca, pers);
+ net->data->options.ssl_ca = NULL;
+ }
+ if (net->data->options.ssl_capath) {
+ mnd_pefree(net->data->options.ssl_capath, pers);
+ net->data->options.ssl_capath = NULL;
+ }
+ if (net->data->options.ssl_cipher) {
+ mnd_pefree(net->data->options.ssl_cipher, pers);
+ net->data->options.ssl_cipher = NULL;
+ }
+
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_vio::close_stream */
+static void
+MYSQLND_METHOD(mysqlnd_vio, close_stream)(MYSQLND_VIO * const net, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info)
+{
+ php_stream * net_stream;
+ DBG_ENTER("mysqlnd_vio::close_stream");
+ if (net && (net_stream = net->data->m.get_stream(net))) {
+ zend_bool pers = net->persistent;
+ DBG_INF_FMT("Freeing stream. abstract=%p", net_stream->abstract);
+ if (pers) {
+ if (EG(active)) {
+ php_stream_free(net_stream, PHP_STREAM_FREE_CLOSE_PERSISTENT | PHP_STREAM_FREE_RSRC_DTOR);
+ } else {
+ /*
+ otherwise we will crash because the EG(persistent_list) has been freed already,
+ before the modules are shut down
+ */
+ php_stream_free(net_stream, PHP_STREAM_FREE_CLOSE | PHP_STREAM_FREE_RSRC_DTOR);
+ }
+ } else {
+ php_stream_free(net_stream, PHP_STREAM_FREE_CLOSE);
+ }
+ net->data->m.set_stream(net, NULL);
+ }
+
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_vio::init */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_vio, init)(MYSQLND_VIO * const net, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info)
+{
+ unsigned int buf_size;
+ DBG_ENTER("mysqlnd_vio::init");
+
+ buf_size = MYSQLND_G(net_read_buffer_size); /* this is long, cast to unsigned int*/
+ net->data->m.set_client_option(net, MYSQLND_OPT_NET_READ_BUFFER_SIZE, (char *)&buf_size);
+
+ buf_size = MYSQLND_G(net_read_timeout); /* this is long, cast to unsigned int*/
+ net->data->m.set_client_option(net, MYSQL_OPT_READ_TIMEOUT, (char *)&buf_size);
+
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_vio::dtor */
+static void
+MYSQLND_METHOD(mysqlnd_vio, dtor)(MYSQLND_VIO * const vio, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info)
+{
+ DBG_ENTER("mysqlnd_vio::dtor");
+ if (vio) {
+ vio->data->m.free_contents(vio);
+ vio->data->m.close_stream(vio, stats, error_info);
+
+ mnd_pefree(vio->data, vio->data->persistent);
+ mnd_pefree(vio, vio->persistent);
+ }
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_vio::get_stream */
+static php_stream *
+MYSQLND_METHOD(mysqlnd_vio, get_stream)(const MYSQLND_VIO * const net)
+{
+ DBG_ENTER("mysqlnd_vio::get_stream");
+ DBG_INF_FMT("%p", net? net->data->stream:NULL);
+ DBG_RETURN(net? net->data->stream:NULL);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_vio::set_stream */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_vio, set_stream)(MYSQLND_VIO * const vio, php_stream * net_stream)
+{
+ DBG_ENTER("mysqlnd_vio::set_stream");
+ if (vio) {
+ vio->data->stream = net_stream;
+ DBG_RETURN(PASS);
+ }
+ DBG_RETURN(FAIL);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_vio::has_valid_stream */
+static zend_bool
+MYSQLND_METHOD(mysqlnd_vio, has_valid_stream)(const MYSQLND_VIO * const vio)
+{
+ DBG_ENTER("mysqlnd_vio::has_valid_stream");
+ DBG_INF_FMT("%p %p", vio, vio? vio->data->stream:NULL);
+ DBG_RETURN((vio && vio->data->stream)? TRUE: FALSE);
+}
+/* }}} */
+
+
+MYSQLND_CLASS_METHODS_START(mysqlnd_vio)
+ MYSQLND_METHOD(mysqlnd_vio, init),
+ MYSQLND_METHOD(mysqlnd_vio, dtor),
+
+ MYSQLND_METHOD(mysqlnd_vio, connect),
+
+ MYSQLND_METHOD(mysqlnd_vio, close_stream),
+ MYSQLND_METHOD(mysqlnd_vio, open_pipe),
+ MYSQLND_METHOD(mysqlnd_vio, open_tcp_or_unix),
+
+ MYSQLND_METHOD(mysqlnd_vio, get_stream),
+ MYSQLND_METHOD(mysqlnd_vio, set_stream),
+ MYSQLND_METHOD(mysqlnd_vio, has_valid_stream),
+ MYSQLND_METHOD(mysqlnd_vio, get_open_stream),
+
+ MYSQLND_METHOD(mysqlnd_vio, set_client_option),
+ MYSQLND_METHOD(mysqlnd_vio, post_connect_set_opt),
+
+ MYSQLND_METHOD(mysqlnd_vio, enable_ssl),
+ MYSQLND_METHOD(mysqlnd_vio, disable_ssl),
+
+ MYSQLND_METHOD(mysqlnd_vio, network_read),
+ MYSQLND_METHOD(mysqlnd_vio, network_write),
+
+ MYSQLND_METHOD(mysqlnd_vio, consume_uneaten_data),
+
+ MYSQLND_METHOD(mysqlnd_vio, free_contents),
+MYSQLND_CLASS_METHODS_END;
+
+
+/* {{{ mysqlnd_vio_init */
+PHPAPI MYSQLND_VIO *
+mysqlnd_vio_init(zend_bool persistent, MYSQLND_CLASS_METHODS_TYPE(mysqlnd_object_factory) *object_factory, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info)
+{
+ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_object_factory) *factory = object_factory? object_factory : &MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_object_factory);
+ MYSQLND_VIO * vio;
+ DBG_ENTER("mysqlnd_vio_init");
+ vio = factory->get_vio(persistent, stats, error_info);
+ DBG_RETURN(vio);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_vio_free */
+PHPAPI void
+mysqlnd_vio_free(MYSQLND_VIO * const vio, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info)
+{
+ DBG_ENTER("mysqlnd_vio_free");
+ if (vio) {
+ vio->data->m.dtor(vio, stats, error_info);
+ }
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/mysqlnd/mysqlnd_net.h b/ext/mysqlnd/mysqlnd_vio.h
index ef3a2b2af2..58bdd473d7 100644
--- a/ext/mysqlnd/mysqlnd_net.h
+++ b/ext/mysqlnd/mysqlnd_vio.h
@@ -14,17 +14,16 @@
+----------------------------------------------------------------------+
| Authors: Andrey Hristov <andrey@php.net> |
| Ulf Wendel <uw@php.net> |
- | Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
-#ifndef MYSQLND_NET_H
-#define MYSQLND_NET_H
+#ifndef MYSQLND_VIO_H
+#define MYSQLND_VIO_H
-PHPAPI MYSQLND_NET * mysqlnd_net_init(zend_bool persistent, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info);
-PHPAPI void mysqlnd_net_free(MYSQLND_NET * const net, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info);
+PHPAPI MYSQLND_VIO * mysqlnd_vio_init(zend_bool persistent, MYSQLND_CLASS_METHODS_TYPE(mysqlnd_object_factory) *object_factory, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info);
+PHPAPI void mysqlnd_vio_free(MYSQLND_VIO * const vio, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info);
-#endif /* MYSQLND_NET_H */
+#endif /* MYSQLND_VIO_H */
/*
* Local variables:
diff --git a/ext/mysqlnd/mysqlnd_wireprotocol.c b/ext/mysqlnd/mysqlnd_wireprotocol.c
index 6113543e2b..90ae615a1a 100644
--- a/ext/mysqlnd/mysqlnd_wireprotocol.c
+++ b/ext/mysqlnd/mysqlnd_wireprotocol.c
@@ -14,51 +14,17 @@
+----------------------------------------------------------------------+
| Authors: Andrey Hristov <andrey@php.net> |
| Ulf Wendel <uw@php.net> |
- | Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
#include "php.h"
-#include "php_globals.h"
#include "mysqlnd.h"
+#include "mysqlnd_connection.h"
+#include "mysqlnd_ps.h"
#include "mysqlnd_priv.h"
#include "mysqlnd_wireprotocol.h"
#include "mysqlnd_statistics.h"
#include "mysqlnd_debug.h"
-#include "zend_ini.h"
-
-#define MYSQLND_SILENT 1
-
-#define MYSQLND_DUMP_HEADER_N_BODY
-
-#define PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_size, packet_type_as_text, packet_type) \
- { \
- DBG_INF_FMT("buf=%p size=%u", (buf), (buf_size)); \
- if (FAIL == mysqlnd_read_header((conn)->net, &((packet)->header), (conn)->stats, ((conn)->error_info))) {\
- CONN_SET_STATE(conn, CONN_QUIT_SENT); \
- SET_CLIENT_ERROR(*conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);\
- php_error_docref(NULL, E_WARNING, "%s", mysqlnd_server_gone); \
- DBG_ERR_FMT("Can't read %s's header", (packet_type_as_text)); \
- DBG_RETURN(FAIL);\
- }\
- if ((buf_size) < (packet)->header.size) { \
- DBG_ERR_FMT("Packet buffer %u wasn't big enough %u, %u bytes will be unread", \
- (buf_size), (packet)->header.size, (packet)->header.size - (buf_size)); \
- DBG_RETURN(FAIL); \
- }\
- if (FAIL == conn->net->data->m.receive_ex((conn)->net, (buf), (packet)->header.size, (conn)->stats, ((conn)->error_info))) { \
- CONN_SET_STATE(conn, CONN_QUIT_SENT); \
- SET_CLIENT_ERROR(*conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);\
- php_error_docref(NULL, E_WARNING, "%s", mysqlnd_server_gone); \
- DBG_ERR_FMT("Empty '%s' packet body", (packet_type_as_text)); \
- DBG_RETURN(FAIL);\
- } \
- MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn->stats, packet_type_to_statistic_byte_count[packet_type], \
- MYSQLND_HEADER_SIZE + (packet)->header.size, \
- packet_type_to_statistic_packet_count[packet_type], \
- 1); \
- }
-
#define BAIL_IF_NO_MORE_DATA \
if ((size_t)(p - begin) > packet->header.size) { \
@@ -126,9 +92,9 @@ static enum_mysqlnd_collected_stats packet_type_to_statistic_packet_count[PROT_L
/* {{{ php_mysqlnd_net_field_length
Get next field's length */
zend_ulong
-php_mysqlnd_net_field_length(zend_uchar **packet)
+php_mysqlnd_net_field_length(const zend_uchar **packet)
{
- register zend_uchar *p= (zend_uchar *)*packet;
+ register const zend_uchar *p= (const zend_uchar *)*packet;
if (*p < 251) {
(*packet)++;
@@ -156,9 +122,9 @@ php_mysqlnd_net_field_length(zend_uchar **packet)
/* {{{ php_mysqlnd_net_field_length_ll
Get next field's length */
uint64_t
-php_mysqlnd_net_field_length_ll(zend_uchar **packet)
+php_mysqlnd_net_field_length_ll(const zend_uchar **packet)
{
- register zend_uchar *p = (zend_uchar *)*packet;
+ register const zend_uchar *p = (zend_uchar *)*packet;
if (*p < 251) {
(*packet)++;
@@ -185,7 +151,7 @@ php_mysqlnd_net_field_length_ll(zend_uchar **packet)
/* {{{ php_mysqlnd_net_store_length */
zend_uchar *
-php_mysqlnd_net_store_length(zend_uchar *packet, uint64_t length)
+php_mysqlnd_net_store_length(zend_uchar *packet, const uint64_t length)
{
if (length < (uint64_t) L64(251)) {
*packet = (zend_uchar) length;
@@ -230,12 +196,12 @@ php_mysqlnd_net_store_length_size(uint64_t length)
/* {{{ php_mysqlnd_read_error_from_line */
static enum_func_status
-php_mysqlnd_read_error_from_line(zend_uchar *buf, size_t buf_len,
- char *error, int error_buf_len,
- unsigned int *error_no, char *sqlstate)
+php_mysqlnd_read_error_from_line(const zend_uchar * const buf, const size_t buf_len,
+ char *error, const size_t error_buf_len,
+ unsigned int *error_no, char *sqlstate)
{
- zend_uchar *p = buf;
- int error_msg_len= 0;
+ const zend_uchar *p = buf;
+ size_t error_msg_len = 0;
DBG_ENTER("php_mysqlnd_read_error_from_line");
@@ -274,64 +240,110 @@ end:
/* {{{ mysqlnd_read_header */
static enum_func_status
-mysqlnd_read_header(MYSQLND_NET * net, MYSQLND_PACKET_HEADER * header,
+mysqlnd_read_header(MYSQLND_PFC * pfc, MYSQLND_VIO * vio, MYSQLND_PACKET_HEADER * header,
MYSQLND_STATS * conn_stats, MYSQLND_ERROR_INFO * error_info)
{
zend_uchar buffer[MYSQLND_HEADER_SIZE];
DBG_ENTER(mysqlnd_read_header_name);
- DBG_INF_FMT("compressed=%u", net->data->compressed);
- if (FAIL == net->data->m.receive_ex(net, buffer, MYSQLND_HEADER_SIZE, conn_stats, error_info)) {
+ DBG_INF_FMT("compressed=%u", pfc->data->compressed);
+ if (FAIL == pfc->data->m.receive(pfc, vio, buffer, MYSQLND_HEADER_SIZE, conn_stats, error_info)) {
DBG_RETURN(FAIL);
}
header->size = uint3korr(buffer);
header->packet_no = uint1korr(buffer + 3);
-#ifdef MYSQLND_DUMP_HEADER_N_BODY
DBG_INF_FMT("HEADER: prot_packet_no=%u size=%3u", header->packet_no, header->size);
-#endif
MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn_stats,
STAT_PROTOCOL_OVERHEAD_IN, MYSQLND_HEADER_SIZE,
STAT_PACKETS_RECEIVED, 1);
- if (net->data->compressed || net->packet_no == header->packet_no) {
+ if (pfc->data->compressed || pfc->data->packet_no == header->packet_no) {
/*
Have to increase the number, so we can send correct number back. It will
round at 255 as this is unsigned char. The server needs this for simple
flow control checking.
*/
- net->packet_no++;
+ pfc->data->packet_no++;
DBG_RETURN(PASS);
}
DBG_ERR_FMT("Logical link: packets out of order. Expected %u received %u. Packet size="MYSQLND_SZ_T_SPEC,
- net->packet_no, header->packet_no, header->size);
+ pfc->data->packet_no, header->packet_no, header->size);
php_error(E_WARNING, "Packets out of order. Expected %u received %u. Packet size="MYSQLND_SZ_T_SPEC,
- net->packet_no, header->packet_no, header->size);
+ pfc->data->packet_no, header->packet_no, header->size);
DBG_RETURN(FAIL);
}
/* }}} */
+/* {{{ mysqlnd_read_packet_header_and_body */
+static enum_func_status
+mysqlnd_read_packet_header_and_body(MYSQLND_PACKET_HEADER * packet_header,
+ MYSQLND_PFC * pfc,
+ MYSQLND_VIO * vio,
+ MYSQLND_STATS * stats,
+ MYSQLND_ERROR_INFO * error_info,
+ MYSQLND_CONNECTION_STATE * connection_state,
+ zend_uchar * buf, size_t buf_size, const char * const packet_type_as_text,
+ enum mysqlnd_packet_type packet_type)
+{
+ DBG_ENTER("mysqlnd_read_packet_header_and_body");
+ DBG_INF_FMT("buf=%p size=%u", buf, buf_size);
+ if (FAIL == mysqlnd_read_header(pfc, vio, packet_header, stats, error_info)) {
+ SET_CONNECTION_STATE(connection_state, CONN_QUIT_SENT);
+ SET_CLIENT_ERROR(error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
+ php_error_docref(NULL, E_WARNING, "%s", mysqlnd_server_gone);
+ DBG_ERR_FMT("Can't read %s's header", packet_type_as_text);
+ DBG_RETURN(FAIL);
+ }
+ if (buf_size < packet_header->size) {
+ DBG_ERR_FMT("Packet buffer %u wasn't big enough %u, %u bytes will be unread",
+ buf_size, packet_header->size, packet_header->size - buf_size);
+ DBG_RETURN(FAIL);
+ }
+ if (FAIL == pfc->data->m.receive(pfc, vio, buf, packet_header->size, stats, error_info)) {
+ SET_CONNECTION_STATE(connection_state, CONN_QUIT_SENT);
+ SET_CLIENT_ERROR(error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
+ php_error_docref(NULL, E_WARNING, "%s", mysqlnd_server_gone);
+ DBG_ERR_FMT("Empty '%s' packet body", packet_type_as_text);
+ DBG_RETURN(FAIL);
+ }
+ MYSQLND_INC_CONN_STATISTIC_W_VALUE2(stats, packet_type_to_statistic_byte_count[packet_type],
+ MYSQLND_HEADER_SIZE + packet_header->size,
+ packet_type_to_statistic_packet_count[packet_type],
+ 1);
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
/* {{{ php_mysqlnd_greet_read */
static enum_func_status
-php_mysqlnd_greet_read(void * _packet, MYSQLND_CONN_DATA * conn)
+php_mysqlnd_greet_read(void * _packet)
{
zend_uchar buf[2048];
- zend_uchar *p = buf;
- zend_uchar *begin = buf;
- zend_uchar *pad_start = NULL;
+ const zend_uchar * p = buf;
+ const zend_uchar * const begin = buf;
+ const zend_uchar * pad_start = NULL;
MYSQLND_PACKET_GREET *packet= (MYSQLND_PACKET_GREET *) _packet;
+ MYSQLND_ERROR_INFO * error_info = packet->header.error_info;
+ MYSQLND_PFC * pfc = packet->header.protocol_frame_codec;
+ MYSQLND_VIO * vio = packet->header.vio;
+ MYSQLND_STATS * stats = packet->header.stats;
+ MYSQLND_CONNECTION_STATE * connection_state = packet->header.connection_state;
DBG_ENTER("php_mysqlnd_greet_read");
- PACKET_READ_HEADER_AND_BODY(packet, conn, buf, sizeof(buf), "greeting", PROT_GREET_PACKET);
+ if (FAIL == mysqlnd_read_packet_header_and_body(&(packet->header), pfc, vio, stats, error_info, connection_state, buf, sizeof(buf), "greeting", PROT_GREET_PACKET)) {
+ DBG_RETURN(FAIL);
+ }
BAIL_IF_NO_MORE_DATA;
- packet->auth_plugin_data = packet->intern_auth_plugin_data;
- packet->auth_plugin_data_len = sizeof(packet->intern_auth_plugin_data);
+ packet->authentication_plugin_data.s = packet->intern_auth_plugin_data;
+ packet->authentication_plugin_data.l = sizeof(packet->intern_auth_plugin_data);
if (packet->header.size < sizeof(buf)) {
/*
@@ -369,7 +381,7 @@ php_mysqlnd_greet_read(void * _packet, MYSQLND_CONN_DATA * conn)
p+=4;
BAIL_IF_NO_MORE_DATA;
- memcpy(packet->auth_plugin_data, p, SCRAMBLE_LENGTH_323);
+ memcpy(packet->authentication_plugin_data.s, p, SCRAMBLE_LENGTH_323);
p+= SCRAMBLE_LENGTH_323;
BAIL_IF_NO_MORE_DATA;
@@ -396,7 +408,7 @@ php_mysqlnd_greet_read(void * _packet, MYSQLND_CONN_DATA * conn)
if ((size_t) (p - buf) < packet->header.size) {
/* auth_plugin_data is split into two parts */
- memcpy(packet->auth_plugin_data + SCRAMBLE_LENGTH_323, p, SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323);
+ memcpy(packet->authentication_plugin_data.s + SCRAMBLE_LENGTH_323, p, SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323);
p+= SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323;
p++; /* 0x0 at the end of the scramble and thus last byte in the packet in 5.1 and previous */
} else {
@@ -411,19 +423,19 @@ php_mysqlnd_greet_read(void * _packet, MYSQLND_CONN_DATA * conn)
/* Additional 16 bits for server capabilities */
packet->server_capabilities |= uint2korr(pad_start) << 16;
/* And a length of the server scramble in one byte */
- packet->auth_plugin_data_len = uint1korr(pad_start + 2);
- if (packet->auth_plugin_data_len > SCRAMBLE_LENGTH) {
+ packet->authentication_plugin_data.l = uint1korr(pad_start + 2);
+ if (packet->authentication_plugin_data.l > SCRAMBLE_LENGTH) {
/* more data*/
- zend_uchar * new_auth_plugin_data = emalloc(packet->auth_plugin_data_len);
+ char * new_auth_plugin_data = emalloc(packet->authentication_plugin_data.l);
if (!new_auth_plugin_data) {
goto premature_end;
}
/* copy what we already have */
- memcpy(new_auth_plugin_data, packet->auth_plugin_data, SCRAMBLE_LENGTH);
+ memcpy(new_auth_plugin_data, packet->authentication_plugin_data.s, SCRAMBLE_LENGTH);
/* add additional scramble data 5.5+ sent us */
- memcpy(new_auth_plugin_data + SCRAMBLE_LENGTH, p, packet->auth_plugin_data_len - SCRAMBLE_LENGTH);
- p+= (packet->auth_plugin_data_len - SCRAMBLE_LENGTH);
- packet->auth_plugin_data = new_auth_plugin_data;
+ memcpy(new_auth_plugin_data + SCRAMBLE_LENGTH, p, packet->authentication_plugin_data.l - SCRAMBLE_LENGTH);
+ p+= (packet->authentication_plugin_data.l - SCRAMBLE_LENGTH);
+ packet->authentication_plugin_data.s = new_auth_plugin_data;
}
}
@@ -439,7 +451,7 @@ php_mysqlnd_greet_read(void * _packet, MYSQLND_CONN_DATA * conn)
DBG_INF_FMT("server_capabilities=%u charset_no=%u server_status=%i auth_protocol=%s scramble_length=%u",
packet->server_capabilities, packet->charset_no, packet->server_status,
- packet->auth_protocol? packet->auth_protocol:"n/a", packet->auth_plugin_data_len);
+ packet->auth_protocol? packet->auth_protocol:"n/a", packet->authentication_plugin_data.l);
DBG_RETURN(PASS);
premature_end:
@@ -460,9 +472,9 @@ void php_mysqlnd_greet_free_mem(void * _packet, zend_bool stack_allocation)
efree(p->server_version);
p->server_version = NULL;
}
- if (p->auth_plugin_data && p->auth_plugin_data != p->intern_auth_plugin_data) {
- efree(p->auth_plugin_data);
- p->auth_plugin_data = NULL;
+ if (p->authentication_plugin_data.s && p->authentication_plugin_data.s != p->intern_auth_plugin_data) {
+ efree(p->authentication_plugin_data.s);
+ p->authentication_plugin_data.s = NULL;
}
if (p->auth_protocol) {
efree(p->auth_protocol);
@@ -479,12 +491,18 @@ void php_mysqlnd_greet_free_mem(void * _packet, zend_bool stack_allocation)
/* {{{ php_mysqlnd_auth_write */
static
-size_t php_mysqlnd_auth_write(void * _packet, MYSQLND_CONN_DATA * conn)
+size_t php_mysqlnd_auth_write(void * _packet)
{
zend_uchar buffer[AUTH_WRITE_BUFFER_LEN];
zend_uchar *p = buffer + MYSQLND_HEADER_SIZE; /* start after the header */
- int len;
+ size_t len;
MYSQLND_PACKET_AUTH * packet= (MYSQLND_PACKET_AUTH *) _packet;
+ MYSQLND_CONN_DATA * conn = packet->header.conn;
+ MYSQLND_ERROR_INFO * error_info = packet->header.error_info;
+ MYSQLND_PFC * pfc = packet->header.protocol_frame_codec;
+ MYSQLND_VIO * vio = packet->header.vio;
+ MYSQLND_STATS * stats = packet->header.stats;
+ MYSQLND_CONNECTION_STATE * connection_state = packet->header.connection_state;
DBG_ENTER("php_mysqlnd_auth_write");
@@ -515,7 +533,7 @@ size_t php_mysqlnd_auth_write(void * _packet, MYSQLND_CONN_DATA * conn)
if (packet->auth_data_len > 0xFF) {
const char * const msg = "Authentication data too long. "
"Won't fit into the buffer and will be truncated. Authentication will thus fail";
- SET_CLIENT_ERROR(*conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, msg);
+ SET_CLIENT_ERROR(error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, msg);
php_error_docref(NULL, E_WARNING, "%s", msg);
DBG_RETURN(0);
}
@@ -551,7 +569,7 @@ size_t php_mysqlnd_auth_write(void * _packet, MYSQLND_CONN_DATA * conn)
}
if (packet->auth_plugin_name) {
- size_t len = MIN(strlen(packet->auth_plugin_name), sizeof(buffer) - (p - buffer) - 1);
+ len = MIN(strlen(packet->auth_plugin_name), sizeof(buffer) - (p - buffer) - 1);
memcpy(p, packet->auth_plugin_name, len);
p+= len;
*p++= '\0';
@@ -559,25 +577,6 @@ size_t php_mysqlnd_auth_write(void * _packet, MYSQLND_CONN_DATA * conn)
if (packet->connect_attr && zend_hash_num_elements(packet->connect_attr)) {
size_t ca_payload_len = 0;
-#ifdef OLD_CODE
- HashPosition pos_value;
- const char ** entry_value;
- zend_hash_internal_pointer_reset_ex(packet->connect_attr, &pos_value);
- while (SUCCESS == zend_hash_get_current_data_ex(packet->connect_attr, (void **)&entry_value, &pos_value)) {
- char *s_key;
- unsigned int s_len;
- zend_ulong num_key;
- size_t value_len = strlen(*entry_value);
-
- if (HASH_KEY_IS_STRING == zend_hash_get_current_key_ex(packet->connect_attr, &s_key, &s_len, &num_key, &pos_value)) {
- ca_payload_len += php_mysqlnd_net_store_length_size(s_len);
- ca_payload_len += s_len;
- ca_payload_len += php_mysqlnd_net_store_length_size(value_len);
- ca_payload_len += value_len;
- }
- zend_hash_move_forward_ex(conn->options->connect_attr, &pos_value);
- }
-#else
{
zend_string * key;
@@ -593,30 +592,10 @@ size_t php_mysqlnd_auth_write(void * _packet, MYSQLND_CONN_DATA * conn)
}
} ZEND_HASH_FOREACH_END();
}
-#endif
+
if (sizeof(buffer) >= (ca_payload_len + php_mysqlnd_net_store_length_size(ca_payload_len) + (p - buffer))) {
p = php_mysqlnd_net_store_length(p, ca_payload_len);
-#ifdef OLD_CODE
- zend_hash_internal_pointer_reset_ex(packet->connect_attr, &pos_value);
- while (SUCCESS == zend_hash_get_current_data_ex(packet->connect_attr, (void **)&entry_value, &pos_value)) {
- char *s_key;
- unsigned int s_len;
- zend_ulong num_key;
- size_t value_len = strlen(*entry_value);
- if (HASH_KEY_IS_STRING == zend_hash_get_current_key_ex(packet->connect_attr, &s_key, &s_len, &num_key, &pos_value)) {
- /* copy key */
- p = php_mysqlnd_net_store_length(p, s_len);
- memcpy(p, s_key, s_len);
- p+= s_len;
- /* copy value */
- p = php_mysqlnd_net_store_length(p, value_len);
- memcpy(p, *entry_value, value_len);
- p+= value_len;
- }
- zend_hash_move_forward_ex(conn->options->connect_attr, &pos_value);
- }
-#else
{
zend_string * key;
zval * entry_value;
@@ -635,23 +614,26 @@ size_t php_mysqlnd_auth_write(void * _packet, MYSQLND_CONN_DATA * conn)
}
} ZEND_HASH_FOREACH_END();
}
-#endif
} else {
/* cannot put the data - skip */
}
}
}
if (packet->is_change_user_packet) {
- if (PASS != conn->m->simple_command(conn, COM_CHANGE_USER, buffer + MYSQLND_HEADER_SIZE, p - buffer - MYSQLND_HEADER_SIZE,
- PROT_LAST /* the caller will handle the OK packet */,
- packet->silent, TRUE)) {
- DBG_RETURN(0);
+ enum_func_status ret = FAIL;
+ const MYSQLND_CSTRING payload = {(char*) buffer + MYSQLND_HEADER_SIZE, p - (buffer + MYSQLND_HEADER_SIZE)};
+ const unsigned int silent = packet->silent;
+ struct st_mysqlnd_protocol_command * command = conn->command_factory(COM_CHANGE_USER, conn, payload, silent);
+ if (command) {
+ ret = command->run(command);
+ command->free_command(command);
}
- DBG_RETURN(p - buffer - MYSQLND_HEADER_SIZE);
+
+ DBG_RETURN(ret == PASS? (p - buffer - MYSQLND_HEADER_SIZE) : 0);
} else {
- size_t sent = conn->net->data->m.send_ex(conn->net, buffer, p - buffer - MYSQLND_HEADER_SIZE, conn->stats, conn->error_info);
+ size_t sent = pfc->data->m.send(pfc, vio, buffer, p - buffer - MYSQLND_HEADER_SIZE, stats, error_info);
if (!sent) {
- CONN_SET_STATE(conn, CONN_QUIT_SENT);
+ SET_CONNECTION_STATE(connection_state, CONN_QUIT_SENT);
}
DBG_RETURN(sent);
}
@@ -675,21 +657,27 @@ void php_mysqlnd_auth_free_mem(void * _packet, zend_bool stack_allocation)
/* {{{ php_mysqlnd_auth_response_read */
static enum_func_status
-php_mysqlnd_auth_response_read(void * _packet, MYSQLND_CONN_DATA * conn)
+php_mysqlnd_auth_response_read(void * _packet)
{
- zend_uchar local_buf[AUTH_RESP_BUFFER_SIZE];
- size_t buf_len = conn->net->cmd_buffer.buffer? conn->net->cmd_buffer.length: AUTH_RESP_BUFFER_SIZE;
- zend_uchar *buf = conn->net->cmd_buffer.buffer? (zend_uchar *) conn->net->cmd_buffer.buffer : local_buf;
- zend_uchar *p = buf;
- zend_uchar *begin = buf;
- zend_ulong i;
register MYSQLND_PACKET_AUTH_RESPONSE * packet= (MYSQLND_PACKET_AUTH_RESPONSE *) _packet;
+ MYSQLND_ERROR_INFO * error_info = packet->header.error_info;
+ MYSQLND_PFC * pfc = packet->header.protocol_frame_codec;
+ MYSQLND_VIO * vio = packet->header.vio;
+ MYSQLND_STATS * stats = packet->header.stats;
+ MYSQLND_CONNECTION_STATE * connection_state = packet->header.connection_state;
+ zend_uchar local_buf[AUTH_RESP_BUFFER_SIZE];
+ size_t buf_len = pfc->cmd_buffer.buffer? pfc->cmd_buffer.length: AUTH_RESP_BUFFER_SIZE;
+ zend_uchar *buf = pfc->cmd_buffer.buffer? (zend_uchar *) pfc->cmd_buffer.buffer : local_buf;
+ const zend_uchar * p = buf;
+ const zend_uchar * const begin = buf;
DBG_ENTER("php_mysqlnd_auth_response_read");
/* leave space for terminating safety \0 */
buf_len--;
- PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "OK", PROT_OK_PACKET);
+ if (FAIL == mysqlnd_read_packet_header_and_body(&(packet->header), pfc, vio, stats, error_info, connection_state, buf, buf_len, "OK", PROT_OK_PACKET)) {
+ DBG_RETURN(FAIL);
+ }
BAIL_IF_NO_MORE_DATA;
/*
@@ -726,6 +714,7 @@ php_mysqlnd_auth_response_read(void * _packet, MYSQLND_CONN_DATA * conn)
DBG_INF_FMT("Server salt : [%d][%.*s]", packet->new_auth_protocol_data_len, packet->new_auth_protocol_data_len, packet->new_auth_protocol_data);
}
} else {
+ zend_ulong net_len;
/* Everything was fine! */
packet->affected_rows = php_mysqlnd_net_field_length_ll(&p);
BAIL_IF_NO_MORE_DATA;
@@ -742,8 +731,8 @@ php_mysqlnd_auth_response_read(void * _packet, MYSQLND_CONN_DATA * conn)
BAIL_IF_NO_MORE_DATA;
/* There is a message */
- if (packet->header.size > (size_t) (p - buf) && (i = php_mysqlnd_net_field_length(&p))) {
- packet->message_len = MIN(i, buf_len - (p - begin));
+ if (packet->header.size > (size_t) (p - buf) && (net_len = php_mysqlnd_net_field_length(&p))) {
+ packet->message_len = MIN(net_len, buf_len - (p - begin));
packet->message = mnd_pestrndup((char *)p, packet->message_len, FALSE);
} else {
packet->message = NULL;
@@ -795,11 +784,16 @@ php_mysqlnd_auth_response_free_mem(void * _packet, zend_bool stack_allocation)
/* {{{ php_mysqlnd_change_auth_response_write */
static size_t
-php_mysqlnd_change_auth_response_write(void * _packet, MYSQLND_CONN_DATA * conn)
+php_mysqlnd_change_auth_response_write(void * _packet)
{
MYSQLND_PACKET_CHANGE_AUTH_RESPONSE *packet= (MYSQLND_PACKET_CHANGE_AUTH_RESPONSE *) _packet;
- zend_uchar * buffer = conn->net->cmd_buffer.length >= packet->auth_data_len? conn->net->cmd_buffer.buffer : mnd_emalloc(packet->auth_data_len);
- zend_uchar *p = buffer + MYSQLND_HEADER_SIZE; /* start after the header */
+ MYSQLND_ERROR_INFO * error_info = packet->header.error_info;
+ MYSQLND_PFC * pfc = packet->header.protocol_frame_codec;
+ MYSQLND_VIO * vio = packet->header.vio;
+ MYSQLND_STATS * stats = packet->header.stats;
+ MYSQLND_CONNECTION_STATE * connection_state = packet->header.connection_state;
+ zend_uchar * buffer = pfc->cmd_buffer.length >= packet->auth_data_len? pfc->cmd_buffer.buffer : mnd_emalloc(packet->auth_data_len);
+ zend_uchar * p = buffer + MYSQLND_HEADER_SIZE; /* start after the header */
DBG_ENTER("php_mysqlnd_change_auth_response_write");
@@ -809,12 +803,12 @@ php_mysqlnd_change_auth_response_write(void * _packet, MYSQLND_CONN_DATA * conn)
}
{
- size_t sent = conn->net->data->m.send_ex(conn->net, buffer, p - buffer - MYSQLND_HEADER_SIZE, conn->stats, conn->error_info);
- if (buffer != conn->net->cmd_buffer.buffer) {
+ size_t sent = pfc->data->m.send(pfc, vio, buffer, p - buffer - MYSQLND_HEADER_SIZE, stats, error_info);
+ if (buffer != pfc->cmd_buffer.buffer) {
mnd_efree(buffer);
}
if (!sent) {
- CONN_SET_STATE(conn, CONN_QUIT_SENT);
+ SET_CONNECTION_STATE(connection_state, CONN_QUIT_SENT);
}
DBG_RETURN(sent);
}
@@ -838,19 +832,26 @@ php_mysqlnd_change_auth_response_free_mem(void * _packet, zend_bool stack_alloca
/* {{{ php_mysqlnd_ok_read */
static enum_func_status
-php_mysqlnd_ok_read(void * _packet, MYSQLND_CONN_DATA * conn)
+php_mysqlnd_ok_read(void * _packet)
{
- zend_uchar local_buf[OK_BUFFER_SIZE];
- size_t buf_len = conn->net->cmd_buffer.buffer? conn->net->cmd_buffer.length : OK_BUFFER_SIZE;
- zend_uchar *buf = conn->net->cmd_buffer.buffer? (zend_uchar *) conn->net->cmd_buffer.buffer : local_buf;
- zend_uchar *p = buf;
- zend_uchar *begin = buf;
- zend_ulong i;
register MYSQLND_PACKET_OK *packet= (MYSQLND_PACKET_OK *) _packet;
+ MYSQLND_ERROR_INFO * error_info = packet->header.error_info;
+ MYSQLND_PFC * pfc = packet->header.protocol_frame_codec;
+ MYSQLND_VIO * vio = packet->header.vio;
+ MYSQLND_STATS * stats = packet->header.stats;
+ MYSQLND_CONNECTION_STATE * connection_state = packet->header.connection_state;
+ zend_uchar local_buf[OK_BUFFER_SIZE];
+ size_t buf_len = pfc->cmd_buffer.buffer? pfc->cmd_buffer.length : OK_BUFFER_SIZE;
+ zend_uchar * buf = pfc->cmd_buffer.buffer? (zend_uchar *) pfc->cmd_buffer.buffer : local_buf;
+ const zend_uchar * p = buf;
+ const zend_uchar * const begin = buf;
+ zend_ulong net_len;
DBG_ENTER("php_mysqlnd_ok_read");
- PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "OK", PROT_OK_PACKET);
+ if (FAIL == mysqlnd_read_packet_header_and_body(&(packet->header), pfc, vio, stats, error_info, connection_state, buf, buf_len, "OK", PROT_OK_PACKET)) {
+ DBG_RETURN(FAIL);
+ }
BAIL_IF_NO_MORE_DATA;
/* Should be always 0x0 or ERROR_MARKER for error */
@@ -863,7 +864,6 @@ php_mysqlnd_ok_read(void * _packet, MYSQLND_CONN_DATA * conn)
packet->error, sizeof(packet->error),
&packet->error_no, packet->sqlstate
);
- DBG_INF_FMT("conn->server_status=%u", conn->upsert_status->server_status);
DBG_RETURN(PASS);
}
/* Everything was fine! */
@@ -882,8 +882,8 @@ php_mysqlnd_ok_read(void * _packet, MYSQLND_CONN_DATA * conn)
BAIL_IF_NO_MORE_DATA;
/* There is a message */
- if (packet->header.size > (size_t) (p - buf) && (i = php_mysqlnd_net_field_length(&p))) {
- packet->message_len = MIN(i, buf_len - (p - begin));
+ if (packet->header.size > (size_t) (p - buf) && (net_len = php_mysqlnd_net_field_length(&p))) {
+ packet->message_len = MIN(net_len, buf_len - (p - begin));
packet->message = mnd_pestrndup((char *)p, packet->message_len, FALSE);
} else {
packet->message = NULL;
@@ -924,7 +924,7 @@ php_mysqlnd_ok_free_mem(void * _packet, zend_bool stack_allocation)
/* {{{ php_mysqlnd_eof_read */
static enum_func_status
-php_mysqlnd_eof_read(void * _packet, MYSQLND_CONN_DATA * conn)
+php_mysqlnd_eof_read(void * _packet)
{
/*
EOF packet is since 4.1 five bytes long,
@@ -933,14 +933,21 @@ php_mysqlnd_eof_read(void * _packet, MYSQLND_CONN_DATA * conn)
Error : error_code + '#' + sqlstate + MYSQLND_ERRMSG_SIZE
*/
MYSQLND_PACKET_EOF *packet= (MYSQLND_PACKET_EOF *) _packet;
- size_t buf_len = conn->net->cmd_buffer.length;
- zend_uchar *buf = (zend_uchar *) conn->net->cmd_buffer.buffer;
- zend_uchar *p = buf;
- zend_uchar *begin = buf;
+ MYSQLND_ERROR_INFO * error_info = packet->header.error_info;
+ MYSQLND_PFC * pfc = packet->header.protocol_frame_codec;
+ MYSQLND_VIO * vio = packet->header.vio;
+ MYSQLND_STATS * stats = packet->header.stats;
+ MYSQLND_CONNECTION_STATE * connection_state = packet->header.connection_state;
+ size_t buf_len = pfc->cmd_buffer.length;
+ zend_uchar * buf = (zend_uchar *) pfc->cmd_buffer.buffer;
+ const zend_uchar * p = buf;
+ const zend_uchar * const begin = buf;
DBG_ENTER("php_mysqlnd_eof_read");
- PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "EOF", PROT_EOF_PACKET);
+ if (FAIL == mysqlnd_read_packet_header_and_body(&(packet->header), pfc, vio, stats, error_info, connection_state, buf, buf_len, "EOF", PROT_EOF_PACKET)) {
+ DBG_RETURN(FAIL);
+ }
BAIL_IF_NO_MORE_DATA;
/* Should be always EODATA_MARKER */
@@ -1001,11 +1008,15 @@ void php_mysqlnd_eof_free_mem(void * _packet, zend_bool stack_allocation)
/* {{{ php_mysqlnd_cmd_write */
-size_t php_mysqlnd_cmd_write(void * _packet, MYSQLND_CONN_DATA * conn)
+size_t php_mysqlnd_cmd_write(void * _packet)
{
/* Let's have some space, which we can use, if not enough, we will allocate new buffer */
MYSQLND_PACKET_COMMAND * packet= (MYSQLND_PACKET_COMMAND *) _packet;
- MYSQLND_NET * net = conn->net;
+ MYSQLND_ERROR_INFO * error_info = packet->header.error_info;
+ MYSQLND_PFC * pfc = packet->header.protocol_frame_codec;
+ MYSQLND_VIO * vio = packet->header.vio;
+ MYSQLND_STATS * stats = packet->header.stats;
+ MYSQLND_CONNECTION_STATE * connection_state = packet->header.connection_state;
unsigned int error_reporting = EG(error_reporting);
size_t sent = 0;
@@ -1014,28 +1025,27 @@ size_t php_mysqlnd_cmd_write(void * _packet, MYSQLND_CONN_DATA * conn)
Reset packet_no, or we will get bad handshake!
Every command starts a new TX and packet numbers are reset to 0.
*/
- net->packet_no = 0;
- net->compressed_envelope_packet_no = 0; /* this is for the response */
+ pfc->data->m.reset(pfc, stats, error_info);
if (error_reporting) {
EG(error_reporting) = 0;
}
- MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_PACKETS_SENT_CMD);
+ MYSQLND_INC_CONN_STATISTIC(stats, STAT_PACKETS_SENT_CMD);
#ifdef MYSQLND_DO_WIRE_CHECK_BEFORE_COMMAND
- net->data->m.consume_uneaten_data(net, packet->command);
+ vio->data->m.consume_uneaten_data(vio, packet->command);
#endif
- if (!packet->argument || !packet->arg_len) {
+ if (!packet->argument.s || !packet->argument.l) {
zend_uchar buffer[MYSQLND_HEADER_SIZE + 1];
int1store(buffer + MYSQLND_HEADER_SIZE, packet->command);
- sent = net->data->m.send_ex(net, buffer, 1, conn->stats, conn->error_info);
+ sent = pfc->data->m.send(pfc, vio, buffer, 1, stats, error_info);
} else {
- size_t tmp_len = packet->arg_len + 1 + MYSQLND_HEADER_SIZE;
+ size_t tmp_len = packet->argument.l + 1 + MYSQLND_HEADER_SIZE;
zend_uchar *tmp, *p;
- tmp = (tmp_len > net->cmd_buffer.length)? mnd_emalloc(tmp_len):net->cmd_buffer.buffer;
+ tmp = (tmp_len > pfc->cmd_buffer.length)? mnd_emalloc(tmp_len):pfc->cmd_buffer.buffer;
if (!tmp) {
goto end;
}
@@ -1044,11 +1054,11 @@ size_t php_mysqlnd_cmd_write(void * _packet, MYSQLND_CONN_DATA * conn)
int1store(p, packet->command);
p++;
- memcpy(p, packet->argument, packet->arg_len);
+ memcpy(p, packet->argument.s, packet->argument.l);
- sent = net->data->m.send_ex(net, tmp, tmp_len - MYSQLND_HEADER_SIZE, conn->stats, conn->error_info);
- if (tmp != net->cmd_buffer.buffer) {
- MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_CMD_BUFFER_TOO_SMALL);
+ sent = pfc->data->m.send(pfc, vio, tmp, tmp_len - MYSQLND_HEADER_SIZE, stats, error_info);
+ if (tmp != pfc->cmd_buffer.buffer) {
+ MYSQLND_INC_CONN_STATISTIC(stats, STAT_CMD_BUFFER_TOO_SMALL);
mnd_efree(tmp);
}
}
@@ -1058,7 +1068,7 @@ end:
EG(error_reporting) = error_reporting;
}
if (!sent) {
- CONN_SET_STATE(conn, CONN_QUIT_SENT);
+ SET_CONNECTION_STATE(connection_state, CONN_QUIT_SENT);
}
DBG_RETURN(sent);
}
@@ -1079,19 +1089,26 @@ void php_mysqlnd_cmd_free_mem(void * _packet, zend_bool stack_allocation)
/* {{{ php_mysqlnd_rset_header_read */
static enum_func_status
-php_mysqlnd_rset_header_read(void * _packet, MYSQLND_CONN_DATA * conn)
+php_mysqlnd_rset_header_read(void * _packet)
{
+ MYSQLND_PACKET_RSET_HEADER * packet= (MYSQLND_PACKET_RSET_HEADER *) _packet;
+ MYSQLND_ERROR_INFO * error_info = packet->header.error_info;
+ MYSQLND_PFC * pfc = packet->header.protocol_frame_codec;
+ MYSQLND_VIO * vio = packet->header.vio;
+ MYSQLND_STATS * stats = packet->header.stats;
+ MYSQLND_CONNECTION_STATE * connection_state = packet->header.connection_state;
enum_func_status ret = PASS;
- size_t buf_len = conn->net->cmd_buffer.length;
- zend_uchar *buf = (zend_uchar *) conn->net->cmd_buffer.buffer;
- zend_uchar *p = buf;
- zend_uchar *begin = buf;
+ size_t buf_len = pfc->cmd_buffer.length;
+ zend_uchar * buf = (zend_uchar *) pfc->cmd_buffer.buffer;
+ const zend_uchar * p = buf;
+ const zend_uchar * const begin = buf;
size_t len;
- MYSQLND_PACKET_RSET_HEADER *packet= (MYSQLND_PACKET_RSET_HEADER *) _packet;
DBG_ENTER("php_mysqlnd_rset_header_read");
- PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "resultset header", PROT_RSET_HEADER_PACKET);
+ if (FAIL == mysqlnd_read_packet_header_and_body(&(packet->header), pfc, vio, stats, error_info, connection_state, buf, buf_len, "resultset header", PROT_RSET_HEADER_PACKET)) {
+ DBG_RETURN(FAIL);
+ }
BAIL_IF_NO_MORE_DATA;
/*
@@ -1106,7 +1123,6 @@ php_mysqlnd_rset_header_read(void * _packet, MYSQLND_CONN_DATA * conn)
packet->error_info.error, sizeof(packet->error_info.error),
&packet->error_info.error_no, packet->error_info.sqlstate
);
- DBG_INF_FMT("conn->server_status=%u", conn->upsert_status->server_status);
DBG_RETURN(PASS);
}
@@ -1124,13 +1140,13 @@ php_mysqlnd_rset_header_read(void * _packet, MYSQLND_CONN_DATA * conn)
would lead to 0 byte allocation but 2^32 or 2^64 bytes copied.
*/
len = packet->header.size - 1;
- packet->info_or_local_file = mnd_emalloc(len + 1);
- if (packet->info_or_local_file) {
- memcpy(packet->info_or_local_file, p, len);
- packet->info_or_local_file[len] = '\0';
- packet->info_or_local_file_len = len;
+ packet->info_or_local_file.s = mnd_emalloc(len + 1);
+ if (packet->info_or_local_file.s) {
+ memcpy(packet->info_or_local_file.s, p, len);
+ packet->info_or_local_file.s[len] = '\0';
+ packet->info_or_local_file.l = len;
} else {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(error_info);
ret = FAIL;
}
break;
@@ -1151,13 +1167,13 @@ php_mysqlnd_rset_header_read(void * _packet, MYSQLND_CONN_DATA * conn)
BAIL_IF_NO_MORE_DATA;
/* Check for additional textual data */
if (packet->header.size > (size_t) (p - buf) && (len = php_mysqlnd_net_field_length(&p))) {
- packet->info_or_local_file = mnd_emalloc(len + 1);
- if (packet->info_or_local_file) {
- memcpy(packet->info_or_local_file, p, len);
- packet->info_or_local_file[len] = '\0';
- packet->info_or_local_file_len = len;
+ packet->info_or_local_file.s = mnd_emalloc(len + 1);
+ if (packet->info_or_local_file.s) {
+ memcpy(packet->info_or_local_file.s, p, len);
+ packet->info_or_local_file.s[len] = '\0';
+ packet->info_or_local_file.l = len;
} else {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(error_info);
ret = FAIL;
}
}
@@ -1188,9 +1204,9 @@ void php_mysqlnd_rset_header_free_mem(void * _packet, zend_bool stack_allocation
{
MYSQLND_PACKET_RSET_HEADER *p= (MYSQLND_PACKET_RSET_HEADER *) _packet;
DBG_ENTER("php_mysqlnd_rset_header_free_mem");
- if (p->info_or_local_file) {
- mnd_efree(p->info_or_local_file);
- p->info_or_local_file = NULL;
+ if (p->info_or_local_file.s) {
+ mnd_efree(p->info_or_local_file.s);
+ p->info_or_local_file.s = NULL;
}
if (!stack_allocation) {
mnd_pefree(p, p->header.persistent);
@@ -1218,14 +1234,19 @@ static size_t rset_field_offsets[] =
/* {{{ php_mysqlnd_rset_field_read */
static enum_func_status
-php_mysqlnd_rset_field_read(void * _packet, MYSQLND_CONN_DATA * conn)
+php_mysqlnd_rset_field_read(void * _packet)
{
/* Should be enough for the metadata of a single row */
MYSQLND_PACKET_RES_FIELD *packet = (MYSQLND_PACKET_RES_FIELD *) _packet;
- size_t buf_len = conn->net->cmd_buffer.length, total_len = 0;
- zend_uchar *buf = (zend_uchar *) conn->net->cmd_buffer.buffer;
- zend_uchar *p = buf;
- zend_uchar *begin = buf;
+ MYSQLND_ERROR_INFO * error_info = packet->header.error_info;
+ MYSQLND_PFC * pfc = packet->header.protocol_frame_codec;
+ MYSQLND_VIO * vio = packet->header.vio;
+ MYSQLND_STATS * stats = packet->header.stats;
+ MYSQLND_CONNECTION_STATE * connection_state = packet->header.connection_state;
+ size_t buf_len = pfc->cmd_buffer.length, total_len = 0;
+ zend_uchar * buf = (zend_uchar *) pfc->cmd_buffer.buffer;
+ const zend_uchar * p = buf;
+ const zend_uchar * const begin = buf;
char *root_ptr;
zend_ulong len;
MYSQLND_FIELD *meta;
@@ -1233,7 +1254,9 @@ php_mysqlnd_rset_field_read(void * _packet, MYSQLND_CONN_DATA * conn)
DBG_ENTER("php_mysqlnd_rset_field_read");
- PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "field", PROT_RSET_FLD_PACKET);
+ if (FAIL == mysqlnd_read_packet_header_and_body(&(packet->header), pfc, vio, stats, error_info, connection_state, buf, buf_len, "field", PROT_RSET_FLD_PACKET)) {
+ DBG_RETURN(FAIL);
+ }
if (packet->skip_parsing) {
DBG_RETURN(PASS);
@@ -1251,9 +1274,8 @@ php_mysqlnd_rset_field_read(void * _packet, MYSQLND_CONN_DATA * conn)
DBG_ERR_FMT("Server error : (%u) %s", packet->error_info.error_no, packet->error_info.error);
DBG_RETURN(PASS);
} else if (EODATA_MARKER == *p && packet->header.size < 8) {
- /* Premature EOF. That should be COM_FIELD_LIST */
+ /* Premature EOF. That should be COM_FIELD_LIST. But we don't support COM_FIELD_LIST anymore, thus this should not happen */
DBG_INF("Premature EOF. That should be COM_FIELD_LIST");
- packet->stupid_list_fields_eof = TRUE;
DBG_RETURN(PASS);
}
@@ -1335,7 +1357,7 @@ php_mysqlnd_rset_field_read(void * _packet, MYSQLND_CONN_DATA * conn)
DBG_INF_FMT("Def found, length %lu, persistent=%u", len, packet->persistent_alloc);
meta->def = mnd_pemalloc(len + 1, packet->persistent_alloc);
if (!meta->def) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(error_info);
DBG_RETURN(FAIL);
}
memcpy(meta->def, p, len);
@@ -1346,7 +1368,7 @@ php_mysqlnd_rset_field_read(void * _packet, MYSQLND_CONN_DATA * conn)
root_ptr = meta->root = mnd_pemalloc(total_len, packet->persistent_alloc);
if (!root_ptr) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(error_info);
DBG_RETURN(FAIL);
}
@@ -1432,35 +1454,39 @@ void php_mysqlnd_rset_field_free_mem(void * _packet, zend_bool stack_allocation)
/* {{{ php_mysqlnd_read_row_ex */
static enum_func_status
-php_mysqlnd_read_row_ex(MYSQLND_CONN_DATA * conn, MYSQLND_MEMORY_POOL * result_set_memory_pool,
+php_mysqlnd_read_row_ex(MYSQLND_PFC * pfc,
+ MYSQLND_VIO * vio,
+ MYSQLND_STATS * stats,
+ MYSQLND_ERROR_INFO * error_info,
+ MYSQLND_MEMORY_POOL * pool,
MYSQLND_MEMORY_POOL_CHUNK ** buffer,
- size_t * data_size, zend_bool persistent_alloc,
- unsigned int prealloc_more_bytes)
+ size_t * data_size, zend_bool persistent_alloc)
{
enum_func_status ret = PASS;
MYSQLND_PACKET_HEADER header;
zend_uchar * p = NULL;
zend_bool first_iteration = TRUE;
+ size_t prealloc_more_bytes;
DBG_ENTER("php_mysqlnd_read_row_ex");
/*
- * We're allocating 1 extra byte, as php_mysqlnd_rowp_read_text_protocol_aux
- * needs to be able to add a terminating \0 for atoi/atof.
- */
- prealloc_more_bytes++;
-
- /*
To ease the process the server splits everything in packets up to 2^24 - 1.
Even in the case the payload is evenly divisible by this value, the last
packet will be empty, namely 0 bytes. Thus, we can read every packet and ask
for next one if they have 2^24 - 1 sizes. But just read the header of a
zero-length byte, don't read the body, there is no such.
*/
+
+ /*
+ We're allocating an extra byte, as php_mysqlnd_rowp_read_text_protocol_aux
+ needs to be able to append a terminating \0 for atoi/atof.
+ */
+ prealloc_more_bytes = 1;
*data_size = 0;
while (1) {
- if (FAIL == mysqlnd_read_header(conn->net, &header, conn->stats, conn->error_info)) {
+ if (FAIL == mysqlnd_read_header(pfc, vio, &header, stats, error_info)) {
ret = FAIL;
break;
}
@@ -1469,8 +1495,7 @@ php_mysqlnd_read_row_ex(MYSQLND_CONN_DATA * conn, MYSQLND_MEMORY_POOL * result_s
if (first_iteration) {
first_iteration = FALSE;
- *buffer = result_set_memory_pool->get_chunk(
- result_set_memory_pool, *data_size + prealloc_more_bytes);
+ *buffer = pool->get_chunk(pool, *data_size + prealloc_more_bytes);
if (!*buffer) {
ret = FAIL;
break;
@@ -1485,8 +1510,8 @@ php_mysqlnd_read_row_ex(MYSQLND_CONN_DATA * conn, MYSQLND_MEMORY_POOL * result_s
/*
We have to realloc the buffer.
*/
- if (FAIL == (*buffer)->resize_chunk((*buffer), *data_size + prealloc_more_bytes)) {
- SET_OOM_ERROR(*conn->error_info);
+ if (FAIL == pool->resize_chunk(pool, *buffer, *data_size + prealloc_more_bytes)) {
+ SET_OOM_ERROR(error_info);
ret = FAIL;
break;
}
@@ -1494,7 +1519,7 @@ php_mysqlnd_read_row_ex(MYSQLND_CONN_DATA * conn, MYSQLND_MEMORY_POOL * result_s
p = (*buffer)->ptr + (*data_size - header.size);
}
- if (PASS != (ret = conn->net->data->m.receive_ex(conn->net, p, header.size, conn->stats, conn->error_info))) {
+ if (PASS != (ret = pfc->data->m.receive(pfc, vio, p, header.size, stats, error_info))) {
DBG_ERR("Empty row packet body");
php_error(E_WARNING, "Empty row packet body");
break;
@@ -1505,7 +1530,7 @@ php_mysqlnd_read_row_ex(MYSQLND_CONN_DATA * conn, MYSQLND_MEMORY_POOL * result_s
}
}
if (ret == FAIL && *buffer) {
- (*buffer)->free_chunk((*buffer));
+ pool->free_chunk(pool, *buffer);
*buffer = NULL;
}
DBG_RETURN(ret);
@@ -1520,8 +1545,9 @@ php_mysqlnd_rowp_read_binary_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zv
zend_bool as_int_or_float, MYSQLND_STATS * stats)
{
unsigned int i;
- zend_uchar *p = row_buffer->ptr;
- zend_uchar *null_ptr, bit;
+ const zend_uchar * p = row_buffer->ptr;
+ const zend_uchar * null_ptr;
+ zend_uchar bit;
zval *current_field, *end_field, *start_field;
DBG_ENTER("php_mysqlnd_rowp_read_binary_protocol");
@@ -1540,7 +1566,7 @@ php_mysqlnd_rowp_read_binary_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zv
for (i = 0, current_field = start_field; current_field < end_field; current_field++, i++) {
enum_mysqlnd_collected_stats statistic;
- zend_uchar * orig_p = p;
+ const zend_uchar * orig_p = p;
DBG_INF_FMT("Into zval=%p decoding column %u [%s.%s.%s] type=%u field->flags&unsigned=%u flags=%u is_bit=%u",
current_field, i,
@@ -1590,7 +1616,7 @@ php_mysqlnd_rowp_read_binary_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zv
MYSQLND_INC_CONN_STATISTIC_W_VALUE2(stats, statistic, 1,
STAT_BYTES_RECEIVED_PURE_DATA_PS,
(Z_TYPE_P(current_field) == IS_STRING)?
- Z_STRLEN_P(current_field) : (p - orig_p));
+ Z_STRLEN_P(current_field) : (size_t)(p - orig_p));
if (!((bit<<=1) & 255)) {
bit = 1; /* to the following byte */
@@ -1613,8 +1639,6 @@ php_mysqlnd_rowp_read_text_protocol_aux(MYSQLND_MEMORY_POOL_CHUNK * row_buffer,
zval *current_field, *end_field, *start_field;
zend_uchar * p = row_buffer->ptr;
size_t data_size = row_buffer->app;
- /* we allocate from here. In pre-7.0 it was +1, as there was an additional \0 for the last string in the packet - because of the zval optimizations - using no-copy */
- zend_uchar * bit_area = (zend_uchar*) row_buffer->ptr + data_size;
const zend_uchar * const packet_end = (zend_uchar*) row_buffer->ptr + data_size;
DBG_ENTER("php_mysqlnd_rowp_read_text_protocol_aux");
@@ -1627,7 +1651,7 @@ php_mysqlnd_rowp_read_text_protocol_aux(MYSQLND_MEMORY_POOL_CHUNK * row_buffer,
for (i = 0, current_field = start_field; current_field < end_field; current_field++, i++) {
/* php_mysqlnd_net_field_length() call should be after *this_field_len_pos = p; */
- const zend_ulong len = php_mysqlnd_net_field_length(&p);
+ const zend_ulong len = php_mysqlnd_net_field_length((const zend_uchar **) &p);
/* NULL or NOT NULL, this is the question! */
if (len == MYSQLND_NULL_LENGTH) {
@@ -1725,46 +1749,22 @@ php_mysqlnd_rowp_read_text_protocol_aux(MYSQLND_MEMORY_POOL_CHUNK * row_buffer,
#endif /* MYSQLND_STRING_TO_INT_CONVERSION */
if (fields_metadata[i].type == MYSQL_TYPE_BIT) {
/*
- BIT fields are specially handled. As they come as bit mask, we have
- to convert it to human-readable representation. As the bits take
- less space in the protocol than the numbers they represent, we don't
- have enough space in the packet buffer to overwrite inside.
- Thus, a bit more space is pre-allocated at the end of the buffer,
- see php_mysqlnd_rowp_read(). And we add the strings at the end.
- Definitely not nice, _hackish_ :(, but works.
+ BIT fields are specially handled. As they come as bit mask, they have
+ to be converted to human-readable representation.
*/
- zend_uchar *start = bit_area;
- ps_fetch_from_1_to_8_bytes(current_field, &(fields_metadata[i]), 0, &p, len);
+ ps_fetch_from_1_to_8_bytes(current_field, &(fields_metadata[i]), 0, (const zend_uchar **) &p, len);
/*
We have advanced in ps_fetch_from_1_to_8_bytes. We should go back because
later in this function there will be an advancement.
*/
p -= len;
- if (Z_TYPE_P(current_field) == IS_LONG) {
- /*
- Andrey : See below. No need of bit_area, as we can use on stack for this.
- The bit area should be removed - the `prealloc_more_bytes` in php_mysqlnd_read_row_ex()
-
- char tmp[22];
- const size_t tmp_len = sprintf((char *)&tmp, MYSQLND_LLU_SPEC, Z_LVAL_P(current_field));
- ZVAL_STRINGL(current_field, tmp, tmp_len);
- */
- bit_area += 1 + sprintf((char *)start, ZEND_LONG_FMT, Z_LVAL_P(current_field));
- ZVAL_STRINGL(current_field, (char *) start, bit_area - start - 1);
+ if (Z_TYPE_P(current_field) == IS_LONG && !as_int_or_float) {
+ /* we are using the text protocol, so convert to string */
+ char tmp[22];
+ const size_t tmp_len = sprintf((char *)&tmp, MYSQLND_LLU_SPEC, Z_LVAL_P(current_field));
+ ZVAL_STRINGL(current_field, tmp, tmp_len);
} else if (Z_TYPE_P(current_field) == IS_STRING) {
- /*
- Andrey : This is totally sensless, but I am not gonna remove it in a production version.
- This copies the data from the zval to the bit area. The destroys the original value
- and creates the same one from the bit area. No need. It was making sense in pre-7.0
- when we used zval IS_STRING with no-copy that referred to the bit area.
- The bit area has no sense in both the case of IS_LONG and IS_STRING as 7.0 zval
- IS_STRING always copies.
- */
- memcpy(bit_area, Z_STRVAL_P(current_field), Z_STRLEN_P(current_field));
- bit_area += Z_STRLEN_P(current_field);
- *bit_area++ = '\0';
- zval_dtor(current_field);
- ZVAL_STRINGL(current_field, (char *) start, bit_area - start - 1);
+ /* nothing to do here, as we want a string and ps_fetch_from_1_to_8_bytes() has given us one */
}
} else {
ZVAL_STRINGL(current_field, (char *)p, len);
@@ -1812,28 +1812,26 @@ php_mysqlnd_rowp_read_text_protocol_c(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zv
if PS => packet->fields is passed from outside
*/
static enum_func_status
-php_mysqlnd_rowp_read(void * _packet, MYSQLND_CONN_DATA * conn)
+php_mysqlnd_rowp_read(void * _packet)
{
+ MYSQLND_PACKET_ROW *packet= (MYSQLND_PACKET_ROW *) _packet;
+ MYSQLND_ERROR_INFO * error_info = packet->header.error_info;
+ MYSQLND_PFC * pfc = packet->header.protocol_frame_codec;
+ MYSQLND_VIO * vio = packet->header.vio;
+ MYSQLND_STATS * stats = packet->header.stats;
zend_uchar *p;
enum_func_status ret = PASS;
- MYSQLND_PACKET_ROW *packet= (MYSQLND_PACKET_ROW *) _packet;
- size_t post_alloc_for_bit_fields = 0;
size_t data_size = 0;
DBG_ENTER("php_mysqlnd_rowp_read");
- if (!packet->binary_protocol && packet->bit_fields_count) {
- /* For every field we need terminating \0 */
- post_alloc_for_bit_fields = packet->bit_fields_total_len + packet->bit_fields_count;
- }
-
- ret = php_mysqlnd_read_row_ex(conn, packet->result_set_memory_pool, &packet->row_buffer, &data_size,
- packet->persistent_alloc, post_alloc_for_bit_fields
- );
+ ret = php_mysqlnd_read_row_ex(pfc, vio, stats, error_info,
+ packet->result_set_memory_pool, &packet->row_buffer, &data_size,
+ packet->persistent_alloc);
if (FAIL == ret) {
goto end;
}
- MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn->stats, packet_type_to_statistic_byte_count[PROT_ROW_PACKET],
+ MYSQLND_INC_CONN_STATISTIC_W_VALUE2(stats, packet_type_to_statistic_byte_count[PROT_ROW_PACKET],
MYSQLND_HEADER_SIZE + packet->header.size,
packet_type_to_statistic_packet_count[PROT_ROW_PACKET],
1);
@@ -1874,7 +1872,7 @@ php_mysqlnd_rowp_read(void * _packet, MYSQLND_CONN_DATA * conn)
DBG_INF_FMT("server_status=%u warning_count=%u", packet->server_status, packet->warning_count);
}
} else {
- MYSQLND_INC_CONN_STATISTIC(conn->stats,
+ MYSQLND_INC_CONN_STATISTIC(stats,
packet->binary_protocol? STAT_ROWS_FETCHED_FROM_SERVER_PS:
STAT_ROWS_FETCHED_FROM_SERVER_NORMAL);
@@ -1898,7 +1896,7 @@ php_mysqlnd_rowp_read(void * _packet, MYSQLND_CONN_DATA * conn)
packet->persistent_alloc);
}
} else {
- MYSQLND_INC_CONN_STATISTIC(conn->stats,
+ MYSQLND_INC_CONN_STATISTIC(stats,
packet->binary_protocol? STAT_ROWS_SKIPPED_PS:
STAT_ROWS_SKIPPED_NORMAL);
}
@@ -1919,7 +1917,7 @@ php_mysqlnd_rowp_free_mem(void * _packet, zend_bool stack_allocation)
DBG_ENTER("php_mysqlnd_rowp_free_mem");
p = (MYSQLND_PACKET_ROW *) _packet;
if (p->row_buffer) {
- p->row_buffer->free_chunk(p->row_buffer);
+ p->result_set_memory_pool->free_chunk(p->result_set_memory_pool, p->row_buffer);
p->row_buffer = NULL;
}
DBG_INF_FMT("stack_allocation=%u persistent=%u", (int)stack_allocation, (int)p->header.persistent);
@@ -1940,20 +1938,27 @@ php_mysqlnd_rowp_free_mem(void * _packet, zend_bool stack_allocation)
/* {{{ php_mysqlnd_stats_read */
static enum_func_status
-php_mysqlnd_stats_read(void * _packet, MYSQLND_CONN_DATA * conn)
+php_mysqlnd_stats_read(void * _packet)
{
MYSQLND_PACKET_STATS *packet= (MYSQLND_PACKET_STATS *) _packet;
- size_t buf_len = conn->net->cmd_buffer.length;
- zend_uchar *buf = (zend_uchar *) conn->net->cmd_buffer.buffer;
+ MYSQLND_ERROR_INFO * error_info = packet->header.error_info;
+ MYSQLND_PFC * pfc = packet->header.protocol_frame_codec;
+ MYSQLND_VIO * vio = packet->header.vio;
+ MYSQLND_STATS * stats = packet->header.stats;
+ MYSQLND_CONNECTION_STATE * connection_state = packet->header.connection_state;
+ size_t buf_len = pfc->cmd_buffer.length;
+ zend_uchar *buf = (zend_uchar *) pfc->cmd_buffer.buffer;
DBG_ENTER("php_mysqlnd_stats_read");
- PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "statistics", PROT_STATS_PACKET);
+ if (FAIL == mysqlnd_read_packet_header_and_body(&(packet->header), pfc, vio, stats, error_info, connection_state, buf, buf_len, "statistics", PROT_STATS_PACKET)) {
+ DBG_RETURN(FAIL);
+ }
- packet->message = mnd_emalloc(packet->header.size + 1);
- memcpy(packet->message, buf, packet->header.size);
- packet->message[packet->header.size] = '\0';
- packet->message_len = packet->header.size;
+ packet->message.s = mnd_emalloc(packet->header.size + 1);
+ memcpy(packet->message.s, buf, packet->header.size);
+ packet->message.s[packet->header.size] = '\0';
+ packet->message.l = packet->header.size;
DBG_RETURN(PASS);
}
@@ -1965,9 +1970,9 @@ static
void php_mysqlnd_stats_free_mem(void * _packet, zend_bool stack_allocation)
{
MYSQLND_PACKET_STATS *p= (MYSQLND_PACKET_STATS *) _packet;
- if (p->message) {
- mnd_efree(p->message);
- p->message = NULL;
+ if (p->message.s) {
+ mnd_efree(p->message.s);
+ p->message.s = NULL;
}
if (!stack_allocation) {
mnd_pefree(p, p->header.persistent);
@@ -1982,19 +1987,26 @@ void php_mysqlnd_stats_free_mem(void * _packet, zend_bool stack_allocation)
/* {{{ php_mysqlnd_prepare_read */
static enum_func_status
-php_mysqlnd_prepare_read(void * _packet, MYSQLND_CONN_DATA * conn)
+php_mysqlnd_prepare_read(void * _packet)
{
+ MYSQLND_PACKET_PREPARE_RESPONSE *packet= (MYSQLND_PACKET_PREPARE_RESPONSE *) _packet;
+ MYSQLND_ERROR_INFO * error_info = packet->header.error_info;
+ MYSQLND_PFC * pfc = packet->header.protocol_frame_codec;
+ MYSQLND_VIO * vio = packet->header.vio;
+ MYSQLND_STATS * stats = packet->header.stats;
+ MYSQLND_CONNECTION_STATE * connection_state = packet->header.connection_state;
/* In case of an error, we should have place to put it */
- size_t buf_len = conn->net->cmd_buffer.length;
- zend_uchar *buf = (zend_uchar *) conn->net->cmd_buffer.buffer;
+ size_t buf_len = pfc->cmd_buffer.length;
+ zend_uchar *buf = (zend_uchar *) pfc->cmd_buffer.buffer;
zend_uchar *p = buf;
- zend_uchar *begin = buf;
+ const zend_uchar * const begin = buf;
unsigned int data_size;
- MYSQLND_PACKET_PREPARE_RESPONSE *packet= (MYSQLND_PACKET_PREPARE_RESPONSE *) _packet;
DBG_ENTER("php_mysqlnd_prepare_read");
- PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "prepare", PROT_PREPARE_RESP_PACKET);
+ if (FAIL == mysqlnd_read_packet_header_and_body(&(packet->header), pfc, vio, stats, error_info, connection_state, buf, buf_len, "prepare", PROT_PREPARE_RESP_PACKET)) {
+ DBG_RETURN(FAIL);
+ }
BAIL_IF_NO_MORE_DATA;
data_size = packet->header.size;
@@ -2070,18 +2082,25 @@ php_mysqlnd_prepare_free_mem(void * _packet, zend_bool stack_allocation)
/* {{{ php_mysqlnd_chg_user_read */
static enum_func_status
-php_mysqlnd_chg_user_read(void * _packet, MYSQLND_CONN_DATA * conn)
+php_mysqlnd_chg_user_read(void * _packet)
{
+ MYSQLND_PACKET_CHG_USER_RESPONSE *packet= (MYSQLND_PACKET_CHG_USER_RESPONSE *) _packet;
+ MYSQLND_ERROR_INFO * error_info = packet->header.error_info;
+ MYSQLND_PFC * pfc = packet->header.protocol_frame_codec;
+ MYSQLND_VIO * vio = packet->header.vio;
+ MYSQLND_STATS * stats = packet->header.stats;
+ MYSQLND_CONNECTION_STATE * connection_state = packet->header.connection_state;
/* There could be an error message */
- size_t buf_len = conn->net->cmd_buffer.length;
- zend_uchar *buf = (zend_uchar *) conn->net->cmd_buffer.buffer;
+ size_t buf_len = pfc->cmd_buffer.length;
+ zend_uchar *buf = (zend_uchar *) pfc->cmd_buffer.buffer;
zend_uchar *p = buf;
- zend_uchar *begin = buf;
- MYSQLND_PACKET_CHG_USER_RESPONSE *packet= (MYSQLND_PACKET_CHG_USER_RESPONSE *) _packet;
+ const zend_uchar * const begin = buf;
DBG_ENTER("php_mysqlnd_chg_user_read");
- PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "change user response", PROT_CHG_USER_RESP_PACKET);
+ if (FAIL == mysqlnd_read_packet_header_and_body(&(packet->header), pfc, vio, stats, error_info, connection_state, buf, buf_len, "change user response", PROT_CHG_USER_RESP_PACKET)) {
+ DBG_RETURN(FAIL);
+ }
BAIL_IF_NO_MORE_DATA;
/*
@@ -2158,15 +2177,20 @@ php_mysqlnd_chg_user_free_mem(void * _packet, zend_bool stack_allocation)
/* {{{ php_mysqlnd_sha256_pk_request_write */
static
-size_t php_mysqlnd_sha256_pk_request_write(void * _packet, MYSQLND_CONN_DATA * conn)
+size_t php_mysqlnd_sha256_pk_request_write(void * _packet)
{
+ MYSQLND_PACKET_SHA256_PK_REQUEST * packet = (MYSQLND_PACKET_SHA256_PK_REQUEST *) _packet;
+ MYSQLND_ERROR_INFO * error_info = packet->header.error_info;
+ MYSQLND_PFC * pfc = packet->header.protocol_frame_codec;
+ MYSQLND_VIO * vio = packet->header.vio;
+ MYSQLND_STATS * stats = packet->header.stats;
zend_uchar buffer[MYSQLND_HEADER_SIZE + 1];
size_t sent;
DBG_ENTER("php_mysqlnd_sha256_pk_request_write");
int1store(buffer + MYSQLND_HEADER_SIZE, '\1');
- sent = conn->net->data->m.send_ex(conn->net, buffer, 1, conn->stats, conn->error_info);
+ sent = pfc->data->m.send(pfc, vio, buffer, 1, stats, error_info);
DBG_RETURN(sent);
}
@@ -2189,17 +2213,24 @@ void php_mysqlnd_sha256_pk_request_free_mem(void * _packet, zend_bool stack_allo
/* {{{ php_mysqlnd_sha256_pk_request_response_read */
static enum_func_status
-php_mysqlnd_sha256_pk_request_response_read(void * _packet, MYSQLND_CONN_DATA * conn)
+php_mysqlnd_sha256_pk_request_response_read(void * _packet)
{
+ MYSQLND_PACKET_SHA256_PK_REQUEST_RESPONSE * packet= (MYSQLND_PACKET_SHA256_PK_REQUEST_RESPONSE *) _packet;
+ MYSQLND_ERROR_INFO * error_info = packet->header.error_info;
+ MYSQLND_PFC * pfc = packet->header.protocol_frame_codec;
+ MYSQLND_VIO * vio = packet->header.vio;
+ MYSQLND_STATS * stats = packet->header.stats;
+ MYSQLND_CONNECTION_STATE * connection_state = packet->header.connection_state;
zend_uchar buf[SHA256_PK_REQUEST_RESP_BUFFER_SIZE];
zend_uchar *p = buf;
- zend_uchar *begin = buf;
- MYSQLND_PACKET_SHA256_PK_REQUEST_RESPONSE * packet= (MYSQLND_PACKET_SHA256_PK_REQUEST_RESPONSE *) _packet;
+ const zend_uchar * const begin = buf;
DBG_ENTER("php_mysqlnd_sha256_pk_request_response_read");
/* leave space for terminating safety \0 */
- PACKET_READ_HEADER_AND_BODY(packet, conn, buf, sizeof(buf), "SHA256_PK_REQUEST_RESPONSE", PROT_SHA256_PK_REQUEST_RESPONSE_PACKET);
+ if (FAIL == mysqlnd_read_packet_header_and_body(&(packet->header), pfc, vio, stats, error_info, connection_state, buf, sizeof(buf), "SHA256_PK_REQUEST_RESPONSE", PROT_SHA256_PK_REQUEST_RESPONSE_PACKET)) {
+ DBG_RETURN(FAIL);
+ }
BAIL_IF_NO_MORE_DATA;
p++;
@@ -2339,12 +2370,20 @@ mysqlnd_packet_methods packet_methods[PROT_LAST] =
/* {{{ mysqlnd_protocol::get_greet_packet */
static struct st_mysqlnd_packet_greet *
-MYSQLND_METHOD(mysqlnd_protocol, get_greet_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent)
+MYSQLND_METHOD(mysqlnd_protocol, get_greet_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent)
{
struct st_mysqlnd_packet_greet * packet = mnd_pecalloc(1, packet_methods[PROT_GREET_PACKET].struct_size, persistent);
DBG_ENTER("mysqlnd_protocol::get_greet_packet");
if (packet) {
packet->header.m = &packet_methods[PROT_GREET_PACKET];
+ packet->header.factory = factory;
+
+ packet->header.protocol_frame_codec = factory->conn->protocol_frame_codec;
+ packet->header.vio = factory->conn->vio;
+ packet->header.stats = factory->conn->stats;
+ packet->header.error_info = factory->conn->error_info;
+ packet->header.connection_state = &factory->conn->state;
+
packet->header.persistent = persistent;
}
DBG_RETURN(packet);
@@ -2354,12 +2393,21 @@ MYSQLND_METHOD(mysqlnd_protocol, get_greet_packet)(MYSQLND_PROTOCOL * const prot
/* {{{ mysqlnd_protocol::get_auth_packet */
static struct st_mysqlnd_packet_auth *
-MYSQLND_METHOD(mysqlnd_protocol, get_auth_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent)
+MYSQLND_METHOD(mysqlnd_protocol, get_auth_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent)
{
struct st_mysqlnd_packet_auth * packet = mnd_pecalloc(1, packet_methods[PROT_AUTH_PACKET].struct_size, persistent);
DBG_ENTER("mysqlnd_protocol::get_auth_packet");
if (packet) {
packet->header.m = &packet_methods[PROT_AUTH_PACKET];
+ packet->header.factory = factory;
+
+ packet->header.conn = factory->conn;
+ packet->header.protocol_frame_codec = factory->conn->protocol_frame_codec;
+ packet->header.vio = factory->conn->vio;
+ packet->header.stats = factory->conn->stats;
+ packet->header.error_info = factory->conn->error_info;
+ packet->header.connection_state = &factory->conn->state;
+
packet->header.persistent = persistent;
}
DBG_RETURN(packet);
@@ -2369,12 +2417,20 @@ MYSQLND_METHOD(mysqlnd_protocol, get_auth_packet)(MYSQLND_PROTOCOL * const proto
/* {{{ mysqlnd_protocol::get_auth_response_packet */
static struct st_mysqlnd_packet_auth_response *
-MYSQLND_METHOD(mysqlnd_protocol, get_auth_response_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent)
+MYSQLND_METHOD(mysqlnd_protocol, get_auth_response_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent)
{
struct st_mysqlnd_packet_auth_response * packet = mnd_pecalloc(1, packet_methods[PROT_AUTH_RESP_PACKET].struct_size, persistent);
DBG_ENTER("mysqlnd_protocol::get_auth_response_packet");
if (packet) {
packet->header.m = &packet_methods[PROT_AUTH_RESP_PACKET];
+ packet->header.factory = factory;
+
+ packet->header.protocol_frame_codec = factory->conn->protocol_frame_codec;
+ packet->header.vio = factory->conn->vio;
+ packet->header.stats = factory->conn->stats;
+ packet->header.error_info = factory->conn->error_info;
+ packet->header.connection_state = &factory->conn->state;
+
packet->header.persistent = persistent;
}
DBG_RETURN(packet);
@@ -2384,12 +2440,20 @@ MYSQLND_METHOD(mysqlnd_protocol, get_auth_response_packet)(MYSQLND_PROTOCOL * co
/* {{{ mysqlnd_protocol::get_change_auth_response_packet */
static struct st_mysqlnd_packet_change_auth_response *
-MYSQLND_METHOD(mysqlnd_protocol, get_change_auth_response_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent)
+MYSQLND_METHOD(mysqlnd_protocol, get_change_auth_response_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent)
{
struct st_mysqlnd_packet_change_auth_response * packet = mnd_pecalloc(1, packet_methods[PROT_CHANGE_AUTH_RESP_PACKET].struct_size, persistent);
DBG_ENTER("mysqlnd_protocol::get_change_auth_response_packet");
if (packet) {
packet->header.m = &packet_methods[PROT_CHANGE_AUTH_RESP_PACKET];
+ packet->header.factory = factory;
+
+ packet->header.protocol_frame_codec = factory->conn->protocol_frame_codec;
+ packet->header.vio = factory->conn->vio;
+ packet->header.stats = factory->conn->stats;
+ packet->header.error_info = factory->conn->error_info;
+ packet->header.connection_state = &factory->conn->state;
+
packet->header.persistent = persistent;
}
DBG_RETURN(packet);
@@ -2399,12 +2463,20 @@ MYSQLND_METHOD(mysqlnd_protocol, get_change_auth_response_packet)(MYSQLND_PROTOC
/* {{{ mysqlnd_protocol::get_ok_packet */
static struct st_mysqlnd_packet_ok *
-MYSQLND_METHOD(mysqlnd_protocol, get_ok_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent)
+MYSQLND_METHOD(mysqlnd_protocol, get_ok_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent)
{
struct st_mysqlnd_packet_ok * packet = mnd_pecalloc(1, packet_methods[PROT_OK_PACKET].struct_size, persistent);
DBG_ENTER("mysqlnd_protocol::get_ok_packet");
if (packet) {
packet->header.m = &packet_methods[PROT_OK_PACKET];
+ packet->header.factory = factory;
+
+ packet->header.protocol_frame_codec = factory->conn->protocol_frame_codec;
+ packet->header.vio = factory->conn->vio;
+ packet->header.stats = factory->conn->stats;
+ packet->header.error_info = factory->conn->error_info;
+ packet->header.connection_state = &factory->conn->state;
+
packet->header.persistent = persistent;
}
DBG_RETURN(packet);
@@ -2414,12 +2486,20 @@ MYSQLND_METHOD(mysqlnd_protocol, get_ok_packet)(MYSQLND_PROTOCOL * const protoco
/* {{{ mysqlnd_protocol::get_eof_packet */
static struct st_mysqlnd_packet_eof *
-MYSQLND_METHOD(mysqlnd_protocol, get_eof_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent)
+MYSQLND_METHOD(mysqlnd_protocol, get_eof_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent)
{
struct st_mysqlnd_packet_eof * packet = mnd_pecalloc(1, packet_methods[PROT_EOF_PACKET].struct_size, persistent);
DBG_ENTER("mysqlnd_protocol::get_eof_packet");
if (packet) {
packet->header.m = &packet_methods[PROT_EOF_PACKET];
+ packet->header.factory = factory;
+
+ packet->header.protocol_frame_codec = factory->conn->protocol_frame_codec;
+ packet->header.vio = factory->conn->vio;
+ packet->header.stats = factory->conn->stats;
+ packet->header.error_info = factory->conn->error_info;
+ packet->header.connection_state = &factory->conn->state;
+
packet->header.persistent = persistent;
}
DBG_RETURN(packet);
@@ -2429,12 +2509,20 @@ MYSQLND_METHOD(mysqlnd_protocol, get_eof_packet)(MYSQLND_PROTOCOL * const protoc
/* {{{ mysqlnd_protocol::get_command_packet */
static struct st_mysqlnd_packet_command *
-MYSQLND_METHOD(mysqlnd_protocol, get_command_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent)
+MYSQLND_METHOD(mysqlnd_protocol, get_command_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent)
{
struct st_mysqlnd_packet_command * packet = mnd_pecalloc(1, packet_methods[PROT_CMD_PACKET].struct_size, persistent);
DBG_ENTER("mysqlnd_protocol::get_command_packet");
if (packet) {
packet->header.m = &packet_methods[PROT_CMD_PACKET];
+ packet->header.factory = factory;
+
+ packet->header.protocol_frame_codec = factory->conn->protocol_frame_codec;
+ packet->header.vio = factory->conn->vio;
+ packet->header.stats = factory->conn->stats;
+ packet->header.error_info = factory->conn->error_info;
+ packet->header.connection_state = &factory->conn->state;
+
packet->header.persistent = persistent;
}
DBG_RETURN(packet);
@@ -2444,12 +2532,20 @@ MYSQLND_METHOD(mysqlnd_protocol, get_command_packet)(MYSQLND_PROTOCOL * const pr
/* {{{ mysqlnd_protocol::get_rset_packet */
static struct st_mysqlnd_packet_rset_header *
-MYSQLND_METHOD(mysqlnd_protocol, get_rset_header_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent)
+MYSQLND_METHOD(mysqlnd_protocol, get_rset_header_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent)
{
struct st_mysqlnd_packet_rset_header * packet = mnd_pecalloc(1, packet_methods[PROT_RSET_HEADER_PACKET].struct_size, persistent);
DBG_ENTER("mysqlnd_protocol::get_rset_header_packet");
if (packet) {
packet->header.m = &packet_methods[PROT_RSET_HEADER_PACKET];
+ packet->header.factory = factory;
+
+ packet->header.protocol_frame_codec = factory->conn->protocol_frame_codec;
+ packet->header.vio = factory->conn->vio;
+ packet->header.stats = factory->conn->stats;
+ packet->header.error_info = factory->conn->error_info;
+ packet->header.connection_state = &factory->conn->state;
+
packet->header.persistent = persistent;
}
DBG_RETURN(packet);
@@ -2459,12 +2555,20 @@ MYSQLND_METHOD(mysqlnd_protocol, get_rset_header_packet)(MYSQLND_PROTOCOL * cons
/* {{{ mysqlnd_protocol::get_result_field_packet */
static struct st_mysqlnd_packet_res_field *
-MYSQLND_METHOD(mysqlnd_protocol, get_result_field_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent)
+MYSQLND_METHOD(mysqlnd_protocol, get_result_field_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent)
{
struct st_mysqlnd_packet_res_field * packet = mnd_pecalloc(1, packet_methods[PROT_RSET_FLD_PACKET].struct_size, persistent);
DBG_ENTER("mysqlnd_protocol::get_result_field_packet");
if (packet) {
packet->header.m = &packet_methods[PROT_RSET_FLD_PACKET];
+ packet->header.factory = factory;
+
+ packet->header.protocol_frame_codec = factory->conn->protocol_frame_codec;
+ packet->header.vio = factory->conn->vio;
+ packet->header.stats = factory->conn->stats;
+ packet->header.error_info = factory->conn->error_info;
+ packet->header.connection_state = &factory->conn->state;
+
packet->header.persistent = persistent;
}
DBG_RETURN(packet);
@@ -2474,12 +2578,21 @@ MYSQLND_METHOD(mysqlnd_protocol, get_result_field_packet)(MYSQLND_PROTOCOL * con
/* {{{ mysqlnd_protocol::get_row_packet */
static struct st_mysqlnd_packet_row *
-MYSQLND_METHOD(mysqlnd_protocol, get_row_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent)
+MYSQLND_METHOD(mysqlnd_protocol, get_row_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent)
{
struct st_mysqlnd_packet_row * packet = mnd_pecalloc(1, packet_methods[PROT_ROW_PACKET].struct_size, persistent);
DBG_ENTER("mysqlnd_protocol::get_row_packet");
if (packet) {
packet->header.m = &packet_methods[PROT_ROW_PACKET];
+ packet->header.factory = factory;
+
+ packet->header.conn = factory->conn;
+ packet->header.protocol_frame_codec = factory->conn->protocol_frame_codec;
+ packet->header.vio = factory->conn->vio;
+ packet->header.stats = factory->conn->stats;
+ packet->header.error_info = factory->conn->error_info;
+ packet->header.connection_state = &factory->conn->state;
+
packet->header.persistent = persistent;
}
DBG_RETURN(packet);
@@ -2489,12 +2602,20 @@ MYSQLND_METHOD(mysqlnd_protocol, get_row_packet)(MYSQLND_PROTOCOL * const protoc
/* {{{ mysqlnd_protocol::get_stats_packet */
static struct st_mysqlnd_packet_stats *
-MYSQLND_METHOD(mysqlnd_protocol, get_stats_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent)
+MYSQLND_METHOD(mysqlnd_protocol, get_stats_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent)
{
struct st_mysqlnd_packet_stats * packet = mnd_pecalloc(1, packet_methods[PROT_STATS_PACKET].struct_size, persistent);
DBG_ENTER("mysqlnd_protocol::get_stats_packet");
if (packet) {
packet->header.m = &packet_methods[PROT_STATS_PACKET];
+ packet->header.factory = factory;
+
+ packet->header.protocol_frame_codec = factory->conn->protocol_frame_codec;
+ packet->header.vio = factory->conn->vio;
+ packet->header.stats = factory->conn->stats;
+ packet->header.error_info = factory->conn->error_info;
+ packet->header.connection_state = &factory->conn->state;
+
packet->header.persistent = persistent;
}
DBG_RETURN(packet);
@@ -2504,12 +2625,20 @@ MYSQLND_METHOD(mysqlnd_protocol, get_stats_packet)(MYSQLND_PROTOCOL * const prot
/* {{{ mysqlnd_protocol::get_prepare_response_packet */
static struct st_mysqlnd_packet_prepare_response *
-MYSQLND_METHOD(mysqlnd_protocol, get_prepare_response_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent)
+MYSQLND_METHOD(mysqlnd_protocol, get_prepare_response_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent)
{
struct st_mysqlnd_packet_prepare_response * packet = mnd_pecalloc(1, packet_methods[PROT_PREPARE_RESP_PACKET].struct_size, persistent);
DBG_ENTER("mysqlnd_protocol::get_prepare_response_packet");
if (packet) {
packet->header.m = &packet_methods[PROT_PREPARE_RESP_PACKET];
+ packet->header.factory = factory;
+
+ packet->header.protocol_frame_codec = factory->conn->protocol_frame_codec;
+ packet->header.vio = factory->conn->vio;
+ packet->header.stats = factory->conn->stats;
+ packet->header.error_info = factory->conn->error_info;
+ packet->header.connection_state = &factory->conn->state;
+
packet->header.persistent = persistent;
}
DBG_RETURN(packet);
@@ -2519,12 +2648,20 @@ MYSQLND_METHOD(mysqlnd_protocol, get_prepare_response_packet)(MYSQLND_PROTOCOL *
/* {{{ mysqlnd_protocol::get_change_user_response_packet */
static struct st_mysqlnd_packet_chg_user_resp*
-MYSQLND_METHOD(mysqlnd_protocol, get_change_user_response_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent)
+MYSQLND_METHOD(mysqlnd_protocol, get_change_user_response_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent)
{
struct st_mysqlnd_packet_chg_user_resp * packet = mnd_pecalloc(1, packet_methods[PROT_CHG_USER_RESP_PACKET].struct_size, persistent);
DBG_ENTER("mysqlnd_protocol::get_change_user_response_packet");
if (packet) {
packet->header.m = &packet_methods[PROT_CHG_USER_RESP_PACKET];
+ packet->header.factory = factory;
+
+ packet->header.protocol_frame_codec = factory->conn->protocol_frame_codec;
+ packet->header.vio = factory->conn->vio;
+ packet->header.stats = factory->conn->stats;
+ packet->header.error_info = factory->conn->error_info;
+ packet->header.connection_state = &factory->conn->state;
+
packet->header.persistent = persistent;
}
DBG_RETURN(packet);
@@ -2534,12 +2671,20 @@ MYSQLND_METHOD(mysqlnd_protocol, get_change_user_response_packet)(MYSQLND_PROTOC
/* {{{ mysqlnd_protocol::get_sha256_pk_request_packet */
static struct st_mysqlnd_packet_sha256_pk_request *
-MYSQLND_METHOD(mysqlnd_protocol, get_sha256_pk_request_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent)
+MYSQLND_METHOD(mysqlnd_protocol, get_sha256_pk_request_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent)
{
struct st_mysqlnd_packet_sha256_pk_request * packet = mnd_pecalloc(1, packet_methods[PROT_SHA256_PK_REQUEST_PACKET].struct_size, persistent);
DBG_ENTER("mysqlnd_protocol::get_sha256_pk_request_packet");
if (packet) {
packet->header.m = &packet_methods[PROT_SHA256_PK_REQUEST_PACKET];
+ packet->header.factory = factory;
+
+ packet->header.protocol_frame_codec = factory->conn->protocol_frame_codec;
+ packet->header.vio = factory->conn->vio;
+ packet->header.stats = factory->conn->stats;
+ packet->header.error_info = factory->conn->error_info;
+ packet->header.connection_state = &factory->conn->state;
+
packet->header.persistent = persistent;
}
DBG_RETURN(packet);
@@ -2549,12 +2694,20 @@ MYSQLND_METHOD(mysqlnd_protocol, get_sha256_pk_request_packet)(MYSQLND_PROTOCOL
/* {{{ mysqlnd_protocol::get_sha256_pk_request_response_packet */
static struct st_mysqlnd_packet_sha256_pk_request_response *
-MYSQLND_METHOD(mysqlnd_protocol, get_sha256_pk_request_response_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent)
+MYSQLND_METHOD(mysqlnd_protocol, get_sha256_pk_request_response_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent)
{
struct st_mysqlnd_packet_sha256_pk_request_response * packet = mnd_pecalloc(1, packet_methods[PROT_SHA256_PK_REQUEST_RESPONSE_PACKET].struct_size, persistent);
DBG_ENTER("mysqlnd_protocol::get_sha256_pk_request_response_packet");
if (packet) {
packet->header.m = &packet_methods[PROT_SHA256_PK_REQUEST_RESPONSE_PACKET];
+ packet->header.factory = factory;
+
+ packet->header.protocol_frame_codec = factory->conn->protocol_frame_codec;
+ packet->header.vio = factory->conn->vio;
+ packet->header.stats = factory->conn->stats;
+ packet->header.error_info = factory->conn->error_info;
+ packet->header.connection_state = &factory->conn->state;
+
packet->header.persistent = persistent;
}
DBG_RETURN(packet);
@@ -2562,8 +2715,220 @@ MYSQLND_METHOD(mysqlnd_protocol, get_sha256_pk_request_response_packet)(MYSQLND_
/* }}} */
+/* {{{ mysqlnd_protocol::send_command */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_protocol, send_command)(
+ MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * payload_decoder_factory,
+ const enum php_mysqlnd_server_command command,
+ const zend_uchar * const arg, const size_t arg_len,
+ const zend_bool silent,
+
+ struct st_mysqlnd_connection_state * connection_state,
+ MYSQLND_ERROR_INFO * error_info,
+ MYSQLND_UPSERT_STATUS * upsert_status,
+ MYSQLND_STATS * stats,
+ func_mysqlnd_conn_data__send_close send_close,
+ void * send_close_ctx)
+{
+ enum_func_status ret = PASS;
+ MYSQLND_PACKET_COMMAND * cmd_packet = NULL;
+ enum mysqlnd_connection_state state;
+ DBG_ENTER("mysqlnd_protocol::send_command");
+ DBG_INF_FMT("command=%s silent=%u", mysqlnd_command_to_text[command], silent);
+ DBG_INF_FMT("server_status=%u", UPSERT_STATUS_GET_SERVER_STATUS(upsert_status));
+ DBG_INF_FMT("sending %u bytes", arg_len + 1); /* + 1 is for the command */
+ state = connection_state->m->get(connection_state);
+
+ switch (state) {
+ case CONN_READY:
+ break;
+ case CONN_QUIT_SENT:
+ SET_CLIENT_ERROR(error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
+ DBG_ERR("Server is gone");
+ DBG_RETURN(FAIL);
+ default:
+ SET_CLIENT_ERROR(error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
+ DBG_ERR_FMT("Command out of sync. State=%u", state);
+ DBG_RETURN(FAIL);
+ }
+
+ UPSERT_STATUS_SET_AFFECTED_ROWS_TO_ERROR(upsert_status);
+ SET_EMPTY_ERROR(error_info);
+
+ cmd_packet = payload_decoder_factory->m.get_command_packet(payload_decoder_factory, FALSE);
+ if (!cmd_packet) {
+ SET_OOM_ERROR(error_info);
+ DBG_RETURN(FAIL);
+ }
+
+ cmd_packet->command = command;
+ if (arg && arg_len) {
+ cmd_packet->argument.s = (char *) arg;
+ cmd_packet->argument.l = arg_len;
+ }
+
+ MYSQLND_INC_CONN_STATISTIC(stats, STAT_COM_QUIT + command - 1 /* because of COM_SLEEP */ );
+
+ if (! PACKET_WRITE(cmd_packet)) {
+ if (!silent) {
+ DBG_ERR_FMT("Error while sending %s packet", mysqlnd_command_to_text[command]);
+ php_error(E_WARNING, "Error while sending %s packet. PID=%d", mysqlnd_command_to_text[command], getpid());
+ }
+ connection_state->m->set(connection_state, CONN_QUIT_SENT);
+ send_close(send_close_ctx);
+ DBG_ERR("Server is gone");
+ ret = FAIL;
+ }
+ PACKET_FREE(cmd_packet);
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_protocol::send_command_handle_OK */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_protocol, send_command_handle_OK)(
+ MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const payload_decoder_factory,
+ MYSQLND_ERROR_INFO * const error_info,
+ MYSQLND_UPSERT_STATUS * const upsert_status,
+ const zend_bool ignore_upsert_status, /* actually used only by LOAD DATA. COM_QUERY and COM_EXECUTE handle the responses themselves */
+ MYSQLND_STRING * const last_message,
+ const zend_bool last_message_persistent)
+{
+ enum_func_status ret = FAIL;
+ MYSQLND_PACKET_OK * ok_response = payload_decoder_factory->m.get_ok_packet(payload_decoder_factory, FALSE);
+
+ DBG_ENTER("mysqlnd_protocol::send_command_handle_OK");
+ if (!ok_response) {
+ SET_OOM_ERROR(error_info);
+ DBG_RETURN(FAIL);
+ }
+ if (FAIL == (ret = PACKET_READ(ok_response))) {
+ DBG_INF("Error while reading OK packet");
+ SET_CLIENT_ERROR(error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE, "Malformed packet");
+ goto end;
+ }
+ DBG_INF_FMT("OK from server");
+ if (0xFF == ok_response->field_count) {
+ /* The server signalled error. Set the error */
+ SET_CLIENT_ERROR(error_info, ok_response->error_no, ok_response->sqlstate, ok_response->error);
+ ret = FAIL;
+ /*
+ Cover a protocol design error: error packet does not
+ contain the server status. Therefore, the client has no way
+ to find out whether there are more result sets of
+ a multiple-result-set statement pending. Luckily, in 5.0 an
+ error always aborts execution of a statement, wherever it is
+ a multi-statement or a stored procedure, so it should be
+ safe to unconditionally turn off the flag here.
+ */
+ upsert_status->server_status &= ~SERVER_MORE_RESULTS_EXISTS;
+ UPSERT_STATUS_SET_AFFECTED_ROWS_TO_ERROR(upsert_status);
+ } else {
+ SET_NEW_MESSAGE(last_message->s, last_message->l,
+ ok_response->message, ok_response->message_len,
+ last_message_persistent);
+ if (!ignore_upsert_status) {
+ UPSERT_STATUS_RESET(upsert_status);
+ UPSERT_STATUS_SET_WARNINGS(upsert_status, ok_response->warning_count);
+ UPSERT_STATUS_SET_SERVER_STATUS(upsert_status, ok_response->server_status);
+ UPSERT_STATUS_SET_AFFECTED_ROWS(upsert_status, ok_response->affected_rows);
+ UPSERT_STATUS_SET_LAST_INSERT_ID(upsert_status, ok_response->last_insert_id);
+ } else {
+ /* LOAD DATA */
+ }
+ }
+end:
+ PACKET_FREE(ok_response);
+ DBG_INF(ret == PASS ? "PASS":"FAIL");
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_protocol::send_command_handle_EOF */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_protocol, send_command_handle_EOF)(
+ MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const payload_decoder_factory,
+ MYSQLND_ERROR_INFO * const error_info,
+ MYSQLND_UPSERT_STATUS * const upsert_status)
+{
+ enum_func_status ret = FAIL;
+ MYSQLND_PACKET_EOF * response = payload_decoder_factory->m.get_eof_packet(payload_decoder_factory, FALSE);
+
+ DBG_ENTER("mysqlnd_protocol::send_command_handle_EOF");
+
+ if (!response) {
+ SET_OOM_ERROR(error_info);
+ DBG_RETURN(FAIL);
+ }
+ if (FAIL == (ret = PACKET_READ(response))) {
+ DBG_INF("Error while reading EOF packet");
+ SET_CLIENT_ERROR(error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE, "Malformed packet");
+ } else if (0xFF == response->field_count) {
+ /* The server signalled error. Set the error */
+ DBG_INF_FMT("Error_no=%d SQLstate=%s Error=%s", response->error_no, response->sqlstate, response->error);
+
+ SET_CLIENT_ERROR(error_info, response->error_no, response->sqlstate, response->error);
+
+ UPSERT_STATUS_SET_AFFECTED_ROWS_TO_ERROR(upsert_status);
+ } else if (0xFE != response->field_count) {
+ SET_CLIENT_ERROR(error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE, "Malformed packet");
+ DBG_ERR_FMT("EOF packet expected, field count wasn't 0xFE but 0x%2X", response->field_count);
+ php_error_docref(NULL, E_WARNING, "EOF packet expected, field count wasn't 0xFE but 0x%2X", response->field_count);
+ } else {
+ DBG_INF_FMT("EOF from server");
+ }
+ PACKET_FREE(response);
+
+ DBG_INF(ret == PASS ? "PASS":"FAIL");
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ send_command_handle_response */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_protocol, send_command_handle_response)(
+ MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * payload_decoder_factory,
+ const enum mysqlnd_packet_type ok_packet,
+ const zend_bool silent,
+ const enum php_mysqlnd_server_command command,
+ const zend_bool ignore_upsert_status, /* actually used only by LOAD DATA. COM_QUERY and COM_EXECUTE handle the responses themselves */
+
+ MYSQLND_ERROR_INFO * error_info,
+ MYSQLND_UPSERT_STATUS * upsert_status,
+ MYSQLND_STRING * last_message,
+ zend_bool last_message_persistent
+ )
+{
+ enum_func_status ret = FAIL;
+
+ DBG_ENTER("mysqlnd_protocol::send_command_handle_response");
+ DBG_INF_FMT("silent=%u packet=%u command=%s", silent, ok_packet, mysqlnd_command_to_text[command]);
-MYSQLND_CLASS_METHODS_START(mysqlnd_protocol)
+ switch (ok_packet) {
+ case PROT_OK_PACKET:
+ ret = payload_decoder_factory->m.send_command_handle_OK(payload_decoder_factory, error_info, upsert_status, ignore_upsert_status, last_message, last_message_persistent);
+ break;
+ case PROT_EOF_PACKET:
+ ret = payload_decoder_factory->m.send_command_handle_EOF(payload_decoder_factory, error_info, upsert_status);
+ break;
+ default:
+ SET_CLIENT_ERROR(error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE, "Malformed packet");
+ php_error_docref(NULL, E_ERROR, "Wrong response packet %u passed to the function", ok_packet);
+ break;
+ }
+ if (!silent && error_info->error_no == CR_MALFORMED_PACKET) {
+ php_error_docref(NULL, E_WARNING, "Error while reading %s's response packet. PID=%d", mysqlnd_command_to_text[command], getpid());
+ }
+ DBG_INF(ret == PASS ? "PASS":"FAIL");
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+MYSQLND_CLASS_METHODS_START(mysqlnd_protocol_payload_decoder_factory)
MYSQLND_METHOD(mysqlnd_protocol, get_greet_packet),
MYSQLND_METHOD(mysqlnd_protocol, get_auth_packet),
MYSQLND_METHOD(mysqlnd_protocol, get_auth_response_packet),
@@ -2578,31 +2943,36 @@ MYSQLND_CLASS_METHODS_START(mysqlnd_protocol)
MYSQLND_METHOD(mysqlnd_protocol, get_prepare_response_packet),
MYSQLND_METHOD(mysqlnd_protocol, get_change_user_response_packet),
MYSQLND_METHOD(mysqlnd_protocol, get_sha256_pk_request_packet),
- MYSQLND_METHOD(mysqlnd_protocol, get_sha256_pk_request_response_packet)
+ MYSQLND_METHOD(mysqlnd_protocol, get_sha256_pk_request_response_packet),
+
+ MYSQLND_METHOD(mysqlnd_protocol, send_command),
+ MYSQLND_METHOD(mysqlnd_protocol, send_command_handle_response),
+ MYSQLND_METHOD(mysqlnd_protocol, send_command_handle_OK),
+ MYSQLND_METHOD(mysqlnd_protocol, send_command_handle_EOF),
MYSQLND_CLASS_METHODS_END;
-/* {{{ mysqlnd_protocol_init */
-PHPAPI MYSQLND_PROTOCOL *
-mysqlnd_protocol_init(zend_bool persistent)
+/* {{{ mysqlnd_protocol_payload_decoder_factory_init */
+PHPAPI MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY *
+mysqlnd_protocol_payload_decoder_factory_init(MYSQLND_CONN_DATA * conn, const zend_bool persistent)
{
- MYSQLND_PROTOCOL * ret;
- DBG_ENTER("mysqlnd_protocol_init");
- ret = MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_object_factory).get_protocol_decoder(persistent);
+ MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * ret;
+ DBG_ENTER("mysqlnd_protocol_payload_decoder_factory_init");
+ ret = MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_object_factory).get_protocol_payload_decoder_factory(conn, persistent);
DBG_RETURN(ret);
}
/* }}} */
-/* {{{ mysqlnd_protocol_free */
+/* {{{ mysqlnd_protocol_payload_decoder_factory_free */
PHPAPI void
-mysqlnd_protocol_free(MYSQLND_PROTOCOL * const protocol)
+mysqlnd_protocol_payload_decoder_factory_free(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory)
{
- DBG_ENTER("mysqlnd_protocol_free");
+ DBG_ENTER("mysqlnd_protocol_payload_decoder_factory_free");
- if (protocol) {
- zend_bool pers = protocol->persistent;
- mnd_pefree(protocol, pers);
+ if (factory) {
+ zend_bool pers = factory->persistent;
+ mnd_pefree(factory, pers);
}
DBG_VOID_RETURN;
}
diff --git a/ext/mysqlnd/mysqlnd_wireprotocol.h b/ext/mysqlnd/mysqlnd_wireprotocol.h
index 2c76c70e9f..7722f8ee6b 100644
--- a/ext/mysqlnd/mysqlnd_wireprotocol.h
+++ b/ext/mysqlnd/mysqlnd_wireprotocol.h
@@ -14,15 +14,12 @@
+----------------------------------------------------------------------+
| Authors: Andrey Hristov <andrey@php.net> |
| Ulf Wendel <uw@php.net> |
- | Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
#ifndef MYSQLND_WIREPROTOCOL_H
#define MYSQLND_WIREPROTOCOL_H
-#include "mysqlnd_net.h"
-
#define MYSQLND_HEADER_SIZE 4
#define COMPRESSED_HEADER_SIZE 3
@@ -34,8 +31,8 @@ PHPAPI extern const char mysqlnd_read_body_name[];
/* Packet handling */
-#define PACKET_WRITE(packet, conn) ((packet)->header.m->write_to_net((packet), (conn)))
-#define PACKET_READ(packet, conn) ((packet)->header.m->read_from_net((packet), (conn)))
+#define PACKET_WRITE(packet) ((packet)->header.m->write_to_net((packet)))
+#define PACKET_READ(packet) ((packet)->header.m->read_from_net((packet)))
#define PACKET_FREE(packet) \
do { \
DBG_INF_FMT("PACKET_FREE(%p)", packet); \
@@ -49,17 +46,26 @@ PHPAPI extern const char * const mysqlnd_command_to_text[COM_END];
/* Low-level extraction functionality */
typedef struct st_mysqlnd_packet_methods {
size_t struct_size;
- enum_func_status (*read_from_net)(void * packet, MYSQLND_CONN_DATA * conn);
- size_t (*write_to_net)(void * packet, MYSQLND_CONN_DATA * conn);
+ enum_func_status (*read_from_net)(void * packet);
+ size_t (*write_to_net)(void * packet);
void (*free_mem)(void *packet, zend_bool stack_allocation);
} mysqlnd_packet_methods;
typedef struct st_mysqlnd_packet_header {
size_t size;
- mysqlnd_packet_methods *m;
zend_uchar packet_no;
zend_bool persistent;
+
+ mysqlnd_packet_methods *m;
+
+ MYSQLND_CONN_DATA * conn;
+ MYSQLND_PFC * protocol_frame_codec;
+ MYSQLND_VIO * vio;
+ MYSQLND_ERROR_INFO * error_info;
+ MYSQLND_STATS * stats;
+ MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * factory;
+ MYSQLND_CONNECTION_STATE * connection_state;
} MYSQLND_PACKET_HEADER;
/* Server greets the client */
@@ -68,9 +74,8 @@ typedef struct st_mysqlnd_packet_greet {
uint8_t protocol_version;
char *server_version;
uint32_t thread_id;
- zend_uchar intern_auth_plugin_data[SCRAMBLE_LENGTH];
- zend_uchar * auth_plugin_data;
- size_t auth_plugin_data_len;
+ char intern_auth_plugin_data[SCRAMBLE_LENGTH];
+ MYSQLND_STRING authentication_plugin_data;
/* 1 byte pad */
uint32_t server_capabilities;
uint8_t charset_no;
@@ -155,8 +160,7 @@ typedef struct st_mysqlnd_packet_ok {
typedef struct st_mysqlnd_packet_command {
MYSQLND_PACKET_HEADER header;
enum php_mysqlnd_server_command command;
- const zend_uchar *argument;
- size_t arg_len;
+ MYSQLND_CSTRING argument;
} MYSQLND_PACKET_COMMAND;
@@ -176,7 +180,7 @@ typedef struct st_mysqlnd_packet_eof {
/* Result Set header*/
typedef struct st_mysqlnd_packet_rset_header {
- MYSQLND_PACKET_HEADER header;
+ MYSQLND_PACKET_HEADER header;
/*
0x00 => ok
~0 => LOAD DATA LOCAL
@@ -193,8 +197,7 @@ typedef struct st_mysqlnd_packet_rset_header {
uint64_t affected_rows;
uint64_t last_insert_id;
/* This is for both LOAD DATA or info, when no result set */
- char *info_or_local_file;
- size_t info_or_local_file_len;
+ MYSQLND_STRING info_or_local_file;
/* If error packet, we use these */
MYSQLND_ERROR_INFO error_info;
} MYSQLND_PACKET_RSET_HEADER;
@@ -206,7 +209,6 @@ typedef struct st_mysqlnd_packet_res_field {
MYSQLND_FIELD *metadata;
/* For table definitions, empty for result sets */
zend_bool skip_parsing;
- zend_bool stupid_list_fields_eof;
zend_bool persistent_alloc;
MYSQLND_ERROR_INFO error_info;
@@ -233,9 +235,6 @@ typedef struct st_mysqlnd_packet_row {
zend_bool binary_protocol;
zend_bool persistent_alloc;
MYSQLND_FIELD *fields_metadata;
- /* We need this to alloc bigger bufs in non-PS mode */
- unsigned int bit_fields_count;
- size_t bit_fields_total_len; /* trailing \0 not counted */
/* If error packet, we use these */
MYSQLND_ERROR_INFO error_info;
@@ -245,9 +244,7 @@ typedef struct st_mysqlnd_packet_row {
/* Statistics packet */
typedef struct st_mysqlnd_packet_stats {
MYSQLND_PACKET_HEADER header;
- char *message;
- /* message_len is not part of the packet*/
- size_t message_len;
+ MYSQLND_STRING message;
} MYSQLND_PACKET_STATS;
@@ -296,13 +293,11 @@ typedef struct st_mysqlnd_packet_sha256_pk_request_response {
} MYSQLND_PACKET_SHA256_PK_REQUEST_RESPONSE;
-PHPAPI void php_mysqlnd_scramble(zend_uchar * const buffer, const zend_uchar * const scramble, const zend_uchar * const pass, size_t pass_len);
-
-zend_ulong php_mysqlnd_net_field_length(zend_uchar **packet);
-zend_uchar * php_mysqlnd_net_store_length(zend_uchar *packet, uint64_t length);
+zend_ulong php_mysqlnd_net_field_length(const zend_uchar **packet);
+zend_uchar * php_mysqlnd_net_store_length(zend_uchar *packet, const uint64_t length);
size_t php_mysqlnd_net_store_length_size(uint64_t length);
-PHPAPI const extern char * const mysqlnd_empty_string;
+PHPAPI extern const char * const mysqlnd_empty_string;
enum_func_status php_mysqlnd_rowp_read_binary_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval * fields,
unsigned int field_count, const MYSQLND_FIELD * fields_metadata,
@@ -318,8 +313,8 @@ enum_func_status php_mysqlnd_rowp_read_text_protocol_c(MYSQLND_MEMORY_POOL_CHUNK
zend_bool as_int_or_float, MYSQLND_STATS * stats);
-PHPAPI MYSQLND_PROTOCOL * mysqlnd_protocol_init(zend_bool persistent);
-PHPAPI void mysqlnd_protocol_free(MYSQLND_PROTOCOL * const protocol);
+PHPAPI MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * mysqlnd_protocol_payload_decoder_factory_init(MYSQLND_CONN_DATA * conn, const zend_bool persistent);
+PHPAPI void mysqlnd_protocol_payload_decoder_factory_free(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const payload_decoder_factory);
#endif /* MYSQLND_WIREPROTOCOL_H */
diff --git a/ext/mysqlnd/php_mysqlnd.c b/ext/mysqlnd/php_mysqlnd.c
index bb1517de35..5eb404349c 100644
--- a/ext/mysqlnd/php_mysqlnd.c
+++ b/ext/mysqlnd/php_mysqlnd.c
@@ -14,12 +14,10 @@
+----------------------------------------------------------------------+
| Authors: Andrey Hristov <andrey@php.net> |
| Ulf Wendel <uw@php.net> |
- | Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
#include "php.h"
-#include "php_ini.h"
#include "mysqlnd.h"
#include "mysqlnd_priv.h"
#include "mysqlnd_debug.h"
diff --git a/ext/mysqlnd/php_mysqlnd.h b/ext/mysqlnd/php_mysqlnd.h
index d0c919925b..5b9485ee0d 100644
--- a/ext/mysqlnd/php_mysqlnd.h
+++ b/ext/mysqlnd/php_mysqlnd.h
@@ -14,7 +14,6 @@
+----------------------------------------------------------------------+
| Authors: Andrey Hristov <andrey@php.net> |
| Ulf Wendel <uw@php.net> |
- | Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
diff --git a/ext/odbc/birdstep.c b/ext/odbc/birdstep.c
index 1cfdd7b34c..7ce35eb88c 100644
--- a/ext/odbc/birdstep.c
+++ b/ext/odbc/birdstep.c
@@ -136,7 +136,7 @@ const zend_function_entry birdstep_functions[] = {
PHP_FALIAS(velocis_fieldnum, birdstep_fieldnum, arginfo_birdstep_fieldnum)
PHP_FALIAS(velocis_fieldname, birdstep_fieldname, arginfo_birdstep_fieldname)
/* End temporary aliases */
- {NULL, NULL, NULL}
+ PHP_FE_END
};
zend_module_entry birdstep_module_entry = {
diff --git a/ext/odbc/config.m4 b/ext/odbc/config.m4
index 5aa7efa376..ecee15fd1c 100644
--- a/ext/odbc/config.m4
+++ b/ext/odbc/config.m4
@@ -101,8 +101,8 @@ dnl configure options
dnl
PHP_ARG_WITH(odbcver,,
-[ --with-odbcver[=HEX] Force support for the passed ODBC version. A hex number is expected, default 0x0300.
- Use the special value of 0 to prevent an explicit ODBCVER to be defined. ], 0x0300)
+[ --with-odbcver[=HEX] Force support for the passed ODBC version. A hex number is expected, default 0x0350.
+ Use the special value of 0 to prevent an explicit ODBCVER to be defined. ], 0x0350)
if test -z "$ODBC_TYPE"; then
PHP_ARG_WITH(adabas,,
diff --git a/ext/odbc/config.w32 b/ext/odbc/config.w32
index 3c238c66de..b710ccd14b 100644
--- a/ext/odbc/config.w32
+++ b/ext/odbc/config.w32
@@ -2,7 +2,7 @@
// vim:ft=javascript
ARG_ENABLE("odbc", "ODBC support", "no");
-ARG_WITH("odbcver", "Force support for the passed ODBC version. A hex number is expected, default 0x0300. Use the special value of 0 to prevent an explicit ODBCVER to be defined.", "0x0300");
+ARG_WITH("odbcver", "Force support for the passed ODBC version. A hex number is expected, default 0x0350. Use the special value of 0 to prevent an explicit ODBCVER to be defined.", "0x0350");
if (PHP_ODBC == "yes") {
if (CHECK_LIB("odbc32.lib", "odbc") && CHECK_LIB("odbccp32.lib", "odbc")
diff --git a/ext/opcache/Optimizer/block_pass.c b/ext/opcache/Optimizer/block_pass.c
index acce8b70d0..ee4b5516a9 100644
--- a/ext/opcache/Optimizer/block_pass.c
+++ b/ext/opcache/Optimizer/block_pass.c
@@ -27,8 +27,8 @@
#include "zend_execute.h"
#include "zend_vm.h"
#include "zend_bitset.h"
-
-#define DEBUG_BLOCKPASS 0
+#include "zend_cfg.h"
+#include "zend_dump.h"
/* Checks if a constant (like "true") may be replaced by its value */
int zend_optimizer_get_persistent_constant(zend_string *name, zval *result, int copy)
@@ -39,7 +39,7 @@ int zend_optimizer_get_persistent_constant(zend_string *name, zval *result, int
ALLOCA_FLAG(use_heap);
if ((c = zend_hash_find_ptr(EG(zend_constants), name)) == NULL) {
- lookup_name = DO_ALLOCA(ZSTR_LEN(name) + 1);
+ lookup_name = do_alloca(ZSTR_LEN(name) + 1, use_heap);
memcpy(lookup_name, ZSTR_VAL(name), ZSTR_LEN(name) + 1);
zend_str_tolower(lookup_name, ZSTR_LEN(name));
@@ -50,7 +50,7 @@ int zend_optimizer_get_persistent_constant(zend_string *name, zval *result, int
} else {
retval = 0;
}
- FREE_ALLOCA(lookup_name);
+ free_alloca(lookup_name, use_heap);
}
if (retval) {
@@ -67,483 +67,10 @@ int zend_optimizer_get_persistent_constant(zend_string *name, zval *result, int
return retval;
}
-#if DEBUG_BLOCKPASS
-# define BLOCK_REF(b) b?op_array->opcodes-b->start_opline:-1
-
-static inline void print_block(zend_code_block *block, zend_op *opcodes, char *txt)
-{
- fprintf(stderr, "%sBlock: %d-%d (%d)", txt, block->start_opline - opcodes, block->start_opline - opcodes + block->len - 1, block->len);
- if (!block->access) {
- fprintf(stderr, " unused");
- }
- if (block->op1_to) {
- fprintf(stderr, " 1: %d", block->op1_to->start_opline - opcodes);
- }
- if (block->op2_to) {
- fprintf(stderr, " 2: %d", block->op2_to->start_opline - opcodes);
- }
- if (block->ext_to) {
- fprintf(stderr, " e: %d", block->ext_to->start_opline - opcodes);
- }
- if (block->follow_to) {
- fprintf(stderr, " f: %d", block->follow_to->start_opline - opcodes);
- }
-
- if (block->sources) {
- zend_block_source *bs = block->sources;
- fprintf(stderr, " s:");
- while (bs) {
- fprintf(stderr, " %d", bs->from->start_opline - opcodes);
- bs = bs->next;
- }
- }
-
- fprintf(stderr, "\n");
- fflush(stderr);
-}
-#else
-#define print_block(a,b,c)
-#endif
-
-#define START_BLOCK_OP(opno) blocks[opno].start_opline = &op_array->opcodes[opno]; blocks[opno].start_opline_no = opno; blocks[opno].access = 1
-
-/* find code blocks in op_array
- code block is a set of opcodes with single flow of control, i.e. without jmps,
- branches, etc. */
-static int find_code_blocks(zend_op_array *op_array, zend_cfg *cfg, zend_optimizer_ctx *ctx)
-{
- zend_op *opline;
- zend_op *end = op_array->opcodes + op_array->last;
- zend_code_block *blocks, *cur_block;
- uint32_t opno = 0;
-
- memset(cfg, 0, sizeof(zend_cfg));
- blocks = cfg->blocks = zend_arena_calloc(&ctx->arena, op_array->last + 2, sizeof(zend_code_block));
- opline = op_array->opcodes;
- blocks[0].start_opline = opline;
- blocks[0].start_opline_no = 0;
- while (opline < end) {
- switch((unsigned)opline->opcode) {
- case ZEND_FAST_CALL:
- START_BLOCK_OP(ZEND_OP1(opline).opline_num);
- if (opline->extended_value) {
- START_BLOCK_OP(ZEND_OP2(opline).opline_num);
- }
- START_BLOCK_OP(opno + 1);
- break;
- case ZEND_FAST_RET:
- if (opline->extended_value) {
- START_BLOCK_OP(ZEND_OP2(opline).opline_num);
- }
- START_BLOCK_OP(opno + 1);
- break;
- case ZEND_JMP:
- case ZEND_DECLARE_ANON_CLASS:
- case ZEND_DECLARE_ANON_INHERITED_CLASS:
- START_BLOCK_OP(ZEND_OP1(opline).opline_num);
- /* break missing intentionally */
- case ZEND_RETURN:
- case ZEND_RETURN_BY_REF:
- case ZEND_GENERATOR_RETURN:
- case ZEND_EXIT:
- case ZEND_THROW:
- /* start new block from this+1 */
- START_BLOCK_OP(opno + 1);
- break;
- /* TODO: if conditional jmp depends on constant,
- don't start block that won't be executed */
- case ZEND_CATCH:
- START_BLOCK_OP(opline->extended_value);
- START_BLOCK_OP(opno + 1);
- break;
- case ZEND_JMPZNZ:
- START_BLOCK_OP(opline->extended_value);
- case ZEND_JMPZ:
- case ZEND_JMPNZ:
- case ZEND_JMPZ_EX:
- case ZEND_JMPNZ_EX:
- case ZEND_FE_RESET_R:
- case ZEND_FE_RESET_RW:
- case ZEND_NEW:
- case ZEND_JMP_SET:
- case ZEND_COALESCE:
- case ZEND_ASSERT_CHECK:
- START_BLOCK_OP(ZEND_OP2(opline).opline_num);
- START_BLOCK_OP(opno + 1);
- break;
- case ZEND_FE_FETCH_R:
- case ZEND_FE_FETCH_RW:
- START_BLOCK_OP(opline->extended_value);
- START_BLOCK_OP(opno + 1);
- break;
- }
- opno++;
- opline++;
- }
-
- /* first find block start points */
- if (op_array->last_try_catch) {
- int i;
- cfg->try = zend_arena_calloc(&ctx->arena, op_array->last_try_catch, sizeof(zend_code_block *));
- cfg->catch = zend_arena_calloc(&ctx->arena, op_array->last_try_catch, sizeof(zend_code_block *));
- for (i = 0; i< op_array->last_try_catch; i++) {
- cfg->try[i] = &blocks[op_array->try_catch_array[i].try_op];
- cfg->catch[i] = &blocks[op_array->try_catch_array[i].catch_op];
- START_BLOCK_OP(op_array->try_catch_array[i].try_op);
- START_BLOCK_OP(op_array->try_catch_array[i].catch_op);
- blocks[op_array->try_catch_array[i].try_op].protected = 1;
- }
- }
- /* Currently, we don't optimize op_arrays with BRK/CONT/GOTO opcodes,
- * but, we have to keep brk_cont_array to avoid memory leaks during
- * exception handling */
- if (op_array->last_brk_cont) {
- int i, j;
-
- j = 0;
- for (i = 0; i< op_array->last_brk_cont; i++) {
- if (op_array->brk_cont_array[i].start >= 0 &&
- (op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FREE ||
- op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FE_FREE ||
- op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_ROPE_END ||
- op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_END_SILENCE)) {
- int parent = op_array->brk_cont_array[i].parent;
-
- while (parent >= 0 &&
- op_array->brk_cont_array[parent].start < 0 &&
- (op_array->opcodes[op_array->brk_cont_array[parent].brk].opcode != ZEND_FREE ||
- op_array->opcodes[op_array->brk_cont_array[parent].brk].opcode != ZEND_FE_FREE ||
- op_array->opcodes[op_array->brk_cont_array[i].brk].opcode != ZEND_ROPE_END ||
- op_array->opcodes[op_array->brk_cont_array[parent].brk].opcode != ZEND_END_SILENCE)) {
- parent = op_array->brk_cont_array[parent].parent;
- }
- op_array->brk_cont_array[i].parent = parent;
- j++;
- }
- }
- if (j) {
- cfg->loop_start = zend_arena_calloc(&ctx->arena, op_array->last_brk_cont, sizeof(zend_code_block *));
- cfg->loop_cont = zend_arena_calloc(&ctx->arena, op_array->last_brk_cont, sizeof(zend_code_block *));
- cfg->loop_brk = zend_arena_calloc(&ctx->arena, op_array->last_brk_cont, sizeof(zend_code_block *));
- j = 0;
- for (i = 0; i< op_array->last_brk_cont; i++) {
- if (op_array->brk_cont_array[i].start >= 0 &&
- (op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FREE ||
- op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FE_FREE ||
- op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_ROPE_END ||
- op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_END_SILENCE)) {
- if (i != j) {
- op_array->brk_cont_array[j] = op_array->brk_cont_array[i];
- }
- cfg->loop_start[j] = &blocks[op_array->brk_cont_array[j].start];
- cfg->loop_cont[j] = &blocks[op_array->brk_cont_array[j].cont];
- cfg->loop_brk[j] = &blocks[op_array->brk_cont_array[j].brk];
- START_BLOCK_OP(op_array->brk_cont_array[j].start);
- START_BLOCK_OP(op_array->brk_cont_array[j].cont);
- START_BLOCK_OP(op_array->brk_cont_array[j].brk);
- blocks[op_array->brk_cont_array[j].start].protected = 1;
- blocks[op_array->brk_cont_array[j].brk].protected = 1;
- j++;
- }
- }
- op_array->last_brk_cont = j;
- } else {
- efree(op_array->brk_cont_array);
- op_array->brk_cont_array = NULL;
- op_array->last_brk_cont = 0;
- }
- }
-
- /* Build CFG (Control Flow Graph) */
- cur_block = blocks;
- for (opno = 1; opno < op_array->last; opno++) {
- if (blocks[opno].start_opline) {
- /* found new block start */
- cur_block->len = blocks[opno].start_opline - cur_block->start_opline;
- cur_block->next = &blocks[opno];
- /* what is the last OP of previous block? */
- opline = blocks[opno].start_opline - 1;
- if (opline->opcode == ZEND_OP_DATA) {
- opline--;
- }
- switch((unsigned)opline->opcode) {
- case ZEND_RETURN:
- case ZEND_RETURN_BY_REF:
- case ZEND_GENERATOR_RETURN:
- case ZEND_EXIT:
- case ZEND_THROW:
- break;
- case ZEND_FAST_CALL:
- if (opline->extended_value) {
- cur_block->op2_to = &blocks[ZEND_OP2(opline).opline_num];
- }
- cur_block->op1_to = &blocks[ZEND_OP1(opline).opline_num];
- break;
- case ZEND_FAST_RET:
- if (opline->extended_value) {
- cur_block->op2_to = &blocks[ZEND_OP2(opline).opline_num];
- }
- break;
- case ZEND_JMP:
- cur_block->op1_to = &blocks[ZEND_OP1(opline).opline_num];
- break;
- case ZEND_DECLARE_ANON_CLASS:
- case ZEND_DECLARE_ANON_INHERITED_CLASS:
- cur_block->op1_to = &blocks[ZEND_OP1(opline).opline_num];
- cur_block->follow_to = &blocks[opno];
- break;
- case ZEND_JMPZNZ:
- cur_block->op2_to = &blocks[ZEND_OP2(opline).opline_num];
- cur_block->ext_to = &blocks[opline->extended_value];
- break;
- case ZEND_CATCH:
- cur_block->ext_to = &blocks[opline->extended_value];
- cur_block->follow_to = &blocks[opno];
- break;
- case ZEND_FE_FETCH_R:
- case ZEND_FE_FETCH_RW:
- cur_block->ext_to = &blocks[opline->extended_value];
- cur_block->follow_to = &blocks[opno];
- break;
- case ZEND_JMPZ:
- case ZEND_JMPNZ:
- case ZEND_JMPZ_EX:
- case ZEND_JMPNZ_EX:
- case ZEND_FE_RESET_R:
- case ZEND_FE_RESET_RW:
- case ZEND_NEW:
- case ZEND_JMP_SET:
- case ZEND_COALESCE:
- case ZEND_ASSERT_CHECK:
- cur_block->op2_to = &blocks[ZEND_OP2(opline).opline_num];
- /* break missing intentionally */
- default:
- /* next block follows this */
- cur_block->follow_to = &blocks[opno];
- break;
- }
- print_block(cur_block, op_array->opcodes, "");
- cur_block = cur_block->next;
- }
- }
- cur_block->len = end - cur_block->start_opline;
- cur_block->next = &blocks[op_array->last + 1];
- print_block(cur_block, op_array->opcodes, "");
-
- return 1;
-}
-
/* CFG back references management */
-#define ADD_SOURCE(fromb, tob) { \
- zend_block_source *__s = tob->sources; \
- while (__s && __s->from != fromb) __s = __s->next; \
- if (__s == NULL) { \
- zend_block_source *__t = zend_arena_alloc(&ctx->arena, sizeof(zend_block_source)); \
- __t->next = tob->sources; \
- tob->sources = __t; \
- __t->from = fromb; \
- } \
-}
-
-#define DEL_SOURCE(cs) do { \
- *(cs) = (*(cs))->next; \
- } while (0)
-
-
-static inline void replace_source(zend_block_source *list, zend_code_block *old, zend_code_block *new)
-{
- /* replace all references to 'old' in 'list' with 'new' */
- zend_block_source **cs;
- int found = 0;
-
- for (cs = &list; *cs; cs = &((*cs)->next)) {
- if ((*cs)->from == new) {
- if (found) {
- DEL_SOURCE(cs);
- } else {
- found = 1;
- }
- }
-
- if ((*cs)->from == old) {
- if (found) {
- DEL_SOURCE(cs);
- } else {
- (*cs)->from = new;
- found = 1;
- }
- }
- }
-}
-
-static inline void del_source(zend_code_block *from, zend_code_block *to)
-{
- /* delete source 'from' from 'to'-s sources list */
- zend_block_source **cs = &to->sources;
-
- if (to->sources == NULL) {
- to->access = 0;
- return;
- }
-
- if (from == to) {
- return;
- }
-
- while (*cs) {
- if ((*cs)->from == from) {
- DEL_SOURCE(cs);
- break;
- }
- cs = &((*cs)->next);
- }
-
- if (to->sources == NULL) {
- /* 'to' has no more sources - it's unused, will be stripped */
- to->access = 0;
- return;
- }
-
- if (!to->protected && to->sources->next == NULL) {
- /* source to only one block */
- zend_code_block *from_block = to->sources->from;
-
- if (from_block->access && from_block->follow_to == to &&
- from_block->op1_to == NULL &&
- from_block->op2_to == NULL &&
- from_block->ext_to == NULL) {
- /* this block follows it's only predecessor - we can join them */
- zend_op *new_to = from_block->start_opline + from_block->len;
- if (new_to != to->start_opline) {
- /* move block to new location */
- memmove(new_to, to->start_opline, sizeof(zend_op)*to->len);
- }
- /* join blocks' lengths */
- from_block->len += to->len;
- /* move 'to'`s references to 'from' */
- to->start_opline = NULL;
- to->access = 0;
- to->sources = NULL;
- from_block->follow_to = to->follow_to;
- if (to->op1_to) {
- from_block->op1_to = to->op1_to;
- replace_source(to->op1_to->sources, to, from_block);
- }
- if (to->op2_to) {
- from_block->op2_to = to->op2_to;
- replace_source(to->op2_to->sources, to, from_block);
- }
- if (to->ext_to) {
- from_block->ext_to = to->ext_to;
- replace_source(to->ext_to->sources, to, from_block);
- }
- if (to->follow_to) {
- replace_source(to->follow_to->sources, to, from_block);
- }
- /* remove "to" from list */
- }
- }
-}
-
-static void delete_code_block(zend_code_block *block, zend_optimizer_ctx *ctx)
-{
- if (block->protected) {
- return;
- }
- if (block->follow_to) {
- zend_block_source *bs = block->sources;
- while (bs) {
- zend_code_block *from_block = bs->from;
- zend_code_block *to = block->follow_to;
- if (from_block->op1_to == block) {
- from_block->op1_to = to;
- ADD_SOURCE(from_block, to);
- }
- if (from_block->op2_to == block) {
- from_block->op2_to = to;
- ADD_SOURCE(from_block, to);
- }
- if (from_block->ext_to == block) {
- from_block->ext_to = to;
- ADD_SOURCE(from_block, to);
- }
- if (from_block->follow_to == block) {
- from_block->follow_to = to;
- ADD_SOURCE(from_block, to);
- }
- bs = bs->next;
- }
- }
- block->access = 0;
-}
-
-static void zend_access_path(zend_code_block *block, zend_optimizer_ctx *ctx)
-{
- if (block->access) {
- return;
- }
-
- block->access = 1;
- if (block->op1_to) {
- zend_access_path(block->op1_to, ctx);
- ADD_SOURCE(block, block->op1_to);
- }
- if (block->op2_to) {
- zend_access_path(block->op2_to, ctx);
- ADD_SOURCE(block, block->op2_to);
- }
- if (block->ext_to) {
- zend_access_path(block->ext_to, ctx);
- ADD_SOURCE(block, block->ext_to);
- }
- if (block->follow_to) {
- zend_access_path(block->follow_to, ctx);
- ADD_SOURCE(block, block->follow_to);
- }
-}
-
-/* Traverse CFG, mark reachable basic blocks and build back references */
-static void zend_rebuild_access_path(zend_cfg *cfg, zend_op_array *op_array, int find_start, zend_optimizer_ctx *ctx)
-{
- zend_code_block *blocks = cfg->blocks;
- zend_code_block *start = find_start? NULL : blocks;
- zend_code_block *b;
-
- /* Mark all blocks as unaccessible and destroy back references */
- b = blocks;
- while (b != NULL) {
- if (!start && b->access) {
- start = b;
- }
- b->access = 0;
- b->sources = NULL;
- b = b->next;
- }
-
- /* Walk thorough all paths */
- zend_access_path(start, ctx);
-
- /* Add brk/cont paths */
- if (op_array->last_brk_cont) {
- int i;
- for (i=0; i< op_array->last_brk_cont; i++) {
- zend_access_path(cfg->loop_start[i], ctx);
- zend_access_path(cfg->loop_cont[i], ctx);
- zend_access_path(cfg->loop_brk[i], ctx);
- }
- }
-
- /* Add exception paths */
- if (op_array->last_try_catch) {
- int i;
- for (i=0; i< op_array->last_try_catch; i++) {
- if (!cfg->catch[i]->access) {
- zend_access_path(cfg->catch[i], ctx);
- }
- }
- }
-}
+#define DEL_SOURCE(from, to)
+#define ADD_SOURCE(from, to)
/* Data dependencies macros */
@@ -552,8 +79,6 @@ static void zend_rebuild_access_path(zend_cfg *cfg, zend_op_array *op_array, int
#define VAR_SOURCE(op) Tsource[VAR_NUM(op.var)]
#define SET_VAR_SOURCE(opline) Tsource[VAR_NUM(opline->result.var)] = opline
-#define VAR_UNSET(op) do { if (op ## _type & (IS_TMP_VAR|IS_VAR)) {VAR_SOURCE(op) = NULL;}} while (0)
-
#define convert_to_string_safe(v) \
if (Z_TYPE_P((v)) == IS_NULL) { \
ZVAL_STRINGL((v), "", 0); \
@@ -561,183 +86,205 @@ static void zend_rebuild_access_path(zend_cfg *cfg, zend_op_array *op_array, int
convert_to_string((v)); \
}
-static int is_predecessor_smart_branch(zend_op *start, zend_op *predecessor) {
- do {
- if (predecessor == start) {
- return 0;
+static void strip_leading_nops(zend_op_array *op_array, zend_basic_block *b)
+{
+ zend_op *opcodes = op_array->opcodes;
+
+ while (b->len > 0 && opcodes[b->start].opcode == ZEND_NOP) {
+ /* check if NOP breaks incorrect smart branch */
+ if (b->len == 2
+ && (op_array->opcodes[b->start + 1].opcode == ZEND_JMPZ
+ || op_array->opcodes[b->start + 1].opcode == ZEND_JMPNZ)
+ && (op_array->opcodes[b->start + 1].op1_type & (IS_CV|IS_CONST))
+ && b->start > 0
+ && zend_is_smart_branch(op_array->opcodes + b->start - 1)) {
+ break;
}
- predecessor--;
- } while (predecessor->opcode == ZEND_NOP);
-
- return zend_is_smart_branch(predecessor);
+ b->start++;
+ b->len--;
+ }
}
-static void strip_nop(zend_code_block *block, zend_op_array *op_array, zend_optimizer_ctx *ctx)
+static void strip_nops(zend_op_array *op_array, zend_basic_block *b)
{
- zend_op *opline = block->start_opline;
- zend_op *end, *new_end;
+ uint32_t i, j;
- /* remove leading NOPs */
- while (block->len > 0 && block->start_opline->opcode == ZEND_NOP) {
- if (block->len == 1) {
- /* this block is all NOPs, join with following block */
- if (block->follow_to) {
- delete_code_block(block, ctx);
- }
- return;
- }
- if (block->len == 2
- && ((block->start_opline + 1)->opcode == ZEND_JMPZ
- || (block->start_opline + 1)->opcode == ZEND_JMPNZ)
- && (block->start_opline + 1)->op1_type & (IS_CV|IS_CONST)
- && block->start_opline > op_array->opcodes
- && zend_is_smart_branch(block->start_opline - 1)) {
- break;
- }
- block->start_opline++;
- block->start_opline_no++;
- block->len--;
+ strip_leading_nops(op_array, b);
+ if (b->len == 0) {
+ return;
}
/* strip the inside NOPs */
- opline = new_end = block->start_opline;
- end = opline + block->len;
-
- while (opline < end) {
- zend_op *src;
- int len = 0;
-
- src = opline;
- while (opline < end && opline->opcode == ZEND_NOP) {
- if (opline + 1 < end
- && ((opline + 1)->opcode == ZEND_JMPZ
- || (opline + 1)->opcode == ZEND_JMPNZ)
- && (opline + 1)->op1_type & (IS_CV|IS_CONST)
- && is_predecessor_smart_branch(op_array->opcodes, opline)) {
- /* don't remove NOP, that splits incorrect smart branch */
- opline++;
- break;
+ i = j = b->start + 1;
+ while (i < b->start + b->len) {
+ if (op_array->opcodes[i].opcode != ZEND_NOP) {
+ if (i != j) {
+ op_array->opcodes[j] = op_array->opcodes[i];
}
- src++;
- opline++;
+ j++;
}
-
- while (opline < end && opline->opcode != ZEND_NOP) {
- opline++;
+ if (i + 1 < b->start + b->len
+ && (op_array->opcodes[i+1].opcode == ZEND_JMPZ
+ || op_array->opcodes[i+1].opcode == ZEND_JMPNZ)
+ && op_array->opcodes[i+1].op1_type & (IS_CV|IS_CONST)
+ && zend_is_smart_branch(op_array->opcodes + j - 1)) {
+ /* don't remove NOP, that splits incorrect smart branch */
+ j++;
}
- len = opline - src;
-
- /* move up non-NOP opcodes */
- memmove(new_end, src, len*sizeof(zend_op));
-
- new_end += len;
+ i++;
+ }
+ b->len = j - b->start;
+ while (j < i) {
+ MAKE_NOP(op_array->opcodes + j);
+ j++;
}
- block->len = new_end - block->start_opline;
}
-static void zend_optimize_block(zend_code_block *block, zend_op_array *op_array, zend_bitset used_ext, zend_cfg *cfg, zend_optimizer_ctx *ctx)
+static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array, zend_bitset used_ext, zend_cfg *cfg, zend_op **Tsource)
{
- zend_op *opline = block->start_opline;
+ zend_op *opline, *src;
zend_op *end, *last_op = NULL;
- zend_op **Tsource = cfg->Tsource;
-
- print_block(block, op_array->opcodes, "Opt ");
/* remove leading NOPs */
- while (block->len > 0 && block->start_opline->opcode == ZEND_NOP) {
- if (block->len == 1) {
- /* this block is all NOPs, join with following block */
- if (block->follow_to) {
- delete_code_block(block, ctx);
- }
- if (block->len == 2
- && ((block->start_opline + 1)->opcode == ZEND_JMPZ
- || (block->start_opline + 1)->opcode == ZEND_JMPNZ)
- && (block->start_opline + 1)->op1_type & (IS_CV|IS_CONST)
- && block->start_opline > op_array->opcodes
- && zend_is_smart_branch(block->start_opline - 1)) {
- break;
- }
- return;
- }
- block->start_opline++;
- block->start_opline_no++;
- block->len--;
- }
+ strip_leading_nops(op_array, block);
- /* we track data dependencies only insight a single basic block */
- memset(Tsource, 0, (op_array->last_var + op_array->T) * sizeof(zend_op *));
- opline = block->start_opline;
+ opline = op_array->opcodes + block->start;
end = opline + block->len;
- while ((op_array->T) && (opline < end)) {
- /* strip X = QM_ASSIGN(const) */
- if ((ZEND_OP1_TYPE(opline) & (IS_TMP_VAR|IS_VAR)) &&
- VAR_SOURCE(opline->op1) &&
- VAR_SOURCE(opline->op1)->opcode == ZEND_QM_ASSIGN &&
- ZEND_OP1_TYPE(VAR_SOURCE(opline->op1)) == IS_CONST &&
- opline->opcode != ZEND_CASE && /* CASE _always_ expects variable */
- opline->opcode != ZEND_FETCH_LIST &&
- (opline->opcode != ZEND_FE_RESET_R || opline->opcode != ZEND_FE_RESET_RW) &&
- opline->opcode != ZEND_FREE
+ while (opline < end) {
+ /* Constant Propagation: strip X = QM_ASSIGN(const) */
+ if ((opline->op1_type & (IS_TMP_VAR|IS_VAR)) &&
+ opline->opcode != ZEND_FREE) {
+ src = VAR_SOURCE(opline->op1);
+ if (src &&
+ src->opcode == ZEND_QM_ASSIGN &&
+ src->op1_type == IS_CONST
) {
- znode_op op1 = opline->op1;
- zend_op *src = VAR_SOURCE(op1);
- zval c = ZEND_OP1_LITERAL(src);
- zval_copy_ctor(&c);
- if (zend_optimizer_update_op1_const(op_array, opline, &c)) {
- VAR_SOURCE(op1) = NULL;
- literal_dtor(&ZEND_OP1_LITERAL(src));
- MAKE_NOP(src);
+ znode_op op1 = opline->op1;
+ if (opline->opcode == ZEND_VERIFY_RETURN_TYPE) {
+ COPY_NODE(opline->result, opline->op1);
+ COPY_NODE(opline->op1, src->op1);
+ VAR_SOURCE(op1) = NULL;
+ MAKE_NOP(src);
+ } else if (opline->opcode != ZEND_FETCH_LIST && opline->opcode != ZEND_CASE) {
+ zval c = ZEND_OP1_LITERAL(src);
+ zval_copy_ctor(&c);
+ if (zend_optimizer_update_op1_const(op_array, opline, &c)) {
+ zend_optimizer_remove_live_range(op_array, op1.var);
+ VAR_SOURCE(op1) = NULL;
+ literal_dtor(&ZEND_OP1_LITERAL(src));
+ MAKE_NOP(src);
+ }
+ }
}
}
- /* T = QM_ASSIGN(C), F(T) => NOP, F(C) */
- if ((ZEND_OP2_TYPE(opline) & (IS_TMP_VAR|IS_VAR)) &&
- VAR_SOURCE(opline->op2) &&
- VAR_SOURCE(opline->op2)->opcode == ZEND_QM_ASSIGN &&
- ZEND_OP1_TYPE(VAR_SOURCE(opline->op2)) == IS_CONST) {
- znode_op op2 = opline->op2;
- zend_op *src = VAR_SOURCE(op2);
- zval c = ZEND_OP1_LITERAL(src);
- zval_copy_ctor(&c);
- if (zend_optimizer_update_op2_const(op_array, opline, &c)) {
- VAR_SOURCE(op2) = NULL;
- literal_dtor(&ZEND_OP1_LITERAL(src));
- MAKE_NOP(src);
- }
- }
+ /* Constant Propagation: strip X = QM_ASSIGN(const) */
+ if (opline->op2_type & (IS_TMP_VAR|IS_VAR)) {
+ src = VAR_SOURCE(opline->op2);
+ if (src &&
+ src->opcode == ZEND_QM_ASSIGN &&
+ src->op1_type == IS_CONST) {
- /* T = CAST(X, String), ECHO(T) => NOP, ECHO(X) */
- if (opline->opcode == ZEND_ECHO &&
- ZEND_OP1_TYPE(opline) & (IS_TMP_VAR|IS_VAR) &&
- VAR_SOURCE(opline->op1) &&
- VAR_SOURCE(opline->op1)->opcode == ZEND_CAST &&
- VAR_SOURCE(opline->op1)->extended_value == IS_STRING) {
- zend_op *src = VAR_SOURCE(opline->op1);
- COPY_NODE(opline->op1, src->op1);
- MAKE_NOP(src);
- }
+ znode_op op2 = opline->op2;
+ zval c = ZEND_OP1_LITERAL(src);
-#if 0
- /* This pattern is unnecessary for PHP7,
- * since compiler won't generate ZEND_FREE for ZEND_BOOL anymore */
- /* T = BOOL(X), FREE(T) => NOP */
- if (opline->opcode == ZEND_FREE &&
- ZEND_OP1_TYPE(opline) == IS_TMP_VAR &&
- VAR_SOURCE(opline->op1)) {
- zend_op *src = VAR_SOURCE(opline->op1);
- if (src->opcode == ZEND_BOOL) {
- if (ZEND_OP1_TYPE(src) == IS_CONST) {
+ zval_copy_ctor(&c);
+ if (zend_optimizer_update_op2_const(op_array, opline, &c)) {
+ zend_optimizer_remove_live_range(op_array, op2.var);
+ VAR_SOURCE(op2) = NULL;
literal_dtor(&ZEND_OP1_LITERAL(src));
- } else if (ZEND_OP1_TYPE(src) == IS_TMP_VAR) {
- src->opcode = ZEND_FREE;
- } else {
MAKE_NOP(src);
}
- MAKE_NOP(opline);
}
}
+ if (opline->opcode == ZEND_ECHO) {
+ if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
+ src = VAR_SOURCE(opline->op1);
+ if (src &&
+ src->opcode == ZEND_CAST &&
+ src->extended_value == IS_STRING) {
+ /* T = CAST(X, String), ECHO(T) => NOP, ECHO(X) */
+ VAR_SOURCE(opline->op1) = NULL;
+ COPY_NODE(opline->op1, src->op1);
+ MAKE_NOP(src);
+ }
+ }
+
+ if (opline->op1_type == IS_CONST) {
+ if (last_op && last_op->opcode == ZEND_ECHO &&
+ last_op->op1_type == IS_CONST &&
+ Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_DOUBLE &&
+ Z_TYPE(ZEND_OP1_LITERAL(last_op)) != IS_DOUBLE) {
+ /* compress consecutive ECHO's.
+ * Float to string conversion may be affected by current
+ * locale setting.
+ */
+ int l, old_len;
+
+ if (Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_STRING) {
+ convert_to_string_safe(&ZEND_OP1_LITERAL(opline));
+ }
+ if (Z_TYPE(ZEND_OP1_LITERAL(last_op)) != IS_STRING) {
+ convert_to_string_safe(&ZEND_OP1_LITERAL(last_op));
+ }
+ old_len = Z_STRLEN(ZEND_OP1_LITERAL(last_op));
+ l = old_len + Z_STRLEN(ZEND_OP1_LITERAL(opline));
+ if (!Z_REFCOUNTED(ZEND_OP1_LITERAL(last_op))) {
+ zend_string *tmp = zend_string_alloc(l, 0);
+ memcpy(ZSTR_VAL(tmp), Z_STRVAL(ZEND_OP1_LITERAL(last_op)), old_len);
+ Z_STR(ZEND_OP1_LITERAL(last_op)) = tmp;
+ } else {
+ Z_STR(ZEND_OP1_LITERAL(last_op)) = zend_string_extend(Z_STR(ZEND_OP1_LITERAL(last_op)), l, 0);
+ }
+ Z_TYPE_INFO(ZEND_OP1_LITERAL(last_op)) = IS_STRING_EX;
+ memcpy(Z_STRVAL(ZEND_OP1_LITERAL(last_op)) + old_len, Z_STRVAL(ZEND_OP1_LITERAL(opline)), Z_STRLEN(ZEND_OP1_LITERAL(opline)));
+ Z_STRVAL(ZEND_OP1_LITERAL(last_op))[l] = '\0';
+ zval_dtor(&ZEND_OP1_LITERAL(opline));
+ ZVAL_STR(&ZEND_OP1_LITERAL(opline), zend_new_interned_string(Z_STR(ZEND_OP1_LITERAL(last_op))));
+ ZVAL_NULL(&ZEND_OP1_LITERAL(last_op));
+ MAKE_NOP(last_op);
+ }
+ last_op = opline;
+ } else {
+ last_op = NULL;
+ }
+ } else {
+ last_op = NULL;
+ }
+
+ switch (opline->opcode) {
+
+ case ZEND_FREE:
+ if (opline->op1_type == IS_TMP_VAR) {
+ src = VAR_SOURCE(opline->op1);
+ if (src &&
+ (src->opcode == ZEND_BOOL || src->opcode == ZEND_BOOL_NOT)) {
+ /* T = BOOL(X), FREE(T) => T = BOOL(X) */
+ /* The remaining BOOL is removed by a separate optimization */
+ VAR_SOURCE(opline->op1) = NULL;
+ MAKE_NOP(opline);
+ }
+ } else if (opline->op1_type == IS_VAR) {
+ src = VAR_SOURCE(opline->op1);
+ /* V = OP, FREE(V) => OP. NOP */
+ if (src &&
+ src->opcode != ZEND_FETCH_R &&
+ src->opcode != ZEND_FETCH_STATIC_PROP_R &&
+ src->opcode != ZEND_FETCH_DIM_R &&
+ src->opcode != ZEND_FETCH_OBJ_R &&
+ src->opcode != ZEND_NEW) {
+ if (opline->extended_value & ZEND_FREE_ON_RETURN) {
+ /* mark as removed (empty live range) */
+ op_array->live_range[opline->op2.num].var = (uint32_t)-1;
+ }
+ ZEND_RESULT_TYPE(src) = IS_UNUSED;
+ MAKE_NOP(opline);
+ }
+ }
+ break;
+
+#if 0
/* pre-evaluate functions:
constant(x)
defined(x)
@@ -800,475 +347,657 @@ static void zend_optimize_block(zend_code_block *block, zend_op_array *op_array,
}
#endif
- /* IS_EQ(TRUE, X) => BOOL(X)
- * IS_EQ(FALSE, X) => BOOL_NOT(X)
- * IS_NOT_EQ(TRUE, X) => BOOL_NOT(X)
- * IS_NOT_EQ(FALSE, X) => BOOL(X)
- * CASE(TRUE, X) => BOOL(X)
- * CASE(FALSE, X) => BOOL_NOT(X)
- */
- if (opline->opcode == ZEND_IS_EQUAL ||
- opline->opcode == ZEND_IS_NOT_EQUAL ||
- /* CASE variable will be deleted later by FREE, so we can't optimize it */
- (opline->opcode == ZEND_CASE && (ZEND_OP1_TYPE(opline) & (IS_CONST|IS_CV)))) {
- if (ZEND_OP1_TYPE(opline) == IS_CONST &&
- (Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_FALSE ||
- Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_TRUE)) {
- /* T = IS_EQUAL(TRUE, X) => T = BOOL(X) */
- /* T = IS_EQUAL(FALSE, X) => T = BOOL_NOT(X) */
- /* T = IS_NOT_EQUAL(TRUE, X) => T = BOOL_NOT(X) */
- /* T = IS_NOT_EQUAL(FALSE, X) => T = BOOL(X) */
- /* Optimization of comparison with "null" is not safe,
- * because ("0" == null) is not equal to !("0")
- */
- opline->opcode =
- ((opline->opcode != ZEND_IS_NOT_EQUAL) == ((Z_TYPE(ZEND_OP1_LITERAL(opline))) == IS_TRUE)) ?
- ZEND_BOOL : ZEND_BOOL_NOT;
- COPY_NODE(opline->op1, opline->op2);
- SET_UNUSED(opline->op2);
- } else if (ZEND_OP2_TYPE(opline) == IS_CONST &&
- (Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_FALSE ||
- Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_TRUE)) {
- /* T = IS_EQUAL(X, TRUE) => T = BOOL(X) */
- /* T = IS_EQUAL(X, FALSE) => T = BOOL_NOT(X) */
- /* T = IS_NOT_EQUAL(X, TRUE) => T = BOOL_NOT(X) */
- /* T = IS_NOT_EQUAL(X, FALSE) => T = BOOL(X) */
- /* Optimization of comparison with "null" is not safe,
- * because ("0" == null) is not equal to !("0")
- */
- opline->opcode =
- ((opline->opcode != ZEND_IS_NOT_EQUAL) == ((Z_TYPE(ZEND_OP2_LITERAL(opline))) == IS_TRUE)) ?
- ZEND_BOOL : ZEND_BOOL_NOT;
- SET_UNUSED(opline->op2);
- }
- }
+ case ZEND_FETCH_LIST:
+ if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
+ /* LIST variable will be deleted later by FREE */
+ Tsource[VAR_NUM(opline->op1.var)] = NULL;
+ }
+ break;
- if ((opline->opcode == ZEND_BOOL ||
- opline->opcode == ZEND_BOOL_NOT ||
- opline->opcode == ZEND_JMPZ ||
- opline->opcode == ZEND_JMPNZ ||
- opline->opcode == ZEND_JMPZNZ) &&
- ZEND_OP1_TYPE(opline) == IS_TMP_VAR &&
- VAR_SOURCE(opline->op1) != NULL &&
- !zend_bitset_in(used_ext, VAR_NUM(ZEND_OP1(opline).var)) &&
- VAR_SOURCE(opline->op1)->opcode == ZEND_BOOL_NOT) {
- /* T = BOOL_NOT(X) + JMPZ(T) -> NOP, JMPNZ(X) */
- zend_op *src = VAR_SOURCE(opline->op1);
-
- COPY_NODE(opline->op1, src->op1);
-
- switch (opline->opcode) {
- case ZEND_BOOL:
- /* T = BOOL_NOT(X) + BOOL(T) -> NOP, BOOL_NOT(X) */
- opline->opcode = ZEND_BOOL_NOT;
- break;
- case ZEND_BOOL_NOT:
- /* T = BOOL_NOT(X) + BOOL_BOOL(T) -> NOP, BOOL(X) */
- opline->opcode = ZEND_BOOL;
+ case ZEND_CASE:
+ if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
+ /* CASE variable will be deleted later by FREE, so we can't optimize it */
+ Tsource[VAR_NUM(opline->op1.var)] = NULL;
break;
- case ZEND_JMPZ:
- /* T = BOOL_NOT(X) + JMPZ(T,L) -> NOP, JMPNZ(X,L) */
- opline->opcode = ZEND_JMPNZ;
- break;
- case ZEND_JMPNZ:
- /* T = BOOL_NOT(X) + JMPNZ(T,L) -> NOP, JMPZ(X,L) */
- opline->opcode = ZEND_JMPZ;
+ }
+ if (opline->op1_type == IS_CONST &&
+ opline->op2_type == IS_CONST) {
break;
- case ZEND_JMPZNZ:
- {
- /* T = BOOL_NOT(X) + JMPZNZ(T,L1,L2) -> NOP, JMPZNZ(X,L2,L1) */
- int op_t;
- zend_code_block *op_b;
-
- op_t = opline->extended_value;
- opline->extended_value = ZEND_OP2(opline).opline_num;
- ZEND_OP2(opline).opline_num = op_t;
-
- op_b = block->ext_to;
- block->ext_to = block->op2_to;
- block->op2_to = op_b;
+ }
+ /* break missing intentionally */
+
+ case ZEND_IS_EQUAL:
+ case ZEND_IS_NOT_EQUAL:
+ if (opline->op1_type == IS_CONST &&
+ opline->op2_type == IS_CONST) {
+ goto optimize_constant_binary_op;
+ }
+ /* IS_EQ(TRUE, X) => BOOL(X)
+ * IS_EQ(FALSE, X) => BOOL_NOT(X)
+ * IS_NOT_EQ(TRUE, X) => BOOL_NOT(X)
+ * IS_NOT_EQ(FALSE, X) => BOOL(X)
+ * CASE(TRUE, X) => BOOL(X)
+ * CASE(FALSE, X) => BOOL_NOT(X)
+ */
+ if (opline->op1_type == IS_CONST &&
+ (Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_FALSE ||
+ Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_TRUE)) {
+ /* Optimization of comparison with "null" is not safe,
+ * because ("0" == null) is not equal to !("0")
+ */
+ opline->opcode =
+ ((opline->opcode != ZEND_IS_NOT_EQUAL) == ((Z_TYPE(ZEND_OP1_LITERAL(opline))) == IS_TRUE)) ?
+ ZEND_BOOL : ZEND_BOOL_NOT;
+ COPY_NODE(opline->op1, opline->op2);
+ SET_UNUSED(opline->op2);
+ goto optimize_bool;
+ } else if (opline->op2_type == IS_CONST &&
+ (Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_FALSE ||
+ Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_TRUE)) {
+ /* Optimization of comparison with "null" is not safe,
+ * because ("0" == null) is not equal to !("0")
+ */
+ opline->opcode =
+ ((opline->opcode != ZEND_IS_NOT_EQUAL) == ((Z_TYPE(ZEND_OP2_LITERAL(opline))) == IS_TRUE)) ?
+ ZEND_BOOL : ZEND_BOOL_NOT;
+ SET_UNUSED(opline->op2);
+ goto optimize_bool;
}
break;
- }
- VAR_UNSET(opline->op1);
- MAKE_NOP(src);
- continue;
- } else
+ case ZEND_BOOL:
+ case ZEND_BOOL_NOT:
+ optimize_bool:
+ if (opline->op1_type == IS_CONST) {
+ goto optimize_const_unary_op;
+ }
+ if (opline->op1_type == IS_TMP_VAR &&
+ !zend_bitset_in(used_ext, VAR_NUM(opline->op1.var))) {
+ src = VAR_SOURCE(opline->op1);
+ if (src) {
+ switch (src->opcode) {
+ case ZEND_BOOL_NOT:
+ /* T = BOOL_NOT(X) + BOOL(T) -> NOP, BOOL_NOT(X) */
+ VAR_SOURCE(opline->op1) = NULL;
+ COPY_NODE(opline->op1, src->op1);
+ opline->opcode = (opline->opcode == ZEND_BOOL) ? ZEND_BOOL_NOT : ZEND_BOOL;
+ MAKE_NOP(src);
+ goto optimize_bool;
+ case ZEND_BOOL:
+ /* T = BOOL(X) + BOOL(T) -> NOP, BOOL(X) */
+ VAR_SOURCE(opline->op1) = NULL;
+ COPY_NODE(opline->op1, src->op1);
+ MAKE_NOP(src);
+ goto optimize_bool;
+ case ZEND_IS_EQUAL:
+ if (opline->opcode == ZEND_BOOL_NOT) {
+ src->opcode = ZEND_IS_NOT_EQUAL;
+ }
+ COPY_NODE(src->result, opline->result);
+ SET_VAR_SOURCE(src);
+ MAKE_NOP(opline);
+ break;
+ case ZEND_IS_NOT_EQUAL:
+ if (opline->opcode == ZEND_BOOL_NOT) {
+ src->opcode = ZEND_IS_EQUAL;
+ }
+ COPY_NODE(src->result, opline->result);
+ SET_VAR_SOURCE(src);
+ MAKE_NOP(opline);
+ break;
+ case ZEND_IS_IDENTICAL:
+ if (opline->opcode == ZEND_BOOL_NOT) {
+ src->opcode = ZEND_IS_NOT_IDENTICAL;
+ }
+ COPY_NODE(src->result, opline->result);
+ SET_VAR_SOURCE(src);
+ MAKE_NOP(opline);
+ break;
+ case ZEND_IS_NOT_IDENTICAL:
+ if (opline->opcode == ZEND_BOOL_NOT) {
+ src->opcode = ZEND_IS_IDENTICAL;
+ }
+ COPY_NODE(src->result, opline->result);
+ SET_VAR_SOURCE(src);
+ MAKE_NOP(opline);
+ break;
+ case ZEND_IS_SMALLER:
+ if (opline->opcode == ZEND_BOOL_NOT) {
+ zend_uchar tmp_type;
+ uint32_t tmp;
+
+ src->opcode = ZEND_IS_SMALLER_OR_EQUAL;
+ tmp_type = src->op1_type;
+ src->op1_type = src->op2_type;
+ src->op2_type = tmp_type;
+ tmp = src->op1.num;
+ src->op1.num = src->op2.num;
+ src->op2.num = tmp;
+ }
+ COPY_NODE(src->result, opline->result);
+ SET_VAR_SOURCE(src);
+ MAKE_NOP(opline);
+ break;
+ case ZEND_IS_SMALLER_OR_EQUAL:
+ if (opline->opcode == ZEND_BOOL_NOT) {
+ zend_uchar tmp_type;
+ uint32_t tmp;
+
+ src->opcode = ZEND_IS_SMALLER;
+ tmp_type = src->op1_type;
+ src->op1_type = src->op2_type;
+ src->op2_type = tmp_type;
+ tmp = src->op1.num;
+ src->op1.num = src->op2.num;
+ src->op2.num = tmp;
+ }
+ COPY_NODE(src->result, opline->result);
+ SET_VAR_SOURCE(src);
+ MAKE_NOP(opline);
+ break;
+ case ZEND_ISSET_ISEMPTY_VAR:
+ case ZEND_ISSET_ISEMPTY_DIM_OBJ:
+ case ZEND_ISSET_ISEMPTY_PROP_OBJ:
+ case ZEND_ISSET_ISEMPTY_STATIC_PROP:
+ case ZEND_INSTANCEOF:
+ case ZEND_TYPE_CHECK:
+ case ZEND_DEFINED:
+ if (opline->opcode == ZEND_BOOL_NOT) {
+ break;
+ }
+ COPY_NODE(src->result, opline->result);
+ SET_VAR_SOURCE(src);
+ MAKE_NOP(opline);
+ break;
+ }
+ }
+ }
+ break;
+
+ case ZEND_JMPZ:
+ case ZEND_JMPNZ:
+ case ZEND_JMPZ_EX:
+ case ZEND_JMPNZ_EX:
+ case ZEND_JMPZNZ:
+ optimize_jmpznz:
+ if (opline->op1_type == IS_TMP_VAR &&
+ (!zend_bitset_in(used_ext, VAR_NUM(opline->op1.var)) ||
+ (opline->result_type == opline->op1_type &&
+ opline->result.var == opline->op1.var))) {
+ src = VAR_SOURCE(opline->op1);
+ if (src) {
+ if (src->opcode == ZEND_BOOL_NOT &&
+ opline->opcode != ZEND_JMPZ_EX &&
+ opline->opcode != ZEND_JMPNZ_EX) {
+ VAR_SOURCE(opline->op1) = NULL;
+ COPY_NODE(opline->op1, src->op1);
+ if (opline->opcode == ZEND_JMPZ) {
+ /* T = BOOL_NOT(X) + JMPZ(T) -> NOP, JMPNZ(X) */
+ opline->opcode = ZEND_JMPNZ;
+ } else if (opline->opcode == ZEND_JMPNZ) {
+ /* T = BOOL_NOT(X) + JMPNZ(T) -> NOP, JMPZ(X) */
+ opline->opcode = ZEND_JMPZ;
#if 0
- /* T = BOOL_NOT(X) + T = JMPZ_EX(T, X) -> T = BOOL_NOT(X), JMPNZ(X) */
- if(0 && (opline->opcode == ZEND_JMPZ_EX ||
- opline->opcode == ZEND_JMPNZ_EX) &&
- ZEND_OP1_TYPE(opline) == IS_TMP_VAR &&
- VAR_SOURCE(opline->op1) != NULL &&
- VAR_SOURCE(opline->op1)->opcode == ZEND_BOOL_NOT &&
- ZEND_OP1(opline).var == ZEND_RESULT(opline).var
- ) {
- zend_op *src = VAR_SOURCE(opline->op1);
- if(opline->opcode == ZEND_JMPZ_EX) {
- opline->opcode = ZEND_JMPNZ;
- } else {
- opline->opcode = ZEND_JMPZ;
- }
- COPY_NODE(opline->op1, src->op1);
- SET_UNUSED(opline->result);
- continue;
- } else
+ } else if (opline->opcode == ZEND_JMPZ_EX) {
+ /* T = BOOL_NOT(X) + JMPZ_EX(T) -> NOP, JMPNZ_EX(X) */
+ opline->opcode = ZEND_JMPNZ_EX;
+ } else if (opline->opcode == ZEND_JMPNZ_EX) {
+ /* T = BOOL_NOT(X) + JMPNZ_EX(T) -> NOP, JMPZ_EX(X) */
+ opline->opcode = ZEND_JMPZ;
#endif
- /* T = BOOL(X) + JMPZ(T) -> NOP, JMPZ(X) */
- if ((opline->opcode == ZEND_BOOL ||
- opline->opcode == ZEND_BOOL_NOT ||
- opline->opcode == ZEND_JMPZ ||
- opline->opcode == ZEND_JMPZ_EX ||
- opline->opcode == ZEND_JMPNZ_EX ||
- opline->opcode == ZEND_JMPNZ ||
- opline->opcode == ZEND_JMPZNZ) &&
- (ZEND_OP1_TYPE(opline) & (IS_TMP_VAR|IS_VAR)) &&
- VAR_SOURCE(opline->op1) != NULL &&
- (!zend_bitset_in(used_ext, VAR_NUM(ZEND_OP1(opline).var)) ||
- ((ZEND_RESULT_TYPE(opline) & (IS_TMP_VAR|IS_VAR)) &&
- ZEND_RESULT(opline).var == ZEND_OP1(opline).var)) &&
- (VAR_SOURCE(opline->op1)->opcode == ZEND_BOOL ||
- VAR_SOURCE(opline->op1)->opcode == ZEND_QM_ASSIGN)) {
- zend_op *src = VAR_SOURCE(opline->op1);
- COPY_NODE(opline->op1, src->op1);
-
- VAR_UNSET(opline->op1);
- MAKE_NOP(src);
- continue;
- } else if (last_op && opline->opcode == ZEND_ECHO &&
- last_op->opcode == ZEND_ECHO &&
- ZEND_OP1_TYPE(opline) == IS_CONST &&
- Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_DOUBLE &&
- ZEND_OP1_TYPE(last_op) == IS_CONST &&
- Z_TYPE(ZEND_OP1_LITERAL(last_op)) != IS_DOUBLE) {
- /* compress consecutive ECHO's.
- * Float to string conversion may be affected by current
- * locale setting.
- */
- int l, old_len;
-
- if (Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_STRING) {
- convert_to_string_safe(&ZEND_OP1_LITERAL(opline));
- }
- if (Z_TYPE(ZEND_OP1_LITERAL(last_op)) != IS_STRING) {
- convert_to_string_safe(&ZEND_OP1_LITERAL(last_op));
- }
- old_len = Z_STRLEN(ZEND_OP1_LITERAL(last_op));
- l = old_len + Z_STRLEN(ZEND_OP1_LITERAL(opline));
- if (!Z_REFCOUNTED(ZEND_OP1_LITERAL(last_op))) {
- zend_string *tmp = zend_string_alloc(l, 0);
- memcpy(ZSTR_VAL(tmp), Z_STRVAL(ZEND_OP1_LITERAL(last_op)), old_len);
- Z_STR(ZEND_OP1_LITERAL(last_op)) = tmp;
- } else {
- Z_STR(ZEND_OP1_LITERAL(last_op)) = zend_string_extend(Z_STR(ZEND_OP1_LITERAL(last_op)), l, 0);
- }
- Z_TYPE_INFO(ZEND_OP1_LITERAL(last_op)) = IS_STRING_EX;
- memcpy(Z_STRVAL(ZEND_OP1_LITERAL(last_op)) + old_len, Z_STRVAL(ZEND_OP1_LITERAL(opline)), Z_STRLEN(ZEND_OP1_LITERAL(opline)));
- Z_STRVAL(ZEND_OP1_LITERAL(last_op))[l] = '\0';
- zval_dtor(&ZEND_OP1_LITERAL(opline));
- ZVAL_STR(&ZEND_OP1_LITERAL(opline), zend_new_interned_string(Z_STR(ZEND_OP1_LITERAL(last_op))));
- ZVAL_NULL(&ZEND_OP1_LITERAL(last_op));
- MAKE_NOP(last_op);
- } else if ((opline->opcode == ZEND_CONCAT) &&
- ZEND_OP2_TYPE(opline) == IS_CONST &&
- ZEND_OP1_TYPE(opline) == IS_TMP_VAR &&
- VAR_SOURCE(opline->op1) &&
- (VAR_SOURCE(opline->op1)->opcode == ZEND_CONCAT ||
- VAR_SOURCE(opline->op1)->opcode == ZEND_FAST_CONCAT) &&
- ZEND_OP2_TYPE(VAR_SOURCE(opline->op1)) == IS_CONST &&
- ZEND_RESULT(VAR_SOURCE(opline->op1)).var == ZEND_OP1(opline).var) {
- /* compress consecutive CONCAT/ADD_STRING/ADD_CHARs */
- zend_op *src = VAR_SOURCE(opline->op1);
- int l, old_len;
-
- if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_STRING) {
- convert_to_string_safe(&ZEND_OP2_LITERAL(opline));
- }
- if (Z_TYPE(ZEND_OP2_LITERAL(src)) != IS_STRING) {
- convert_to_string_safe(&ZEND_OP2_LITERAL(src));
- }
+ } else {
+ /* T = BOOL_NOT(X) + JMPZNZ(T,L1,L2) -> NOP, JMPZNZ(X,L2,L1) */
+ uint32_t tmp;
+
+ ZEND_ASSERT(opline->opcode == ZEND_JMPZNZ);
+ tmp = block->successors[0];
+ block->successors[0] = block->successors[1];
+ block->successors[1] = tmp;
+ }
+ MAKE_NOP(src);
+ goto optimize_jmpznz;
+ } else if (src->opcode == ZEND_BOOL ||
+ src->opcode == ZEND_QM_ASSIGN) {
+ VAR_SOURCE(opline->op1) = NULL;
+ COPY_NODE(opline->op1, src->op1);
+ MAKE_NOP(src);
+ goto optimize_jmpznz;
+ }
+ }
+ }
+ break;
- VAR_UNSET(opline->op1);
- COPY_NODE(opline->op1, src->op1);
- old_len = Z_STRLEN(ZEND_OP2_LITERAL(src));
- l = old_len + Z_STRLEN(ZEND_OP2_LITERAL(opline));
- if (!Z_REFCOUNTED(ZEND_OP2_LITERAL(src))) {
- zend_string *tmp = zend_string_alloc(l, 0);
- memcpy(ZSTR_VAL(tmp), Z_STRVAL(ZEND_OP2_LITERAL(src)), old_len);
- Z_STR(ZEND_OP2_LITERAL(last_op)) = tmp;
- } else {
- Z_STR(ZEND_OP2_LITERAL(src)) = zend_string_extend(Z_STR(ZEND_OP2_LITERAL(src)), l, 0);
- }
- Z_TYPE_INFO(ZEND_OP2_LITERAL(last_op)) = IS_STRING_EX;
- memcpy(Z_STRVAL(ZEND_OP2_LITERAL(src)) + old_len, Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline)));
- Z_STRVAL(ZEND_OP2_LITERAL(src))[l] = '\0';
- zend_string_release(Z_STR(ZEND_OP2_LITERAL(opline)));
- ZVAL_STR(&ZEND_OP2_LITERAL(opline), zend_new_interned_string(Z_STR(ZEND_OP2_LITERAL(src))));
- ZVAL_NULL(&ZEND_OP2_LITERAL(src));
- MAKE_NOP(src);
- } else if ((opline->opcode == ZEND_ADD ||
- opline->opcode == ZEND_SUB ||
- opline->opcode == ZEND_MUL ||
- opline->opcode == ZEND_DIV ||
- opline->opcode == ZEND_MOD ||
- opline->opcode == ZEND_SL ||
- opline->opcode == ZEND_SR ||
- opline->opcode == ZEND_CONCAT ||
- opline->opcode == ZEND_FAST_CONCAT ||
- opline->opcode == ZEND_IS_EQUAL ||
- opline->opcode == ZEND_IS_NOT_EQUAL ||
- opline->opcode == ZEND_IS_SMALLER ||
- opline->opcode == ZEND_IS_SMALLER_OR_EQUAL ||
- opline->opcode == ZEND_IS_IDENTICAL ||
- opline->opcode == ZEND_IS_NOT_IDENTICAL ||
- opline->opcode == ZEND_BOOL_XOR ||
- opline->opcode == ZEND_BW_OR ||
- opline->opcode == ZEND_BW_AND ||
- opline->opcode == ZEND_BW_XOR) &&
- ZEND_OP1_TYPE(opline)==IS_CONST &&
- ZEND_OP2_TYPE(opline)==IS_CONST) {
- /* evaluate constant expressions */
- binary_op_type binary_op = get_binary_op(opline->opcode);
- zval result;
- int er;
-
- if ((opline->opcode == ZEND_DIV || opline->opcode == ZEND_MOD) &&
- zval_get_long(&ZEND_OP2_LITERAL(opline)) == 0) {
- if (RESULT_USED(opline)) {
- SET_VAR_SOURCE(opline);
+ case ZEND_CONCAT:
+ case ZEND_FAST_CONCAT:
+ if (opline->op1_type == IS_CONST &&
+ opline->op2_type == IS_CONST) {
+ goto optimize_constant_binary_op;
}
- opline++;
- continue;
- } else if ((opline->opcode == ZEND_SL || opline->opcode == ZEND_SR) &&
- zval_get_long(&ZEND_OP2_LITERAL(opline)) < 0) {
- if (RESULT_USED(opline)) {
- SET_VAR_SOURCE(opline);
+
+ if (opline->op2_type == IS_CONST &&
+ opline->op1_type == IS_TMP_VAR) {
+
+ src = VAR_SOURCE(opline->op1);
+ if (src &&
+ (src->opcode == ZEND_CONCAT ||
+ src->opcode == ZEND_FAST_CONCAT) &&
+ src->op2_type == IS_CONST) {
+ /* compress consecutive CONCATs */
+ int l, old_len;
+
+ if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_STRING) {
+ convert_to_string_safe(&ZEND_OP2_LITERAL(opline));
+ }
+ if (Z_TYPE(ZEND_OP2_LITERAL(src)) != IS_STRING) {
+ convert_to_string_safe(&ZEND_OP2_LITERAL(src));
+ }
+
+ VAR_SOURCE(opline->op1) = NULL;
+ COPY_NODE(opline->op1, src->op1);
+ old_len = Z_STRLEN(ZEND_OP2_LITERAL(src));
+ l = old_len + Z_STRLEN(ZEND_OP2_LITERAL(opline));
+ if (!Z_REFCOUNTED(ZEND_OP2_LITERAL(src))) {
+ zend_string *tmp = zend_string_alloc(l, 0);
+ memcpy(ZSTR_VAL(tmp), Z_STRVAL(ZEND_OP2_LITERAL(src)), old_len);
+ Z_STR(ZEND_OP2_LITERAL(src)) = tmp;
+ } else {
+ Z_STR(ZEND_OP2_LITERAL(src)) = zend_string_extend(Z_STR(ZEND_OP2_LITERAL(src)), l, 0);
+ }
+ Z_TYPE_INFO(ZEND_OP2_LITERAL(src)) = IS_STRING_EX;
+ memcpy(Z_STRVAL(ZEND_OP2_LITERAL(src)) + old_len, Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline)));
+ Z_STRVAL(ZEND_OP2_LITERAL(src))[l] = '\0';
+ zend_string_release(Z_STR(ZEND_OP2_LITERAL(opline)));
+ ZVAL_STR(&ZEND_OP2_LITERAL(opline), zend_new_interned_string(Z_STR(ZEND_OP2_LITERAL(src))));
+ ZVAL_NULL(&ZEND_OP2_LITERAL(src));
+ MAKE_NOP(src);
+ }
}
- opline++;
- continue;
- }
- er = EG(error_reporting);
- EG(error_reporting) = 0;
- if (binary_op(&result, &ZEND_OP1_LITERAL(opline), &ZEND_OP2_LITERAL(opline)) == SUCCESS) {
- literal_dtor(&ZEND_OP1_LITERAL(opline));
- literal_dtor(&ZEND_OP2_LITERAL(opline));
- opline->opcode = ZEND_QM_ASSIGN;
- SET_UNUSED(opline->op2);
- zend_optimizer_update_op1_const(op_array, opline, &result);
- }
- EG(error_reporting) = er;
- } else if ((opline->opcode == ZEND_BOOL ||
- opline->opcode == ZEND_BOOL_NOT ||
- opline->opcode == ZEND_BW_NOT) && ZEND_OP1_TYPE(opline) == IS_CONST) {
- /* evaluate constant unary ops */
- unary_op_type unary_op = get_unary_op(opline->opcode);
- zval result;
-
- if (unary_op) {
- unary_op(&result, &ZEND_OP1_LITERAL(opline));
- literal_dtor(&ZEND_OP1_LITERAL(opline));
- } else {
- /* BOOL */
- ZVAL_COPY_VALUE(&result, &ZEND_OP1_LITERAL(opline));
- convert_to_boolean(&result);
- ZVAL_NULL(&ZEND_OP1_LITERAL(opline));
- }
- opline->opcode = ZEND_QM_ASSIGN;
- zend_optimizer_update_op1_const(op_array, opline, &result);
- } else if ((opline->opcode == ZEND_RETURN || opline->opcode == ZEND_EXIT) &&
- (ZEND_OP1_TYPE(opline) & (IS_TMP_VAR|IS_VAR)) &&
- VAR_SOURCE(opline->op1) &&
- VAR_SOURCE(opline->op1)->opcode == ZEND_QM_ASSIGN) {
- /* T = QM_ASSIGN(X), RETURN(T) to RETURN(X) */
- zend_op *src = VAR_SOURCE(opline->op1);
- VAR_UNSET(opline->op1);
- COPY_NODE(opline->op1, src->op1);
- MAKE_NOP(src);
- } else if (opline->opcode == ZEND_CONCAT || opline->opcode == ZEND_FAST_CONCAT) {
- if ((ZEND_OP1_TYPE(opline) & (IS_TMP_VAR|IS_VAR)) &&
- VAR_SOURCE(opline->op1) &&
- VAR_SOURCE(opline->op1)->opcode == ZEND_CAST &&
- VAR_SOURCE(opline->op1)->extended_value == IS_STRING) {
- /* convert T1 = CAST(STRING, X), T2 = CONCAT(T1, Y) to T2 = CONCAT(X,Y) */
- zend_op *src = VAR_SOURCE(opline->op1);
- VAR_UNSET(opline->op1);
- COPY_NODE(opline->op1, src->op1);
- MAKE_NOP(src);
- }
- if ((ZEND_OP2_TYPE(opline) & (IS_TMP_VAR|IS_VAR)) &&
- VAR_SOURCE(opline->op2) &&
- VAR_SOURCE(opline->op2)->opcode == ZEND_CAST &&
- VAR_SOURCE(opline->op2)->extended_value == IS_STRING) {
- /* convert T1 = CAST(STRING, X), T2 = CONCAT(Y, T1) to T2 = CONCAT(Y,X) */
- zend_op *src = VAR_SOURCE(opline->op2);
- VAR_UNSET(opline->op2);
- COPY_NODE(opline->op2, src->op1);
- MAKE_NOP(src);
- }
- if (ZEND_OP1_TYPE(opline) == IS_CONST &&
- Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING &&
- Z_STRLEN(ZEND_OP1_LITERAL(opline)) == 0) {
- /* convert CONCAT('', X) => CAST(STRING, X) */
- literal_dtor(&ZEND_OP1_LITERAL(opline));
- opline->opcode = ZEND_CAST;
- opline->extended_value = IS_STRING;
- COPY_NODE(opline->op1, opline->op2);
- opline->op2_type = IS_UNUSED;
- opline->op2.var = 0;
- } else if (ZEND_OP2_TYPE(opline) == IS_CONST &&
+
+ if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
+ src = VAR_SOURCE(opline->op1);
+ if (src &&
+ src->opcode == ZEND_CAST &&
+ src->extended_value == IS_STRING) {
+ /* convert T1 = CAST(STRING, X), T2 = CONCAT(T1, Y) to T2 = CONCAT(X,Y) */
+ VAR_SOURCE(opline->op1) = NULL;
+ COPY_NODE(opline->op1, src->op1);
+ MAKE_NOP(src);
+ }
+ }
+ if (opline->op2_type & (IS_TMP_VAR|IS_VAR)) {
+ src = VAR_SOURCE(opline->op2);
+ if (src &&
+ src->opcode == ZEND_CAST &&
+ src->extended_value == IS_STRING) {
+ /* convert T1 = CAST(STRING, X), T2 = CONCAT(Y, T1) to T2 = CONCAT(Y,X) */
+ zend_op *src = VAR_SOURCE(opline->op2);
+ VAR_SOURCE(opline->op2) = NULL;
+ COPY_NODE(opline->op2, src->op1);
+ MAKE_NOP(src);
+ }
+ }
+ if (opline->op1_type == IS_CONST &&
+ Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING &&
+ Z_STRLEN(ZEND_OP1_LITERAL(opline)) == 0) {
+ /* convert CONCAT('', X) => CAST(STRING, X) */
+ literal_dtor(&ZEND_OP1_LITERAL(opline));
+ opline->opcode = ZEND_CAST;
+ opline->extended_value = IS_STRING;
+ COPY_NODE(opline->op1, opline->op2);
+ opline->op2_type = IS_UNUSED;
+ opline->op2.var = 0;
+ } else if (opline->op2_type == IS_CONST &&
Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING &&
Z_STRLEN(ZEND_OP2_LITERAL(opline)) == 0) {
- /* convert CONCAT(X, '') => CAST(STRING, X) */
- literal_dtor(&ZEND_OP2_LITERAL(opline));
- opline->opcode = ZEND_CAST;
- opline->extended_value = IS_STRING;
- opline->op2_type = IS_UNUSED;
- opline->op2.var = 0;
- } else if (opline->opcode == ZEND_CONCAT &&
- (opline->op1_type == IS_CONST ||
- (opline->op1_type == IS_TMP_VAR &&
- VAR_SOURCE(opline->op1) &&
- (VAR_SOURCE(opline->op1)->opcode == ZEND_FAST_CONCAT ||
- VAR_SOURCE(opline->op1)->opcode == ZEND_ROPE_END ||
- VAR_SOURCE(opline->op1)->opcode == ZEND_FETCH_CONSTANT))) &&
- (opline->op2_type == IS_CONST ||
- (opline->op2_type == IS_TMP_VAR &&
- VAR_SOURCE(opline->op2) &&
- (VAR_SOURCE(opline->op2)->opcode == ZEND_FAST_CONCAT ||
- VAR_SOURCE(opline->op2)->opcode == ZEND_ROPE_END ||
- VAR_SOURCE(opline->op2)->opcode == ZEND_FETCH_CONSTANT)))) {
- opline->opcode = ZEND_FAST_CONCAT;
- }
- } else if (opline->opcode == ZEND_QM_ASSIGN &&
- ZEND_OP1_TYPE(opline) == ZEND_RESULT_TYPE(opline) &&
- ZEND_OP1(opline).var == ZEND_RESULT(opline).var) {
- /* strip T = QM_ASSIGN(T) */
- MAKE_NOP(opline);
- } else if (opline->opcode == ZEND_BOOL &&
- ZEND_OP1_TYPE(opline) == IS_TMP_VAR &&
- VAR_SOURCE(opline->op1) &&
- (VAR_SOURCE(opline->op1)->opcode == ZEND_IS_EQUAL ||
- VAR_SOURCE(opline->op1)->opcode == ZEND_IS_NOT_EQUAL ||
- VAR_SOURCE(opline->op1)->opcode == ZEND_IS_SMALLER ||
- VAR_SOURCE(opline->op1)->opcode == ZEND_IS_SMALLER_OR_EQUAL ||
- VAR_SOURCE(opline->op1)->opcode == ZEND_BOOL ||
- VAR_SOURCE(opline->op1)->opcode == ZEND_IS_IDENTICAL ||
- VAR_SOURCE(opline->op1)->opcode == ZEND_IS_NOT_IDENTICAL ||
- VAR_SOURCE(opline->op1)->opcode == ZEND_ISSET_ISEMPTY_VAR ||
- VAR_SOURCE(opline->op1)->opcode == ZEND_ISSET_ISEMPTY_DIM_OBJ) &&
- !zend_bitset_in(used_ext, VAR_NUM(ZEND_OP1(opline).var))) {
- /* T = IS_SMALLER(X, Y), T1 = BOOL(T) => T = IS_SMALLER(X, Y), T1 = QM_ASSIGN(T) */
- zend_op *src = VAR_SOURCE(opline->op1);
- COPY_NODE(src->result, opline->result);
- SET_VAR_SOURCE(src);
- MAKE_NOP(opline);
+ /* convert CONCAT(X, '') => CAST(STRING, X) */
+ literal_dtor(&ZEND_OP2_LITERAL(opline));
+ opline->opcode = ZEND_CAST;
+ opline->extended_value = IS_STRING;
+ opline->op2_type = IS_UNUSED;
+ opline->op2.var = 0;
+ } else if (opline->opcode == ZEND_CONCAT &&
+ (opline->op1_type == IS_CONST ||
+ (opline->op1_type == IS_TMP_VAR &&
+ VAR_SOURCE(opline->op1) &&
+ (VAR_SOURCE(opline->op1)->opcode == ZEND_FAST_CONCAT ||
+ VAR_SOURCE(opline->op1)->opcode == ZEND_ROPE_END ||
+ VAR_SOURCE(opline->op1)->opcode == ZEND_FETCH_CONSTANT ||
+ VAR_SOURCE(opline->op1)->opcode == ZEND_FETCH_CLASS_CONSTANT))) &&
+ (opline->op2_type == IS_CONST ||
+ (opline->op2_type == IS_TMP_VAR &&
+ VAR_SOURCE(opline->op2) &&
+ (VAR_SOURCE(opline->op2)->opcode == ZEND_FAST_CONCAT ||
+ VAR_SOURCE(opline->op2)->opcode == ZEND_ROPE_END ||
+ VAR_SOURCE(opline->op2)->opcode == ZEND_FETCH_CONSTANT ||
+ VAR_SOURCE(opline->op2)->opcode == ZEND_FETCH_CLASS_CONSTANT)))) {
+ opline->opcode = ZEND_FAST_CONCAT;
+ }
+ break;
+
+ case ZEND_ADD:
+ case ZEND_SUB:
+ case ZEND_MUL:
+ case ZEND_DIV:
+ case ZEND_MOD:
+ case ZEND_SL:
+ case ZEND_SR:
+ case ZEND_IS_SMALLER:
+ case ZEND_IS_SMALLER_OR_EQUAL:
+ case ZEND_IS_IDENTICAL:
+ case ZEND_IS_NOT_IDENTICAL:
+ case ZEND_BOOL_XOR:
+ case ZEND_BW_OR:
+ case ZEND_BW_AND:
+ case ZEND_BW_XOR:
+ if (opline->op1_type == IS_CONST &&
+ opline->op2_type == IS_CONST) {
+ /* evaluate constant expressions */
+ binary_op_type binary_op;
+ zval result;
+ int er;
+
+optimize_constant_binary_op:
+ binary_op = get_binary_op(opline->opcode);
+ if ((opline->opcode == ZEND_DIV || opline->opcode == ZEND_MOD) &&
+ zval_get_long(&ZEND_OP2_LITERAL(opline)) == 0) {
+ SET_VAR_SOURCE(opline);
+ opline++;
+ continue;
+ } else if ((opline->opcode == ZEND_SL || opline->opcode == ZEND_SR) &&
+ zval_get_long(&ZEND_OP2_LITERAL(opline)) < 0) {
+ SET_VAR_SOURCE(opline);
+ opline++;
+ continue;
+ } else if (zend_binary_op_produces_numeric_string_error(opline->opcode, &ZEND_OP1_LITERAL(opline), &ZEND_OP2_LITERAL(opline))) {
+ SET_VAR_SOURCE(opline);
+ opline++;
+ continue;
+ }
+ er = EG(error_reporting);
+ EG(error_reporting) = 0;
+ if (binary_op(&result, &ZEND_OP1_LITERAL(opline), &ZEND_OP2_LITERAL(opline)) == SUCCESS) {
+ literal_dtor(&ZEND_OP1_LITERAL(opline));
+ literal_dtor(&ZEND_OP2_LITERAL(opline));
+ opline->opcode = ZEND_QM_ASSIGN;
+ SET_UNUSED(opline->op2);
+ zend_optimizer_update_op1_const(op_array, opline, &result);
+ }
+ EG(error_reporting) = er;
+ }
+ break;
+
+ case ZEND_BW_NOT:
+ if (opline->op1_type == IS_CONST) {
+ /* evaluate constant unary ops */
+ unary_op_type unary_op;
+ zval result;
+
+optimize_const_unary_op:
+ unary_op = get_unary_op(opline->opcode);
+ if (unary_op) {
+ unary_op(&result, &ZEND_OP1_LITERAL(opline));
+ literal_dtor(&ZEND_OP1_LITERAL(opline));
+ } else {
+ /* BOOL */
+ ZVAL_COPY_VALUE(&result, &ZEND_OP1_LITERAL(opline));
+ convert_to_boolean(&result);
+ ZVAL_NULL(&ZEND_OP1_LITERAL(opline));
+ }
+ opline->opcode = ZEND_QM_ASSIGN;
+ zend_optimizer_update_op1_const(op_array, opline, &result);
+ }
+ break;
+
+ case ZEND_RETURN:
+ case ZEND_EXIT:
+ if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
+ src = VAR_SOURCE(opline->op1);
+ if (src && src->opcode == ZEND_QM_ASSIGN) {
+ zend_op *op = src + 1;
+ zend_bool optimize = 1;
+
+ while (op < opline) {
+ if ((op->op1_type == opline->op1_type
+ && op->op1.var == opline->op1.var)
+ || (op->op2_type == opline->op1_type
+ && op->op2.var == opline->op1.var)) {
+ optimize = 0;
+ break;
+ }
+ op++;
+ }
+
+ if (optimize) {
+ /* T = QM_ASSIGN(X), RETURN(T) to NOP, RETURN(X) */
+ VAR_SOURCE(opline->op1) = NULL;
+ COPY_NODE(opline->op1, src->op1);
+ MAKE_NOP(src);
+ }
+ }
+ }
+ break;
+
+ case ZEND_QM_ASSIGN:
+ if (opline->op1_type == opline->result_type &&
+ opline->op1.var == opline->result.var) {
+ /* strip T = QM_ASSIGN(T) */
+ MAKE_NOP(opline);
+ }
+ break;
}
+
/* get variable source */
- if (RESULT_USED(opline)) {
+ if (opline->result_type & (IS_VAR|IS_TMP_VAR)) {
SET_VAR_SOURCE(opline);
}
- if (opline->opcode != ZEND_NOP) {
- last_op = opline;
- }
opline++;
}
- strip_nop(block, op_array, ctx);
+ strip_nops(op_array, block);
}
/* Rebuild plain (optimized) op_array from CFG */
static void assemble_code_blocks(zend_cfg *cfg, zend_op_array *op_array)
{
- zend_code_block *blocks = cfg->blocks;
- zend_op *new_opcodes = emalloc(op_array->last * sizeof(zend_op));
- zend_op *opline = new_opcodes;
- zend_code_block *cur_block = blocks;
+ zend_basic_block *blocks = cfg->blocks;
+ zend_basic_block *end = blocks + cfg->blocks_count;
+ zend_basic_block *b;
+ zend_op *new_opcodes;
+ zend_op *opline;
+ uint32_t len = 0;
+ int n;
- /* Copy code of reachable blocks into a single buffer */
- while (cur_block) {
- if (cur_block->access) {
- memcpy(opline, cur_block->start_opline, cur_block->len * sizeof(zend_op));
- cur_block->start_opline = opline;
- opline += cur_block->len;
- if ((opline - 1)->opcode == ZEND_JMP) {
- zend_code_block *next;
- next = cur_block->next;
- while (next && !next->access) {
- next = next->next;
+ for (b = blocks; b < end; b++) {
+ if (b->len == 0) {
+ continue;
+ }
+ if (b->flags & ZEND_BB_REACHABLE) {
+ opline = op_array->opcodes + b->start + b->len - 1;
+ if (opline->opcode == ZEND_JMP) {
+ zend_basic_block *next = b + 1;
+
+ while (next < end && !(next->flags & ZEND_BB_REACHABLE)) {
+ next++;
}
- if (next && next == cur_block->op1_to) {
+ if (next < end && next == blocks + b->successors[0]) {
/* JMP to the next block - strip it */
- cur_block->follow_to = cur_block->op1_to;
- cur_block->op1_to = NULL;
- MAKE_NOP((opline - 1));
- opline--;
- cur_block->len--;
+ MAKE_NOP(opline);
+ b->len--;
}
+ } else if (b->len == 1 && opline->opcode == ZEND_NOP) {
+ /* skip empty block */
+ b->len--;
}
+ len += b->len;
} else {
/* this block will not be used, delete all constants there */
- zend_op *_opl;
- zend_op *end = cur_block->start_opline + cur_block->len;
- for (_opl = cur_block->start_opline; _opl && _opl < end; _opl++) {
- if (ZEND_OP1_TYPE(_opl) == IS_CONST) {
- literal_dtor(&ZEND_OP1_LITERAL(_opl));
+ zend_op *op = op_array->opcodes + b->start;
+ zend_op *end = op + b->len;
+ for (; op < end; op++) {
+ if (ZEND_OP1_TYPE(op) == IS_CONST) {
+ literal_dtor(&ZEND_OP1_LITERAL(op));
}
- if (ZEND_OP2_TYPE(_opl) == IS_CONST) {
- literal_dtor(&ZEND_OP2_LITERAL(_opl));
+ if (ZEND_OP2_TYPE(op) == IS_CONST) {
+ literal_dtor(&ZEND_OP2_LITERAL(op));
}
}
}
- cur_block = cur_block->next;
}
- op_array->last = opline-new_opcodes;
+ new_opcodes = emalloc(len * sizeof(zend_op));
+ opline = new_opcodes;
+
+ /* Copy code of reachable blocks into a single buffer */
+ for (b = blocks; b < end; b++) {
+ if (b->flags & ZEND_BB_REACHABLE) {
+ memcpy(opline, op_array->opcodes + b->start, b->len * sizeof(zend_op));
+ b->start = opline - new_opcodes;
+ opline += b->len;
+ }
+ }
+
+ /* adjust jump targets */
+ efree(op_array->opcodes);
+ op_array->opcodes = new_opcodes;
+ op_array->last = len;
+
+ for (b = blocks; b < end; b++) {
+ if (!(b->flags & ZEND_BB_REACHABLE) || b->len == 0) {
+ continue;
+ }
+ opline = op_array->opcodes + b->start + b->len - 1;
+ switch (opline->opcode) {
+ case ZEND_FAST_CALL:
+ case ZEND_JMP:
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op1, new_opcodes + blocks[b->successors[0]].start);
+ break;
+ case ZEND_JMPZNZ:
+ opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, new_opcodes + blocks[b->successors[1]].start);
+ /* break missing intentionally */
+ case ZEND_JMPZ:
+ case ZEND_JMPNZ:
+ case ZEND_JMPZ_EX:
+ case ZEND_JMPNZ_EX:
+ case ZEND_FE_RESET_R:
+ case ZEND_FE_RESET_RW:
+ case ZEND_JMP_SET:
+ case ZEND_COALESCE:
+ case ZEND_ASSERT_CHECK:
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op2, new_opcodes + blocks[b->successors[0]].start);
+ break;
+ case ZEND_CATCH:
+ if (!opline->result.var) {
+ opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, new_opcodes + blocks[b->successors[0]].start);
+ }
+ break;
+ case ZEND_DECLARE_ANON_CLASS:
+ case ZEND_DECLARE_ANON_INHERITED_CLASS:
+ case ZEND_FE_FETCH_R:
+ case ZEND_FE_FETCH_RW:
+ opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, new_opcodes + blocks[b->successors[0]].start);
+ break;
+ }
+ }
- /* adjust exception jump targets */
+ /* adjust exception jump targets & remove unused try_catch_array entries */
if (op_array->last_try_catch) {
int i, j;
+ uint32_t *map;
+ ALLOCA_FLAG(use_heap);
+
+ map = (uint32_t *)do_alloca(sizeof(uint32_t) * op_array->last_try_catch, use_heap);
for (i = 0, j = 0; i< op_array->last_try_catch; i++) {
- if (cfg->try[i]->access) {
- op_array->try_catch_array[j].try_op = cfg->try[i]->start_opline - new_opcodes;
- op_array->try_catch_array[j].catch_op = cfg->catch[i]->start_opline - new_opcodes;
+ if (blocks[cfg->map[op_array->try_catch_array[i].try_op]].flags & ZEND_BB_REACHABLE) {
+ map[i] = j;
+ op_array->try_catch_array[j].try_op = blocks[cfg->map[op_array->try_catch_array[i].try_op]].start;
+ if (op_array->try_catch_array[i].catch_op) {
+ op_array->try_catch_array[j].catch_op = blocks[cfg->map[op_array->try_catch_array[i].catch_op]].start;
+ } else {
+ op_array->try_catch_array[j].catch_op = 0;
+ }
+ if (op_array->try_catch_array[i].finally_op) {
+ op_array->try_catch_array[j].finally_op = blocks[cfg->map[op_array->try_catch_array[i].finally_op]].start;
+ } else {
+ op_array->try_catch_array[j].finally_op = 0;
+ }
+ if (!op_array->try_catch_array[i].finally_end) {
+ op_array->try_catch_array[j].finally_end = 0;
+ } else {
+ op_array->try_catch_array[j].finally_end = blocks[cfg->map[op_array->try_catch_array[i].finally_end]].start;
+ }
j++;
}
}
- op_array->last_try_catch = j;
- }
-
- /* adjust loop jump targets */
- if (op_array->last_brk_cont) {
- int i;
- for (i = 0; i< op_array->last_brk_cont; i++) {
- op_array->brk_cont_array[i].start = cfg->loop_start[i]->start_opline - new_opcodes;
- op_array->brk_cont_array[i].cont = cfg->loop_cont[i]->start_opline - new_opcodes;
- op_array->brk_cont_array[i].brk = cfg->loop_brk[i]->start_opline - new_opcodes;
+ if (i != j) {
+ op_array->last_try_catch = j;
+ if (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) {
+ zend_op *opline = new_opcodes;
+ zend_op *end = opline + len;
+ while (opline < end) {
+ if (opline->opcode == ZEND_FAST_RET &&
+ opline->op2.num != (uint32_t)-1 &&
+ opline->op2.num < (uint32_t)j) {
+ opline->op2.num = map[opline->op2.num];
+ }
+ opline++;
+ }
+ }
}
+ free_alloca(map, use_heap);
}
- /* adjust jump targets */
- for (cur_block = blocks; cur_block; cur_block = cur_block->next) {
- if (!cur_block->access) {
- continue;
- }
- opline = cur_block->start_opline + cur_block->len - 1;
- if (opline->opcode == ZEND_OP_DATA) {
- opline--;
- }
- if (cur_block->op1_to) {
- ZEND_OP1(opline).opline_num = cur_block->op1_to->start_opline - new_opcodes;
- }
- if (cur_block->op2_to) {
- ZEND_OP2(opline).opline_num = cur_block->op2_to->start_opline - new_opcodes;
+ /* adjust loop jump targets & remove unused live range entries */
+ if (op_array->last_live_range) {
+ int i, j;
+ uint32_t *map;
+ ALLOCA_FLAG(use_heap);
+
+ map = (uint32_t *)do_alloca(sizeof(uint32_t) * op_array->last_live_range, use_heap);
+
+ for (i = 0, j = 0; i < op_array->last_live_range; i++) {
+ if (op_array->live_range[i].var == (uint32_t)-1) {
+ /* this live range already removed */
+ continue;
+ }
+ if (!(blocks[cfg->map[op_array->live_range[i].start]].flags & ZEND_BB_REACHABLE)) {
+ ZEND_ASSERT(!(blocks[cfg->map[op_array->live_range[i].end]].flags & ZEND_BB_REACHABLE));
+ } else {
+ uint32_t start_op = blocks[cfg->map[op_array->live_range[i].start]].start;
+ uint32_t end_op = blocks[cfg->map[op_array->live_range[i].end]].start;
+
+ if (start_op == end_op) {
+ /* skip empty live range */
+ continue;
+ }
+ op_array->live_range[i].start = start_op;
+ op_array->live_range[i].end = end_op;
+ map[i] = j;
+ if (i != j) {
+ op_array->live_range[j] = op_array->live_range[i];
+ }
+ j++;
+ }
}
- if (cur_block->ext_to) {
- opline->extended_value = cur_block->ext_to->start_opline - new_opcodes;
+
+ if (i != j) {
+ if ((op_array->last_live_range = j)) {
+ zend_op *opline = new_opcodes;
+ zend_op *end = opline + len;
+ while (opline != end) {
+ if ((opline->opcode == ZEND_FREE || opline->opcode == ZEND_FE_FREE) &&
+ opline->extended_value == ZEND_FREE_ON_RETURN) {
+ ZEND_ASSERT(opline->op2.num < (uint32_t) i);
+ opline->op2.num = map[opline->op2.num];
+ }
+ opline++;
+ }
+ } else {
+ efree(op_array->live_range);
+ op_array->live_range = NULL;
+ }
}
- print_block(cur_block, new_opcodes, "Out ");
+ free_alloca(map, use_heap);
}
- efree(op_array->opcodes);
- op_array->opcodes = erealloc(new_opcodes, op_array->last * sizeof(zend_op));
/* adjust early binding list */
if (op_array->early_binding != (uint32_t)-1) {
@@ -1286,45 +1015,56 @@ static void assemble_code_blocks(zend_cfg *cfg, zend_op_array *op_array)
}
*opline_num = -1;
}
+
+ /* rebuild map (just for printing) */
+ memset(cfg->map, -1, sizeof(int) * op_array->last);
+ for (n = 0; n < cfg->blocks_count; n++) {
+ if (cfg->blocks[n].flags & ZEND_BB_REACHABLE) {
+ cfg->map[cfg->blocks[n].start] = n;
+ }
+ }
}
-static void zend_jmp_optimization(zend_code_block *block, zend_op_array *op_array, zend_code_block *blocks, zend_cfg *cfg, zend_optimizer_ctx *ctx)
+static void zend_jmp_optimization(zend_basic_block *block, zend_op_array *op_array, zend_cfg *cfg, zend_uchar *same_t)
{
/* last_op is the last opcode of the current block */
- zend_op *last_op = (block->start_opline + block->len - 1);
+ zend_basic_block *blocks = cfg->blocks;
+ zend_op *last_op;
- if (!block->len) {
+ if (block->len == 0) {
return;
}
+
+ last_op = op_array->opcodes + block->start + block->len - 1;
switch (last_op->opcode) {
case ZEND_JMP:
{
- zend_op *target = block->op1_to->start_opline;
- zend_code_block *next = block->next;
+ zend_basic_block *target_block = blocks + block->successors[0];
+ zend_op *target = op_array->opcodes + target_block->start;
+ int next = (block - blocks) + 1;
- while (next && !next->access) {
+ while (next < cfg->blocks_count && !(blocks[next].flags & ZEND_BB_REACHABLE)) {
/* find used one */
- next = next->next;
+ next++;
}
/* JMP(next) -> NOP */
- if (block->op1_to == next) {
- block->follow_to = block->op1_to;
- block->op1_to = NULL;
+ if (block->successors[0] == next) {
MAKE_NOP(last_op);
block->len--;
- if (block->len == 0) {
- /* this block is nothing but NOP now */
- delete_code_block(block, ctx);
- }
break;
}
- if (((target->opcode == ZEND_JMP &&
- block->op1_to != block->op1_to->op1_to) ||
- target->opcode == ZEND_JMPZNZ) &&
- !block->op1_to->protected) {
+ if (target->opcode == ZEND_JMP &&
+ block->successors[0] != target_block->successors[0] &&
+ !(target_block->flags & ZEND_BB_PROTECTED)) {
/* JMP L, L: JMP L1 -> JMP L1 */
+ *last_op = *target;
+ DEL_SOURCE(block, block->successors[0]);
+ block->successors[0] = target_block->successors[0];
+ ADD_SOURCE(block, block->successors[0]);
+ } else if (target->opcode == ZEND_JMPZNZ &&
+ !(target_block->flags & ZEND_BB_PROTECTED)) {
/* JMP L, L: JMPZNZ L1,L2 -> JMPZNZ L1,L2 */
*last_op = *target;
if (ZEND_OP1_TYPE(last_op) == IS_CONST) {
@@ -1332,25 +1072,15 @@ static void zend_jmp_optimization(zend_code_block *block, zend_op_array *op_arra
zval_copy_ctor(&zv);
last_op->op1.constant = zend_optimizer_add_literal(op_array, &zv);
}
- del_source(block, block->op1_to);
- if (block->op1_to->op2_to) {
- block->op2_to = block->op1_to->op2_to;
- ADD_SOURCE(block, block->op2_to);
- }
- if (block->op1_to->ext_to) {
- block->ext_to = block->op1_to->ext_to;
- ADD_SOURCE(block, block->ext_to);
- }
- if (block->op1_to->op1_to) {
- block->op1_to = block->op1_to->op1_to;
- ADD_SOURCE(block, block->op1_to);
- } else {
- block->op1_to = NULL;
- }
- } else if (target->opcode == ZEND_RETURN ||
- target->opcode == ZEND_RETURN_BY_REF ||
- target->opcode == ZEND_FAST_RET ||
- target->opcode == ZEND_EXIT) {
+ DEL_SOURCE(block, block->successors[0]);
+ block->successors[0] = target_block->successors[0];
+ block->successors[1] = target_block->successors[1];
+ ADD_SOURCE(block, block->successors[0]);
+ ADD_SOURCE(block, block->successors[1]);
+ } else if ((target->opcode == ZEND_RETURN ||
+ target->opcode == ZEND_RETURN_BY_REF ||
+ target->opcode == ZEND_EXIT) &&
+ !(op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK)) {
/* JMP L, L: RETURN to immediate RETURN */
*last_op = *target;
if (ZEND_OP1_TYPE(last_op) == IS_CONST) {
@@ -1358,8 +1088,8 @@ static void zend_jmp_optimization(zend_code_block *block, zend_op_array *op_arra
zval_copy_ctor(&zv);
last_op->op1.constant = zend_optimizer_add_literal(op_array, &zv);
}
- del_source(block, block->op1_to);
- block->op1_to = NULL;
+ DEL_SOURCE(block, block->successors[0]);
+ block->successors[0] = -1;
#if 0
/* Temporarily disabled - see bug #0025274 */
} else if (0&& block->op1_to != block &&
@@ -1392,7 +1122,7 @@ static void zend_jmp_optimization(zend_code_block *block, zend_op_array *op_arra
next = next->follow_to;
}
if (can_reorder) {
- zend_code_block *prev = blocks;
+ zend_basic_block *prev = blocks;
while (prev->next != block->op1_to) {
prev = prev->next;
@@ -1431,161 +1161,138 @@ static void zend_jmp_optimization(zend_code_block *block, zend_op_array *op_arra
if (should_jmp) {
/* JMPNZ(true) -> JMP */
last_op->opcode = ZEND_JMP;
- COPY_NODE(last_op->op1, last_op->op2);
- block->op1_to = block->op2_to;
- del_source(block, block->follow_to);
- block->op2_to = NULL;
- block->follow_to = NULL;
+ DEL_SOURCE(block, block->successors[1]);
+ block->successors[1] = -1;
} else {
/* JMPNZ(false) -> NOP */
MAKE_NOP(last_op);
- del_source(block, block->op2_to);
- block->op2_to = NULL;
+ DEL_SOURCE(block, block->successors[0]);
+ block->successors[0] = block->successors[1];
+ block->successors[1] = -1;
}
break;
}
- if (block->op2_to == block->follow_to) {
- /* L: JMPZ(X, L+1) -> NOP or FREE(X) */
-
- if (last_op->op1_type == IS_VAR) {
- zend_op **Tsource = cfg->Tsource;
- zend_op *src = VAR_SOURCE(last_op->op1);
+ if (block->successors[0] == block->successors[1]) {
+ /* L: JMP[N]Z(X, L+1) -> NOP or FREE(X) */
- if (src &&
- src->opcode != ZEND_FETCH_R &&
- src->opcode != ZEND_FETCH_DIM_R &&
- src->opcode != ZEND_FETCH_OBJ_R) {
- ZEND_RESULT_TYPE(src) |= EXT_TYPE_UNUSED;
- MAKE_NOP(last_op);
- block->op2_to = NULL;
- break;
- }
- }
if (last_op->op1_type == IS_CV) {
- break;
+ last_op->opcode = ZEND_CHECK_VAR;
+ last_op->op2.num = 0;
} else if (last_op->op1_type & (IS_VAR|IS_TMP_VAR)) {
last_op->opcode = ZEND_FREE;
last_op->op2.num = 0;
- block->op2_to = NULL;
} else {
MAKE_NOP(last_op);
- block->op2_to = NULL;
}
+ block->successors[1] = -1;
break;
}
- if (block->op2_to) {
+ if (1) {
zend_uchar same_type = ZEND_OP1_TYPE(last_op);
uint32_t same_var = VAR_NUM_EX(last_op->op1);
zend_op *target;
zend_op *target_end;
- zend_code_block *target_block = block->op2_to;;
+ zend_basic_block *target_block = blocks + block->successors[0];
next_target:
- target = target_block->start_opline;
- target_end = target_block->start_opline + target_block->len;
+ target = op_array->opcodes + target_block->start;
+ target_end = target + target_block->len;
while (target < target_end && target->opcode == ZEND_NOP) {
target++;
}
/* next block is only NOP's */
if (target == target_end) {
- target_block = target_block->follow_to;
+ target_block = blocks + target_block->successors[0];
goto next_target;
} else if (target->opcode == INV_COND(last_op->opcode) &&
/* JMPZ(X, L), L: JMPNZ(X, L2) -> JMPZ(X, L+1) */
(ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
same_type == ZEND_OP1_TYPE(target) &&
same_var == VAR_NUM_EX(target->op1) &&
- target_block->follow_to &&
- !target_block->protected
+ !(target_block->flags & ZEND_BB_PROTECTED)
) {
- del_source(block, block->op2_to);
- block->op2_to = target_block->follow_to;
- ADD_SOURCE(block, block->op2_to);
+ DEL_SOURCE(block, block->successors[0]);
+ block->successors[0] = target_block->successors[1];
+ ADD_SOURCE(block, block->successors[0]);
} else if (target->opcode == INV_COND_EX(last_op->opcode) &&
(ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
same_type == ZEND_OP1_TYPE(target) &&
same_var == VAR_NUM_EX(target->op1) &&
- target_block->follow_to &&
- !target_block->protected) {
+ !(target_block->flags & ZEND_BB_PROTECTED)) {
/* JMPZ(X, L), L: T = JMPNZ_EX(X, L2) -> T = JMPZ_EX(X, L+1) */
last_op->opcode += 3;
COPY_NODE(last_op->result, target->result);
- del_source(block, block->op2_to);
- block->op2_to = target_block->follow_to;
- ADD_SOURCE(block, block->op2_to);
- } else if (target_block->op2_to &&
- target->opcode == last_op->opcode &&
+ DEL_SOURCE(block, block->successors[0]);
+ block->successors[0] = target_block->successors[1];
+ ADD_SOURCE(block, block->successors[0]);
+ } else if (target->opcode == last_op->opcode &&
(ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
same_type == ZEND_OP1_TYPE(target) &&
same_var == VAR_NUM_EX(target->op1) &&
- !target_block->protected) {
+ !(target_block->flags & ZEND_BB_PROTECTED)) {
/* JMPZ(X, L), L: JMPZ(X, L2) -> JMPZ(X, L2) */
- del_source(block, block->op2_to);
- block->op2_to = target_block->op2_to;
- ADD_SOURCE(block, block->op2_to);
- } else if (target_block->op1_to &&
- target->opcode == ZEND_JMP &&
- !target_block->protected) {
+ DEL_SOURCE(block, block->successors[0]);
+ block->successors[0] = target_block->successors[0];
+ ADD_SOURCE(block, block->successors[0]);
+ } else if (target->opcode == ZEND_JMP &&
+ !(target_block->flags & ZEND_BB_PROTECTED)) {
/* JMPZ(X, L), L: JMP(L2) -> JMPZ(X, L2) */
- del_source(block, block->op2_to);
- block->op2_to = target_block->op1_to;
- ADD_SOURCE(block, block->op2_to);
- } else if (target_block->op2_to &&
- target_block->ext_to &&
- target->opcode == ZEND_JMPZNZ &&
- (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
- same_type == ZEND_OP1_TYPE(target) &&
- same_var == VAR_NUM_EX(target->op1) &&
- !target_block->protected) {
+ DEL_SOURCE(block, block->successors[0]);
+ block->successors[0] = target_block->successors[0];
+ ADD_SOURCE(block, block->successors[0]);
+ } else if (target->opcode == ZEND_JMPZNZ &&
+ (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
+ same_type == ZEND_OP1_TYPE(target) &&
+ same_var == VAR_NUM_EX(target->op1) &&
+ !(target_block->flags & ZEND_BB_PROTECTED)) {
/* JMPZ(X, L), L: JMPZNZ(X, L2, L3) -> JMPZ(X, L2) */
- del_source(block, block->op2_to);
+ DEL_SOURCE(block, block->successors[0]);
if (last_op->opcode == ZEND_JMPZ) {
- block->op2_to = target_block->op2_to;
+ block->successors[0] = target_block->successors[0];
} else {
- block->op2_to = target_block->ext_to;
+ block->successors[0] = target_block->successors[1];
}
- ADD_SOURCE(block, block->op2_to);
+ ADD_SOURCE(block, block->successors[0]);
}
}
- if (block->follow_to &&
- (last_op->opcode == ZEND_JMPZ || last_op->opcode == ZEND_JMPNZ)) {
+ if (last_op->opcode == ZEND_JMPZ || last_op->opcode == ZEND_JMPNZ) {
zend_op *target;
zend_op *target_end;
+ zend_basic_block *target_block;
while (1) {
- target = block->follow_to->start_opline;
- target_end = block->follow_to->start_opline + block->follow_to->len;
+ target_block = blocks + block->successors[1];
+ target = op_array->opcodes + target_block->start;
+ target_end = op_array->opcodes + target_block->start + 1;
while (target < target_end && target->opcode == ZEND_NOP) {
target++;
}
/* next block is only NOP's */
- if (target == target_end && ! block->follow_to->protected) {
- del_source(block, block->follow_to);
- block->follow_to = block->follow_to->follow_to;
- ADD_SOURCE(block, block->follow_to);
+ if (target == target_end && !(target_block->flags & ZEND_BB_PROTECTED)) {
+ DEL_SOURCE(block, block->successors[1]);
+ block->successors[1] = target_block->successors[0];
+ ADD_SOURCE(block, block->successors[1]);
} else {
break;
}
}
/* JMPZ(X,L1), JMP(L2) -> JMPZNZ(X,L1,L2) */
if (target->opcode == ZEND_JMP &&
- block->follow_to->op1_to &&
- !block->follow_to->protected) {
- del_source(block, block->follow_to);
+ !(target_block->flags & ZEND_BB_PROTECTED)) {
+ DEL_SOURCE(block, block->successors[1]);
if (last_op->opcode == ZEND_JMPZ) {
- block->ext_to = block->follow_to->op1_to;
- ADD_SOURCE(block, block->ext_to);
+ block->successors[1] = target_block->successors[0];
+ ADD_SOURCE(block, block->successors[1]);
} else {
- block->ext_to = block->op2_to;
- block->op2_to = block->follow_to->op1_to;
- ADD_SOURCE(block, block->op2_to);
+ block->successors[1] = block->successors[0];
+ block->successors[0] = target_block->successors[0];
+ ADD_SOURCE(block, block->successors[0]);
}
- block->follow_to = NULL;
last_op->opcode = ZEND_JMPZNZ;
}
}
@@ -1606,207 +1313,182 @@ next_target:
*/
last_op->opcode = ZEND_QM_ASSIGN;
SET_UNUSED(last_op->op2);
- del_source(block, block->op2_to);
- block->op2_to = NULL;
+ DEL_SOURCE(block, block->successors[0]);
+ block->successors[0] = block->successors[1];
+ block->successors[1] = -1;
}
break;
}
- if (block->op2_to) {
+ if (1) {
zend_op *target, *target_end;
- char *same_t=NULL;
- zend_code_block *target_block;
+ zend_basic_block *target_block;
int var_num = op_array->last_var + op_array->T;
if (var_num <= 0) {
return;
}
- same_t = cfg->same_t;
memset(same_t, 0, var_num);
same_t[VAR_NUM_EX(last_op->op1)] |= ZEND_OP1_TYPE(last_op);
same_t[VAR_NUM_EX(last_op->result)] |= ZEND_RESULT_TYPE(last_op);
- target_block = block->op2_to;
+ target_block = blocks + block->successors[0];
next_target_ex:
- target = target_block->start_opline;
- target_end = target_block->start_opline + target_block->len;
+ target = op_array->opcodes + target_block->start;
+ target_end = target + target_block->len;
while (target < target_end && target->opcode == ZEND_NOP) {
target++;
}
/* next block is only NOP's */
if (target == target_end) {
- target_block = target_block->follow_to;
+ target_block = blocks + target_block->successors[0];
goto next_target_ex;
- } else if (target_block->op2_to &&
- target->opcode == last_op->opcode-3 &&
+ } else if (target->opcode == last_op->opcode-3 &&
(ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
(same_t[VAR_NUM_EX(target->op1)] & ZEND_OP1_TYPE(target)) != 0 &&
- !target_block->protected) {
+ !(target_block->flags & ZEND_BB_PROTECTED)) {
/* T = JMPZ_EX(X, L1), L1: JMPZ({X|T}, L2) -> T = JMPZ_EX(X, L2) */
- del_source(block, block->op2_to);
- block->op2_to = target_block->op2_to;
- ADD_SOURCE(block, block->op2_to);
- } else if (target_block->op2_to &&
- target->opcode == INV_EX_COND(last_op->opcode) &&
+ DEL_SOURCE(block, block->successors[0]);
+ block->successors[0] = target_block->successors[0];
+ ADD_SOURCE(block, block->successors[0]);
+ } else if (target->opcode == INV_EX_COND(last_op->opcode) &&
(ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
(same_t[VAR_NUM_EX(target->op1)] & ZEND_OP1_TYPE(target)) != 0 &&
- !target_block->protected) {
+ !(target_block->flags & ZEND_BB_PROTECTED)) {
/* T = JMPZ_EX(X, L1), L1: JMPNZ({X|T1}, L2) -> T = JMPZ_EX(X, L1+1) */
- del_source(block, block->op2_to);
- block->op2_to = target_block->follow_to;
- ADD_SOURCE(block, block->op2_to);
- } else if (target_block->op2_to &&
- target->opcode == INV_EX_COND_EX(last_op->opcode) &&
+ DEL_SOURCE(block, block->successors[0]);
+ block->successors[0] = target_block->successors[1];
+ ADD_SOURCE(block, block->successors[0]);
+ } else if (target->opcode == INV_EX_COND_EX(last_op->opcode) &&
(ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
(same_t[VAR_NUM_EX(target->op1)] & ZEND_OP1_TYPE(target)) != 0 &&
(same_t[VAR_NUM_EX(target->result)] & ZEND_RESULT_TYPE(target)) != 0 &&
- !target_block->protected) {
+ !(target_block->flags & ZEND_BB_PROTECTED)) {
/* T = JMPZ_EX(X, L1), L1: T = JMPNZ_EX(T, L2) -> T = JMPZ_EX(X, L1+1) */
- del_source(block, block->op2_to);
- block->op2_to = target_block->follow_to;
- ADD_SOURCE(block, block->op2_to);
- } else if (target_block->op2_to &&
- target->opcode == last_op->opcode &&
+ DEL_SOURCE(block, block->successors[0]);
+ block->successors[0] = target_block->successors[1];
+ ADD_SOURCE(block, block->successors[0]);
+ } else if (target->opcode == last_op->opcode &&
(ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
(same_t[VAR_NUM_EX(target->op1)] & ZEND_OP1_TYPE(target)) != 0 &&
(same_t[VAR_NUM_EX(target->result)] & ZEND_RESULT_TYPE(target)) != 0 &&
- !target_block->protected) {
+ !(target_block->flags & ZEND_BB_PROTECTED)) {
/* T = JMPZ_EX(X, L1), L1: T = JMPZ({X|T}, L2) -> T = JMPZ_EX(X, L2) */
- del_source(block, block->op2_to);
- block->op2_to = target_block->op2_to;
- ADD_SOURCE(block, block->op2_to);
- } else if (target_block->op1_to &&
- target->opcode == ZEND_JMP &&
- !target_block->protected) {
+ DEL_SOURCE(block, block->successors[0]);
+ block->successors[0] = target_block->successors[0];
+ ADD_SOURCE(block, block->successors[0]);
+ } else if (target->opcode == ZEND_JMP &&
+ !(target_block->flags & ZEND_BB_PROTECTED)) {
/* T = JMPZ_EX(X, L), L: JMP(L2) -> T = JMPZ(X, L2) */
- del_source(block, block->op2_to);
- block->op2_to = target_block->op1_to;
- ADD_SOURCE(block, block->op2_to);
- } else if (target_block->op2_to &&
- target_block->ext_to &&
- target->opcode == ZEND_JMPZNZ &&
+ DEL_SOURCE(block, block->successors[0]);
+ block->successors[0] = target_block->successors[0];
+ ADD_SOURCE(block, block->successors[0]);
+ } else if (target->opcode == ZEND_JMPZNZ &&
(ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
(same_t[VAR_NUM_EX(target->op1)] & ZEND_OP1_TYPE(target)) != 0 &&
- !target_block->protected) {
+ !(target_block->flags & ZEND_BB_PROTECTED)) {
/* T = JMPZ_EX(X, L), L: JMPZNZ({X|T}, L2, L3) -> T = JMPZ_EX(X, L2) */
- del_source(block, block->op2_to);
+ DEL_SOURCE(block, block->successors[0]);
if (last_op->opcode == ZEND_JMPZ_EX) {
- block->op2_to = target_block->op2_to;
+ block->successors[0] = target_block->successors[0];
} else {
- block->op2_to = target_block->ext_to;
+ block->successors[0] = target_block->successors[1];
}
- ADD_SOURCE(block, block->op2_to);
+ ADD_SOURCE(block, block->successors[0]);
}
}
break;
case ZEND_JMPZNZ: {
- zend_code_block *next = block->next;
+ int next = (block - blocks) + 1;
- while (next && !next->access) {
+ while (next < cfg->blocks_count && !(blocks[next].flags & ZEND_BB_REACHABLE)) {
/* find first accessed one */
- next = next->next;
+ next++;
}
if (ZEND_OP1_TYPE(last_op) == IS_CONST) {
if (!zend_is_true(&ZEND_OP1_LITERAL(last_op))) {
/* JMPZNZ(false,L1,L2) -> JMP(L1) */
- zend_code_block *todel;
-
literal_dtor(&ZEND_OP1_LITERAL(last_op));
last_op->opcode = ZEND_JMP;
SET_UNUSED(last_op->op1);
SET_UNUSED(last_op->op2);
- block->op1_to = block->op2_to;
- todel = block->ext_to;
- block->op2_to = NULL;
- block->ext_to = NULL;
- del_source(block, todel);
+ DEL_SOURCE(block, block->successors[1]);
+ block->successors[1] = -1;
} else {
/* JMPZNZ(true,L1,L2) -> JMP(L2) */
- zend_code_block *todel;
-
literal_dtor(&ZEND_OP1_LITERAL(last_op));
last_op->opcode = ZEND_JMP;
SET_UNUSED(last_op->op1);
SET_UNUSED(last_op->op2);
- block->op1_to = block->ext_to;
- todel = block->op2_to;
- block->op2_to = NULL;
- block->ext_to = NULL;
- del_source(block, todel);
+ DEL_SOURCE(block, block->successors[0]);
+ block->successors[0] = block->successors[1];
+ block->successors[1] = -1;
}
- } else if (block->op2_to == block->ext_to) {
+ } else if (block->successors[0] == block->successors[1]) {
/* both goto the same one - it's JMP */
if (!(last_op->op1_type & (IS_VAR|IS_TMP_VAR))) {
/* JMPZNZ(?,L,L) -> JMP(L) */
last_op->opcode = ZEND_JMP;
SET_UNUSED(last_op->op1);
SET_UNUSED(last_op->op2);
- block->op1_to = block->op2_to;
- block->op2_to = NULL;
- block->ext_to = NULL;
+ block->successors[1] = -1;
}
- } else if (block->op2_to == next) {
+ } else if (block->successors[0] == next) {
/* jumping to next on Z - can follow to it and jump only on NZ */
/* JMPZNZ(X,L1,L2) L1: -> JMPNZ(X,L2) */
last_op->opcode = ZEND_JMPNZ;
- block->op2_to = block->ext_to;
- block->follow_to = next;
- block->ext_to = NULL;
- /* no need to add source - it's block->op2_to */
- } else if (block->ext_to == next) {
+ block->successors[0] = block->successors[1];
+ block->successors[1] = next;
+ /* no need to add source */
+ } else if (block->successors[1] == next) {
/* jumping to next on NZ - can follow to it and jump only on Z */
/* JMPZNZ(X,L1,L2) L2: -> JMPZ(X,L1) */
last_op->opcode = ZEND_JMPZ;
- block->follow_to = next;
- block->ext_to = NULL;
- /* no need to add source - it's block->ext_to */
+ /* no need to add source */
}
- if (last_op->opcode == ZEND_JMPZNZ && block->op2_to) {
+ if (last_op->opcode == ZEND_JMPZNZ) {
zend_uchar same_type = ZEND_OP1_TYPE(last_op);
zend_uchar same_var = VAR_NUM_EX(last_op->op1);
zend_op *target;
zend_op *target_end;
- zend_code_block *target_block = block->op2_to;
+ zend_basic_block *target_block = blocks + block->successors[0];
next_target_znz:
- target = target_block->start_opline;
- target_end = target_block->start_opline + target_block->len;
+ target = op_array->opcodes + target_block->start;
+ target_end = target + target_block->len;
while (target < target_end && target->opcode == ZEND_NOP) {
target++;
}
/* next block is only NOP's */
if (target == target_end) {
- target_block = target_block->follow_to;
+ target_block = blocks + target_block->successors[0];
goto next_target_znz;
- } else if (target_block->op2_to &&
- (target->opcode == ZEND_JMPZ || target->opcode == ZEND_JMPZNZ) &&
+ } else if ((target->opcode == ZEND_JMPZ || target->opcode == ZEND_JMPZNZ) &&
(ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
same_type == ZEND_OP1_TYPE(target) &&
same_var == VAR_NUM_EX(target->op1) &&
- !target_block->protected) {
+ !(target_block->flags & ZEND_BB_PROTECTED)) {
/* JMPZNZ(X, L1, L2), L1: JMPZ(X, L3) -> JMPZNZ(X, L3, L2) */
- del_source(block, block->op2_to);
- block->op2_to = target_block->op2_to;
- ADD_SOURCE(block, block->op2_to);
+ DEL_SOURCE(block, block->successors[0]);
+ block->successors[0] = target_block->successors[0];
+ ADD_SOURCE(block, block->successors[0]);
} else if (target->opcode == ZEND_JMPNZ &&
(ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
same_type == ZEND_OP1_TYPE(target) &&
same_var == VAR_NUM_EX(target->op1) &&
- target_block->follow_to &&
- !target_block->protected) {
+ !(target_block->flags & ZEND_BB_PROTECTED)) {
/* JMPZNZ(X, L1, L2), L1: X = JMPNZ(X, L3) -> JMPZNZ(X, L1+1, L2) */
- del_source(block, block->op2_to);
- block->op2_to = target_block->follow_to;
- ADD_SOURCE(block, block->op2_to);
- } else if (target_block->op1_to &&
- target->opcode == ZEND_JMP &&
- !target_block->protected) {
+ DEL_SOURCE(block, block->successors[0]);
+ block->successors[0] = target_block->successors[1];
+ ADD_SOURCE(block, block->successors[0]);
+ } else if (target->opcode == ZEND_JMP &&
+ !(target_block->flags & ZEND_BB_PROTECTED)) {
/* JMPZNZ(X, L1, L2), L1: JMP(L3) -> JMPZNZ(X, L3, L2) */
- del_source(block, block->op2_to);
- block->op2_to = target_block->op1_to;
- ADD_SOURCE(block, block->op2_to);
+ DEL_SOURCE(block, block->successors[0]);
+ block->successors[0] = target_block->successors[0];
+ ADD_SOURCE(block, block->successors[0]);
}
}
break;
@@ -1816,25 +1498,19 @@ next_target_znz:
/* Global data dependencies */
-#define T_USAGE(op) do { \
- if ((op ## _type & (IS_VAR | IS_TMP_VAR)) && \
- !zend_bitset_in(defined_here, VAR_NUM(op.var)) && !zend_bitset_in(used_ext, VAR_NUM(op.var))) { \
- zend_bitset_incl(used_ext, VAR_NUM(op.var)); \
- } \
- } while (0)
-
-#define NEVER_USED(op) ((op ## _type & (IS_VAR | IS_TMP_VAR)) && !zend_bitset_in(usage, VAR_NUM(op.var))) /* !zend_bitset_in(used_ext, op.var) && */
-#define RES_NEVER_USED(opline) (opline->result_type == IS_UNUSED || NEVER_USED(opline->result))
-
/* Find a set of variables which are used outside of the block where they are
* defined. We won't apply some optimization patterns for such variables. */
-static void zend_t_usage(zend_code_block *block, zend_op_array *op_array, zend_bitset used_ext, zend_optimizer_ctx *ctx)
+static void zend_t_usage(zend_cfg *cfg, zend_op_array *op_array, zend_bitset used_ext, zend_optimizer_ctx *ctx)
{
- zend_code_block *next_block = block->next;
+ int n;
+ zend_basic_block *block, *next_block;
+ uint32_t var_num;
uint32_t bitset_len;
zend_bitset usage;
zend_bitset defined_here;
void *checkpoint;
+ zend_op *opline, *end;
+
if (op_array->T == 0) {
/* shortcut - if no Ts, nothing to do */
@@ -1843,218 +1519,347 @@ static void zend_t_usage(zend_code_block *block, zend_op_array *op_array, zend_b
checkpoint = zend_arena_checkpoint(ctx->arena);
bitset_len = zend_bitset_len(op_array->last_var + op_array->T);
- usage = zend_arena_alloc(&ctx->arena, bitset_len * ZEND_BITSET_ELM_SIZE);
- zend_bitset_clear(usage, bitset_len);
defined_here = zend_arena_alloc(&ctx->arena, bitset_len * ZEND_BITSET_ELM_SIZE);
- while (next_block) {
- zend_op *opline = next_block->start_opline;
- zend_op *end = opline + next_block->len;
+ zend_bitset_clear(defined_here, bitset_len);
+ for (n = 1; n < cfg->blocks_count; n++) {
+ block = cfg->blocks + n;
- if (!next_block->access) {
- next_block = next_block->next;
+ if (!(block->flags & ZEND_BB_REACHABLE)) {
continue;
}
- zend_bitset_clear(defined_here, bitset_len);
+
+ opline = op_array->opcodes + block->start;
+ end = opline + block->len;
+ if (!(block->flags & ZEND_BB_FOLLOW) ||
+ (block->flags & ZEND_BB_TARGET)) {
+ /* Skip continuation of "extended" BB */
+ zend_bitset_clear(defined_here, bitset_len);
+ }
while (opline<end) {
- T_USAGE(opline->op1);
- if (opline->op2_type & (IS_VAR | IS_TMP_VAR)) {
- if (opline->opcode == ZEND_FE_FETCH_R || opline->opcode == ZEND_FE_FETCH_RW) {
+ if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) {
+ var_num = VAR_NUM(opline->op1.var);
+ if (!zend_bitset_in(defined_here, var_num)) {
+ zend_bitset_incl(used_ext, var_num);
+ }
+ }
+ if (opline->op2_type == IS_VAR) {
+ var_num = VAR_NUM(opline->op2.var);
+ if (opline->opcode == ZEND_FE_FETCH_R ||
+ opline->opcode == ZEND_FE_FETCH_RW) {
/* these opcode use the op2 as result */
- zend_bitset_incl(defined_here, VAR_NUM(ZEND_OP2(opline).var));
- } else {
- T_USAGE(opline->op2);
+ zend_bitset_incl(defined_here, var_num);
+ } else if (!zend_bitset_in(defined_here, var_num)) {
+ zend_bitset_incl(used_ext, var_num);
+ }
+ } else if (opline->op2_type == IS_TMP_VAR) {
+ var_num = VAR_NUM(opline->op2.var);
+ if (!zend_bitset_in(defined_here, var_num)) {
+ zend_bitset_incl(used_ext, var_num);
}
}
- if (RESULT_USED(opline)) {
- if (!zend_bitset_in(defined_here, VAR_NUM(ZEND_RESULT(opline).var)) && !zend_bitset_in(used_ext, VAR_NUM(ZEND_RESULT(opline).var)) &&
- opline->opcode == ZEND_ADD_ARRAY_ELEMENT) {
- /* these opcode use the result as argument */
- zend_bitset_incl(used_ext, VAR_NUM(ZEND_RESULT(opline).var));
+ if (opline->result_type == IS_VAR) {
+ var_num = VAR_NUM(opline->result.var);
+ zend_bitset_incl(defined_here, var_num);
+ } else if (opline->result_type == IS_TMP_VAR) {
+ var_num = VAR_NUM(opline->result.var);
+ switch (opline->opcode) {
+ case ZEND_ADD_ARRAY_ELEMENT:
+ case ZEND_ROPE_ADD:
+ /* these opcodes use the result as argument */
+ if (!zend_bitset_in(defined_here, var_num)) {
+ zend_bitset_incl(used_ext, var_num);
+ }
+ break;
+ default :
+ zend_bitset_incl(defined_here, var_num);
}
- zend_bitset_incl(defined_here, VAR_NUM(ZEND_RESULT(opline).var));
}
opline++;
}
- next_block = next_block->next;
}
-#if DEBUG_BLOCKPASS
- {
- int i;
+ if (ctx->debug_level & ZEND_DUMP_BLOCK_PASS_VARS) {
+ int printed = 0;
+ uint32_t i;
+
for (i = op_array->last_var; i< op_array->T; i++) {
- fprintf(stderr, "T%d: %c\n", i, zend_bitset_in(used_ext, i) + '0');
+ if (zend_bitset_in(used_ext, i)) {
+ if (!printed) {
+ fprintf(stderr, "NON-LOCAL-VARS: %d", i);
+ printed = 1;
+ } else {
+ fprintf(stderr, ", %d", i);
+ }
+ }
+ }
+ if (printed) {
+ fprintf(stderr, "\n");
}
}
-#endif
- while (block) {
- zend_op *opline = block->start_opline + block->len - 1;
+ usage = defined_here;
+ next_block = NULL;
+ for (n = cfg->blocks_count; n > 0;) {
+ block = cfg->blocks + (--n);
- if (!block->access) {
- block = block->next;
+ if (!(block->flags & ZEND_BB_REACHABLE) || block->len == 0) {
continue;
}
- zend_bitset_copy(usage, used_ext, bitset_len);
+ end = op_array->opcodes + block->start;
+ opline = end + block->len - 1;
+ if (!next_block ||
+ !(next_block->flags & ZEND_BB_FOLLOW) ||
+ (next_block->flags & ZEND_BB_TARGET)) {
+ /* Skip continuation of "extended" BB */
+ zend_bitset_copy(usage, used_ext, bitset_len);
+ } else if (block->successors[1] != -1) {
+ zend_bitset_union(usage, used_ext, bitset_len);
+ }
+ next_block = block;
- while (opline >= block->start_opline) {
+ while (opline >= end) {
/* usage checks */
- if (RES_NEVER_USED(opline)) {
- switch (opline->opcode) {
- case ZEND_ASSIGN_ADD:
- case ZEND_ASSIGN_SUB:
- case ZEND_ASSIGN_MUL:
- case ZEND_ASSIGN_DIV:
- case ZEND_ASSIGN_POW:
- case ZEND_ASSIGN_MOD:
- case ZEND_ASSIGN_SL:
- case ZEND_ASSIGN_SR:
- case ZEND_ASSIGN_CONCAT:
- case ZEND_ASSIGN_BW_OR:
- case ZEND_ASSIGN_BW_AND:
- case ZEND_ASSIGN_BW_XOR:
- case ZEND_PRE_INC:
- case ZEND_PRE_DEC:
- case ZEND_POST_INC:
- case ZEND_POST_DEC:
- case ZEND_ASSIGN:
- case ZEND_ASSIGN_REF:
- case ZEND_DO_FCALL:
- case ZEND_DO_ICALL:
- case ZEND_DO_UCALL:
- case ZEND_DO_FCALL_BY_NAME:
- if (ZEND_RESULT_TYPE(opline) == IS_VAR) {
- ZEND_RESULT_TYPE(opline) |= EXT_TYPE_UNUSED;
- }
- break;
- case ZEND_QM_ASSIGN:
- case ZEND_BOOL:
- case ZEND_BOOL_NOT:
- if (ZEND_OP1_TYPE(opline) == IS_TMP_VAR) {
- opline->opcode = ZEND_FREE;
- } else {
- if (ZEND_OP1_TYPE(opline) == IS_CONST) {
- literal_dtor(&ZEND_OP1_LITERAL(opline));
+ if (opline->result_type == IS_VAR) {
+ if (!zend_bitset_in(usage, VAR_NUM(opline->result.var))) {
+ switch (opline->opcode) {
+ case ZEND_ASSIGN_ADD:
+ case ZEND_ASSIGN_SUB:
+ case ZEND_ASSIGN_MUL:
+ case ZEND_ASSIGN_DIV:
+ case ZEND_ASSIGN_POW:
+ case ZEND_ASSIGN_MOD:
+ case ZEND_ASSIGN_SL:
+ case ZEND_ASSIGN_SR:
+ case ZEND_ASSIGN_CONCAT:
+ case ZEND_ASSIGN_BW_OR:
+ case ZEND_ASSIGN_BW_AND:
+ case ZEND_ASSIGN_BW_XOR:
+ case ZEND_PRE_INC:
+ case ZEND_PRE_DEC:
+ case ZEND_ASSIGN:
+ case ZEND_ASSIGN_REF:
+ case ZEND_DO_FCALL:
+ case ZEND_DO_ICALL:
+ case ZEND_DO_UCALL:
+ case ZEND_DO_FCALL_BY_NAME:
+ opline->result_type = IS_UNUSED;
+ break;
+ }
+ } else {
+ zend_bitset_excl(usage, VAR_NUM(opline->result.var));
+ }
+ } else if (opline->result_type == IS_TMP_VAR) {
+ if (!zend_bitset_in(usage, VAR_NUM(opline->result.var))) {
+ switch (opline->opcode) {
+ case ZEND_POST_INC:
+ case ZEND_POST_DEC:
+ opline->opcode -= 2;
+ opline->result_type = IS_UNUSED;
+ break;
+ case ZEND_QM_ASSIGN:
+ case ZEND_BOOL:
+ case ZEND_BOOL_NOT:
+ if (ZEND_OP1_TYPE(opline) == IS_CV) {
+ opline->opcode = ZEND_CHECK_VAR;
+ SET_UNUSED(opline->result);
+ } else if (ZEND_OP1_TYPE(opline) & (IS_TMP_VAR|IS_VAR)) {
+ opline->opcode = ZEND_FREE;
+ SET_UNUSED(opline->result);
+ } else {
+ if (ZEND_OP1_TYPE(opline) == IS_CONST) {
+ literal_dtor(&ZEND_OP1_LITERAL(opline));
+ }
+ MAKE_NOP(opline);
}
- MAKE_NOP(opline);
- }
- break;
- case ZEND_JMPZ_EX:
- case ZEND_JMPNZ_EX:
- opline->opcode -= 3;
- SET_UNUSED(opline->result);
- break;
+ break;
+ case ZEND_JMPZ_EX:
+ case ZEND_JMPNZ_EX:
+ opline->opcode -= 3;
+ SET_UNUSED(opline->result);
+ break;
+ case ZEND_ADD_ARRAY_ELEMENT:
+ case ZEND_ROPE_ADD:
+ zend_bitset_incl(usage, VAR_NUM(opline->result.var));
+ break;
+ }
+ } else {
+ switch (opline->opcode) {
+ case ZEND_ADD_ARRAY_ELEMENT:
+ case ZEND_ROPE_ADD:
+ break;
+ default:
+ zend_bitset_excl(usage, VAR_NUM(opline->result.var));
+ break;
+ }
}
}
- if (opline->opcode == ZEND_ADD_ARRAY_ELEMENT) {
- if (ZEND_OP1_TYPE(opline) == IS_VAR || ZEND_OP1_TYPE(opline) == IS_TMP_VAR) {
- zend_bitset_incl(usage, VAR_NUM(ZEND_RESULT(opline).var));
- }
- } else {
- if (RESULT_USED(opline)) {
- zend_bitset_excl(usage, VAR_NUM(ZEND_RESULT(opline).var));
+ if (opline->op2_type == IS_VAR) {
+ switch (opline->opcode) {
+ case ZEND_FE_FETCH_R:
+ case ZEND_FE_FETCH_RW:
+ zend_bitset_excl(usage, VAR_NUM(opline->op2.var));
+ break;
+ default:
+ zend_bitset_incl(usage, VAR_NUM(opline->op2.var));
+ break;
}
+ } else if (opline->op2_type == IS_TMP_VAR) {
+ zend_bitset_incl(usage, VAR_NUM(opline->op2.var));
}
- if (ZEND_OP1_TYPE(opline) == IS_VAR || ZEND_OP1_TYPE(opline) == IS_TMP_VAR) {
- zend_bitset_incl(usage, VAR_NUM(ZEND_OP1(opline).var));
+ if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) {
+ zend_bitset_incl(usage, VAR_NUM(opline->op1.var));
}
- if (ZEND_OP2_TYPE(opline) == IS_VAR || ZEND_OP2_TYPE(opline) == IS_TMP_VAR) {
- zend_bitset_incl(usage, VAR_NUM(ZEND_OP2(opline).var));
- }
-
- if ((ZEND_RESULT_TYPE(opline) & IS_VAR) &&
- (ZEND_RESULT_TYPE(opline) & EXT_TYPE_UNUSED) &&
- zend_bitset_in(usage, VAR_NUM(ZEND_RESULT(opline).var))) {
- ZEND_RESULT_TYPE(opline) &= ~EXT_TYPE_UNUSED;
- }
-
opline--;
}
- block = block->next;
- } /* end blocks */
+ }
zend_arena_release(&ctx->arena, checkpoint);
}
+static void zend_merge_blocks(zend_op_array *op_array, zend_cfg *cfg)
+{
+ int i;
+ zend_basic_block *b, *bb;
+ zend_basic_block *prev = NULL;
+
+ for (i = 0; i < cfg->blocks_count; i++) {
+ b = cfg->blocks + i;
+ if (b->flags & ZEND_BB_REACHABLE) {
+ if ((b->flags & ZEND_BB_FOLLOW) &&
+ !(b->flags & (ZEND_BB_TARGET | ZEND_BB_PROTECTED)) &&
+ prev &&
+ prev->successors[0] == i && prev->successors[1] == -1)
+ {
+ zend_op *last_op = op_array->opcodes + prev->start + prev->len - 1;
+ if (prev->len != 0 && last_op->opcode == ZEND_JMP) {
+ MAKE_NOP(last_op);
+ }
+
+ for (bb = prev + 1; bb != b; bb++) {
+ zend_op *op = op_array->opcodes + bb->start;
+ zend_op *end = op + bb->len;
+ while (op < end) {
+ if (ZEND_OP1_TYPE(op) == IS_CONST) {
+ literal_dtor(&ZEND_OP1_LITERAL(op));
+ }
+ if (ZEND_OP2_TYPE(op) == IS_CONST) {
+ literal_dtor(&ZEND_OP2_LITERAL(op));
+ }
+ MAKE_NOP(op);
+ op++;
+ }
+ /* make block empty */
+ bb->len = 0;
+ }
+
+ /* re-link */
+ prev->flags |= (b->flags & ZEND_BB_EXIT);
+ prev->len = b->start + b->len - prev->start;
+ prev->successors[0] = b->successors[0];
+ prev->successors[1] = b->successors[1];
+
+ /* unlink & make block empty and unreachable */
+ b->flags = 0;
+ b->len = 0;
+ b->successors[0] = -1;
+ b->successors[1] = -1;
+ } else {
+ prev = b;
+ }
+ }
+ }
+}
+
#define PASSES 3
-void optimize_cfg(zend_op_array *op_array, zend_optimizer_ctx *ctx)
+void zend_optimize_cfg(zend_op_array *op_array, zend_optimizer_ctx *ctx)
{
zend_cfg cfg;
- zend_code_block *cur_block;
+ zend_basic_block *blocks, *end, *b;
int pass;
uint32_t bitset_len;
zend_bitset usage;
void *checkpoint;
+ zend_op **Tsource;
+ zend_uchar *same_t;
-#if DEBUG_BLOCKPASS
- fprintf(stderr, "File %s func %s\n", op_array->filename, op_array->function_name? op_array->function_name : "main");
- fflush(stderr);
-#endif
-
- if (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) {
+ /* Build CFG */
+ checkpoint = zend_arena_checkpoint(ctx->arena);
+ if (zend_build_cfg(&ctx->arena, op_array, ZEND_CFG_SPLIT_AT_LIVE_RANGES, &cfg, NULL) != SUCCESS) {
+ zend_arena_release(&ctx->arena, checkpoint);
return;
}
- if ((uint64_t) op_array->last * (op_array->last_var + op_array->T) > 512 * 1024 * 1024) {
+ if (cfg.blocks_count * (op_array->last_var + op_array->T) > 64 * 1024 * 1024) {
+ zend_arena_release(&ctx->arena, checkpoint);
return;
}
- /* Build CFG */
- checkpoint = zend_arena_checkpoint(ctx->arena);
- if (!find_code_blocks(op_array, &cfg, ctx)) {
- zend_arena_release(&ctx->arena, checkpoint);
- return;
+ if (ctx->debug_level & ZEND_DUMP_BEFORE_BLOCK_PASS) {
+ zend_dump_op_array(op_array, ZEND_DUMP_CFG, "before block pass", &cfg);
}
- zend_rebuild_access_path(&cfg, op_array, 0, ctx);
- /* full rebuild here to produce correct sources! */
if (op_array->last_var || op_array->T) {
bitset_len = zend_bitset_len(op_array->last_var + op_array->T);
- cfg.Tsource = zend_arena_calloc(&ctx->arena, op_array->last_var + op_array->T, sizeof(zend_op *));
- cfg.same_t = zend_arena_alloc(&ctx->arena, op_array->last_var + op_array->T);
+ Tsource = zend_arena_calloc(&ctx->arena, op_array->last_var + op_array->T, sizeof(zend_op *));
+ same_t = zend_arena_alloc(&ctx->arena, op_array->last_var + op_array->T);
usage = zend_arena_alloc(&ctx->arena, bitset_len * ZEND_BITSET_ELM_SIZE);
} else {
bitset_len = 0;
- cfg.Tsource = NULL;
- cfg.same_t = NULL;
+ Tsource = NULL;
+ same_t = NULL;
usage = NULL;
}
+ blocks = cfg.blocks;
+ end = blocks + cfg.blocks_count;
for (pass = 0; pass < PASSES; pass++) {
/* Compute data dependencies */
zend_bitset_clear(usage, bitset_len);
- zend_t_usage(cfg.blocks, op_array, usage, ctx);
+ zend_t_usage(&cfg, op_array, usage, ctx);
/* optimize each basic block separately */
- for (cur_block = cfg.blocks; cur_block; cur_block = cur_block->next) {
- if (!cur_block->access) {
+ for (b = blocks; b < end; b++) {
+ if (!(b->flags & ZEND_BB_REACHABLE)) {
continue;
}
- zend_optimize_block(cur_block, op_array, usage, &cfg, ctx);
+ /* we track data dependencies only insight a single basic block */
+ if (!(b->flags & ZEND_BB_FOLLOW) ||
+ (b->flags & ZEND_BB_TARGET)) {
+ /* Skip continuation of "extended" BB */
+ memset(Tsource, 0, (op_array->last_var + op_array->T) * sizeof(zend_op *));
+ }
+ zend_optimize_block(b, op_array, usage, &cfg, Tsource);
}
/* Jump optimization for each block */
- for (cur_block = cfg.blocks; cur_block; cur_block = cur_block->next) {
- if (!cur_block->access) {
- continue;
+ for (b = blocks; b < end; b++) {
+ if (b->flags & ZEND_BB_REACHABLE) {
+ zend_jmp_optimization(b, op_array, &cfg, same_t);
}
- zend_jmp_optimization(cur_block, op_array, cfg.blocks, &cfg, ctx);
}
/* Eliminate unreachable basic blocks */
- zend_rebuild_access_path(&cfg, op_array, 1, ctx);
+ zend_cfg_remark_reachable_blocks(op_array, &cfg);
+
+ /* Merge Blocks */
+ zend_merge_blocks(op_array, &cfg);
}
zend_bitset_clear(usage, bitset_len);
- zend_t_usage(cfg.blocks, op_array, usage, ctx);
+ zend_t_usage(&cfg, op_array, usage, ctx);
assemble_code_blocks(&cfg, op_array);
+ if (ctx->debug_level & ZEND_DUMP_AFTER_BLOCK_PASS) {
+ zend_dump_op_array(op_array, ZEND_DUMP_CFG | ZEND_DUMP_HIDE_UNREACHABLE, "after block pass", &cfg);
+ }
+
/* Destroy CFG */
zend_arena_release(&ctx->arena, checkpoint);
}
diff --git a/ext/opcache/Optimizer/compact_literals.c b/ext/opcache/Optimizer/compact_literals.c
index f417bef5fb..c133cd9714 100644
--- a/ext/opcache/Optimizer/compact_literals.c
+++ b/ext/opcache/Optimizer/compact_literals.c
@@ -72,9 +72,9 @@ typedef struct _literal_info {
info[n].u.num = (_num); \
} while (0)
-#define LITERAL_INFO_OBJ(n, kind, merge, slots, related, _num) do { \
+#define LITERAL_INFO_OBJ(n, kind, merge, slots, related) do { \
info[n].flags = (LITERAL_EX_OBJ | ((merge) ? LITERAL_MAY_MERGE : 0) | LITERAL_FLAGS(kind, slots, related)); \
- info[n].u.num = (_num); \
+ info[n].u.num = (uint32_t)-1; \
} while (0)
static void optimizer_literal_obj_info(literal_info *info,
@@ -92,7 +92,7 @@ static void optimizer_literal_obj_info(literal_info *info,
*/
if (Z_TYPE(op_array->literals[constant]) == IS_STRING &&
op_type == IS_UNUSED) {
- LITERAL_INFO_OBJ(constant, kind, 1, slots, related, op_array->this_var);
+ LITERAL_INFO_OBJ(constant, kind, 1, slots, related);
} else {
LITERAL_INFO(constant, kind, 0, slots, related);
}
@@ -180,52 +180,45 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
LITERAL_INFO(opline->op1.constant, LITERAL_CONST, 1, 1, 2);
break;
case ZEND_FETCH_CONSTANT:
- if (ZEND_OP1_TYPE(opline) == IS_UNUSED) {
- if ((opline->extended_value & (IS_CONSTANT_IN_NAMESPACE|IS_CONSTANT_UNQUALIFIED)) == (IS_CONSTANT_IN_NAMESPACE|IS_CONSTANT_UNQUALIFIED)) {
- LITERAL_INFO(opline->op2.constant, LITERAL_CONST, 1, 1, 5);
- } else {
- LITERAL_INFO(opline->op2.constant, LITERAL_CONST, 1, 1, 3);
- }
+ if ((opline->extended_value & (IS_CONSTANT_IN_NAMESPACE|IS_CONSTANT_UNQUALIFIED)) == (IS_CONSTANT_IN_NAMESPACE|IS_CONSTANT_UNQUALIFIED)) {
+ LITERAL_INFO(opline->op2.constant, LITERAL_CONST, 1, 1, 5);
} else {
- if (ZEND_OP1_TYPE(opline) == IS_CONST) {
- LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 1, 1, 2);
- }
+ LITERAL_INFO(opline->op2.constant, LITERAL_CONST, 1, 1, 3);
+ }
+ break;
+ case ZEND_FETCH_CLASS_CONSTANT:
+ if (ZEND_OP1_TYPE(opline) == IS_CONST) {
+ LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 1, 1, 2);
+ }
+ optimizer_literal_class_info(
+ info,
+ opline->op1_type,
+ opline->op1,
+ opline->op2.constant,
+ LITERAL_CLASS_CONST, (ZEND_OP1_TYPE(opline) == IS_CONST) ? 1 : 2, 1,
+ op_array);
+ break;
+ case ZEND_FETCH_STATIC_PROP_R:
+ case ZEND_FETCH_STATIC_PROP_W:
+ case ZEND_FETCH_STATIC_PROP_RW:
+ case ZEND_FETCH_STATIC_PROP_IS:
+ case ZEND_FETCH_STATIC_PROP_UNSET:
+ case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
+ case ZEND_UNSET_STATIC_PROP:
+ case ZEND_ISSET_ISEMPTY_STATIC_PROP:
+ if (ZEND_OP2_TYPE(opline) == IS_CONST) {
+ LITERAL_INFO(opline->op2.constant, LITERAL_CLASS, 1, 1, 2);
+ }
+ if (ZEND_OP1_TYPE(opline) == IS_CONST) {
optimizer_literal_class_info(
info,
- opline->op1_type,
- opline->op1,
- opline->op2.constant,
- LITERAL_CLASS_CONST, (ZEND_OP1_TYPE(opline) == IS_CONST) ? 1 : 2, 1,
+ opline->op2_type,
+ opline->op2,
+ opline->op1.constant,
+ LITERAL_STATIC_PROPERTY, 2, 1,
op_array);
}
break;
- case ZEND_FETCH_R:
- case ZEND_FETCH_W:
- case ZEND_FETCH_RW:
- case ZEND_FETCH_IS:
- case ZEND_FETCH_UNSET:
- case ZEND_FETCH_FUNC_ARG:
- case ZEND_UNSET_VAR:
- case ZEND_ISSET_ISEMPTY_VAR:
- if (ZEND_OP2_TYPE(opline) == IS_UNUSED) {
- if (ZEND_OP1_TYPE(opline) == IS_CONST) {
- LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 1, 0, 1);
- }
- } else {
- if (ZEND_OP2_TYPE(opline) == IS_CONST) {
- LITERAL_INFO(opline->op2.constant, LITERAL_CLASS, 1, 1, 2);
- }
- if (ZEND_OP1_TYPE(opline) == IS_CONST) {
- optimizer_literal_class_info(
- info,
- opline->op2_type,
- opline->op2,
- opline->op1.constant,
- LITERAL_STATIC_PROPERTY, 2, 1,
- op_array);
- }
- }
- break;
case ZEND_FETCH_CLASS:
case ZEND_ADD_INTERFACE:
case ZEND_ADD_TRAIT:
@@ -293,15 +286,21 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
break;
case ZEND_RECV_INIT:
LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 0, 0, 1);
- if (Z_CACHE_SLOT(op_array->literals[opline->op2.constant]) != -1) {
+ if (Z_CACHE_SLOT(op_array->literals[opline->op2.constant]) != (uint32_t)-1) {
Z_CACHE_SLOT(op_array->literals[opline->op2.constant]) = cache_size;
cache_size += sizeof(void *);
}
break;
+ case ZEND_DECLARE_FUNCTION:
+ case ZEND_DECLARE_CLASS:
+ case ZEND_DECLARE_INHERITED_CLASS:
+ case ZEND_DECLARE_INHERITED_CLASS_DELAYED:
+ LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 0, 0, 2);
+ break;
case ZEND_RECV:
case ZEND_RECV_VARIADIC:
case ZEND_VERIFY_RETURN_TYPE:
- if (opline->op2.num != -1) {
+ if (opline->op2.num != (uint32_t)-1) {
opline->op2.num = cache_size;
cache_size += sizeof(void *);
}
@@ -425,9 +424,11 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
case IS_CONSTANT:
if (info[i].flags & LITERAL_MAY_MERGE) {
if (info[i].flags & LITERAL_EX_OBJ) {
- int key_len = MAX_LENGTH_OF_LONG + sizeof("->") - 1 + Z_STRLEN(op_array->literals[i]);
+ int key_len = sizeof("$this->") - 1 + Z_STRLEN(op_array->literals[i]);
key = zend_string_alloc(key_len, 0);
- ZSTR_LEN(key) = snprintf(ZSTR_VAL(key), ZSTR_LEN(key)-1, "%d->%s", info[i].u.num, Z_STRVAL(op_array->literals[i]));
+ memcpy(ZSTR_VAL(key), "$this->", sizeof("$this->") - 1);
+ memcpy(ZSTR_VAL(key) + sizeof("$this->") - 1, Z_STRVAL(op_array->literals[i]), Z_STRLEN(op_array->literals[i]) + 1);
+ ZSTR_LEN(key) = key_len;
} else if (info[i].flags & LITERAL_EX_CLASS) {
int key_len;
zval *class_name = &op_array->literals[(info[i].u.num < i) ? map[info[i].u.num] : info[i].u.num];
diff --git a/ext/opcache/Optimizer/dfa_pass.c b/ext/opcache/Optimizer/dfa_pass.c
new file mode 100644
index 0000000000..3abf997d8d
--- /dev/null
+++ b/ext/opcache/Optimizer/dfa_pass.c
@@ -0,0 +1,670 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend OPcache |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1998-2017 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Dmitry Stogov <dmitry@zend.com> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "php.h"
+#include "Optimizer/zend_optimizer.h"
+#include "Optimizer/zend_optimizer_internal.h"
+#include "zend_API.h"
+#include "zend_constants.h"
+#include "zend_execute.h"
+#include "zend_vm.h"
+#include "zend_bitset.h"
+#include "zend_cfg.h"
+#include "zend_ssa.h"
+#include "zend_func_info.h"
+#include "zend_call_graph.h"
+#include "zend_inference.h"
+#include "zend_dump.h"
+
+int zend_dfa_analyze_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx, zend_ssa *ssa, uint32_t *flags)
+{
+ uint32_t build_flags;
+
+ if (op_array->last_try_catch) {
+ /* TODO: we can't analyze functions with try/catch/finally ??? */
+ return FAILURE;
+ }
+
+ /* Build SSA */
+ memset(ssa, 0, sizeof(zend_ssa));
+
+ if (zend_build_cfg(&ctx->arena, op_array,
+ ZEND_CFG_NO_ENTRY_PREDECESSORS, &ssa->cfg, flags) != SUCCESS) {
+ return FAILURE;
+ }
+
+ if (*flags & ZEND_FUNC_INDIRECT_VAR_ACCESS) {
+ /* TODO: we can't analyze functions with indirect variable access ??? */
+ return FAILURE;
+ }
+
+ if (zend_cfg_build_predecessors(&ctx->arena, &ssa->cfg) != SUCCESS) {
+ return FAILURE;
+ }
+
+ if (ctx->debug_level & ZEND_DUMP_DFA_CFG) {
+ zend_dump_op_array(op_array, ZEND_DUMP_CFG, "dfa cfg", &ssa->cfg);
+ }
+
+ /* Compute Dominators Tree */
+ if (zend_cfg_compute_dominators_tree(op_array, &ssa->cfg) != SUCCESS) {
+ return FAILURE;
+ }
+
+ /* Identify reducible and irreducible loops */
+ if (zend_cfg_identify_loops(op_array, &ssa->cfg, flags) != SUCCESS) {
+ return FAILURE;
+ }
+
+ if (ctx->debug_level & ZEND_DUMP_DFA_DOMINATORS) {
+ zend_dump_dominators(op_array, &ssa->cfg);
+ }
+
+ build_flags = 0;
+ if (ctx->debug_level & ZEND_DUMP_DFA_LIVENESS) {
+ build_flags |= ZEND_SSA_DEBUG_LIVENESS;
+ }
+ if (ctx->debug_level & ZEND_DUMP_DFA_PHI) {
+ build_flags |= ZEND_SSA_DEBUG_PHI_PLACEMENT;
+ }
+ if (zend_build_ssa(&ctx->arena, ctx->script, op_array, build_flags, ssa, flags) != SUCCESS) {
+ return FAILURE;
+ }
+
+ if (ctx->debug_level & ZEND_DUMP_DFA_SSA) {
+ zend_dump_op_array(op_array, ZEND_DUMP_SSA, "before dfa pass", ssa);
+ }
+
+
+ if (zend_ssa_compute_use_def_chains(&ctx->arena, op_array, ssa) != SUCCESS){
+ return FAILURE;
+ }
+
+ if (zend_ssa_find_false_dependencies(op_array, ssa) != SUCCESS) {
+ return FAILURE;
+ }
+
+ if (zend_ssa_find_sccs(op_array, ssa) != SUCCESS){
+ return FAILURE;
+ }
+
+ if (zend_ssa_inference(&ctx->arena, op_array, ctx->script, ssa) != SUCCESS) {
+ return FAILURE;
+ }
+
+ if (ctx->debug_level & ZEND_DUMP_DFA_SSA_VARS) {
+ zend_dump_ssa_variables(op_array, ssa, 0);
+ }
+
+ return SUCCESS;
+}
+
+static void zend_ssa_remove_nops(zend_op_array *op_array, zend_ssa *ssa)
+{
+ zend_basic_block *blocks = ssa->cfg.blocks;
+ zend_basic_block *end = blocks + ssa->cfg.blocks_count;
+ zend_basic_block *b;
+ zend_func_info *func_info;
+ int j;
+ uint32_t i;
+ uint32_t target = 0;
+ uint32_t *shiftlist;
+ ALLOCA_FLAG(use_heap);
+
+ shiftlist = (uint32_t *)do_alloca(sizeof(uint32_t) * op_array->last, use_heap);
+ memset(shiftlist, 0, sizeof(uint32_t) * op_array->last);
+ for (b = blocks; b < end; b++) {
+ if (b->flags & (ZEND_BB_REACHABLE|ZEND_BB_UNREACHABLE_FREE)) {
+ uint32_t end;
+ if (b->flags & ZEND_BB_UNREACHABLE_FREE) {
+ /* Only keep the FREE for the loop var */
+ ZEND_ASSERT(op_array->opcodes[b->start].opcode == ZEND_FREE
+ || op_array->opcodes[b->start].opcode == ZEND_FE_FREE);
+ b->len = 1;
+ }
+
+ end = b->start + b->len;
+ i = b->start;
+ b->start = target;
+ while (i < end) {
+ shiftlist[i] = i - target;
+ if (EXPECTED(op_array->opcodes[i].opcode != ZEND_NOP) ||
+ /*keep NOP to support ZEND_VM_SMART_BRANCH */
+ (i > 0 &&
+ i + 1 < op_array->last &&
+ (op_array->opcodes[i+1].opcode == ZEND_JMPZ ||
+ op_array->opcodes[i+1].opcode == ZEND_JMPNZ) &&
+ zend_is_smart_branch(op_array->opcodes + i - 1))) {
+ if (i != target) {
+ op_array->opcodes[target] = op_array->opcodes[i];
+ ssa->ops[target] = ssa->ops[i];
+ }
+ target++;
+ }
+ i++;
+ }
+ if (target != end && b->len != 0) {
+ zend_op *opline;
+ zend_op *new_opline;
+
+ b->len = target - b->start;
+ opline = op_array->opcodes + end - 1;
+ if (opline->opcode == ZEND_NOP) {
+ continue;
+ }
+
+ new_opline = op_array->opcodes + target - 1;
+ switch (new_opline->opcode) {
+ case ZEND_JMP:
+ case ZEND_FAST_CALL:
+ ZEND_SET_OP_JMP_ADDR(new_opline, new_opline->op1, ZEND_OP1_JMP_ADDR(opline));
+ break;
+ case ZEND_JMPZNZ:
+ new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
+ /* break missing intentionally */
+ case ZEND_JMPZ:
+ case ZEND_JMPNZ:
+ case ZEND_JMPZ_EX:
+ case ZEND_JMPNZ_EX:
+ case ZEND_FE_RESET_R:
+ case ZEND_FE_RESET_RW:
+ case ZEND_JMP_SET:
+ case ZEND_COALESCE:
+ case ZEND_ASSERT_CHECK:
+ ZEND_SET_OP_JMP_ADDR(new_opline, new_opline->op2, ZEND_OP2_JMP_ADDR(opline));
+ break;
+ case ZEND_CATCH:
+ if (!opline->result.num) {
+ new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
+ }
+ break;
+ case ZEND_DECLARE_ANON_CLASS:
+ case ZEND_DECLARE_ANON_INHERITED_CLASS:
+ case ZEND_FE_FETCH_R:
+ case ZEND_FE_FETCH_RW:
+ new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
+ break;
+ }
+ }
+ }
+ }
+
+ if (target != op_array->last) {
+ /* reset rest opcodes */
+ for (i = target; i < op_array->last; i++) {
+ MAKE_NOP(op_array->opcodes + i);
+ }
+
+ /* update SSA variables */
+ for (j = 0; j < ssa->vars_count; j++) {
+ if (ssa->vars[j].definition >= 0) {
+ ssa->vars[j].definition -= shiftlist[ssa->vars[j].definition];
+ }
+ if (ssa->vars[j].use_chain >= 0) {
+ ssa->vars[j].use_chain -= shiftlist[ssa->vars[j].use_chain];
+ }
+ }
+ for (i = 0; i < op_array->last; i++) {
+ if (ssa->ops[i].op1_use_chain >= 0) {
+ ssa->ops[i].op1_use_chain -= shiftlist[ssa->ops[i].op1_use_chain];
+ }
+ if (ssa->ops[i].op2_use_chain >= 0) {
+ ssa->ops[i].op2_use_chain -= shiftlist[ssa->ops[i].op2_use_chain];
+ }
+ if (ssa->ops[i].res_use_chain >= 0) {
+ ssa->ops[i].res_use_chain -= shiftlist[ssa->ops[i].res_use_chain];
+ }
+ }
+
+ /* update branch targets */
+ for (b = blocks; b < end; b++) {
+ if ((b->flags & ZEND_BB_REACHABLE) && b->len != 0) {
+ zend_op *opline = op_array->opcodes + b->start + b->len - 1;
+
+ switch (opline->opcode) {
+ case ZEND_JMP:
+ case ZEND_FAST_CALL:
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op1, ZEND_OP1_JMP_ADDR(opline) - shiftlist[ZEND_OP1_JMP_ADDR(opline) - op_array->opcodes]);
+ break;
+ case ZEND_JMPZNZ:
+ opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value) - shiftlist[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]);
+ /* break missing intentionally */
+ case ZEND_JMPZ:
+ case ZEND_JMPNZ:
+ case ZEND_JMPZ_EX:
+ case ZEND_JMPNZ_EX:
+ case ZEND_FE_RESET_R:
+ case ZEND_FE_RESET_RW:
+ case ZEND_JMP_SET:
+ case ZEND_COALESCE:
+ case ZEND_ASSERT_CHECK:
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP2_JMP_ADDR(opline) - shiftlist[ZEND_OP2_JMP_ADDR(opline) - op_array->opcodes]);
+ break;
+ case ZEND_DECLARE_ANON_CLASS:
+ case ZEND_DECLARE_ANON_INHERITED_CLASS:
+ case ZEND_FE_FETCH_R:
+ case ZEND_FE_FETCH_RW:
+ case ZEND_CATCH:
+ opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value) - shiftlist[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]);
+ break;
+ }
+ }
+ }
+
+ /* update brk/cont array */
+ for (j = 0; j < op_array->last_live_range; j++) {
+ op_array->live_range[j].start -= shiftlist[op_array->live_range[j].start];
+ op_array->live_range[j].end -= shiftlist[op_array->live_range[j].end];
+ }
+
+ /* update try/catch array */
+ for (j = 0; j < op_array->last_try_catch; j++) {
+ op_array->try_catch_array[j].try_op -= shiftlist[op_array->try_catch_array[j].try_op];
+ op_array->try_catch_array[j].catch_op -= shiftlist[op_array->try_catch_array[j].catch_op];
+ if (op_array->try_catch_array[j].finally_op) {
+ op_array->try_catch_array[j].finally_op -= shiftlist[op_array->try_catch_array[j].finally_op];
+ op_array->try_catch_array[j].finally_end -= shiftlist[op_array->try_catch_array[j].finally_end];
+ }
+ }
+
+ /* update early binding list */
+ if (op_array->early_binding != (uint32_t)-1) {
+ uint32_t *opline_num = &op_array->early_binding;
+
+ do {
+ *opline_num -= shiftlist[*opline_num];
+ opline_num = &ZEND_RESULT(&op_array->opcodes[*opline_num]).opline_num;
+ } while (*opline_num != (uint32_t)-1);
+ }
+
+ /* update call graph */
+ func_info = ZEND_FUNC_INFO(op_array);
+ if (func_info) {
+ zend_call_info *call_info = func_info->callee_info;
+ while (call_info) {
+ call_info->caller_init_opline -=
+ shiftlist[call_info->caller_init_opline - op_array->opcodes];
+ call_info->caller_call_opline -=
+ shiftlist[call_info->caller_call_opline - op_array->opcodes];
+ call_info = call_info->next_callee;
+ }
+ }
+
+ op_array->last = target;
+ }
+ free_alloca(shiftlist, use_heap);
+}
+
+static inline zend_bool can_elide_return_type_check(
+ zend_op_array *op_array, zend_ssa *ssa, zend_ssa_op *ssa_op) {
+ zend_arg_info *info = &op_array->arg_info[-1];
+ zend_ssa_var_info *use_info = &ssa->var_info[ssa_op->op1_use];
+ zend_ssa_var_info *def_info = &ssa->var_info[ssa_op->op1_def];
+
+ if (use_info->type & MAY_BE_REF) {
+ return 0;
+ }
+
+ /* A type is possible that is not in the allowed types */
+ if ((use_info->type & (MAY_BE_ANY|MAY_BE_UNDEF)) & ~(def_info->type & MAY_BE_ANY)) {
+ return 0;
+ }
+
+ if (info->type_hint == IS_CALLABLE) {
+ return 0;
+ }
+
+ if (info->class_name) {
+ if (!use_info->ce || !def_info->ce || !instanceof_function(use_info->ce, def_info->ce)) {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static zend_bool opline_supports_assign_contraction(
+ zend_ssa *ssa, zend_op *opline, int src_var, uint32_t cv_var) {
+ if (opline->opcode == ZEND_NEW) {
+ /* see Zend/tests/generators/aborted_yield_during_new.phpt */
+ return 0;
+ }
+
+ if (opline->opcode == ZEND_DO_ICALL || opline->opcode == ZEND_DO_UCALL
+ || opline->opcode == ZEND_DO_FCALL || opline->opcode == ZEND_DO_FCALL_BY_NAME) {
+ /* Function calls may dtor the return value after it has already been written -- allow
+ * direct assignment only for types where a double-dtor does not matter. */
+ uint32_t type = ssa->var_info[src_var].type;
+ uint32_t simple = MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE;
+ return !((type & MAY_BE_ANY) & ~simple);
+ }
+
+ if (opline->opcode == ZEND_POST_INC || opline->opcode == ZEND_POST_DEC) {
+ /* POST_INC/DEC write the result variable before performing the inc/dec. For $i = $i++
+ * eliding the temporary variable would thus yield an incorrect result. */
+ return opline->op1_type != IS_CV || opline->op1.var != cv_var;
+ }
+
+ if (opline->opcode == ZEND_INIT_ARRAY) {
+ /* INIT_ARRAY initializes the result array before reading key/value. */
+ return (opline->op1_type != IS_CV || opline->op1.var != cv_var)
+ && (opline->op2_type != IS_CV || opline->op2.var != cv_var);
+ }
+
+ if (opline->opcode == ZEND_CAST
+ && (opline->extended_value == IS_ARRAY || opline->extended_value == IS_OBJECT)) {
+ /* CAST to array/object may initialize the result to an empty array/object before
+ * reading the expression. */
+ return opline->op1_type != IS_CV || opline->op1.var != cv_var;
+ }
+
+ return 1;
+}
+
+void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx, zend_ssa *ssa)
+{
+ if (ctx->debug_level & ZEND_DUMP_BEFORE_DFA_PASS) {
+ zend_dump_op_array(op_array, ZEND_DUMP_SSA, "before dfa pass", ssa);
+ }
+
+ if (ssa->var_info) {
+ int op_1;
+ int v;
+ int remove_nops = 0;
+ zend_op *opline;
+ zval tmp;
+
+ for (v = op_array->last_var; v < ssa->vars_count; v++) {
+
+ op_1 = ssa->vars[v].definition;
+
+ if (op_1 < 0) {
+ continue;
+ }
+
+ opline = op_array->opcodes + op_1;
+
+ /* Convert LONG constants to DOUBLE */
+ if (ssa->var_info[v].use_as_double) {
+ if (opline->opcode == ZEND_ASSIGN
+ && opline->op2_type == IS_CONST
+ && ssa->ops[op_1].op1_def == v
+ && !RETURN_VALUE_USED(opline)
+ ) {
+
+// op_1: ASSIGN ? -> #v [use_as_double], long(?) => ASSIGN ? -> #v, double(?)
+
+ zval *zv = CT_CONSTANT_EX(op_array, opline->op2.constant);
+ ZEND_ASSERT(Z_TYPE_INFO_P(zv) == IS_LONG);
+ ZVAL_DOUBLE(&tmp, zval_get_double(zv));
+ opline->op2.constant = zend_optimizer_add_literal(op_array, &tmp);
+
+ } else if (opline->opcode == ZEND_QM_ASSIGN
+ && opline->op1_type == IS_CONST
+ ) {
+
+// op_1: QM_ASSIGN #v [use_as_double], long(?) => QM_ASSIGN #v, double(?)
+
+ zval *zv = CT_CONSTANT_EX(op_array, opline->op1.constant);
+ ZEND_ASSERT(Z_TYPE_INFO_P(zv) == IS_LONG);
+ ZVAL_DOUBLE(&tmp, zval_get_double(zv));
+ opline->op1.constant = zend_optimizer_add_literal(op_array, &tmp);
+ }
+
+ } else {
+ if (opline->opcode == ZEND_ADD
+ || opline->opcode == ZEND_SUB
+ || opline->opcode == ZEND_MUL
+ || opline->opcode == ZEND_IS_EQUAL
+ || opline->opcode == ZEND_IS_NOT_EQUAL
+ || opline->opcode == ZEND_IS_SMALLER
+ || opline->opcode == ZEND_IS_SMALLER_OR_EQUAL
+ ) {
+
+ if (opline->op1_type == IS_CONST
+ && opline->op2_type != IS_CONST
+ && (OP2_INFO() & MAY_BE_ANY) == MAY_BE_DOUBLE
+ && Z_TYPE_INFO_P(CT_CONSTANT_EX(op_array, opline->op1.constant)) == IS_LONG
+ ) {
+
+// op_1: #v.? = ADD long(?), #?.? [double] => #v.? = ADD double(?), #?.? [double]
+
+ zval *zv = CT_CONSTANT_EX(op_array, opline->op1.constant);
+ ZVAL_DOUBLE(&tmp, zval_get_double(zv));
+ opline->op1.constant = zend_optimizer_add_literal(op_array, &tmp);
+
+ } else if (opline->op1_type != IS_CONST
+ && opline->op2_type == IS_CONST
+ && (OP1_INFO() & MAY_BE_ANY) == MAY_BE_DOUBLE
+ && Z_TYPE_INFO_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == IS_LONG
+ ) {
+
+// op_1: #v.? = ADD #?.? [double], long(?) => #v.? = ADD #?.? [double], double(?)
+
+ zval *zv = CT_CONSTANT_EX(op_array, opline->op2.constant);
+ ZVAL_DOUBLE(&tmp, zval_get_double(zv));
+ opline->op2.constant = zend_optimizer_add_literal(op_array, &tmp);
+ }
+ }
+ }
+
+ if (ssa->vars[v].var >= op_array->last_var) {
+ /* skip TMP and VAR */
+ continue;
+ }
+
+ if (opline->opcode == ZEND_ASSIGN
+ && ssa->ops[op_1].op1_def == v
+ && !RETURN_VALUE_USED(opline)
+ ) {
+ int orig_var = ssa->ops[op_1].op1_use;
+
+ if (orig_var >= 0
+ && !(ssa->var_info[orig_var].type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))
+ ) {
+
+ int src_var = ssa->ops[op_1].op2_use;
+
+ if ((opline->op2_type & (IS_TMP_VAR|IS_VAR))
+ && src_var >= 0
+ && !(ssa->var_info[src_var].type & MAY_BE_REF)
+ && ssa->vars[src_var].definition >= 0
+ && ssa->ops[ssa->vars[src_var].definition].result_def == src_var
+ && ssa->ops[ssa->vars[src_var].definition].result_use < 0
+ && ssa->vars[src_var].use_chain == op_1
+ && ssa->ops[op_1].op2_use_chain < 0
+ && !ssa->vars[src_var].phi_use_chain
+ && !ssa->vars[src_var].sym_use_chain
+ && opline_supports_assign_contraction(
+ ssa, &op_array->opcodes[ssa->vars[src_var].definition],
+ src_var, opline->op1.var)
+ ) {
+
+ int op_2 = ssa->vars[src_var].definition;
+
+// op_2: #src_var.T = OP ... => #v.CV = OP ...
+// op_1: ASSIGN #orig_var.CV [undef,scalar] -> #v.CV, #src_var.T NOP
+
+ if (zend_ssa_unlink_use_chain(ssa, op_1, orig_var)) {
+ /* Reconstruct SSA */
+ ssa->vars[v].definition = op_2;
+ ssa->ops[op_2].result_def = v;
+
+ ssa->vars[src_var].definition = -1;
+ ssa->vars[src_var].use_chain = -1;
+
+ ssa->ops[op_1].op1_use = -1;
+ ssa->ops[op_1].op2_use = -1;
+ ssa->ops[op_1].op1_def = -1;
+ ssa->ops[op_1].op1_use_chain = -1;
+
+ /* Update opcodes */
+ op_array->opcodes[op_2].result_type = opline->op1_type;
+ op_array->opcodes[op_2].result.var = opline->op1.var;
+ MAKE_NOP(opline);
+ remove_nops = 1;
+ }
+ } else if (opline->op2_type == IS_CONST
+ || ((opline->op2_type & (IS_TMP_VAR|IS_VAR|IS_CV))
+ && ssa->ops[op_1].op2_use >= 0
+ && ssa->ops[op_1].op2_def < 0)
+ ) {
+
+// op_1: ASSIGN #orig_var.CV [undef,scalar] -> #v.CV, CONST|TMPVAR => QM_ASSIGN v.CV, CONST|TMPVAR
+
+ if (zend_ssa_unlink_use_chain(ssa, op_1, orig_var)) {
+ /* Reconstruct SSA */
+ ssa->ops[op_1].result_def = v;
+ ssa->ops[op_1].op1_def = -1;
+ ssa->ops[op_1].op1_use = ssa->ops[op_1].op2_use;
+ ssa->ops[op_1].op1_use_chain = ssa->ops[op_1].op2_use_chain;
+ ssa->ops[op_1].op2_use = -1;
+ ssa->ops[op_1].op2_use_chain = -1;
+
+ /* Update opcode */
+ opline->result_type = opline->op1_type;
+ opline->result.var = opline->op1.var;
+ opline->op1_type = opline->op2_type;
+ opline->op1.var = opline->op2.var;
+ opline->op2_type = IS_UNUSED;
+ opline->op2.var = 0;
+ opline->opcode = ZEND_QM_ASSIGN;
+ }
+ }
+ }
+
+ } else if (opline->opcode == ZEND_ASSIGN_ADD
+ && opline->extended_value == 0
+ && ssa->ops[op_1].op1_def == v
+ && opline->op2_type == IS_CONST
+ && Z_TYPE_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == IS_LONG
+ && Z_LVAL_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == 1
+ && ssa->ops[op_1].op1_use >= 0
+ && !(ssa->var_info[ssa->ops[op_1].op1_use].type & (MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
+
+// op_1: ASSIGN_ADD #?.CV [undef,null,int,foat] ->#v.CV, int(1) => PRE_INC #?.CV ->#v.CV
+
+ opline->opcode = ZEND_PRE_INC;
+ SET_UNUSED(opline->op2);
+
+ } else if (opline->opcode == ZEND_ASSIGN_SUB
+ && opline->extended_value == 0
+ && ssa->ops[op_1].op1_def == v
+ && opline->op2_type == IS_CONST
+ && Z_TYPE_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == IS_LONG
+ && Z_LVAL_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == 1
+ && ssa->ops[op_1].op1_use >= 0
+ && !(ssa->var_info[ssa->ops[op_1].op1_use].type & (MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
+
+// op_1: ASSIGN_SUB #?.CV [undef,null,int,foat] -> #v.CV, int(1) => PRE_DEC #?.CV ->#v.CV
+
+ opline->opcode = ZEND_PRE_DEC;
+ SET_UNUSED(opline->op2);
+
+ } else if (opline->opcode == ZEND_VERIFY_RETURN_TYPE
+ && ssa->ops[op_1].op1_def == v
+ && ssa->ops[op_1].op1_use >= 0
+ && ssa->ops[op_1].op1_use_chain == -1
+ && ssa->vars[v].use_chain >= 0
+ && can_elide_return_type_check(op_array, ssa, &ssa->ops[op_1])) {
+
+// op_1: VERIFY_RETURN_TYPE #orig_var.CV [T] -> #v.CV [T] => NOP
+
+ int orig_var = ssa->ops[op_1].op1_use;
+ if (zend_ssa_unlink_use_chain(ssa, op_1, orig_var)) {
+
+ int ret = ssa->vars[v].use_chain;
+
+ ssa->ops[ret].op1_use = orig_var;
+ ssa->ops[ret].op1_use_chain = ssa->vars[orig_var].use_chain;
+ ssa->vars[orig_var].use_chain = ret;
+
+ ssa->vars[v].definition = -1;
+ ssa->vars[v].use_chain = -1;
+
+ ssa->ops[op_1].op1_def = -1;
+ ssa->ops[op_1].op1_use = -1;
+
+ MAKE_NOP(opline);
+ remove_nops = 1;
+ }
+
+ } else if (ssa->ops[op_1].op1_def == v
+ && !RETURN_VALUE_USED(opline)
+ && ssa->ops[op_1].op1_use >= 0
+ && !(ssa->var_info[ssa->ops[op_1].op1_use].type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))
+ && (opline->opcode == ZEND_ASSIGN_ADD
+ || opline->opcode == ZEND_ASSIGN_SUB
+ || opline->opcode == ZEND_ASSIGN_MUL
+ || opline->opcode == ZEND_ASSIGN_DIV
+ || opline->opcode == ZEND_ASSIGN_MOD
+ || opline->opcode == ZEND_ASSIGN_SL
+ || opline->opcode == ZEND_ASSIGN_SR
+ || opline->opcode == ZEND_ASSIGN_BW_OR
+ || opline->opcode == ZEND_ASSIGN_BW_AND
+ || opline->opcode == ZEND_ASSIGN_BW_XOR)
+ && opline->extended_value == 0) {
+
+// op_1: ASSIGN_ADD #orig_var.CV [undef,null,bool,int,double] -> #v.CV, ? => #v.CV = ADD #orig_var.CV, ?
+
+ /* Reconstruct SSA */
+ ssa->ops[op_1].result_def = ssa->ops[op_1].op1_def;
+ ssa->ops[op_1].op1_def = -1;
+
+ /* Update opcode */
+ opline->opcode -= (ZEND_ASSIGN_ADD - ZEND_ADD);
+ opline->result_type = opline->op1_type;
+ opline->result.var = opline->op1.var;
+
+ }
+ }
+
+ if (remove_nops) {
+ zend_ssa_remove_nops(op_array, ssa);
+ }
+ }
+
+ if (ctx->debug_level & ZEND_DUMP_AFTER_DFA_PASS) {
+ zend_dump_op_array(op_array, ZEND_DUMP_SSA, "after dfa pass", ssa);
+ }
+}
+
+void zend_optimize_dfa(zend_op_array *op_array, zend_optimizer_ctx *ctx)
+{
+ void *checkpoint = zend_arena_checkpoint(ctx->arena);
+ uint32_t flags = 0;
+ zend_ssa ssa;
+
+ if (zend_dfa_analyze_op_array(op_array, ctx, &ssa, &flags) != SUCCESS) {
+ zend_arena_release(&ctx->arena, checkpoint);
+ return;
+ }
+
+ zend_dfa_optimize_op_array(op_array, ctx, &ssa);
+
+ /* Destroy SSA */
+ zend_arena_release(&ctx->arena, checkpoint);
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * indent-tabs-mode: t
+ * End:
+ */
diff --git a/ext/opcache/Optimizer/nop_removal.c b/ext/opcache/Optimizer/nop_removal.c
index a42ede8cc1..c7ff73a61b 100644
--- a/ext/opcache/Optimizer/nop_removal.c
+++ b/ext/opcache/Optimizer/nop_removal.c
@@ -39,15 +39,15 @@ void zend_optimizer_nop_removal(zend_op_array *op_array)
uint32_t *shiftlist;
ALLOCA_FLAG(use_heap);
- shiftlist = (uint32_t *)DO_ALLOCA(sizeof(uint32_t) * op_array->last);
+ shiftlist = (uint32_t *)do_alloca(sizeof(uint32_t) * op_array->last, use_heap);
i = new_count = shift = 0;
end = op_array->opcodes + op_array->last;
for (opline = op_array->opcodes; opline < end; opline++) {
/* Kill JMP-over-NOP-s */
- if (opline->opcode == ZEND_JMP && ZEND_OP1(opline).opline_num > i) {
+ if (opline->opcode == ZEND_JMP && ZEND_OP1_JMP_ADDR(opline) > op_array->opcodes + i) {
/* check if there are only NOPs under the branch */
- zend_op *target = op_array->opcodes + ZEND_OP1(opline).opline_num - 1;
+ zend_op *target = ZEND_OP1_JMP_ADDR(opline) - 1;
while (target->opcode == ZEND_NOP) {
target--;
@@ -63,7 +63,40 @@ void zend_optimizer_nop_removal(zend_op_array *op_array)
shift++;
} else {
if (shift) {
- op_array->opcodes[new_count] = *opline;
+ zend_op *new_opline = op_array->opcodes + new_count;
+
+ *new_opline = *opline;
+ switch (new_opline->opcode) {
+ case ZEND_JMP:
+ case ZEND_FAST_CALL:
+ ZEND_SET_OP_JMP_ADDR(new_opline, new_opline->op1, ZEND_OP1_JMP_ADDR(opline));
+ break;
+ case ZEND_JMPZNZ:
+ new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
+ /* break missing intentionally */
+ case ZEND_JMPZ:
+ case ZEND_JMPNZ:
+ case ZEND_JMPZ_EX:
+ case ZEND_JMPNZ_EX:
+ case ZEND_FE_RESET_R:
+ case ZEND_FE_RESET_RW:
+ case ZEND_JMP_SET:
+ case ZEND_COALESCE:
+ case ZEND_ASSERT_CHECK:
+ ZEND_SET_OP_JMP_ADDR(new_opline, new_opline->op2, ZEND_OP2_JMP_ADDR(opline));
+ break;
+ case ZEND_CATCH:
+ if (!opline->result.num) {
+ new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
+ }
+ break;
+ case ZEND_DECLARE_ANON_CLASS:
+ case ZEND_DECLARE_ANON_INHERITED_CLASS:
+ case ZEND_FE_FETCH_R:
+ case ZEND_FE_FETCH_RW:
+ new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
+ break;
+ }
}
new_count++;
}
@@ -78,41 +111,36 @@ void zend_optimizer_nop_removal(zend_op_array *op_array)
switch (opline->opcode) {
case ZEND_JMP:
case ZEND_FAST_CALL:
- case ZEND_DECLARE_ANON_CLASS:
- case ZEND_DECLARE_ANON_INHERITED_CLASS:
- ZEND_OP1(opline).opline_num -= shiftlist[ZEND_OP1(opline).opline_num];
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op1, ZEND_OP1_JMP_ADDR(opline) - shiftlist[ZEND_OP1_JMP_ADDR(opline) - op_array->opcodes]);
break;
+ case ZEND_JMPZNZ:
+ opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value) - shiftlist[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]);
+ /* break missing intentionally */
case ZEND_JMPZ:
case ZEND_JMPNZ:
case ZEND_JMPZ_EX:
case ZEND_JMPNZ_EX:
case ZEND_FE_RESET_R:
case ZEND_FE_RESET_RW:
- case ZEND_NEW:
case ZEND_JMP_SET:
case ZEND_COALESCE:
case ZEND_ASSERT_CHECK:
- ZEND_OP2(opline).opline_num -= shiftlist[ZEND_OP2(opline).opline_num];
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP2_JMP_ADDR(opline) - shiftlist[ZEND_OP2_JMP_ADDR(opline) - op_array->opcodes]);
break;
+ case ZEND_DECLARE_ANON_CLASS:
+ case ZEND_DECLARE_ANON_INHERITED_CLASS:
case ZEND_FE_FETCH_R:
case ZEND_FE_FETCH_RW:
- opline->extended_value -= shiftlist[opline->extended_value];
- break;
- case ZEND_JMPZNZ:
- ZEND_OP2(opline).opline_num -= shiftlist[ZEND_OP2(opline).opline_num];
- opline->extended_value -= shiftlist[opline->extended_value];
- break;
case ZEND_CATCH:
- opline->extended_value -= shiftlist[opline->extended_value];
+ opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value) - shiftlist[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]);
break;
}
}
/* update brk/cont array */
- for (j = 0; j < op_array->last_brk_cont; j++) {
- op_array->brk_cont_array[j].brk -= shiftlist[op_array->brk_cont_array[j].brk];
- op_array->brk_cont_array[j].cont -= shiftlist[op_array->brk_cont_array[j].cont];
- op_array->brk_cont_array[j].start -= shiftlist[op_array->brk_cont_array[j].start];
+ for (j = 0; j < op_array->last_live_range; j++) {
+ op_array->live_range[j].start -= shiftlist[op_array->live_range[j].start];
+ op_array->live_range[j].end -= shiftlist[op_array->live_range[j].end];
}
/* update try/catch array */
@@ -135,5 +163,5 @@ void zend_optimizer_nop_removal(zend_op_array *op_array)
} while (*opline_num != (uint32_t)-1);
}
}
- FREE_ALLOCA(shiftlist);
+ free_alloca(shiftlist, use_heap);
}
diff --git a/ext/opcache/Optimizer/optimize_func_calls.c b/ext/opcache/Optimizer/optimize_func_calls.c
index 3b1c84a00c..5d477c1a73 100644
--- a/ext/opcache/Optimizer/optimize_func_calls.c
+++ b/ext/opcache/Optimizer/optimize_func_calls.c
@@ -29,6 +29,9 @@
#include "zend_execute.h"
#include "zend_vm.h"
+#define ZEND_OP1_IS_CONST_STRING(opline) \
+ (ZEND_OP1_TYPE(opline) == IS_CONST && \
+ Z_TYPE(op_array->literals[(opline)->op1.constant]) == IS_STRING)
#define ZEND_OP2_IS_CONST_STRING(opline) \
(ZEND_OP2_TYPE(opline) == IS_CONST && \
Z_TYPE(op_array->literals[(opline)->op2.constant]) == IS_STRING)
@@ -36,9 +39,115 @@
typedef struct _optimizer_call_info {
zend_function *func;
zend_op *opline;
+ zend_bool try_inline;
} optimizer_call_info;
-void optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
+static void zend_delete_call_instructions(zend_op *opline)
+{
+ int call = 0;
+
+ while (1) {
+ switch (opline->opcode) {
+ case ZEND_INIT_FCALL_BY_NAME:
+ case ZEND_INIT_NS_FCALL_BY_NAME:
+ case ZEND_INIT_STATIC_METHOD_CALL:
+ case ZEND_INIT_METHOD_CALL:
+ case ZEND_INIT_FCALL:
+ if (call == 0) {
+ MAKE_NOP(opline);
+ return;
+ }
+ /* break missing intentionally */
+ case ZEND_NEW:
+ case ZEND_INIT_DYNAMIC_CALL:
+ case ZEND_INIT_USER_CALL:
+ call--;
+ break;
+ case ZEND_DO_FCALL:
+ case ZEND_DO_ICALL:
+ case ZEND_DO_UCALL:
+ case ZEND_DO_FCALL_BY_NAME:
+ call++;
+ break;
+ case ZEND_SEND_VAL:
+ case ZEND_SEND_VAR:
+ if (call == 0) {
+ if (opline->op1_type == IS_CONST) {
+ MAKE_NOP(opline);
+ } else if (opline->op1_type == IS_CV) {
+ opline->opcode = ZEND_CHECK_VAR;
+ opline->extended_value = 0;
+ opline->result.var = 0;
+ } else {
+ opline->opcode = ZEND_FREE;
+ opline->extended_value = 0;
+ opline->result.var = 0;
+ }
+ }
+ break;
+ }
+ opline--;
+ }
+}
+
+static void zend_try_inline_call(zend_op_array *op_array, zend_op *fcall, zend_op *opline, zend_function *func)
+{
+ if (func->type == ZEND_USER_FUNCTION
+ && !(func->op_array.fn_flags & (ZEND_ACC_ABSTRACT|ZEND_ACC_HAS_TYPE_HINTS))
+ && fcall->extended_value >= func->op_array.required_num_args
+ && func->op_array.opcodes[func->op_array.num_args].opcode == ZEND_RETURN) {
+
+ zend_op *ret_opline = func->op_array.opcodes + func->op_array.num_args;
+
+ if (ret_opline->op1_type == IS_CONST) {
+ uint32_t i, num_args = func->op_array.num_args;
+ num_args += (func->op_array.fn_flags & ZEND_ACC_VARIADIC) != 0;
+
+ if (fcall->opcode == ZEND_INIT_METHOD_CALL && fcall->op1_type == IS_UNUSED) {
+ /* TODO: we can't inlne methods, because $this may be used
+ * not in object context ???
+ */
+ return;
+ }
+
+ for (i = 0; i < num_args; i++) {
+ /* Don't inline functions with by-reference arguments. This would require
+ * correct handling of INDIRECT arguments. */
+ if (func->op_array.arg_info[i].pass_by_reference) {
+ return;
+ }
+ }
+
+ if (fcall->extended_value < func->op_array.num_args) {
+ /* don't inline funcions with named constants in default arguments */
+ i = fcall->extended_value;
+
+ do {
+ if (Z_CONSTANT_P(RT_CONSTANT(&func->op_array, func->op_array.opcodes[i].op2))) {
+ return;
+ }
+ i++;
+ } while (i < func->op_array.num_args);
+ }
+
+ if (RETURN_VALUE_USED(opline)) {
+ zval zv;
+
+ ZVAL_DUP(&zv, RT_CONSTANT(&func->op_array, ret_opline->op1));
+ opline->opcode = ZEND_QM_ASSIGN;
+ opline->op1_type = IS_CONST;
+ opline->op1.constant = zend_optimizer_add_literal(op_array, &zv);
+ SET_UNUSED(opline->op2);
+ } else {
+ MAKE_NOP(opline);
+ }
+
+ zend_delete_call_instructions(opline-1);
+ }
+ }
+}
+
+void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
{
zend_op *opline = op_array->opcodes;
zend_op *end = opline + op_array->last;
@@ -56,20 +165,15 @@ void optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
switch (opline->opcode) {
case ZEND_INIT_FCALL_BY_NAME:
case ZEND_INIT_NS_FCALL_BY_NAME:
- if (ZEND_OP2_IS_CONST_STRING(opline)) {
- zend_function *func;
- zval *function_name = &op_array->literals[opline->op2.constant + 1];
- if ((func = zend_hash_find_ptr(&ctx->script->function_table,
- Z_STR_P(function_name))) != NULL) {
- call_stack[call].func = func;
- }
- }
- /* break missing intentionally */
- case ZEND_NEW:
- case ZEND_INIT_DYNAMIC_CALL:
- case ZEND_INIT_METHOD_CALL:
case ZEND_INIT_STATIC_METHOD_CALL:
+ case ZEND_INIT_METHOD_CALL:
case ZEND_INIT_FCALL:
+ case ZEND_NEW:
+ call_stack[call].func = zend_optimizer_get_called_func(
+ ctx->script, op_array, opline, 0);
+ call_stack[call].try_inline = opline->opcode != ZEND_NEW;
+ /* break missing intentionally */
+ case ZEND_INIT_DYNAMIC_CALL:
case ZEND_INIT_USER_CALL:
call_stack[call].opline = opline;
call++;
@@ -82,13 +186,15 @@ void optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
if (call_stack[call].func && call_stack[call].opline) {
zend_op *fcall = call_stack[call].opline;
- if (fcall->opcode == ZEND_INIT_FCALL_BY_NAME) {
+ if (fcall->opcode == ZEND_INIT_FCALL) {
+ /* nothing to do */
+ } else if (fcall->opcode == ZEND_INIT_FCALL_BY_NAME) {
fcall->opcode = ZEND_INIT_FCALL;
fcall->op1.num = zend_vm_calc_used_stack(fcall->extended_value, call_stack[call].func);
Z_CACHE_SLOT(op_array->literals[fcall->op2.constant + 1]) = Z_CACHE_SLOT(op_array->literals[fcall->op2.constant]);
literal_dtor(&ZEND_OP2_LITERAL(fcall));
fcall->op2.constant = fcall->op2.constant + 1;
- opline->opcode = zend_get_call_op(ZEND_INIT_FCALL, call_stack[call].func);
+ opline->opcode = zend_get_call_op(fcall, call_stack[call].func);
} else if (fcall->opcode == ZEND_INIT_NS_FCALL_BY_NAME) {
fcall->opcode = ZEND_INIT_FCALL;
fcall->op1.num = zend_vm_calc_used_stack(fcall->extended_value, call_stack[call].func);
@@ -96,31 +202,51 @@ void optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
literal_dtor(&op_array->literals[fcall->op2.constant]);
literal_dtor(&op_array->literals[fcall->op2.constant + 2]);
fcall->op2.constant = fcall->op2.constant + 1;
- opline->opcode = zend_get_call_op(ZEND_INIT_FCALL, call_stack[call].func);
+ opline->opcode = zend_get_call_op(fcall, call_stack[call].func);
+ } else if (fcall->opcode == ZEND_INIT_STATIC_METHOD_CALL
+ || fcall->opcode == ZEND_INIT_METHOD_CALL
+ || fcall->opcode == ZEND_NEW) {
+ /* We don't have specialized opcodes for this, do nothing */
} else {
ZEND_ASSERT(0);
}
+
+ if ((ZEND_OPTIMIZER_PASS_16 & ctx->optimization_level)
+ && call_stack[call].try_inline) {
+ zend_try_inline_call(op_array, fcall, opline, call_stack[call].func);
+ }
}
call_stack[call].func = NULL;
call_stack[call].opline = NULL;
+ call_stack[call].try_inline = 0;
break;
case ZEND_FETCH_FUNC_ARG:
+ case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
case ZEND_FETCH_OBJ_FUNC_ARG:
case ZEND_FETCH_DIM_FUNC_ARG:
if (call_stack[call - 1].func) {
if (ARG_SHOULD_BE_SENT_BY_REF(call_stack[call - 1].func, (opline->extended_value & ZEND_FETCH_ARG_MASK))) {
opline->extended_value &= ZEND_FETCH_TYPE_MASK;
- opline->opcode -= 9;
+ if (opline->opcode != ZEND_FETCH_STATIC_PROP_FUNC_ARG) {
+ opline->opcode -= 9;
+ } else {
+ opline->opcode = ZEND_FETCH_STATIC_PROP_W;
+ }
} else {
if (opline->opcode == ZEND_FETCH_DIM_FUNC_ARG
&& opline->op2_type == IS_UNUSED) {
/* FETCH_DIM_FUNC_ARG supports UNUSED op2, while FETCH_DIM_R does not.
* Performing the replacement would create an invalid opcode. */
+ call_stack[call - 1].try_inline = 0;
break;
}
opline->extended_value &= ZEND_FETCH_TYPE_MASK;
- opline->opcode -= 12;
+ if (opline->opcode != ZEND_FETCH_STATIC_PROP_FUNC_ARG) {
+ opline->opcode -= 12;
+ } else {
+ opline->opcode = ZEND_FETCH_STATIC_PROP_R;
+ }
}
}
break;
@@ -143,27 +269,21 @@ void optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
}
}
break;
- case ZEND_SEND_VAR_NO_REF:
- if (!(opline->extended_value & ZEND_ARG_COMPILE_TIME_BOUND) && call_stack[call - 1].func) {
- if (ARG_SHOULD_BE_SENT_BY_REF(call_stack[call - 1].func, opline->op2.num)) {
- opline->extended_value |= ZEND_ARG_COMPILE_TIME_BOUND | ZEND_ARG_SEND_BY_REF;
+ case ZEND_SEND_VAR_NO_REF_EX:
+ if (call_stack[call - 1].func) {
+ if (ARG_MUST_BE_SENT_BY_REF(call_stack[call - 1].func, opline->op2.num)) {
+ opline->opcode = ZEND_SEND_VAR_NO_REF;
+ } else if (ARG_MAY_BE_SENT_BY_REF(call_stack[call - 1].func, opline->op2.num)) {
+ opline->opcode = ZEND_SEND_VAL;
} else {
opline->opcode = ZEND_SEND_VAR;
- opline->extended_value = 0;
}
}
break;
-#if 0
- case ZEND_SEND_REF:
- if (opline->extended_value != ZEND_ARG_COMPILE_TIME_BOUND && call_stack[call - 1].func) {
- /* We won't handle run-time pass by reference */
- call_stack[call - 1].opline = NULL;
- }
- break;
-#endif
case ZEND_SEND_UNPACK:
- call_stack[call - 1].func = NULL;
- call_stack[call - 1].opline = NULL;
+ case ZEND_SEND_USER:
+ case ZEND_SEND_ARRAY:
+ call_stack[call - 1].try_inline = 0;
break;
default:
break;
diff --git a/ext/opcache/Optimizer/optimize_temp_vars_5.c b/ext/opcache/Optimizer/optimize_temp_vars_5.c
index 5cc7b79e89..930a926a0e 100644
--- a/ext/opcache/Optimizer/optimize_temp_vars_5.c
+++ b/ext/opcache/Optimizer/optimize_temp_vars_5.c
@@ -39,7 +39,7 @@
max = i; \
}
-void optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *ctx)
+void zend_optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *ctx)
{
int T = op_array->T;
int offset = op_array->last_var;
@@ -140,13 +140,6 @@ void optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *c
}
}
- /* Skip OP_DATA */
- if (opline->opcode == ZEND_OP_DATA &&
- (opline-1)->opcode == ZEND_ASSIGN_DIM) {
- opline--;
- continue;
- }
-
if ((ZEND_OP2_TYPE(opline) & (IS_VAR | IS_TMP_VAR))) {
currT = VAR_NUM(ZEND_OP2(opline).var) - offset;
if (!zend_bitset_in(valid_T, currT)) {
@@ -157,31 +150,6 @@ void optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *c
ZEND_OP2(opline).var = NUM_VAR(map_T[currT] + offset);
}
- if (opline->opcode == ZEND_DECLARE_INHERITED_CLASS ||
- opline->opcode == ZEND_DECLARE_ANON_INHERITED_CLASS ||
- opline->opcode == ZEND_DECLARE_INHERITED_CLASS_DELAYED) {
- currT = VAR_NUM(opline->extended_value) - offset;
- if (!zend_bitset_in(valid_T, currT)) {
- GET_AVAILABLE_T();
- map_T[currT] = i;
- zend_bitset_incl(valid_T, currT);
- }
- opline->extended_value = NUM_VAR(map_T[currT] + offset);
- }
-
- /* Allocate OP_DATA->op2 after "operands", but before "result" */
- if (opline->opcode == ZEND_ASSIGN_DIM &&
- (opline + 1)->opcode == ZEND_OP_DATA &&
- ZEND_OP2_TYPE(opline + 1) & (IS_VAR | IS_TMP_VAR)) {
- currT = VAR_NUM(ZEND_OP2(opline + 1).var) - offset;
- GET_AVAILABLE_T();
- map_T[currT] = i;
- zend_bitset_incl(valid_T, currT);
- zend_bitset_excl(taken_T, i);
- ZEND_OP2(opline + 1).var = NUM_VAR(i + offset);
- var_to_free = i;
- }
-
if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR)) {
currT = VAR_NUM(ZEND_RESULT(opline).var) - offset;
if (zend_bitset_in(valid_T, currT)) {
@@ -203,16 +171,11 @@ void optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *c
}
}
}
- } else { /* Au still needs to be assigned a T which is a bit dumb. Should consider changing Zend */
+ } else {
+ /* Code which gets here is using a wrongly built opcode such as RECV() */
GET_AVAILABLE_T();
-
- if (RESULT_UNUSED(opline)) {
- zend_bitset_excl(taken_T, i);
- } else {
- /* Code which gets here is using a wrongly built opcode such as RECV() */
- map_T[currT] = i;
- zend_bitset_incl(valid_T, currT);
- }
+ map_T[currT] = i;
+ zend_bitset_incl(valid_T, currT);
ZEND_RESULT(opline).var = NUM_VAR(i + offset);
}
}
@@ -225,6 +188,14 @@ void optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *c
opline--;
}
+ if (op_array->live_range) {
+ for (i = 0; i < op_array->last_live_range; i++) {
+ op_array->live_range[i].var =
+ NUM_VAR(map_T[VAR_NUM(op_array->live_range[i].var & ~ZEND_LIVE_MASK) - offset] + offset) |
+ (op_array->live_range[i].var & ZEND_LIVE_MASK);
+ }
+ }
+
zend_arena_release(&ctx->arena, checkpoint);
op_array->T = max + 1;
}
diff --git a/ext/opcache/Optimizer/pass1_5.c b/ext/opcache/Optimizer/pass1_5.c
index b29e90c767..fdfb16e328 100644
--- a/ext/opcache/Optimizer/pass1_5.c
+++ b/ext/opcache/Optimizer/pass1_5.c
@@ -42,7 +42,7 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
int i = 0;
zend_op *opline = op_array->opcodes;
zend_op *end = opline + op_array->last;
- zend_bool collect_constants = (ZEND_OPTIMIZER_PASS_15 & OPTIMIZATION_LEVEL)?
+ zend_bool collect_constants = (ZEND_OPTIMIZER_PASS_15 & ctx->optimization_level)?
(op_array == &ctx->script->main_op_array) : 0;
while (opline < end) {
@@ -84,6 +84,9 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
zval_get_long(&ZEND_OP2_LITERAL(opline)) < 0) {
/* shift by negative number */
break;
+ } else if (zend_binary_op_produces_numeric_string_error(opline->opcode, &ZEND_OP1_LITERAL(opline), &ZEND_OP2_LITERAL(opline))) {
+ /* produces numeric string E_NOTICE/E_WARNING */
+ break;
}
er = EG(error_reporting);
EG(error_reporting) = 0;
@@ -242,8 +245,7 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
#endif
case ZEND_FETCH_CONSTANT:
- if (ZEND_OP1_TYPE(opline) == IS_UNUSED &&
- ZEND_OP2_TYPE(opline) == IS_CONST &&
+ if (ZEND_OP2_TYPE(opline) == IS_CONST &&
Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING &&
Z_STRLEN(ZEND_OP2_LITERAL(opline)) == sizeof("__COMPILER_HALT_OFFSET__") - 1 &&
memcmp(Z_STRVAL(ZEND_OP2_LITERAL(opline)), "__COMPILER_HALT_OFFSET__", sizeof("__COMPILER_HALT_OFFSET__") - 1) == 0) {
@@ -267,8 +269,7 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
break;
}
- if (ZEND_OP1_TYPE(opline) == IS_UNUSED &&
- ZEND_OP2_TYPE(opline) == IS_CONST &&
+ if (ZEND_OP2_TYPE(opline) == IS_CONST &&
Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING) {
/* substitute persistent constants */
uint32_t tv = ZEND_RESULT(opline).var;
@@ -287,10 +288,10 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
MAKE_NOP(opline);
}
}
+ break;
- /* class constant */
- if (ZEND_OP1_TYPE(opline) != IS_UNUSED &&
- ZEND_OP2_TYPE(opline) == IS_CONST &&
+ case ZEND_FETCH_CLASS_CONSTANT:
+ if (ZEND_OP2_TYPE(opline) == IS_CONST &&
Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING) {
zend_class_entry *ce = NULL;
@@ -308,15 +309,20 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
(ce->type == ZEND_INTERNAL_CLASS &&
ce->info.internal.module->type != MODULE_PERSISTENT) ||
(ce->type == ZEND_USER_CLASS &&
- ZEND_CE_FILENAME(ce) != op_array->filename)) {
+ ce->info.user.filename != op_array->filename)) {
break;
}
}
} else if (op_array->scope &&
+ ZEND_OP1_TYPE(opline) == IS_UNUSED &&
+ (opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) {
+ /* for self::B */
+ ce = op_array->scope;
+ } else if (op_array->scope &&
ZEND_OP1_TYPE(opline) == IS_VAR &&
(opline - 1)->opcode == ZEND_FETCH_CLASS &&
(ZEND_OP1_TYPE(opline - 1) == IS_UNUSED &&
- ((opline - 1)->extended_value & ~ZEND_FETCH_CLASS_NO_AUTOLOAD) == ZEND_FETCH_CLASS_SELF) &&
+ ((opline - 1)->extended_value & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) &&
ZEND_RESULT((opline - 1)).var == ZEND_OP1(opline).var) {
/* for self::B */
ce = op_array->scope;
@@ -324,11 +330,13 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
if (ce) {
uint32_t tv = ZEND_RESULT(opline).var;
+ zend_class_constant *cc;
zval *c, t;
- if ((c = zend_hash_find(&ce->constants_table,
- Z_STR(ZEND_OP2_LITERAL(opline)))) != NULL) {
- ZVAL_DEREF(c);
+ if ((cc = zend_hash_find_ptr(&ce->constants_table,
+ Z_STR(ZEND_OP2_LITERAL(opline)))) != NULL &&
+ (Z_ACCESS_FLAGS(cc->value) & ZEND_ACC_PPP_MASK) == ZEND_ACC_PUBLIC) {
+ c = &cc->value;
if (Z_TYPE_P(c) == IS_CONSTANT_AST) {
break;
}
@@ -342,12 +350,12 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
zval_copy_ctor(&t);
}
- if (ZEND_OP1_TYPE(opline) == IS_CONST) {
- literal_dtor(&ZEND_OP1_LITERAL(opline));
- } else {
- MAKE_NOP((opline - 1));
- }
if (zend_optimizer_replace_by_const(op_array, opline, IS_TMP_VAR, tv, &t)) {
+ if (ZEND_OP1_TYPE(opline) == IS_CONST) {
+ literal_dtor(&ZEND_OP1_LITERAL(opline));
+ } else if (ZEND_OP1_TYPE(opline) == IS_VAR) {
+ MAKE_NOP((opline - 1));
+ }
literal_dtor(&ZEND_OP2_LITERAL(opline));
MAKE_NOP(opline);
}
@@ -640,74 +648,11 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
case ZEND_FE_RESET_RW:
case ZEND_FE_FETCH_R:
case ZEND_FE_FETCH_RW:
- case ZEND_NEW:
case ZEND_JMP_SET:
case ZEND_COALESCE:
case ZEND_ASSERT_CHECK:
collect_constants = 0;
break;
- case ZEND_FETCH_R:
- case ZEND_FETCH_W:
- case ZEND_FETCH_RW:
- case ZEND_FETCH_FUNC_ARG:
- case ZEND_FETCH_IS:
- case ZEND_FETCH_UNSET:
- if (opline != op_array->opcodes &&
- (opline-1)->opcode == ZEND_BEGIN_SILENCE &&
- (opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_LOCAL &&
- opline->op1_type == IS_CONST &&
- opline->op2_type == IS_UNUSED &&
- Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING &&
- (Z_STRLEN(ZEND_OP1_LITERAL(opline)) != sizeof("this")-1 ||
- memcmp(Z_STRVAL(ZEND_OP1_LITERAL(opline)), "this", sizeof("this") - 1) != 0)) {
-
- int var = opline->result.var;
- int level = 0;
- zend_op *op = opline + 1;
- zend_op *use = NULL;
-
- while (op < end) {
- if (op->opcode == ZEND_BEGIN_SILENCE) {
- level++;
- } else if (op->opcode == ZEND_END_SILENCE) {
- if (level == 0) {
- break;
- } else {
- level--;
- }
- }
- if (op->op1_type == IS_VAR && op->op1.var == var) {
- if (use) {
- /* used more than once */
- use = NULL;
- break;
- }
- use = op;
- } else if (op->op2_type == IS_VAR && op->op2.var == var) {
- if (use) {
- /* used more than once */
- use = NULL;
- break;
- }
- use = op;
- }
- op++;
- }
- if (use) {
- if (use->op1_type == IS_VAR && use->op1.var == var) {
- use->op1_type = IS_CV;
- use->op1.var = zend_optimizer_lookup_cv(op_array,
- Z_STR(ZEND_OP1_LITERAL(opline)));
- MAKE_NOP(opline);
- } else if (use->op2_type == IS_VAR && use->op2.var == var) {
- use->op2_type = IS_CV;
- use->op2.var = zend_optimizer_lookup_cv(op_array,
- Z_STR(ZEND_OP1_LITERAL(opline)));
- MAKE_NOP(opline);
- }
- }
- }
- break;
}
opline++;
i++;
diff --git a/ext/opcache/Optimizer/pass2.c b/ext/opcache/Optimizer/pass2.c
index 098be5146b..d592938256 100644
--- a/ext/opcache/Optimizer/pass2.c
+++ b/ext/opcache/Optimizer/pass2.c
@@ -22,7 +22,6 @@
/* pass 2:
* - convert non-numeric constants to numeric constants in numeric operators
* - optimize constant conditional JMPs
- * - optimize static BRKs and CONTs
*/
#include "php.h"
@@ -48,7 +47,10 @@ void zend_optimizer_pass2(zend_op_array *op_array)
case ZEND_POW:
if (ZEND_OP1_TYPE(opline) == IS_CONST) {
if (Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING) {
- convert_scalar_to_number(&ZEND_OP1_LITERAL(opline));
+ /* don't optimise if it should produce a runtime numeric string error */
+ if (is_numeric_string(Z_STRVAL(ZEND_OP1_LITERAL(opline)), Z_STRLEN(ZEND_OP1_LITERAL(opline)), NULL, NULL, 0)) {
+ convert_scalar_to_number(&ZEND_OP1_LITERAL(opline));
+ }
}
}
/* break missing *intentionally* - the assign_op's may only optimize op2 */
@@ -63,7 +65,10 @@ void zend_optimizer_pass2(zend_op_array *op_array)
}
if (ZEND_OP2_TYPE(opline) == IS_CONST) {
if (Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING) {
- convert_scalar_to_number(&ZEND_OP2_LITERAL(opline));
+ /* don't optimise if it should produce a runtime numeric string error */
+ if (is_numeric_string(Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline)), NULL, NULL, 0)) {
+ convert_scalar_to_number(&ZEND_OP2_LITERAL(opline));
+ }
}
}
break;
@@ -73,7 +78,11 @@ void zend_optimizer_pass2(zend_op_array *op_array)
case ZEND_SR:
if (ZEND_OP1_TYPE(opline) == IS_CONST) {
if (Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_LONG) {
- convert_to_long(&ZEND_OP1_LITERAL(opline));
+ /* don't optimise if it should produce a runtime numeric string error */
+ if (!(Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING
+ && !is_numeric_string(Z_STRVAL(ZEND_OP1_LITERAL(opline)), Z_STRLEN(ZEND_OP1_LITERAL(opline)), NULL, NULL, 0))) {
+ convert_to_long(&ZEND_OP1_LITERAL(opline));
+ }
}
}
/* break missing *intentionally - the assign_op's may only optimize op2 */
@@ -86,7 +95,11 @@ void zend_optimizer_pass2(zend_op_array *op_array)
}
if (ZEND_OP2_TYPE(opline) == IS_CONST) {
if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_LONG) {
- convert_to_long(&ZEND_OP2_LITERAL(opline));
+ /* don't optimise if it should produce a runtime numeric string error */
+ if (!(Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING
+ && !is_numeric_string(Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline)), NULL, NULL, 0))) {
+ convert_to_long(&ZEND_OP2_LITERAL(opline));
+ }
}
}
break;
@@ -114,14 +127,21 @@ void zend_optimizer_pass2(zend_op_array *op_array)
case ZEND_JMPZ_EX:
case ZEND_JMPNZ_EX:
/* convert Ti = JMPZ_EX(Ti, L) to JMPZ(Ti, L) */
- if (0 && /* FIXME: temporary disable unsafe pattern */
- ZEND_OP1_TYPE(opline) == IS_TMP_VAR &&
+#if 0
+ /* Disabled unsafe pattern: in conjunction with
+ * ZEND_VM_SMART_BRANCH() this may improperly eliminate
+ * assignment to Ti.
+ */
+ if (ZEND_OP1_TYPE(opline) == IS_TMP_VAR &&
ZEND_RESULT_TYPE(opline) == IS_TMP_VAR &&
ZEND_OP1(opline).var == ZEND_RESULT(opline).var) {
opline->opcode -= 3;
+ SET_UNUSED(opline->result);
+ } else
+#endif
/* convert Ti = JMPZ_EX(C, L) => Ti = QM_ASSIGN(C)
in case we know it wouldn't jump */
- } else if (ZEND_OP1_TYPE(opline) == IS_CONST) {
+ if (ZEND_OP1_TYPE(opline) == IS_CONST) {
int should_jmp = zend_is_true(&ZEND_OP1_LITERAL(opline));
if (opline->opcode == ZEND_JMPZ_EX) {
should_jmp = !should_jmp;
@@ -154,10 +174,11 @@ void zend_optimizer_pass2(zend_op_array *op_array)
if ((opline + 1)->opcode == ZEND_JMP) {
/* JMPZ(X, L1), JMP(L2) => JMPZNZ(X, L1, L2) */
/* JMPNZ(X, L1), JMP(L2) => JMPZNZ(X, L2, L1) */
- if (ZEND_OP2(opline).opline_num == ZEND_OP1(opline + 1).opline_num) {
+ if (ZEND_OP2_JMP_ADDR(opline) == ZEND_OP1_JMP_ADDR(opline + 1)) {
/* JMPZ(X, L1), JMP(L1) => NOP, JMP(L1) */
if (opline->op1_type == IS_CV) {
- break;
+ opline->opcode = ZEND_CHECK_VAR;
+ opline->op2.num = 0;
} else if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
opline->opcode = ZEND_FREE;
opline->op2.num = 0;
@@ -166,10 +187,10 @@ void zend_optimizer_pass2(zend_op_array *op_array)
}
} else {
if (opline->opcode == ZEND_JMPZ) {
- opline->extended_value = ZEND_OP1(opline + 1).opline_num;
+ opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, ZEND_OP1_JMP_ADDR(opline + 1));
} else {
- opline->extended_value = ZEND_OP2(opline).opline_num;
- COPY_NODE(opline->op2, (opline + 1)->op1);
+ opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, ZEND_OP2_JMP_ADDR(opline));
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP1_JMP_ADDR(opline + 1));
}
opline->opcode = ZEND_JMPZNZ;
}
@@ -178,14 +199,15 @@ void zend_optimizer_pass2(zend_op_array *op_array)
case ZEND_JMPZNZ:
if (ZEND_OP1_TYPE(opline) == IS_CONST) {
- int opline_num;
+ zend_op *target_opline;
+
if (zend_is_true(&ZEND_OP1_LITERAL(opline))) {
- opline_num = opline->extended_value; /* JMPNZ */
+ target_opline = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value); /* JMPNZ */
} else {
- opline_num = ZEND_OP2(opline).opline_num; /* JMPZ */
+ target_opline = ZEND_OP2_JMP_ADDR(opline); /* JMPZ */
}
literal_dtor(&ZEND_OP1_LITERAL(opline));
- ZEND_OP1(opline).opline_num = opline_num;
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op1, target_opline);
ZEND_OP1_TYPE(opline) = IS_UNUSED;
opline->opcode = ZEND_JMP;
}
diff --git a/ext/opcache/Optimizer/pass3.c b/ext/opcache/Optimizer/pass3.c
index 411764398f..e5d032cd29 100644
--- a/ext/opcache/Optimizer/pass3.c
+++ b/ext/opcache/Optimizer/pass3.c
@@ -39,31 +39,31 @@
/* we use "jmp_hitlist" to avoid infinity loops during jmp optimization */
#define CHECK_JMP(target, label) \
for (i=0; i<jmp_hitlist_count; i++) { \
- if (jmp_hitlist[i] == ZEND_OP1(&op_array->opcodes[target]).opline_num) { \
+ if (jmp_hitlist[i] == ZEND_OP1_JMP_ADDR(target)) { \
goto label; \
} \
} \
- jmp_hitlist[jmp_hitlist_count++] = ZEND_OP1(&op_array->opcodes[target]).opline_num;
+ jmp_hitlist[jmp_hitlist_count++] = ZEND_OP1_JMP_ADDR(target);
#define CHECK_JMP2(target, label) \
for (i=0; i<jmp_hitlist_count; i++) { \
- if (jmp_hitlist[i] == ZEND_OP2(&op_array->opcodes[target]).opline_num) { \
+ if (jmp_hitlist[i] == ZEND_OP2_JMP_ADDR(target)) { \
goto label; \
} \
} \
- jmp_hitlist[jmp_hitlist_count++] = ZEND_OP2(&op_array->opcodes[target]).opline_num;
+ jmp_hitlist[jmp_hitlist_count++] = ZEND_OP2_JMP_ADDR(target);
void zend_optimizer_pass3(zend_op_array *op_array)
{
zend_op *opline;
zend_op *end = op_array->opcodes + op_array->last;
- uint32_t *jmp_hitlist;
+ zend_op **jmp_hitlist;
int jmp_hitlist_count;
int i;
uint32_t opline_num = 0;
ALLOCA_FLAG(use_heap);
- jmp_hitlist = (uint32_t *)DO_ALLOCA(sizeof(uint32_t)*op_array->last);
+ jmp_hitlist = (zend_op**)do_alloca(sizeof(zend_op*)*op_array->last, use_heap);
opline = op_array->opcodes;
while (opline < end) {
@@ -93,7 +93,7 @@ void zend_optimizer_pass3(zend_op_array *op_array)
break;
}
- if ((ZEND_OP2_TYPE(opline) == IS_VAR || ZEND_OP2_TYPE(opline) == IS_CV)
+ if ((ZEND_OP2_TYPE(opline) & (IS_VAR | IS_CV))
&& ZEND_OP2(opline).var == ZEND_OP1(next_opline).var &&
(opline->opcode == ZEND_ADD ||
opline->opcode == ZEND_MUL ||
@@ -114,7 +114,7 @@ void zend_optimizer_pass3(zend_op_array *op_array)
COPY_NODE(opline->op2, tmp);
}
}
- if ((ZEND_OP1_TYPE(opline) == IS_VAR || ZEND_OP1_TYPE(opline) == IS_CV)
+ if ((ZEND_OP1_TYPE(opline) & (IS_VAR | IS_CV))
&& ZEND_OP1(opline).var == ZEND_OP1(next_opline).var
&& ZEND_OP1_TYPE(opline) == ZEND_OP1_TYPE(next_opline)) {
switch (opline->opcode) {
@@ -169,17 +169,17 @@ void zend_optimizer_pass3(zend_op_array *op_array)
}
/* convert L: JMP L+1 to NOP */
- if (ZEND_OP1(opline).opline_num == opline_num + 1) {
+ if (ZEND_OP1_JMP_ADDR(opline) == opline + 1) {
MAKE_NOP(opline);
goto done_jmp_optimization;
}
/* convert JMP L1 ... L1: JMP L2 to JMP L2 .. L1: JMP L2 */
- while (ZEND_OP1(opline).opline_num < op_array->last
- && op_array->opcodes[ZEND_OP1(opline).opline_num].opcode == ZEND_JMP) {
- int target = ZEND_OP1(opline).opline_num;
+ while (ZEND_OP1_JMP_ADDR(opline) < end
+ && ZEND_OP1_JMP_ADDR(opline)->opcode == ZEND_JMP) {
+ zend_op *target = ZEND_OP1_JMP_ADDR(opline);
CHECK_JMP(target, done_jmp_optimization);
- ZEND_OP1(opline).opline_num = ZEND_OP1(&op_array->opcodes[target]).opline_num;
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op1, ZEND_OP1_JMP_ADDR(target));
}
break;
@@ -189,10 +189,10 @@ void zend_optimizer_pass3(zend_op_array *op_array)
break;
}
- while (ZEND_OP2(opline).opline_num < op_array->last) {
- int target = ZEND_OP2(opline).opline_num;
- if (op_array->opcodes[target].opcode == ZEND_JMP) {
- ZEND_OP2(opline).opline_num = ZEND_OP1(&op_array->opcodes[target]).opline_num;
+ while (ZEND_OP2_JMP_ADDR(opline) < end) {
+ zend_op *target = ZEND_OP2_JMP_ADDR(opline);
+ if (target->opcode == ZEND_JMP) {
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP1_JMP_ADDR(target));
} else {
break;
}
@@ -204,40 +204,41 @@ void zend_optimizer_pass3(zend_op_array *op_array)
break;
}
- while (ZEND_OP2(opline).opline_num < op_array->last) {
- int target = ZEND_OP2(opline).opline_num;
+ while (ZEND_OP2_JMP_ADDR(opline) < end) {
+ zend_op *target = ZEND_OP2_JMP_ADDR(opline);
- if (op_array->opcodes[target].opcode == ZEND_JMP) {
+ if (target->opcode == ZEND_JMP) {
/* plain JMP */
/* JMPZ(X,L1), L1: JMP(L2) => JMPZ(X,L2), L1: JMP(L2) */
CHECK_JMP(target, done_jmp_optimization);
- ZEND_OP2(opline).opline_num = ZEND_OP1(&op_array->opcodes[target]).opline_num;
- } else if (op_array->opcodes[target].opcode == opline->opcode &&
- SAME_VAR(opline->op1, op_array->opcodes[target].op1)) {
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP1_JMP_ADDR(target));
+ } else if (target->opcode == opline->opcode &&
+ SAME_VAR(opline->op1, target->op1)) {
/* same opcode and same var as this opcode */
/* JMPZ(X,L1), L1: JMPZ(X,L2) => JMPZ(X,L2), L1: JMPZ(X,L2) */
CHECK_JMP2(target, done_jmp_optimization);
- ZEND_OP2(opline).opline_num = ZEND_OP2(&op_array->opcodes[target]).opline_num;
- } else if (op_array->opcodes[target].opcode == opline->opcode + 3 &&
- SAME_VAR(opline->op1, op_array->opcodes[target].op1)) {
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP2_JMP_ADDR(target));
+ } else if (target->opcode == opline->opcode + 3 &&
+ SAME_VAR(opline->op1, target->op1)) {
/* convert JMPZ(X,L1), L1: T JMPZ_EX(X,L2) to
T = JMPZ_EX(X, L2) */
- ZEND_OP2(opline).opline_num = ZEND_OP2(&op_array->opcodes[target]).opline_num;opline->opcode += 3;
- COPY_NODE(opline->result, op_array->opcodes[target].result);
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP2_JMP_ADDR(target));
+ opline->opcode += 3;
+ COPY_NODE(opline->result, target->result);
break;
- } else if (op_array->opcodes[target].opcode == INV_COND(opline->opcode) &&
- SAME_VAR(opline->op1, op_array->opcodes[target].op1)) {
+ } else if (target->opcode == INV_COND(opline->opcode) &&
+ SAME_VAR(opline->op1, target->op1)) {
/* convert JMPZ(X,L1), L1: JMPNZ(X,L2) to
JMPZ(X,L1+1) */
- ZEND_OP2(opline).opline_num = target + 1;
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target + 1);
break;
- } else if (op_array->opcodes[target].opcode == INV_COND_EX(opline->opcode) &&
- SAME_VAR(opline->op1, op_array->opcodes[target].op1)) {
+ } else if (target->opcode == INV_COND_EX(opline->opcode) &&
+ SAME_VAR(opline->op1, target->op1)) {
/* convert JMPZ(X,L1), L1: T = JMPNZ_EX(X,L2) to
T = JMPZ_EX(X,L1+1) */
- ZEND_OP2(opline).opline_num = target + 1;
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target + 1);
opline->opcode += 3;
- COPY_NODE(opline->result, op_array->opcodes[target].result);
+ COPY_NODE(opline->result, target->result);
break;
} else {
break;
@@ -256,7 +257,7 @@ void zend_optimizer_pass3(zend_op_array *op_array)
/* convert L: T = JMPZ_EX X,L+1 to T = BOOL(X) */
/* convert L: T = JMPZ_EX T,L+1 to NOP */
- if (ZEND_OP2(opline).opline_num == opline_num + 1) {
+ if (ZEND_OP2_JMP_ADDR(opline) == opline + 1) {
if (ZEND_OP1(opline).var == ZEND_RESULT(opline).var) {
MAKE_NOP(opline);
} else {
@@ -266,36 +267,38 @@ void zend_optimizer_pass3(zend_op_array *op_array)
goto done_jmp_optimization;
}
- while (ZEND_OP2(opline).opline_num < op_array->last) {
- int target = ZEND_OP2(opline).opline_num;
- if (SAME_OPCODE_EX(opline->opcode, op_array->opcodes[target].opcode) &&
- SAME_VAR(op_array->opcodes[target].op1, T)) {
+ while (ZEND_OP2_JMP_ADDR(opline) < end) {
+ zend_op *target = ZEND_OP2_JMP_ADDR(opline);
+
+ if (SAME_OPCODE_EX(opline->opcode, target->opcode) &&
+ SAME_VAR(target->op1, T)) {
/* Check for JMPZ_EX to JMPZ[_EX] with the same condition, either with _EX or not */
- if (op_array->opcodes[target].opcode == opline->opcode) {
+ if (target->opcode == opline->opcode) {
/* change T only if we have _EX opcode there */
- COPY_NODE(T, op_array->opcodes[target].result);
+ COPY_NODE(T, target->result);
}
CHECK_JMP2(target, continue_jmp_ex_optimization);
- ZEND_OP2(opline).opline_num = ZEND_OP2(&op_array->opcodes[target]).opline_num;
- } else if (op_array->opcodes[target].opcode == ZEND_JMPZNZ &&
- SAME_VAR(op_array->opcodes[target].op1, T)) {
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP2_JMP_ADDR(target));
+ } else if (target->opcode == ZEND_JMPZNZ &&
+ SAME_VAR(target->op1, T)) {
/* Check for JMPZNZ with same cond variable */
- int new_target;
+ zend_op *new_target;
+
CHECK_JMP2(target, continue_jmp_ex_optimization);
if (opline->opcode == ZEND_JMPZ_EX) {
- new_target = ZEND_OP2(&op_array->opcodes[target]).opline_num;
+ new_target = ZEND_OP2_JMP_ADDR(target);
} else {
/* JMPNZ_EX */
- new_target = op_array->opcodes[target].extended_value;
+ new_target = ZEND_OFFSET_TO_OPLINE(target, target->extended_value);
}
- ZEND_OP2(opline).opline_num = new_target;
- } else if ((op_array->opcodes[target].opcode == INV_EX_COND_EX(opline->opcode) ||
- op_array->opcodes[target].opcode == INV_EX_COND(opline->opcode)) &&
- SAME_VAR(opline->op1, op_array->opcodes[target].op1)) {
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op2, new_target);
+ } else if ((target->opcode == INV_EX_COND_EX(opline->opcode) ||
+ target->opcode == INV_EX_COND(opline->opcode)) &&
+ SAME_VAR(opline->op1, target->op1)) {
/* convert JMPZ_EX(X,L1), L1: JMPNZ_EX(X,L2) to
JMPZ_EX(X,L1+1) */
- ZEND_OP2(opline).opline_num = target + 1;
- break;
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target + 1);
+ break;
} else {
break;
}
@@ -387,19 +390,19 @@ continue_jmp_ex_optimization:
}
/* JMPZNZ(X,L1,L2), L1: JMP(L3) => JMPZNZ(X,L3,L2), L1: JMP(L3) */
- while (ZEND_OP2(opline).opline_num < op_array->last
- && op_array->opcodes[ZEND_OP2(opline).opline_num].opcode == ZEND_JMP) {
- int target = ZEND_OP2(opline).opline_num;
+ while (ZEND_OP2_JMP_ADDR(opline) < end
+ && ZEND_OP2_JMP_ADDR(opline)->opcode == ZEND_JMP) {
+ zend_op *target = ZEND_OP2_JMP_ADDR(opline);
CHECK_JMP(target, continue_jmpznz_optimization);
- ZEND_OP2(opline).opline_num = ZEND_OP1(&op_array->opcodes[target]).opline_num;
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP1_JMP_ADDR(target));
}
continue_jmpznz_optimization:
/* JMPZNZ(X,L1,L2), L2: JMP(L3) => JMPZNZ(X,L1,L3), L2: JMP(L3) */
- while (opline->extended_value < op_array->last
- && op_array->opcodes[opline->extended_value].opcode == ZEND_JMP) {
- int target = opline->extended_value;
+ while (ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value) < end
+ && ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value)->opcode == ZEND_JMP) {
+ zend_op *target = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value);
CHECK_JMP(target, done_jmp_optimization);
- opline->extended_value = ZEND_OP1(&op_array->opcodes[target]).opline_num;
+ opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, ZEND_OP1_JMP_ADDR(target));
}
break;
@@ -414,15 +417,8 @@ continue_jmpznz_optimization:
if (next_op->opcode == ZEND_FREE &&
ZEND_OP1(next_op).var == ZEND_RESULT(opline).var) {
MAKE_NOP(next_op);
- switch (opline->opcode) {
- case ZEND_POST_INC:
- opline->opcode = ZEND_PRE_INC;
- break;
- case ZEND_POST_DEC:
- opline->opcode = ZEND_PRE_DEC;
- break;
- }
- ZEND_RESULT_TYPE(opline) = IS_VAR | EXT_TYPE_UNUSED;
+ opline->opcode -= 2;
+ ZEND_RESULT_TYPE(opline) = IS_UNUSED;
}
}
break;
@@ -431,5 +427,5 @@ done_jmp_optimization:
opline++;
opline_num++;
}
- FREE_ALLOCA(jmp_hitlist);
+ free_alloca(jmp_hitlist, use_heap);
}
diff --git a/ext/opcache/Optimizer/zend_call_graph.c b/ext/opcache/Optimizer/zend_call_graph.c
new file mode 100644
index 0000000000..5800a220bc
--- /dev/null
+++ b/ext/opcache/Optimizer/zend_call_graph.c
@@ -0,0 +1,298 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine, Call Graph |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1998-2017 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Dmitry Stogov <dmitry@zend.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id:$ */
+
+#include "php.h"
+#include "zend_compile.h"
+#include "zend_extensions.h"
+#include "Optimizer/zend_optimizer.h"
+#include "zend_optimizer_internal.h"
+#include "zend_inference.h"
+#include "zend_call_graph.h"
+#include "zend_func_info.h"
+#include "zend_inference.h"
+#include "zend_call_graph.h"
+
+typedef int (*zend_op_array_func_t)(zend_call_graph *call_graph, zend_op_array *op_array);
+
+static int zend_op_array_calc(zend_call_graph *call_graph, zend_op_array *op_array)
+{
+ (void) op_array;
+
+ call_graph->op_arrays_count++;
+ return SUCCESS;
+}
+
+static int zend_op_array_collect(zend_call_graph *call_graph, zend_op_array *op_array)
+{
+ zend_func_info *func_info = call_graph->func_infos + call_graph->op_arrays_count;
+
+ ZEND_SET_FUNC_INFO(op_array, func_info);
+ call_graph->op_arrays[call_graph->op_arrays_count] = op_array;
+ func_info->num = call_graph->op_arrays_count;
+ func_info->num_args = -1;
+ func_info->return_value_used = -1;
+ call_graph->op_arrays_count++;
+ return SUCCESS;
+}
+
+static int zend_foreach_op_array(zend_call_graph *call_graph, zend_script *script, zend_op_array_func_t func)
+{
+ zend_class_entry *ce;
+ zend_op_array *op_array;
+
+ if (func(call_graph, &script->main_op_array) != SUCCESS) {
+ return FAILURE;
+ }
+
+ ZEND_HASH_FOREACH_PTR(&script->function_table, op_array) {
+ if (func(call_graph, op_array) != SUCCESS) {
+ return FAILURE;
+ }
+ } ZEND_HASH_FOREACH_END();
+
+ ZEND_HASH_FOREACH_PTR(&script->class_table, ce) {
+ ZEND_HASH_FOREACH_PTR(&ce->function_table, op_array) {
+ if (op_array->scope == ce) {
+ if (func(call_graph, op_array) != SUCCESS) {
+ return FAILURE;
+ }
+ }
+ } ZEND_HASH_FOREACH_END();
+ } ZEND_HASH_FOREACH_END();
+
+ return SUCCESS;
+}
+
+int zend_analyze_calls(zend_arena **arena, zend_script *script, uint32_t build_flags, zend_op_array *op_array, zend_func_info *func_info)
+{
+ zend_op *opline = op_array->opcodes;
+ zend_op *end = opline + op_array->last;
+ zend_function *func;
+ zend_call_info *call_info;
+ int call = 0;
+ zend_call_info **call_stack;
+ ALLOCA_FLAG(use_heap);
+
+ call_stack = do_alloca((op_array->last / 2) * sizeof(zend_call_info*), use_heap);
+ call_info = NULL;
+ while (opline != end) {
+ switch (opline->opcode) {
+ case ZEND_INIT_FCALL:
+ case ZEND_INIT_METHOD_CALL:
+ case ZEND_INIT_STATIC_METHOD_CALL:
+ call_stack[call] = call_info;
+ func = zend_optimizer_get_called_func(
+ script, op_array, opline, (build_flags & ZEND_RT_CONSTANTS) != 0);
+ if (func) {
+ call_info = zend_arena_calloc(arena, 1, sizeof(zend_call_info) + (sizeof(zend_send_arg_info) * ((int)opline->extended_value - 1)));
+ call_info->caller_op_array = op_array;
+ call_info->caller_init_opline = opline;
+ call_info->caller_call_opline = NULL;
+ call_info->callee_func = func;
+ call_info->num_args = opline->extended_value;
+ call_info->next_callee = func_info->callee_info;
+ func_info->callee_info = call_info;
+
+ if (build_flags & ZEND_CALL_TREE) {
+ call_info->next_caller = NULL;
+ } else if (func->type == ZEND_INTERNAL_FUNCTION) {
+ call_info->next_caller = NULL;
+ } else {
+ zend_func_info *callee_func_info = ZEND_FUNC_INFO(&func->op_array);
+ if (callee_func_info) {
+ call_info->next_caller = callee_func_info->caller_info;
+ callee_func_info->caller_info = call_info;
+ } else {
+ call_info->next_caller = NULL;
+ }
+ }
+ } else {
+ call_info = NULL;
+ }
+ call++;
+ break;
+ case ZEND_INIT_FCALL_BY_NAME:
+ case ZEND_INIT_NS_FCALL_BY_NAME:
+ case ZEND_INIT_DYNAMIC_CALL:
+ case ZEND_NEW:
+ case ZEND_INIT_USER_CALL:
+ call_stack[call] = call_info;
+ call_info = NULL;
+ call++;
+ break;
+ case ZEND_DO_FCALL:
+ case ZEND_DO_ICALL:
+ case ZEND_DO_UCALL:
+ case ZEND_DO_FCALL_BY_NAME:
+ func_info->flags |= ZEND_FUNC_HAS_CALLS;
+ if (call_info) {
+ call_info->caller_call_opline = opline;
+ }
+ call--;
+ call_info = call_stack[call];
+ break;
+ case ZEND_SEND_VAL:
+ case ZEND_SEND_VAR:
+ case ZEND_SEND_VAL_EX:
+ case ZEND_SEND_VAR_EX:
+ case ZEND_SEND_REF:
+ case ZEND_SEND_VAR_NO_REF:
+ case ZEND_SEND_VAR_NO_REF_EX:
+ if (call_info) {
+ uint32_t num = opline->op2.num;
+
+ if (num > 0) {
+ num--;
+ }
+ call_info->arg_info[num].opline = opline;
+ }
+ break;
+ case ZEND_SEND_ARRAY:
+ case ZEND_SEND_USER:
+ case ZEND_SEND_UNPACK:
+ /* TODO: set info about var_arg call ??? */
+ break;
+ }
+ opline++;
+ }
+ free_alloca(call_stack, use_heap);
+ return SUCCESS;
+}
+
+static int zend_is_indirectly_recursive(zend_op_array *root, zend_op_array *op_array, zend_bitset visited)
+{
+ zend_func_info *func_info;
+ zend_call_info *call_info;
+ int ret = 0;
+
+ if (op_array == root) {
+ return 1;
+ }
+
+ func_info = ZEND_FUNC_INFO(op_array);
+ if (zend_bitset_in(visited, func_info->num)) {
+ return 0;
+ }
+ zend_bitset_incl(visited, func_info->num);
+ call_info = func_info->caller_info;
+ while (call_info) {
+ if (zend_is_indirectly_recursive(root, call_info->caller_op_array, visited)) {
+ call_info->recursive = 1;
+ ret = 1;
+ }
+ call_info = call_info->next_caller;
+ }
+ return ret;
+}
+
+static void zend_analyze_recursion(zend_call_graph *call_graph)
+{
+ zend_op_array *op_array;
+ zend_func_info *func_info;
+ zend_call_info *call_info;
+ int i;
+ int set_len = zend_bitset_len(call_graph->op_arrays_count);
+ zend_bitset visited;
+ ALLOCA_FLAG(use_heap);
+
+ visited = ZEND_BITSET_ALLOCA(set_len, use_heap);
+ for (i = 0; i < call_graph->op_arrays_count; i++) {
+ op_array = call_graph->op_arrays[i];
+ func_info = call_graph->func_infos + i;
+ call_info = func_info->caller_info;
+ while (call_info) {
+ if (call_info->caller_op_array == op_array) {
+ call_info->recursive = 1;
+ func_info->flags |= ZEND_FUNC_RECURSIVE | ZEND_FUNC_RECURSIVE_DIRECTLY;
+ } else {
+ memset(visited, 0, sizeof(zend_ulong) * set_len);
+ if (zend_is_indirectly_recursive(op_array, call_info->caller_op_array, visited)) {
+ call_info->recursive = 1;
+ func_info->flags |= ZEND_FUNC_RECURSIVE | ZEND_FUNC_RECURSIVE_INDIRECTLY;
+ }
+ }
+ call_info = call_info->next_caller;
+ }
+ }
+
+ free_alloca(visited, use_heap);
+}
+
+static void zend_sort_op_arrays(zend_call_graph *call_graph)
+{
+ (void) call_graph;
+
+ // TODO: perform topological sort of cyclic call graph
+}
+
+int zend_build_call_graph(zend_arena **arena, zend_script *script, uint32_t build_flags, zend_call_graph *call_graph) /* {{{ */
+{
+ int i;
+
+ call_graph->op_arrays_count = 0;
+ if (zend_foreach_op_array(call_graph, script, zend_op_array_calc) != SUCCESS) {
+ return FAILURE;
+ }
+ call_graph->op_arrays = (zend_op_array**)zend_arena_calloc(arena, call_graph->op_arrays_count, sizeof(zend_op_array*));
+ call_graph->func_infos = (zend_func_info*)zend_arena_calloc(arena, call_graph->op_arrays_count, sizeof(zend_func_info));
+ call_graph->op_arrays_count = 0;
+ if (zend_foreach_op_array(call_graph, script, zend_op_array_collect) != SUCCESS) {
+ return FAILURE;
+ }
+ for (i = 0; i < call_graph->op_arrays_count; i++) {
+ zend_analyze_calls(arena, script, build_flags, call_graph->op_arrays[i], call_graph->func_infos + i);
+ }
+ zend_analyze_recursion(call_graph);
+ zend_sort_op_arrays(call_graph);
+
+ return SUCCESS;
+}
+/* }}} */
+
+zend_call_info **zend_build_call_map(zend_arena **arena, zend_func_info *info, zend_op_array *op_array) /* {{{ */
+{
+ zend_call_info **map, *call;
+ if (!info->callee_info) {
+ /* Don't build call map if function contains no calls */
+ return NULL;
+ }
+
+ map = zend_arena_calloc(arena, sizeof(zend_call_info *), op_array->last);
+ for (call = info->callee_info; call; call = call->next_callee) {
+ int i;
+ map[call->caller_init_opline - op_array->opcodes] = call;
+ map[call->caller_call_opline - op_array->opcodes] = call;
+ for (i = 0; i < call->num_args; i++) {
+ if (call->arg_info[i].opline) {
+ map[call->arg_info[i].opline - op_array->opcodes] = call;
+ }
+ }
+ }
+ return map;
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * indent-tabs-mode: t
+ * End:
+ */
diff --git a/ext/opcache/Optimizer/zend_call_graph.h b/ext/opcache/Optimizer/zend_call_graph.h
new file mode 100644
index 0000000000..49c7217c40
--- /dev/null
+++ b/ext/opcache/Optimizer/zend_call_graph.h
@@ -0,0 +1,86 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine, Call Graph |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1998-2017 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Dmitry Stogov <dmitry@zend.com> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef ZEND_CALL_GRAPH_H
+#define ZEND_CALL_GRAPH_H
+
+#include "zend_ssa.h"
+#include "zend_func_info.h"
+#include "zend_optimizer.h"
+
+typedef struct _zend_send_arg_info {
+ zend_op *opline;
+} zend_send_arg_info;
+
+typedef struct _zend_recv_arg_info {
+ int ssa_var;
+ zend_ssa_var_info info;
+} zend_recv_arg_info;
+
+struct _zend_call_info {
+ zend_op_array *caller_op_array;
+ zend_op *caller_init_opline;
+ zend_op *caller_call_opline;
+ zend_function *callee_func;
+ zend_call_info *next_caller;
+ zend_call_info *next_callee;
+ zend_func_info *clone;
+ int recursive;
+ int num_args;
+ zend_send_arg_info arg_info[1];
+};
+
+struct _zend_func_info {
+ int num;
+ uint32_t flags;
+ zend_ssa ssa; /* Static Single Assignmnt Form */
+ zend_call_info *caller_info; /* where this function is called from */
+ zend_call_info *callee_info; /* which functions are called from this one */
+ zend_call_info **call_map; /* Call info associated with init/call/send opnum */
+ int num_args; /* (-1 - unknown) */
+ zend_recv_arg_info *arg_info;
+ zend_ssa_var_info return_info;
+ zend_func_info *clone;
+ int clone_num;
+ int return_value_used; /* -1 unknown, 0 no, 1 yes */
+ void *codegen_data;
+};
+
+typedef struct _zend_call_graph {
+ int op_arrays_count;
+ zend_op_array **op_arrays;
+ zend_func_info *func_infos;
+} zend_call_graph;
+
+BEGIN_EXTERN_C()
+
+int zend_build_call_graph(zend_arena **arena, zend_script *script, uint32_t build_flags, zend_call_graph *call_graph);
+zend_call_info **zend_build_call_map(zend_arena **arena, zend_func_info *info, zend_op_array *op_array);
+int zend_analyze_calls(zend_arena **arena, zend_script *script, uint32_t build_flags, zend_op_array *op_array, zend_func_info *func_info);
+
+END_EXTERN_C()
+
+#endif /* ZEND_CALL_GRAPH_H */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * indent-tabs-mode: t
+ * End:
+ */
diff --git a/ext/opcache/Optimizer/zend_cfg.c b/ext/opcache/Optimizer/zend_cfg.c
new file mode 100644
index 0000000000..ec7116691e
--- /dev/null
+++ b/ext/opcache/Optimizer/zend_cfg.c
@@ -0,0 +1,885 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine, CFG - Control Flow Graph |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1998-2017 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Dmitry Stogov <dmitry@zend.com> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "php.h"
+#include "zend_compile.h"
+#include "zend_cfg.h"
+#include "zend_func_info.h"
+#include "zend_worklist.h"
+#include "zend_optimizer.h"
+#include "zend_optimizer_internal.h"
+
+static void zend_mark_reachable(zend_op *opcodes, zend_cfg *cfg, zend_basic_block *b) /* {{{ */
+{
+ zend_uchar opcode;
+ zend_basic_block *b0;
+ int successor_0, successor_1;
+ zend_basic_block *blocks = cfg->blocks;
+
+ while (1) {
+ b->flags |= ZEND_BB_REACHABLE;
+ successor_0 = b->successors[0];
+ if (successor_0 >= 0) {
+ successor_1 = b->successors[1];
+ if (successor_1 >= 0) {
+ b0 = blocks + successor_0;
+ b0->flags |= ZEND_BB_TARGET;
+ if (!(b0->flags & ZEND_BB_REACHABLE)) {
+ zend_mark_reachable(opcodes, cfg, b0);
+ }
+
+ ZEND_ASSERT(b->len != 0);
+ opcode = opcodes[b->start + b->len - 1].opcode;
+ b = blocks + successor_1;
+ if (opcode == ZEND_JMPZNZ) {
+ b->flags |= ZEND_BB_TARGET;
+ } else {
+ b->flags |= ZEND_BB_FOLLOW;
+ }
+ } else if (b->len != 0) {
+ opcode = opcodes[b->start + b->len - 1].opcode;
+ b = blocks + successor_0;
+ if (opcode == ZEND_JMP) {
+ b->flags |= ZEND_BB_TARGET;
+ } else {
+ b->flags |= ZEND_BB_FOLLOW;
+
+ if (cfg->split_at_calls) {
+ if (opcode == ZEND_INCLUDE_OR_EVAL ||
+ opcode == ZEND_GENERATOR_CREATE ||
+ opcode == ZEND_YIELD ||
+ opcode == ZEND_YIELD_FROM ||
+ opcode == ZEND_DO_FCALL ||
+ opcode == ZEND_DO_UCALL ||
+ opcode == ZEND_DO_FCALL_BY_NAME) {
+ b->flags |= ZEND_BB_ENTRY;
+ }
+ }
+ if (cfg->split_at_recv) {
+ if (opcode == ZEND_RECV ||
+ opcode == ZEND_RECV_INIT) {
+ b->flags |= ZEND_BB_RECV_ENTRY;
+ }
+ }
+ }
+ } else {
+ b = blocks + successor_0;
+ b->flags |= ZEND_BB_FOLLOW;
+ }
+ if (b->flags & ZEND_BB_REACHABLE) return;
+ } else {
+ b->flags |= ZEND_BB_EXIT;
+ return;
+ }
+ }
+}
+/* }}} */
+
+static void zend_mark_reachable_blocks(const zend_op_array *op_array, zend_cfg *cfg, int start) /* {{{ */
+{
+ zend_basic_block *blocks = cfg->blocks;
+
+ blocks[start].flags = ZEND_BB_START;
+ zend_mark_reachable(op_array->opcodes, cfg, blocks + start);
+
+ if (op_array->last_live_range || op_array->last_try_catch) {
+ zend_basic_block *b;
+ int j, changed;
+ uint32_t *block_map = cfg->map;
+
+ do {
+ changed = 0;
+
+ /* Add live range paths */
+ for (j = 0; j < op_array->last_live_range; j++) {
+ zend_live_range *live_range = &op_array->live_range[j];
+ if (live_range->var == (uint32_t)-1) {
+ /* this live range already removed */
+ continue;
+ }
+ b = blocks + block_map[live_range->start];
+ if (b->flags & ZEND_BB_REACHABLE) {
+ while (b->len > 0 && op_array->opcodes[b->start].opcode == ZEND_NOP) {
+ /* check if NOP breaks incorrect smart branch */
+ if (b->len == 2
+ && (op_array->opcodes[b->start + 1].opcode == ZEND_JMPZ
+ || op_array->opcodes[b->start + 1].opcode == ZEND_JMPNZ)
+ && (op_array->opcodes[b->start + 1].op1_type & (IS_CV|IS_CONST))
+ && b->start > 0
+ && zend_is_smart_branch(op_array->opcodes + b->start - 1)) {
+ break;
+ }
+ b->start++;
+ b->len--;
+ }
+ if (b->len == 0 && (uint32_t)b->successors[0] == block_map[live_range->end]) {
+ /* mark as removed (empty live range) */
+ live_range->var = (uint32_t)-1;
+ continue;
+ }
+ b->flags |= ZEND_BB_GEN_VAR;
+ b = blocks + block_map[live_range->end];
+ b->flags |= ZEND_BB_KILL_VAR;
+ if (!(b->flags & (ZEND_BB_REACHABLE|ZEND_BB_UNREACHABLE_FREE))) {
+ if (cfg->split_at_live_ranges) {
+ changed = 1;
+ zend_mark_reachable(op_array->opcodes, cfg, b);
+ } else {
+ ZEND_ASSERT(b->start == live_range->end);
+ b->flags |= ZEND_BB_UNREACHABLE_FREE;
+ }
+ }
+ } else {
+ ZEND_ASSERT(!(blocks[block_map[live_range->end]].flags & ZEND_BB_REACHABLE));
+ }
+ }
+
+ /* Add exception paths */
+ for (j = 0; j < op_array->last_try_catch; j++) {
+
+ /* check for jumps into the middle of try block */
+ b = blocks + block_map[op_array->try_catch_array[j].try_op];
+ if (!(b->flags & ZEND_BB_REACHABLE)) {
+ zend_basic_block *end;
+
+ if (op_array->try_catch_array[j].catch_op) {
+ end = blocks + block_map[op_array->try_catch_array[j].catch_op];
+ while (b != end) {
+ if (b->flags & ZEND_BB_REACHABLE) {
+ op_array->try_catch_array[j].try_op = b->start;
+ break;
+ }
+ b++;
+ }
+ }
+ b = blocks + block_map[op_array->try_catch_array[j].try_op];
+ if (!(b->flags & ZEND_BB_REACHABLE)) {
+ if (op_array->try_catch_array[j].finally_op) {
+ end = blocks + block_map[op_array->try_catch_array[j].finally_op];
+ while (b != end) {
+ if (b->flags & ZEND_BB_REACHABLE) {
+ op_array->try_catch_array[j].try_op = op_array->try_catch_array[j].catch_op;
+ changed = 1;
+ zend_mark_reachable(op_array->opcodes, cfg, blocks + block_map[op_array->try_catch_array[j].try_op]);
+ break;
+ }
+ b++;
+ }
+ }
+ }
+ }
+
+ b = blocks + block_map[op_array->try_catch_array[j].try_op];
+ if (b->flags & ZEND_BB_REACHABLE) {
+ b->flags |= ZEND_BB_TRY;
+ if (op_array->try_catch_array[j].catch_op) {
+ b = blocks + block_map[op_array->try_catch_array[j].catch_op];
+ b->flags |= ZEND_BB_CATCH;
+ if (!(b->flags & ZEND_BB_REACHABLE)) {
+ changed = 1;
+ zend_mark_reachable(op_array->opcodes, cfg, b);
+ }
+ }
+ if (op_array->try_catch_array[j].finally_op) {
+ b = blocks + block_map[op_array->try_catch_array[j].finally_op];
+ b->flags |= ZEND_BB_FINALLY;
+ if (!(b->flags & ZEND_BB_REACHABLE)) {
+ changed = 1;
+ zend_mark_reachable(op_array->opcodes, cfg, b);
+ }
+ }
+ if (op_array->try_catch_array[j].finally_end) {
+ b = blocks + block_map[op_array->try_catch_array[j].finally_end];
+ b->flags |= ZEND_BB_FINALLY_END;
+ if (!(b->flags & ZEND_BB_REACHABLE)) {
+ changed = 1;
+ zend_mark_reachable(op_array->opcodes, cfg, b);
+ }
+ }
+ } else {
+ if (op_array->try_catch_array[j].catch_op) {
+ ZEND_ASSERT(!(blocks[block_map[op_array->try_catch_array[j].catch_op]].flags & ZEND_BB_REACHABLE));
+ }
+ if (op_array->try_catch_array[j].finally_op) {
+ ZEND_ASSERT(!(blocks[block_map[op_array->try_catch_array[j].finally_op]].flags & ZEND_BB_REACHABLE));
+ }
+ if (op_array->try_catch_array[j].finally_end) {
+ ZEND_ASSERT(!(blocks[block_map[op_array->try_catch_array[j].finally_end]].flags & ZEND_BB_REACHABLE));
+ }
+ }
+ }
+ } while (changed);
+ }
+}
+/* }}} */
+
+void zend_cfg_remark_reachable_blocks(const zend_op_array *op_array, zend_cfg *cfg) /* {{{ */
+{
+ zend_basic_block *blocks = cfg->blocks;
+ int i;
+ int start = 0;
+
+ for (i = 0; i < cfg->blocks_count; i++) {
+ if (blocks[i].flags & ZEND_BB_REACHABLE) {
+ start = i;
+ i++;
+ break;
+ }
+ }
+
+ /* clear all flags */
+ for (i = 0; i < cfg->blocks_count; i++) {
+ blocks[i].flags = 0;
+ }
+
+ zend_mark_reachable_blocks(op_array, cfg, start);
+}
+/* }}} */
+
+static void record_successor(zend_basic_block *blocks, int pred, int n, int succ)
+{
+ blocks[pred].successors[n] = succ;
+}
+
+static void initialize_block(zend_basic_block *block) {
+ block->flags = 0;
+ block->successors[0] = -1;
+ block->successors[1] = -1;
+ block->predecessors_count = 0;
+ block->predecessor_offset = -1;
+ block->idom = -1;
+ block->loop_header = -1;
+ block->level = -1;
+ block->children = -1;
+ block->next_child = -1;
+}
+
+#define BB_START(i) do { \
+ if (!block_map[i]) { blocks_count++;} \
+ block_map[i]++; \
+ } while (0)
+
+int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t build_flags, zend_cfg *cfg, uint32_t *func_flags) /* {{{ */
+{
+ uint32_t flags = 0;
+ uint32_t i;
+ int j;
+ uint32_t *block_map;
+ zend_function *fn;
+ int blocks_count = 0;
+ zend_basic_block *blocks;
+ zval *zv;
+ zend_bool extra_entry_block = 0;
+
+ cfg->split_at_live_ranges = (build_flags & ZEND_CFG_SPLIT_AT_LIVE_RANGES) != 0;
+ cfg->split_at_calls = (build_flags & ZEND_CFG_STACKLESS) != 0;
+ cfg->split_at_recv = (build_flags & ZEND_CFG_RECV_ENTRY) != 0 && (op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS) == 0;
+
+ cfg->map = block_map = zend_arena_calloc(arena, op_array->last, sizeof(uint32_t));
+ if (!block_map) {
+ return FAILURE;
+ }
+
+ /* Build CFG, Step 1: Find basic blocks starts, calculate number of blocks */
+ BB_START(0);
+ for (i = 0; i < op_array->last; i++) {
+ zend_op *opline = op_array->opcodes + i;
+ switch(opline->opcode) {
+ case ZEND_RECV:
+ case ZEND_RECV_INIT:
+ if (build_flags & ZEND_CFG_RECV_ENTRY) {
+ BB_START(i + 1);
+ }
+ break;
+ case ZEND_RETURN:
+ case ZEND_RETURN_BY_REF:
+ case ZEND_GENERATOR_RETURN:
+ case ZEND_EXIT:
+ case ZEND_THROW:
+ if (i + 1 < op_array->last) {
+ BB_START(i + 1);
+ }
+ break;
+ case ZEND_INCLUDE_OR_EVAL:
+ flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
+ case ZEND_GENERATOR_CREATE:
+ case ZEND_YIELD:
+ case ZEND_YIELD_FROM:
+ if (build_flags & ZEND_CFG_STACKLESS) {
+ BB_START(i + 1);
+ }
+ break;
+ case ZEND_DO_FCALL:
+ case ZEND_DO_UCALL:
+ case ZEND_DO_FCALL_BY_NAME:
+ flags |= ZEND_FUNC_HAS_CALLS;
+ if (build_flags & ZEND_CFG_STACKLESS) {
+ BB_START(i + 1);
+ }
+ break;
+ case ZEND_DO_ICALL:
+ flags |= ZEND_FUNC_HAS_CALLS;
+ break;
+ case ZEND_INIT_FCALL:
+ case ZEND_INIT_NS_FCALL_BY_NAME:
+ zv = CRT_CONSTANT(opline->op2);
+ if (opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME) {
+ /* The third literal is the lowercased unqualified name */
+ zv += 2;
+ }
+ if ((fn = zend_hash_find_ptr(EG(function_table), Z_STR_P(zv))) != NULL) {
+ if (fn->type == ZEND_INTERNAL_FUNCTION) {
+ flags |= zend_optimizer_classify_function(
+ Z_STR_P(zv), opline->extended_value);
+ }
+ }
+ break;
+ case ZEND_FAST_CALL:
+ BB_START(OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes);
+ BB_START(i + 1);
+ break;
+ case ZEND_FAST_RET:
+ if (i + 1 < op_array->last) {
+ BB_START(i + 1);
+ }
+ break;
+ case ZEND_JMP:
+ BB_START(OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes);
+ if (i + 1 < op_array->last) {
+ BB_START(i + 1);
+ }
+ break;
+ case ZEND_JMPZNZ:
+ BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
+ BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
+ if (i + 1 < op_array->last) {
+ BB_START(i + 1);
+ }
+ break;
+ case ZEND_JMPZ:
+ case ZEND_JMPNZ:
+ case ZEND_JMPZ_EX:
+ case ZEND_JMPNZ_EX:
+ case ZEND_JMP_SET:
+ case ZEND_COALESCE:
+ case ZEND_ASSERT_CHECK:
+ BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
+ BB_START(i + 1);
+ break;
+ case ZEND_CATCH:
+ if (!opline->result.num) {
+ BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
+ }
+ BB_START(i + 1);
+ break;
+ case ZEND_DECLARE_ANON_CLASS:
+ case ZEND_DECLARE_ANON_INHERITED_CLASS:
+ case ZEND_FE_FETCH_R:
+ case ZEND_FE_FETCH_RW:
+ BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
+ BB_START(i + 1);
+ break;
+ case ZEND_FE_RESET_R:
+ case ZEND_FE_RESET_RW:
+ BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
+ BB_START(i + 1);
+ break;
+ case ZEND_UNSET_VAR:
+ case ZEND_ISSET_ISEMPTY_VAR:
+ if (((opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_LOCAL) &&
+ !(opline->extended_value & ZEND_QUICK_SET)) {
+ flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
+ } else if (((opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_GLOBAL ||
+ (opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_GLOBAL_LOCK) &&
+ !op_array->function_name) {
+ flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
+ }
+ break;
+ case ZEND_FETCH_R:
+ case ZEND_FETCH_W:
+ case ZEND_FETCH_RW:
+ case ZEND_FETCH_FUNC_ARG:
+ case ZEND_FETCH_IS:
+ case ZEND_FETCH_UNSET:
+ if ((opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_LOCAL) {
+ flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
+ } else if (((opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_GLOBAL ||
+ (opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_GLOBAL_LOCK) &&
+ !op_array->function_name) {
+ flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
+ }
+ break;
+ }
+ }
+
+ /* If the entry block has predecessors, we may need to split it */
+ if ((build_flags & ZEND_CFG_NO_ENTRY_PREDECESSORS)
+ && op_array->last > 0 && block_map[0] > 1) {
+ extra_entry_block = 1;
+ }
+
+ if (cfg->split_at_live_ranges) {
+ for (j = 0; j < op_array->last_live_range; j++) {
+ BB_START(op_array->live_range[j].start);
+ BB_START(op_array->live_range[j].end);
+ }
+ }
+
+ if (op_array->last_try_catch) {
+ for (j = 0; j < op_array->last_try_catch; j++) {
+ BB_START(op_array->try_catch_array[j].try_op);
+ if (op_array->try_catch_array[j].catch_op) {
+ BB_START(op_array->try_catch_array[j].catch_op);
+ }
+ if (op_array->try_catch_array[j].finally_op) {
+ BB_START(op_array->try_catch_array[j].finally_op);
+ }
+ if (op_array->try_catch_array[j].finally_end) {
+ BB_START(op_array->try_catch_array[j].finally_end);
+ }
+ }
+ }
+
+ blocks_count += extra_entry_block;
+ cfg->blocks_count = blocks_count;
+
+ /* Build CFG, Step 2: Build Array of Basic Blocks */
+ cfg->blocks = blocks = zend_arena_calloc(arena, sizeof(zend_basic_block), blocks_count);
+ if (!blocks) {
+ return FAILURE;
+ }
+
+ blocks_count = -1;
+
+ if (extra_entry_block) {
+ initialize_block(&blocks[0]);
+ blocks[0].start = 0;
+ blocks[0].len = 0;
+ blocks_count++;
+ }
+
+ for (i = 0; i < op_array->last; i++) {
+ if (block_map[i]) {
+ if (blocks_count >= 0) {
+ blocks[blocks_count].len = i - blocks[blocks_count].start;
+ }
+ blocks_count++;
+ initialize_block(&blocks[blocks_count]);
+ blocks[blocks_count].start = i;
+ }
+ block_map[i] = blocks_count;
+ }
+
+ blocks[blocks_count].len = i - blocks[blocks_count].start;
+ blocks_count++;
+
+ /* Build CFG, Step 3: Calculate successors */
+ for (j = 0; j < blocks_count; j++) {
+ zend_op *opline;
+ if (blocks[j].len == 0) {
+ record_successor(blocks, j, 0, j + 1);
+ continue;
+ }
+
+ opline = op_array->opcodes + blocks[j].start + blocks[j].len - 1;
+ switch (opline->opcode) {
+ case ZEND_FAST_RET:
+ case ZEND_RETURN:
+ case ZEND_RETURN_BY_REF:
+ case ZEND_GENERATOR_RETURN:
+ case ZEND_EXIT:
+ case ZEND_THROW:
+ break;
+ case ZEND_JMP:
+ record_successor(blocks, j, 0, block_map[OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes]);
+ break;
+ case ZEND_JMPZNZ:
+ record_successor(blocks, j, 0, block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes]);
+ record_successor(blocks, j, 1, block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]);
+ break;
+ case ZEND_JMPZ:
+ case ZEND_JMPNZ:
+ case ZEND_JMPZ_EX:
+ case ZEND_JMPNZ_EX:
+ case ZEND_JMP_SET:
+ case ZEND_COALESCE:
+ case ZEND_ASSERT_CHECK:
+ record_successor(blocks, j, 0, block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes]);
+ record_successor(blocks, j, 1, j + 1);
+ break;
+ case ZEND_CATCH:
+ if (!opline->result.num) {
+ record_successor(blocks, j, 0, block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]);
+ record_successor(blocks, j, 1, j + 1);
+ } else {
+ record_successor(blocks, j, 0, j + 1);
+ }
+ break;
+ case ZEND_DECLARE_ANON_CLASS:
+ case ZEND_DECLARE_ANON_INHERITED_CLASS:
+ case ZEND_FE_FETCH_R:
+ case ZEND_FE_FETCH_RW:
+ record_successor(blocks, j, 0, block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]);
+ record_successor(blocks, j, 1, j + 1);
+ break;
+ case ZEND_FE_RESET_R:
+ case ZEND_FE_RESET_RW:
+ record_successor(blocks, j, 0, block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes]);
+ record_successor(blocks, j, 1, j + 1);
+ break;
+ case ZEND_FAST_CALL:
+ record_successor(blocks, j, 0, block_map[OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes]);
+ record_successor(blocks, j, 1, j + 1);
+ break;
+ default:
+ record_successor(blocks, j, 0, j + 1);
+ break;
+ }
+ }
+
+ /* Build CFG, Step 4, Mark Reachable Basic Blocks */
+ zend_mark_reachable_blocks(op_array, cfg, 0);
+
+ if (func_flags) {
+ *func_flags |= flags;
+ }
+
+ return SUCCESS;
+}
+/* }}} */
+
+int zend_cfg_build_predecessors(zend_arena **arena, zend_cfg *cfg) /* {{{ */
+{
+ int j, edges;
+ zend_basic_block *b;
+ zend_basic_block *blocks = cfg->blocks;
+ zend_basic_block *end = blocks + cfg->blocks_count;
+ int *predecessors;
+
+ edges = 0;
+ for (b = blocks; b < end; b++) {
+ b->predecessors_count = 0;
+ }
+ for (b = blocks; b < end; b++) {
+ if (!(b->flags & ZEND_BB_REACHABLE)) {
+ b->successors[0] = -1;
+ b->successors[1] = -1;
+ b->predecessors_count = 0;
+ } else {
+ if (b->successors[0] >= 0) {
+ edges++;
+ blocks[b->successors[0]].predecessors_count++;
+ if (b->successors[1] >= 0 && b->successors[1] != b->successors[0]) {
+ edges++;
+ blocks[b->successors[1]].predecessors_count++;
+ }
+ }
+ }
+ }
+
+ cfg->predecessors = predecessors = (int*)zend_arena_calloc(arena, sizeof(int), edges);
+
+ if (!predecessors) {
+ return FAILURE;
+ }
+
+ edges = 0;
+ for (b = blocks; b < end; b++) {
+ if (b->flags & ZEND_BB_REACHABLE) {
+ b->predecessor_offset = edges;
+ edges += b->predecessors_count;
+ b->predecessors_count = 0;
+ }
+ }
+
+ for (j = 0; j < cfg->blocks_count; j++) {
+ if (blocks[j].flags & ZEND_BB_REACHABLE) {
+ if (blocks[j].successors[0] >= 0) {
+ zend_basic_block *b = blocks + blocks[j].successors[0];
+ predecessors[b->predecessor_offset + b->predecessors_count] = j;
+ b->predecessors_count++;
+ if (blocks[j].successors[1] >= 0
+ && blocks[j].successors[1] != blocks[j].successors[0]) {
+ zend_basic_block *b = blocks + blocks[j].successors[1];
+ predecessors[b->predecessor_offset + b->predecessors_count] = j;
+ b->predecessors_count++;
+ }
+ }
+ }
+ }
+
+ return SUCCESS;
+}
+/* }}} */
+
+/* Computes a postorder numbering of the CFG */
+static void compute_postnum_recursive(
+ int *postnum, int *cur, const zend_cfg *cfg, int block_num) /* {{{ */
+{
+ zend_basic_block *block = &cfg->blocks[block_num];
+ if (postnum[block_num] != -1) {
+ return;
+ }
+
+ postnum[block_num] = -2; /* Marker for "currently visiting" */
+ if (block->successors[0] >= 0) {
+ compute_postnum_recursive(postnum, cur, cfg, block->successors[0]);
+ if (block->successors[1] >= 0) {
+ compute_postnum_recursive(postnum, cur, cfg, block->successors[1]);
+ }
+ }
+ postnum[block_num] = (*cur)++;
+}
+/* }}} */
+
+/* Computes dominator tree using algorithm from "A Simple, Fast Dominance Algorithm" by
+ * Cooper, Harvey and Kennedy. */
+int zend_cfg_compute_dominators_tree(const zend_op_array *op_array, zend_cfg *cfg) /* {{{ */
+{
+ zend_basic_block *blocks = cfg->blocks;
+ int blocks_count = cfg->blocks_count;
+ int j, k, changed;
+
+ ALLOCA_FLAG(use_heap)
+ int *postnum = do_alloca(sizeof(int) * cfg->blocks_count, use_heap);
+ memset(postnum, -1, sizeof(int) * cfg->blocks_count);
+ j = 0;
+ compute_postnum_recursive(postnum, &j, cfg, 0);
+
+ /* FIXME: move declarations */
+ blocks[0].idom = 0;
+ do {
+ changed = 0;
+ /* Iterating in RPO here would converge faster */
+ for (j = 1; j < blocks_count; j++) {
+ int idom = -1;
+
+ if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) {
+ continue;
+ }
+ for (k = 0; k < blocks[j].predecessors_count; k++) {
+ int pred = cfg->predecessors[blocks[j].predecessor_offset + k];
+
+ if (idom < 0) {
+ if (blocks[pred].idom >= 0)
+ idom = pred;
+ continue;
+ }
+
+ if (blocks[pred].idom >= 0) {
+ while (idom != pred) {
+ while (postnum[pred] < postnum[idom]) pred = blocks[pred].idom;
+ while (postnum[idom] < postnum[pred]) idom = blocks[idom].idom;
+ }
+ }
+ }
+
+ if (idom >= 0 && blocks[j].idom != idom) {
+ blocks[j].idom = idom;
+ changed = 1;
+ }
+ }
+ } while (changed);
+ blocks[0].idom = -1;
+
+ for (j = 1; j < blocks_count; j++) {
+ if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) {
+ continue;
+ }
+ if (blocks[j].idom >= 0) {
+ /* Sort by block number to traverse children in pre-order */
+ if (blocks[blocks[j].idom].children < 0 ||
+ j < blocks[blocks[j].idom].children) {
+ blocks[j].next_child = blocks[blocks[j].idom].children;
+ blocks[blocks[j].idom].children = j;
+ } else {
+ int k = blocks[blocks[j].idom].children;
+ while (blocks[k].next_child >=0 && j > blocks[k].next_child) {
+ k = blocks[k].next_child;
+ }
+ blocks[j].next_child = blocks[k].next_child;
+ blocks[k].next_child = j;
+ }
+ }
+ }
+
+ for (j = 0; j < blocks_count; j++) {
+ int idom = blocks[j].idom, level = 0;
+ if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) {
+ continue;
+ }
+ while (idom >= 0) {
+ level++;
+ if (blocks[idom].level >= 0) {
+ level += blocks[idom].level;
+ break;
+ } else {
+ idom = blocks[idom].idom;
+ }
+ }
+ blocks[j].level = level;
+ }
+
+ free_alloca(postnum, use_heap);
+ return SUCCESS;
+}
+/* }}} */
+
+static int dominates(zend_basic_block *blocks, int a, int b) /* {{{ */
+{
+ while (blocks[b].level > blocks[a].level) {
+ b = blocks[b].idom;
+ }
+ return a == b;
+}
+/* }}} */
+
+typedef struct {
+ int id;
+ int level;
+} block_info;
+static int compare_block_level(const block_info *a, const block_info *b) {
+ return b->level - a->level;
+}
+static void swap_blocks(block_info *a, block_info *b) {
+ block_info tmp = *a;
+ *a = *b;
+ *b = tmp;
+}
+
+int zend_cfg_identify_loops(const zend_op_array *op_array, zend_cfg *cfg, uint32_t *flags) /* {{{ */
+{
+ int i, j, k, n;
+ int time;
+ zend_basic_block *blocks = cfg->blocks;
+ int *entry_times, *exit_times;
+ zend_worklist work;
+ int flag = ZEND_FUNC_NO_LOOPS;
+ block_info *sorted_blocks;
+ ALLOCA_FLAG(list_use_heap)
+ ALLOCA_FLAG(tree_use_heap)
+ ALLOCA_FLAG(sorted_blocks_use_heap)
+
+ ZEND_WORKLIST_ALLOCA(&work, cfg->blocks_count, list_use_heap);
+
+ /* We don't materialize the DJ spanning tree explicitly, as we are only interested in ancestor
+ * queries. These are implemented by checking entry/exit times of the DFS search. */
+ entry_times = do_alloca(2 * sizeof(int) * cfg->blocks_count, tree_use_heap);
+ exit_times = entry_times + cfg->blocks_count;
+ memset(entry_times, -1, 2 * sizeof(int) * cfg->blocks_count);
+
+ zend_worklist_push(&work, 0);
+ time = 0;
+ while (zend_worklist_len(&work)) {
+ next:
+ i = zend_worklist_peek(&work);
+ if (entry_times[i] == -1) {
+ entry_times[i] = time++;
+ }
+ /* Visit blocks immediately dominated by i. */
+ for (j = blocks[i].children; j >= 0; j = blocks[j].next_child) {
+ if (zend_worklist_push(&work, j)) {
+ goto next;
+ }
+ }
+ /* Visit join edges. */
+ for (j = 0; j < 2; j++) {
+ int succ = blocks[i].successors[j];
+ if (succ < 0) {
+ continue;
+ } else if (blocks[succ].idom == i) {
+ continue;
+ } else if (zend_worklist_push(&work, succ)) {
+ goto next;
+ }
+ }
+ exit_times[i] = time++;
+ zend_worklist_pop(&work);
+ }
+
+ /* Sort blocks by decreasing level, which is the order in which we want to process them */
+ sorted_blocks = do_alloca(sizeof(block_info) * cfg->blocks_count, sorted_blocks_use_heap);
+ for (i = 0; i < cfg->blocks_count; i++) {
+ sorted_blocks[i].id = i;
+ sorted_blocks[i].level = blocks[i].level;
+ }
+ zend_sort(sorted_blocks, cfg->blocks_count, sizeof(block_info),
+ (compare_func_t) compare_block_level, (swap_func_t) swap_blocks);
+
+ /* Identify loops. See Sreedhar et al, "Identifying Loops Using DJ
+ Graphs". */
+
+ for (n = 0; n < cfg->blocks_count; n++) {
+ i = sorted_blocks[n].id;
+
+ zend_bitset_clear(work.visited, zend_bitset_len(cfg->blocks_count));
+ for (j = 0; j < blocks[i].predecessors_count; j++) {
+ int pred = cfg->predecessors[blocks[i].predecessor_offset + j];
+
+ /* A join edge is one for which the predecessor does not
+ immediately dominate the successor. */
+ if (blocks[i].idom == pred) {
+ continue;
+ }
+
+ /* In a loop back-edge (back-join edge), the successor dominates
+ the predecessor. */
+ if (dominates(blocks, i, pred)) {
+ blocks[i].flags |= ZEND_BB_LOOP_HEADER;
+ flag &= ~ZEND_FUNC_NO_LOOPS;
+ zend_worklist_push(&work, pred);
+ } else {
+ /* Otherwise it's a cross-join edge. See if it's a branch
+ to an ancestor on the DJ spanning tree. */
+ if (entry_times[pred] > entry_times[i] && exit_times[pred] < exit_times[i]) {
+ blocks[i].flags |= ZEND_BB_IRREDUCIBLE_LOOP;
+ flag |= ZEND_FUNC_IRREDUCIBLE;
+ flag &= ~ZEND_FUNC_NO_LOOPS;
+ }
+ }
+ }
+ while (zend_worklist_len(&work)) {
+ j = zend_worklist_pop(&work);
+ while (blocks[j].loop_header >= 0) {
+ j = blocks[j].loop_header;
+ }
+ if (j != i) {
+ blocks[j].loop_header = i;
+ for (k = 0; k < blocks[j].predecessors_count; k++) {
+ zend_worklist_push(&work, cfg->predecessors[blocks[j].predecessor_offset + k]);
+ }
+ }
+ }
+ }
+
+ free_alloca(sorted_blocks, sorted_blocks_use_heap);
+ free_alloca(entry_times, tree_use_heap);
+ ZEND_WORKLIST_FREE_ALLOCA(&work, list_use_heap);
+ *flags |= flag;
+
+ return SUCCESS;
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * indent-tabs-mode: t
+ * End:
+ */
diff --git a/ext/opcache/Optimizer/zend_cfg.h b/ext/opcache/Optimizer/zend_cfg.h
new file mode 100644
index 0000000000..7b80d83f11
--- /dev/null
+++ b/ext/opcache/Optimizer/zend_cfg.h
@@ -0,0 +1,137 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine, CFG - Control Flow Graph |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1998-2017 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Dmitry Stogov <dmitry@zend.com> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef ZEND_CFG_H
+#define ZEND_CFG_H
+
+/* zend_basic_bloc.flags */
+#define ZEND_BB_START (1<<0) /* fist block */
+#define ZEND_BB_FOLLOW (1<<1) /* follows the next block */
+#define ZEND_BB_TARGET (1<<2) /* jump taget */
+#define ZEND_BB_EXIT (1<<3) /* without successors */
+#define ZEND_BB_ENTRY (1<<4) /* stackless entry */
+#define ZEND_BB_TRY (1<<5) /* start of try block */
+#define ZEND_BB_CATCH (1<<6) /* start of catch block */
+#define ZEND_BB_FINALLY (1<<7) /* start of finally block */
+#define ZEND_BB_FINALLY_END (1<<8) /* end of finally block */
+#define ZEND_BB_GEN_VAR (1<<9) /* start of live range */
+#define ZEND_BB_KILL_VAR (1<<10) /* end of live range */
+#define ZEND_BB_UNREACHABLE_FREE (1<<11) /* unreachable loop free */
+#define ZEND_BB_RECV_ENTRY (1<<12) /* RECV entry */
+
+#define ZEND_BB_LOOP_HEADER (1<<16)
+#define ZEND_BB_IRREDUCIBLE_LOOP (1<<17)
+
+#define ZEND_BB_REACHABLE (1<<31)
+
+#define ZEND_BB_PROTECTED (ZEND_BB_ENTRY|ZEND_BB_RECV_ENTRY|ZEND_BB_TRY|ZEND_BB_CATCH|ZEND_BB_FINALLY|ZEND_BB_FINALLY_END|ZEND_BB_GEN_VAR|ZEND_BB_KILL_VAR)
+
+typedef struct _zend_basic_block {
+ uint32_t flags;
+ uint32_t start; /* first opcode number */
+ uint32_t len; /* number of opcodes */
+ int successors[2]; /* up to 2 successor blocks */
+ int predecessors_count; /* number of predecessors */
+ int predecessor_offset; /* offset of 1-st predecessor */
+ int idom; /* immediate dominator block */
+ int loop_header; /* closest loop header, or -1 */
+ int level; /* steps away from the entry in the dom. tree */
+ int children; /* list of dominated blocks */
+ int next_child; /* next dominated block */
+} zend_basic_block;
+
+/*
++------------+---+---+---+---+---+
+| |OP1|OP2|EXT| 0 | 1 |
++------------+---+---+---+---+---+
+|JMP |ADR| | |OP1| - |
+|JMPZ | |ADR| |OP2|FOL|
+|JMPNZ | |ADR| |OP2|FOL|
+|JMPZNZ | |ADR|ADR|OP2|EXT|
+|JMPZ_EX | |ADR| |OP2|FOL|
+|JMPNZ_EX | |ADR| |OP2|FOL|
+|JMP_SET | |ADR| |OP2|FOL|
+|COALESCE | |ADR| |OP2|FOL|
+|ASSERT_CHK | |ADR| |OP2|FOL|
+|NEW | |ADR| |OP2|FOL|
+|DCL_ANON* |ADR| | |OP1|FOL|
+|FE_RESET_* | |ADR| |OP2|FOL|
+|FE_FETCH_* | | |ADR|EXT|FOL|
+|CATCH | | |ADR|EXT|FOL|
+|FAST_CALL |ADR| | |OP1|FOL|
+|FAST_RET | | | | - | - |
+|RETURN* | | | | - | - |
+|EXIT | | | | - | - |
+|THROW | | | | - | - |
+|* | | | |FOL| - |
++------------+---+---+---+---+---+
+*/
+
+typedef struct _zend_cfg {
+ int blocks_count; /* number of basic blocks */
+ zend_basic_block *blocks; /* array of basic blocks */
+ int *predecessors;
+ uint32_t *map;
+ unsigned int split_at_live_ranges : 1;
+ unsigned int split_at_calls : 1;
+ unsigned int split_at_recv : 1;
+} zend_cfg;
+
+/* Build Flags */
+#define ZEND_RT_CONSTANTS (1<<31)
+#define ZEND_CFG_STACKLESS (1<<30)
+#define ZEND_SSA_DEBUG_LIVENESS (1<<29)
+#define ZEND_SSA_DEBUG_PHI_PLACEMENT (1<<28)
+#define ZEND_SSA_RC_INFERENCE (1<<27)
+#define ZEND_CFG_SPLIT_AT_LIVE_RANGES (1<<26)
+#define ZEND_CFG_NO_ENTRY_PREDECESSORS (1<<25)
+#define ZEND_CFG_RECV_ENTRY (1<<24)
+#define ZEND_CALL_TREE (1<<23)
+
+#define CRT_CONSTANT_EX(op_array, node, rt_constants) \
+ ((rt_constants) ? \
+ RT_CONSTANT(op_array, (node)) \
+ : \
+ CT_CONSTANT_EX(op_array, (node).constant) \
+ )
+
+#define CRT_CONSTANT(node) \
+ CRT_CONSTANT_EX(op_array, node, (build_flags & ZEND_RT_CONSTANTS))
+
+#define RETURN_VALUE_USED(opline) \
+ ((opline)->result_type != IS_UNUSED)
+
+BEGIN_EXTERN_C()
+
+int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t build_flags, zend_cfg *cfg, uint32_t *func_flags);
+void zend_cfg_remark_reachable_blocks(const zend_op_array *op_array, zend_cfg *cfg);
+int zend_cfg_build_predecessors(zend_arena **arena, zend_cfg *cfg);
+int zend_cfg_compute_dominators_tree(const zend_op_array *op_array, zend_cfg *cfg);
+int zend_cfg_identify_loops(const zend_op_array *op_array, zend_cfg *cfg, uint32_t *flags);
+
+END_EXTERN_C()
+
+#endif /* ZEND_CFG_H */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * indent-tabs-mode: t
+ * End:
+ */
diff --git a/ext/opcache/Optimizer/zend_dfg.c b/ext/opcache/Optimizer/zend_dfg.c
new file mode 100644
index 0000000000..f39c6368cd
--- /dev/null
+++ b/ext/opcache/Optimizer/zend_dfg.c
@@ -0,0 +1,254 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine, DFG - Data Flow Graph |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1998-2017 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Dmitry Stogov <dmitry@zend.com> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "php.h"
+#include "zend_compile.h"
+#include "zend_dfg.h"
+
+int zend_build_dfg(const zend_op_array *op_array, const zend_cfg *cfg, zend_dfg *dfg, uint32_t build_flags) /* {{{ */
+{
+ int set_size;
+ zend_basic_block *blocks = cfg->blocks;
+ int blocks_count = cfg->blocks_count;
+ zend_bitset tmp, def, use, in, out;
+ int k;
+ uint32_t var_num;
+ int j;
+
+ set_size = dfg->size;
+ tmp = dfg->tmp;
+ def = dfg->def;
+ use = dfg->use;
+ in = dfg->in;
+ out = dfg->out;
+
+ /* Collect "def" and "use" sets */
+ for (j = 0; j < blocks_count; j++) {
+ zend_op *opline, *end;
+ if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) {
+ continue;
+ }
+
+ opline = op_array->opcodes + blocks[j].start;
+ end = opline + blocks[j].len;
+ for (; opline < end; opline++) {
+ if (opline->opcode != ZEND_OP_DATA) {
+ zend_op *next = opline + 1;
+ if (next < end && next->opcode == ZEND_OP_DATA) {
+ if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
+ var_num = EX_VAR_TO_NUM(next->op1.var);
+ if (!DFG_ISSET(def, set_size, j, var_num)) {
+ DFG_SET(use, set_size, j, var_num);
+ }
+ }
+ if (next->op2_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
+ var_num = EX_VAR_TO_NUM(next->op2.var);
+ if (!DFG_ISSET(def, set_size, j, var_num)) {
+ DFG_SET(use, set_size, j, var_num);
+ }
+ }
+ }
+ if (opline->op1_type == IS_CV) {
+ var_num = EX_VAR_TO_NUM(opline->op1.var);
+ switch (opline->opcode) {
+ case ZEND_ADD_ARRAY_ELEMENT:
+ case ZEND_INIT_ARRAY:
+ if ((build_flags & ZEND_SSA_RC_INFERENCE)
+ || (opline->extended_value & ZEND_ARRAY_ELEMENT_REF)) {
+ goto op1_def;
+ }
+ goto op1_use;
+ case ZEND_FE_RESET_R:
+ case ZEND_SEND_VAR:
+ case ZEND_CAST:
+ case ZEND_QM_ASSIGN:
+ case ZEND_JMP_SET:
+ case ZEND_COALESCE:
+ if (build_flags & ZEND_SSA_RC_INFERENCE) {
+ goto op1_def;
+ }
+ goto op1_use;
+ case ZEND_YIELD:
+ if ((build_flags & ZEND_SSA_RC_INFERENCE)
+ || (op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE)) {
+ goto op1_def;
+ }
+ goto op1_use;
+ case ZEND_UNSET_VAR:
+ ZEND_ASSERT(opline->extended_value & ZEND_QUICK_SET);
+ /* break missing intentionally */
+ case ZEND_ASSIGN:
+ case ZEND_ASSIGN_REF:
+ case ZEND_BIND_GLOBAL:
+ case ZEND_BIND_STATIC:
+ case ZEND_SEND_VAR_EX:
+ case ZEND_SEND_REF:
+ case ZEND_SEND_VAR_NO_REF:
+ case ZEND_SEND_VAR_NO_REF_EX:
+ case ZEND_FE_RESET_RW:
+ case ZEND_ASSIGN_ADD:
+ case ZEND_ASSIGN_SUB:
+ case ZEND_ASSIGN_MUL:
+ case ZEND_ASSIGN_DIV:
+ case ZEND_ASSIGN_MOD:
+ case ZEND_ASSIGN_SL:
+ case ZEND_ASSIGN_SR:
+ case ZEND_ASSIGN_CONCAT:
+ case ZEND_ASSIGN_BW_OR:
+ case ZEND_ASSIGN_BW_AND:
+ case ZEND_ASSIGN_BW_XOR:
+ case ZEND_ASSIGN_POW:
+ case ZEND_PRE_INC:
+ case ZEND_PRE_DEC:
+ case ZEND_POST_INC:
+ case ZEND_POST_DEC:
+ case ZEND_ASSIGN_DIM:
+ case ZEND_ASSIGN_OBJ:
+ case ZEND_UNSET_DIM:
+ case ZEND_UNSET_OBJ:
+ case ZEND_FETCH_DIM_W:
+ case ZEND_FETCH_DIM_RW:
+ case ZEND_FETCH_DIM_FUNC_ARG:
+ case ZEND_FETCH_DIM_UNSET:
+ case ZEND_FETCH_OBJ_W:
+ case ZEND_FETCH_OBJ_RW:
+ case ZEND_FETCH_OBJ_FUNC_ARG:
+ case ZEND_FETCH_OBJ_UNSET:
+ case ZEND_VERIFY_RETURN_TYPE:
+op1_def:
+ /* `def` always come along with dtor or separation,
+ * thus the origin var info might be also `use`d in the feature(CG) */
+ DFG_SET(use, set_size, j, var_num);
+ DFG_SET(def, set_size, j, var_num);
+ break;
+ default:
+op1_use:
+ if (!DFG_ISSET(def, set_size, j, var_num)) {
+ DFG_SET(use, set_size, j, var_num);
+ }
+ }
+ } else if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) {
+ var_num = EX_VAR_TO_NUM(opline->op1.var);
+ if (!DFG_ISSET(def, set_size, j, var_num)) {
+ DFG_SET(use, set_size, j, var_num);
+ }
+ if (opline->opcode == ZEND_VERIFY_RETURN_TYPE) {
+ DFG_SET(def, set_size, j, var_num);
+ }
+ }
+ if (opline->op2_type == IS_CV) {
+ var_num = EX_VAR_TO_NUM(opline->op2.var);
+ switch (opline->opcode) {
+ case ZEND_ASSIGN:
+ if (build_flags & ZEND_SSA_RC_INFERENCE) {
+ goto op2_def;
+ }
+ goto op2_use;
+ case ZEND_BIND_LEXICAL:
+ if ((build_flags & ZEND_SSA_RC_INFERENCE) || opline->extended_value) {
+ goto op2_def;
+ }
+ goto op2_use;
+ case ZEND_ASSIGN_REF:
+ case ZEND_FE_FETCH_R:
+ case ZEND_FE_FETCH_RW:
+op2_def:
+ // FIXME: include into "use" too ...?
+ DFG_SET(use, set_size, j, var_num);
+ DFG_SET(def, set_size, j, var_num);
+ break;
+ default:
+op2_use:
+ if (!DFG_ISSET(def, set_size, j, var_num)) {
+ DFG_SET(use, set_size, j, var_num);
+ }
+ break;
+ }
+ } else if (opline->op2_type & (IS_VAR|IS_TMP_VAR)) {
+ var_num = EX_VAR_TO_NUM(opline->op2.var);
+ if (opline->opcode == ZEND_FE_FETCH_R || opline->opcode == ZEND_FE_FETCH_RW) {
+ DFG_SET(def, set_size, j, var_num);
+ } else {
+ if (!DFG_ISSET(def, set_size, j, var_num)) {
+ DFG_SET(use, set_size, j, var_num);
+ }
+ }
+ }
+ if (opline->result_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
+ var_num = EX_VAR_TO_NUM(opline->result.var);
+ DFG_SET(def, set_size, j, var_num);
+ }
+ }
+ }
+ }
+
+ /* Calculate "in" and "out" sets */
+ {
+ uint32_t worklist_len = zend_bitset_len(blocks_count);
+ zend_bitset worklist;
+ ALLOCA_FLAG(use_heap);
+ worklist = ZEND_BITSET_ALLOCA(worklist_len, use_heap);
+ memset(worklist, 0, worklist_len * ZEND_BITSET_ELM_SIZE);
+ for (j = 0; j < blocks_count; j++) {
+ zend_bitset_incl(worklist, j);
+ }
+ while (!zend_bitset_empty(worklist, worklist_len)) {
+ /* We use the last block on the worklist, because predecessors tend to be located
+ * before the succeeding block, so this converges faster. */
+ j = zend_bitset_last(worklist, worklist_len);
+ zend_bitset_excl(worklist, j);
+
+ if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) {
+ continue;
+ }
+ if (blocks[j].successors[0] >= 0) {
+ zend_bitset_copy(DFG_BITSET(out, set_size, j), DFG_BITSET(in, set_size, blocks[j].successors[0]), set_size);
+ if (blocks[j].successors[1] >= 0) {
+ zend_bitset_union(DFG_BITSET(out, set_size, j), DFG_BITSET(in, set_size, blocks[j].successors[1]), set_size);
+ }
+ } else {
+ zend_bitset_clear(DFG_BITSET(out, set_size, j), set_size);
+ }
+ zend_bitset_union_with_difference(tmp, DFG_BITSET(use, set_size, j), DFG_BITSET(out, set_size, j), DFG_BITSET(def, set_size, j), set_size);
+ if (!zend_bitset_equal(DFG_BITSET(in, set_size, j), tmp, set_size)) {
+ zend_bitset_copy(DFG_BITSET(in, set_size, j), tmp, set_size);
+
+ /* Add predecessors of changed block to worklist */
+ {
+ int *predecessors = &cfg->predecessors[blocks[j].predecessor_offset];
+ for (k = 0; k < blocks[j].predecessors_count; k++) {
+ zend_bitset_incl(worklist, predecessors[k]);
+ }
+ }
+ }
+ }
+
+ free_alloca(worklist, use_heap);
+ }
+
+ return SUCCESS;
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * indent-tabs-mode: t
+ * End:
+ */
diff --git a/ext/opcache/Optimizer/zend_dfg.h b/ext/opcache/Optimizer/zend_dfg.h
new file mode 100644
index 0000000000..06ee46be32
--- /dev/null
+++ b/ext/opcache/Optimizer/zend_dfg.h
@@ -0,0 +1,58 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine, DFG - Data Flow Graph |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1998-2017 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Dmitry Stogov <dmitry@zend.com> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef ZEND_DFG_H
+#define ZEND_DFG_H
+
+#include "zend_bitset.h"
+#include "zend_cfg.h"
+
+typedef struct _zend_dfg {
+ int vars;
+ uint32_t size;
+ zend_bitset tmp;
+ zend_bitset def;
+ zend_bitset use;
+ zend_bitset in;
+ zend_bitset out;
+} zend_dfg;
+
+#define DFG_BITSET(set, set_size, block_num) \
+ ((set) + ((block_num) * (set_size)))
+
+#define DFG_SET(set, set_size, block_num, var_num) \
+ zend_bitset_incl(DFG_BITSET(set, set_size, block_num), (var_num))
+
+#define DFG_ISSET(set, set_size, block_num, var_num) \
+ zend_bitset_in(DFG_BITSET(set, set_size, block_num), (var_num))
+
+BEGIN_EXTERN_C()
+
+int zend_build_dfg(const zend_op_array *op_array, const zend_cfg *cfg, zend_dfg *dfg, uint32_t build_flags);
+
+END_EXTERN_C()
+
+#endif /* ZEND_DFG_H */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * indent-tabs-mode: t
+ * End:
+ */
diff --git a/ext/opcache/Optimizer/zend_dump.c b/ext/opcache/Optimizer/zend_dump.c
new file mode 100644
index 0000000000..2167fa6e6b
--- /dev/null
+++ b/ext/opcache/Optimizer/zend_dump.c
@@ -0,0 +1,1177 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine, Bytecode Visualisation |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1998-2017 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Dmitry Stogov <dmitry@zend.com> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "php.h"
+#include "zend_compile.h"
+#include "zend_cfg.h"
+#include "zend_ssa.h"
+#include "zend_inference.h"
+#include "zend_func_info.h"
+#include "zend_call_graph.h"
+#include "zend_dump.h"
+
+static void zend_dump_const(const zval *zv)
+{
+ switch (Z_TYPE_P(zv)) {
+ case IS_NULL:
+ fprintf(stderr, " null");
+ break;
+ case IS_FALSE:
+ fprintf(stderr, " bool(false)");
+ break;
+ case IS_TRUE:
+ fprintf(stderr, " bool(true)");
+ break;
+ case IS_LONG:
+ fprintf(stderr, " int(" ZEND_LONG_FMT ")", Z_LVAL_P(zv));
+ break;
+ case IS_DOUBLE:
+ fprintf(stderr, " float(%g)", Z_DVAL_P(zv));
+ break;
+ case IS_STRING:
+ fprintf(stderr, " string(\"%s\")", Z_STRVAL_P(zv));
+ break;
+ case IS_ARRAY:
+ fprintf(stderr, " array(...)");
+ break;
+ default:
+ fprintf(stderr, " zval(type=%d)", Z_TYPE_P(zv));
+ break;
+ }
+}
+
+static void zend_dump_class_fetch_type(uint32_t fetch_type)
+{
+ switch (fetch_type & ZEND_FETCH_CLASS_MASK) {
+ case ZEND_FETCH_CLASS_SELF:
+ fprintf(stderr, " (self)");
+ break;
+ case ZEND_FETCH_CLASS_PARENT:
+ fprintf(stderr, " (parent)");
+ break;
+ case ZEND_FETCH_CLASS_STATIC:
+ fprintf(stderr, " (static)");
+ break;
+ case ZEND_FETCH_CLASS_AUTO:
+ fprintf(stderr, " (auto)");
+ break;
+ case ZEND_FETCH_CLASS_INTERFACE:
+ fprintf(stderr, " (interface)");
+ break;
+ case ZEND_FETCH_CLASS_TRAIT:
+ fprintf(stderr, " (trait)");
+ break;
+ }
+ if (fetch_type & ZEND_FETCH_CLASS_NO_AUTOLOAD) {
+ fprintf(stderr, " (no-autolod)");
+ }
+ if (fetch_type & ZEND_FETCH_CLASS_SILENT) {
+ fprintf(stderr, " (silent)");
+ }
+ if (fetch_type & ZEND_FETCH_CLASS_EXCEPTION) {
+ fprintf(stderr, " (exception)");
+ }
+}
+
+static void zend_dump_unused_op(const zend_op *opline, znode_op op, uint32_t flags) {
+ if (ZEND_VM_OP_NUM == (flags & ZEND_VM_OP_MASK)) {
+ fprintf(stderr, " %u", op.num);
+ } else if (ZEND_VM_OP_TRY_CATCH == (flags & ZEND_VM_OP_MASK)) {
+ if (op.num != (uint32_t)-1) {
+ fprintf(stderr, " try-catch(%u)", op.num);
+ }
+ } else if (ZEND_VM_OP_LIVE_RANGE == (flags & ZEND_VM_OP_MASK)) {
+ if (opline->extended_value & ZEND_FREE_ON_RETURN) {
+ fprintf(stderr, " live-range(%u)", op.num);
+ }
+ } else if (ZEND_VM_OP_THIS == (flags & ZEND_VM_OP_MASK)) {
+ fprintf(stderr, " THIS");
+ } else if (ZEND_VM_OP_NEXT == (flags & ZEND_VM_OP_MASK)) {
+ fprintf(stderr, " NEXT");
+ } else if (ZEND_VM_OP_CLASS_FETCH == (flags & ZEND_VM_OP_MASK)) {
+ zend_dump_class_fetch_type(op.num);
+ } else if (ZEND_VM_OP_CONSTRUCTOR == (flags & ZEND_VM_OP_MASK)) {
+ fprintf(stderr, " CONSTRUCTOR");
+ }
+}
+
+void zend_dump_var(const zend_op_array *op_array, zend_uchar var_type, int var_num)
+{
+ if (var_type == IS_CV && var_num < op_array->last_var) {
+ fprintf(stderr, "CV%d($%s)", var_num, op_array->vars[var_num]->val);
+ } else if (var_type == IS_VAR) {
+ fprintf(stderr, "V%d", var_num);
+ } else if (var_type == IS_TMP_VAR) {
+ fprintf(stderr, "T%d", var_num);
+ } else {
+ fprintf(stderr, "X%d", var_num);
+ }
+}
+
+static void zend_dump_range(const zend_ssa_range *r)
+{
+ if (r->underflow && r->overflow) {
+ return;
+ }
+ fprintf(stderr, " RANGE[");
+ if (r->underflow) {
+ fprintf(stderr, "--..");
+ } else {
+ fprintf(stderr, ZEND_LONG_FMT "..", r->min);
+ }
+ if (r->overflow) {
+ fprintf(stderr, "++]");
+ } else {
+ fprintf(stderr, ZEND_LONG_FMT "]", r->max);
+ }
+}
+
+static void zend_dump_type_info(uint32_t info, zend_class_entry *ce, int is_instanceof, uint32_t dump_flags)
+{
+ int first = 1;
+
+ fprintf(stderr, " [");
+ if (info & MAY_BE_UNDEF) {
+ if (first) first = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "undef");
+ }
+ if (info & MAY_BE_REF) {
+ if (first) first = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "ref");
+ }
+ if (dump_flags & ZEND_DUMP_RC_INFERENCE) {
+ if (info & MAY_BE_RC1) {
+ if (first) first = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "rc1");
+ }
+ if (info & MAY_BE_RCN) {
+ if (first) first = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "rcn");
+ }
+ }
+ if (info & MAY_BE_CLASS) {
+ if (first) first = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "class");
+ if (ce) {
+ if (is_instanceof) {
+ fprintf(stderr, " (instanceof %s)", ce->name->val);
+ } else {
+ fprintf(stderr, " (%s)", ce->name->val);
+ }
+ }
+ } else if ((info & MAY_BE_ANY) == MAY_BE_ANY) {
+ if (first) first = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "any");
+ } else {
+ if (info & MAY_BE_NULL) {
+ if (first) first = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "null");
+ }
+ if ((info & MAY_BE_FALSE) && (info & MAY_BE_TRUE)) {
+ if (first) first = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "bool");
+ } else if (info & MAY_BE_FALSE) {
+ if (first) first = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "false");
+ } else if (info & MAY_BE_TRUE) {
+ if (first) first = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "true");
+ }
+ if (info & MAY_BE_LONG) {
+ if (first) first = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "long");
+ }
+ if (info & MAY_BE_DOUBLE) {
+ if (first) first = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "double");
+ }
+ if (info & MAY_BE_STRING) {
+ if (first) first = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "string");
+ }
+ if (info & MAY_BE_ARRAY) {
+ if (first) first = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "array");
+ if ((info & MAY_BE_ARRAY_KEY_ANY) != 0 &&
+ (info & MAY_BE_ARRAY_KEY_ANY) != MAY_BE_ARRAY_KEY_ANY) {
+ int afirst = 1;
+ fprintf(stderr, " [");
+ if (info & MAY_BE_ARRAY_KEY_LONG) {
+ if (afirst) afirst = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "long");
+ }
+ if (info & MAY_BE_ARRAY_KEY_STRING) {
+ if (afirst) afirst = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "string");
+ }
+ fprintf(stderr, "]");
+ }
+ if (info & (MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF)) {
+ int afirst = 1;
+ fprintf(stderr, " of [");
+ if ((info & MAY_BE_ARRAY_OF_ANY) == MAY_BE_ARRAY_OF_ANY) {
+ if (afirst) afirst = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "any");
+ } else {
+ if (info & MAY_BE_ARRAY_OF_NULL) {
+ if (afirst) afirst = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "null");
+ }
+ if (info & MAY_BE_ARRAY_OF_FALSE) {
+ if (afirst) afirst = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "false");
+ }
+ if (info & MAY_BE_ARRAY_OF_TRUE) {
+ if (afirst) afirst = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "true");
+ }
+ if (info & MAY_BE_ARRAY_OF_LONG) {
+ if (afirst) afirst = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "long");
+ }
+ if (info & MAY_BE_ARRAY_OF_DOUBLE) {
+ if (afirst) afirst = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "double");
+ }
+ if (info & MAY_BE_ARRAY_OF_STRING) {
+ if (afirst) afirst = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "string");
+ }
+ if (info & MAY_BE_ARRAY_OF_ARRAY) {
+ if (afirst) afirst = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "array");
+ }
+ if (info & MAY_BE_ARRAY_OF_OBJECT) {
+ if (afirst) afirst = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "object");
+ }
+ if (info & MAY_BE_ARRAY_OF_RESOURCE) {
+ if (afirst) afirst = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "resource");
+ }
+ }
+ if (info & MAY_BE_ARRAY_OF_REF) {
+ if (afirst) afirst = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "ref");
+ }
+ fprintf(stderr, "]");
+ }
+ }
+ if (info & MAY_BE_OBJECT) {
+ if (first) first = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "object");
+ if (ce) {
+ if (is_instanceof) {
+ fprintf(stderr, " (instanceof %s)", ce->name->val);
+ } else {
+ fprintf(stderr, " (%s)", ce->name->val);
+ }
+ }
+ }
+ if (info & MAY_BE_RESOURCE) {
+ if (first) first = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "resource");
+ }
+ }
+ if (info & MAY_BE_ERROR) {
+ if (first) first = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "error");
+ }
+//TODO: this is useful only for JIT???
+ if (info & MAY_BE_IN_REG) {
+ if (first) first = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "reg");
+ }
+ fprintf(stderr, "]");
+}
+
+static void zend_dump_ssa_var_info(const zend_ssa *ssa, int ssa_var_num, uint32_t dump_flags)
+{
+ zend_dump_type_info(
+ ssa->var_info[ssa_var_num].type,
+ ssa->var_info[ssa_var_num].ce,
+ ssa->var_info[ssa_var_num].ce ?
+ ssa->var_info[ssa_var_num].is_instanceof : 0,
+ dump_flags);
+}
+
+static void zend_dump_ssa_var(const zend_op_array *op_array, const zend_ssa *ssa, int ssa_var_num, zend_uchar var_type, int var_num, uint32_t dump_flags)
+{
+ if (ssa_var_num >= 0) {
+ fprintf(stderr, "#%d.", ssa_var_num);
+ } else {
+ fprintf(stderr, "#?.");
+ }
+ zend_dump_var(op_array, (var_num < op_array->last_var ? IS_CV : var_type), var_num);
+
+ if (ssa_var_num >= 0 && ssa->vars) {
+ if (ssa_var_num >= 0 && ssa->vars[ssa_var_num].no_val) {
+ fprintf(stderr, " NOVAL");
+ }
+ if (ssa->var_info) {
+ zend_dump_ssa_var_info(ssa, ssa_var_num, dump_flags);
+ if (ssa->var_info[ssa_var_num].has_range) {
+ zend_dump_range(&ssa->var_info[ssa_var_num].range);
+ }
+ }
+ }
+}
+
+static void zend_dump_type_constraint(const zend_op_array *op_array, const zend_ssa *ssa, const zend_ssa_type_constraint *constraint, uint32_t dump_flags)
+{
+ fprintf(stderr, " TYPE");
+ zend_dump_type_info(constraint->type_mask, constraint->ce, 1, dump_flags);
+}
+
+static void zend_dump_range_constraint(const zend_op_array *op_array, const zend_ssa *ssa, const zend_ssa_range_constraint *r, uint32_t dump_flags)
+{
+ if (r->range.underflow && r->range.overflow) {
+ return;
+ }
+ fprintf(stderr, " RANGE");
+ if (r->negative) {
+ fprintf(stderr, "~");
+ }
+ fprintf(stderr, "[");
+ if (r->range.underflow) {
+ fprintf(stderr, "-- .. ");
+ } else {
+ if (r->min_ssa_var >= 0) {
+ zend_dump_ssa_var(op_array, ssa, r->min_ssa_var, (r->min_var < op_array->last_var ? IS_CV : 0), r->min_var, dump_flags);
+ if (r->range.min > 0) {
+ fprintf(stderr, " + " ZEND_LONG_FMT, r->range.min);
+ } else if (r->range.min < 0) {
+ fprintf(stderr, " - " ZEND_LONG_FMT, -r->range.min);
+ }
+ fprintf(stderr, " .. ");
+ } else {
+ fprintf(stderr, ZEND_LONG_FMT " .. ", r->range.min);
+ }
+ }
+ if (r->range.overflow) {
+ fprintf(stderr, "++]");
+ } else {
+ if (r->max_ssa_var >= 0) {
+ zend_dump_ssa_var(op_array, ssa, r->max_ssa_var, (r->max_var < op_array->last_var ? IS_CV : 0), r->max_var, dump_flags);
+ if (r->range.max > 0) {
+ fprintf(stderr, " + " ZEND_LONG_FMT, r->range.max);
+ } else if (r->range.max < 0) {
+ fprintf(stderr, " - " ZEND_LONG_FMT, -r->range.max);
+ }
+ fprintf(stderr, "]");
+ } else {
+ fprintf(stderr, ZEND_LONG_FMT "]", r->range.max);
+ }
+ }
+}
+
+static void zend_dump_op(const zend_op_array *op_array, const zend_basic_block *b, const zend_op *opline, uint32_t dump_flags, const void *data)
+{
+ const char *name = zend_get_opcode_name(opline->opcode);
+ uint32_t flags = zend_get_opcode_flags(opline->opcode);
+ uint32_t n = 0;
+ int len = 0;
+ const zend_ssa *ssa = NULL;
+
+ if (dump_flags & ZEND_DUMP_SSA) {
+ ssa = (const zend_ssa*)data;
+ }
+
+ if (!b) {
+ len = fprintf(stderr, "L%u:", (uint32_t)(opline - op_array->opcodes));
+ }
+ fprintf(stderr, "%*c", 8-len, ' ');
+
+ if (!ssa || !ssa->ops || ssa->ops[opline - op_array->opcodes].result_use < 0) {
+ if (opline->result_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
+ if (ssa && ssa->ops && ssa->ops[opline - op_array->opcodes].result_def >= 0) {
+ int ssa_var_num = ssa->ops[opline - op_array->opcodes].result_def;
+ zend_dump_ssa_var(op_array, ssa, ssa_var_num, opline->result_type, EX_VAR_TO_NUM(opline->result.var), dump_flags);
+ } else {
+ zend_dump_var(op_array, opline->result_type, EX_VAR_TO_NUM(opline->result.var));
+ }
+ fprintf(stderr, " = ");
+ }
+ }
+
+ if (name) {
+ fprintf(stderr, "%s", (name + 5));
+ } else {
+ fprintf(stderr, "OP_%d", (int)opline->opcode);
+ }
+
+ if (ZEND_VM_EXT_NUM == (flags & ZEND_VM_EXT_MASK)) {
+ fprintf(stderr, " %u", opline->extended_value);
+ } else if (ZEND_VM_EXT_DIM_OBJ == (flags & ZEND_VM_EXT_MASK)) {
+ if (opline->extended_value == ZEND_ASSIGN_DIM) {
+ fprintf(stderr, " (dim)");
+ } else if (opline->extended_value == ZEND_ASSIGN_OBJ) {
+ fprintf(stderr, " (obj)");
+ }
+ } else if (ZEND_VM_EXT_CLASS_FETCH == (flags & ZEND_VM_EXT_MASK)) {
+ zend_dump_class_fetch_type(opline->extended_value);
+ } else if (ZEND_VM_EXT_CONST_FETCH == (flags & ZEND_VM_EXT_MASK)) {
+ if (opline->extended_value & IS_CONSTANT_UNQUALIFIED) {
+ fprintf(stderr, " (unqualified)");
+ }
+ if (opline->extended_value & IS_CONSTANT_CLASS) {
+ fprintf(stderr, " (__class__)");
+ }
+ if (opline->extended_value & IS_CONSTANT_IN_NAMESPACE) {
+ fprintf(stderr, " (in-namespace)");
+ }
+ } else if (ZEND_VM_EXT_TYPE == (flags & ZEND_VM_EXT_MASK)) {
+ switch (opline->extended_value) {
+ case IS_NULL:
+ fprintf(stderr, " (null)");
+ break;
+ case IS_FALSE:
+ fprintf(stderr, " (false)");
+ break;
+ case IS_TRUE:
+ fprintf(stderr, " (true)");
+ break;
+ case IS_LONG:
+ fprintf(stderr, " (long)");
+ break;
+ case IS_DOUBLE:
+ fprintf(stderr, " (double)");
+ break;
+ case IS_STRING:
+ fprintf(stderr, " (string)");
+ break;
+ case IS_ARRAY:
+ fprintf(stderr, " (array)");
+ break;
+ case IS_OBJECT:
+ fprintf(stderr, " (object)");
+ break;
+ case IS_RESOURCE:
+ fprintf(stderr, " (resource)");
+ break;
+ case _IS_BOOL:
+ fprintf(stderr, " (bool)");
+ break;
+ case IS_CALLABLE:
+ fprintf(stderr, " (callable)");
+ break;
+ case IS_VOID:
+ fprintf(stderr, " (void)");
+ break;
+ default:
+ fprintf(stderr, " (\?\?\?)");
+ break;
+ }
+ } else if (ZEND_VM_EXT_EVAL == (flags & ZEND_VM_EXT_MASK)) {
+ switch (opline->extended_value) {
+ case ZEND_EVAL:
+ fprintf(stderr, " (eval)");
+ break;
+ case ZEND_INCLUDE:
+ fprintf(stderr, " (include)");
+ break;
+ case ZEND_INCLUDE_ONCE:
+ fprintf(stderr, " (include_once)");
+ break;
+ case ZEND_REQUIRE:
+ fprintf(stderr, " (require)");
+ break;
+ case ZEND_REQUIRE_ONCE:
+ fprintf(stderr, " (require_once)");
+ break;
+ default:
+ fprintf(stderr, " (\?\?\?)");
+ break;
+ }
+ } else if (ZEND_VM_EXT_SRC == (flags & ZEND_VM_EXT_MASK)) {
+ if (opline->extended_value == ZEND_RETURNS_VALUE) {
+ fprintf(stderr, " (value)");
+ } else if (opline->extended_value == ZEND_RETURNS_FUNCTION) {
+ fprintf(stderr, " (function)");
+ }
+ } else {
+ if (ZEND_VM_EXT_VAR_FETCH & flags) {
+ switch (opline->extended_value & ZEND_FETCH_TYPE_MASK) {
+ case ZEND_FETCH_GLOBAL:
+ fprintf(stderr, " (global)");
+ break;
+ case ZEND_FETCH_LOCAL:
+ fprintf(stderr, " (local)");
+ break;
+ case ZEND_FETCH_GLOBAL_LOCK:
+ fprintf(stderr, " (global+lock)");
+ break;
+ }
+ }
+ if (ZEND_VM_EXT_ISSET & flags) {
+ if (opline->extended_value & ZEND_QUICK_SET) {
+ fprintf(stderr, " (quick)");
+ }
+ if (opline->extended_value & ZEND_ISSET) {
+ fprintf(stderr, " (isset)");
+ } else if (opline->extended_value & ZEND_ISEMPTY) {
+ fprintf(stderr, " (empty)");
+ }
+ }
+ if (ZEND_VM_EXT_ARG_NUM & flags) {
+ fprintf(stderr, " %u", opline->extended_value & ZEND_FETCH_ARG_MASK);
+ }
+ if (ZEND_VM_EXT_ARRAY_INIT & flags) {
+ fprintf(stderr, " %u", opline->extended_value >> ZEND_ARRAY_SIZE_SHIFT);
+ if (!(opline->extended_value & ZEND_ARRAY_NOT_PACKED)) {
+ fprintf(stderr, " (packed)");
+ }
+ }
+ if (ZEND_VM_EXT_REF & flags) {
+ if (opline->extended_value & ZEND_ARRAY_ELEMENT_REF) {
+ fprintf(stderr, " (ref)");
+ }
+ }
+ }
+
+ if (opline->op1_type == IS_CONST) {
+ zend_dump_const(CRT_CONSTANT_EX(op_array, opline->op1, (dump_flags & ZEND_DUMP_RT_CONSTANTS)));
+ } else if (opline->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
+ if (ssa && ssa->ops) {
+ int ssa_var_num = ssa->ops[opline - op_array->opcodes].op1_use;
+ if (ssa_var_num >= 0) {
+ fprintf(stderr, " ");
+ zend_dump_ssa_var(op_array, ssa, ssa_var_num, opline->op1_type, EX_VAR_TO_NUM(opline->op1.var), dump_flags);
+ } else if (ssa->ops[opline - op_array->opcodes].op1_def < 0) {
+ fprintf(stderr, " ");
+ zend_dump_var(op_array, opline->op1_type, EX_VAR_TO_NUM(opline->op1.var));
+ }
+ } else {
+ fprintf(stderr, " ");
+ zend_dump_var(op_array, opline->op1_type, EX_VAR_TO_NUM(opline->op1.var));
+ }
+ if (ssa && ssa->ops) {
+ int ssa_var_num = ssa->ops[opline - op_array->opcodes].op1_def;
+ if (ssa_var_num >= 0) {
+ fprintf(stderr, " -> ");
+ zend_dump_ssa_var(op_array, ssa, ssa_var_num, opline->op1_type, EX_VAR_TO_NUM(opline->op1.var), dump_flags);
+ }
+ }
+ } else {
+ uint32_t op1_flags = ZEND_VM_OP1_FLAGS(flags);
+ if (ZEND_VM_OP_JMP_ADDR == (op1_flags & ZEND_VM_OP_MASK)) {
+ if (b) {
+ fprintf(stderr, " BB%d", b->successors[n++]);
+ } else {
+ fprintf(stderr, " L%u", (uint32_t)(OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes));
+ }
+ } else {
+ zend_dump_unused_op(opline, opline->op1, op1_flags);
+ }
+ }
+
+ if (opline->op2_type == IS_CONST) {
+ zend_dump_const(CRT_CONSTANT_EX(op_array, opline->op2, (dump_flags & ZEND_DUMP_RT_CONSTANTS)));
+ } else if (opline->op2_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
+ if (ssa && ssa->ops) {
+ int ssa_var_num = ssa->ops[opline - op_array->opcodes].op2_use;
+ if (ssa_var_num >= 0) {
+ fprintf(stderr, " ");
+ zend_dump_ssa_var(op_array, ssa, ssa_var_num, opline->op2_type, EX_VAR_TO_NUM(opline->op2.var), dump_flags);
+ } else if (ssa->ops[opline - op_array->opcodes].op2_def < 0) {
+ fprintf(stderr, " ");
+ zend_dump_var(op_array, opline->op2_type, EX_VAR_TO_NUM(opline->op2.var));
+ }
+ } else {
+ fprintf(stderr, " ");
+ zend_dump_var(op_array, opline->op2_type, EX_VAR_TO_NUM(opline->op2.var));
+ }
+ if (ssa && ssa->ops) {
+ int ssa_var_num = ssa->ops[opline - op_array->opcodes].op2_def;
+ if (ssa_var_num >= 0) {
+ fprintf(stderr, " -> ");
+ zend_dump_ssa_var(op_array, ssa, ssa_var_num, opline->op2_type, EX_VAR_TO_NUM(opline->op2.var), dump_flags);
+ }
+ }
+ } else {
+ uint32_t op2_flags = ZEND_VM_OP2_FLAGS(flags);
+ if (ZEND_VM_OP_JMP_ADDR == (op2_flags & ZEND_VM_OP_MASK)) {
+ if (b) {
+ fprintf(stderr, " BB%d", b->successors[n++]);
+ } else {
+ fprintf(stderr, " L%u", (uint32_t)(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes));
+ }
+ } else {
+ zend_dump_unused_op(opline, opline->op2, op2_flags);
+ }
+ }
+
+ if (ZEND_VM_EXT_JMP_ADDR == (flags & ZEND_VM_EXT_MASK)) {
+ if (opline->opcode != ZEND_CATCH || !opline->result.num) {
+ if (b) {
+ fprintf(stderr, " BB%d", b->successors[n++]);
+ } else {
+ fprintf(stderr, " L%u", (uint32_t)ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
+ }
+ }
+ }
+ if (opline->result_type == IS_CONST) {
+ zend_dump_const(CRT_CONSTANT_EX(op_array, opline->result, (dump_flags & ZEND_DUMP_RT_CONSTANTS)));
+ } else if (ssa && ssa->ops && ssa->ops[opline - op_array->opcodes].result_use >= 0) {
+ if (opline->result_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
+ if (ssa && ssa->ops) {
+ int ssa_var_num = ssa->ops[opline - op_array->opcodes].result_use;
+ if (ssa_var_num >= 0) {
+ fprintf(stderr, " ");
+ zend_dump_ssa_var(op_array, ssa, ssa_var_num, opline->result_type, EX_VAR_TO_NUM(opline->result.var), dump_flags);
+ }
+ } else {
+ fprintf(stderr, " ");
+ zend_dump_var(op_array, opline->result_type, EX_VAR_TO_NUM(opline->result.var));
+ }
+ if (ssa && ssa->ops) {
+ int ssa_var_num = ssa->ops[opline - op_array->opcodes].result_def;
+ if (ssa_var_num >= 0) {
+ fprintf(stderr, " -> ");
+ zend_dump_ssa_var(op_array, ssa, ssa_var_num, opline->result_type, EX_VAR_TO_NUM(opline->result.var), dump_flags);
+ }
+ }
+ }
+ }
+ fprintf(stderr, "\n");
+}
+
+static void zend_dump_block_info(const zend_cfg *cfg, int n, uint32_t dump_flags)
+{
+ zend_basic_block *b = cfg->blocks + n;
+ int printed = 0;
+
+ fprintf(stderr, "BB%d:", n);
+ if (b->flags & ZEND_BB_START) {
+ fprintf(stderr, " start");
+ }
+ if (b->flags & ZEND_BB_FOLLOW) {
+ fprintf(stderr, " follow");
+ }
+ if (b->flags & ZEND_BB_TARGET) {
+ fprintf(stderr, " target");
+ }
+ if (b->flags & ZEND_BB_EXIT) {
+ fprintf(stderr, " exit");
+ }
+ if (b->flags & (ZEND_BB_ENTRY|ZEND_BB_RECV_ENTRY)) {
+ fprintf(stderr, " entry");
+ }
+ if (b->flags & ZEND_BB_TRY) {
+ fprintf(stderr, " try");
+ }
+ if (b->flags & ZEND_BB_CATCH) {
+ fprintf(stderr, " catch");
+ }
+ if (b->flags & ZEND_BB_FINALLY) {
+ fprintf(stderr, " finally");
+ }
+ if (b->flags & ZEND_BB_FINALLY_END) {
+ fprintf(stderr, " finally_end");
+ }
+ if (b->flags & ZEND_BB_GEN_VAR) {
+ fprintf(stderr, " gen_var");
+ }
+ if (b->flags & ZEND_BB_KILL_VAR) {
+ fprintf(stderr, " kill_var");
+ }
+ if (!(dump_flags & ZEND_DUMP_HIDE_UNREACHABLE) & !(b->flags & ZEND_BB_REACHABLE)) {
+ fprintf(stderr, " unreachable");
+ }
+ if (b->flags & ZEND_BB_LOOP_HEADER) {
+ fprintf(stderr, " loop_header");
+ }
+ if (b->flags & ZEND_BB_IRREDUCIBLE_LOOP) {
+ fprintf(stderr, " irreducible");
+ }
+ if (b->len != 0) {
+ fprintf(stderr, " lines=[%d-%d]", b->start, b->start + b->len - 1);
+ } else {
+ fprintf(stderr, " empty");
+ }
+ fprintf(stderr, "\n");
+
+ if (b->predecessors_count) {
+ int *p = cfg->predecessors + b->predecessor_offset;
+ int *end = p + b->predecessors_count;
+
+ fprintf(stderr, " ; from=(BB%d", *p);
+ for (p++; p < end; p++) {
+ fprintf(stderr, ", BB%d", *p);
+ }
+ fprintf(stderr, ")\n");
+ }
+
+ if (b->successors[0] != -1) {
+ fprintf(stderr, " ; to=(BB%d", b->successors[0]);
+ printed = 1;
+ if (b->successors[1] != -1) {
+ fprintf(stderr, ", BB%d", b->successors[1]);
+ }
+ }
+ if (printed) {
+ fprintf(stderr, ")\n");
+ }
+
+ if (b->idom >= 0) {
+ fprintf(stderr, " ; idom=BB%d\n", b->idom);
+ }
+ if (b->level >= 0) {
+ fprintf(stderr, " ; level=%d\n", b->level);
+ }
+ if (b->loop_header >= 0) {
+ fprintf(stderr, " ; loop_header=%d\n", b->loop_header);
+ }
+ if (b->children >= 0) {
+ int j = b->children;
+ fprintf(stderr, " ; children=(BB%d", j);
+ j = cfg->blocks[j].next_child;
+ while (j >= 0) {
+ fprintf(stderr, ", BB%d", j);
+ j = cfg->blocks[j].next_child;
+ }
+ fprintf(stderr, ")\n");
+ }
+}
+
+static void zend_dump_block_header(const zend_cfg *cfg, const zend_op_array *op_array, const zend_ssa *ssa, int n, uint32_t dump_flags)
+{
+ zend_dump_block_info(cfg, n, dump_flags);
+ if (ssa && ssa->blocks && ssa->blocks[n].phis) {
+ zend_ssa_phi *p = ssa->blocks[n].phis;
+
+ do {
+ int j;
+
+ fprintf(stderr, " ");
+ zend_dump_ssa_var(op_array, ssa, p->ssa_var, 0, p->var, dump_flags);
+ if (p->pi < 0) {
+ fprintf(stderr, " = Phi(");
+ for (j = 0; j < cfg->blocks[n].predecessors_count; j++) {
+ if (j > 0) {
+ fprintf(stderr, ", ");
+ }
+ zend_dump_ssa_var(op_array, ssa, p->sources[j], 0, p->var, dump_flags);
+ }
+ fprintf(stderr, ")\n");
+ } else {
+ fprintf(stderr, " = Pi<BB%d>(", p->pi);
+ zend_dump_ssa_var(op_array, ssa, p->sources[0], 0, p->var, dump_flags);
+ fprintf(stderr, " &");
+ if (p->has_range_constraint) {
+ zend_dump_range_constraint(op_array, ssa, &p->constraint.range, dump_flags);
+ } else {
+ zend_dump_type_constraint(op_array, ssa, &p->constraint.type, dump_flags);
+ }
+ fprintf(stderr, ")\n");
+ }
+ p = p->next;
+ } while (p);
+ }
+}
+
+static void zend_dump_op_array_name(const zend_op_array *op_array)
+{
+ zend_func_info *func_info = NULL;
+
+ func_info = ZEND_FUNC_INFO(op_array);
+ if (op_array->function_name) {
+ if (op_array->scope && op_array->scope->name) {
+ fprintf(stderr, "%s::%s", op_array->scope->name->val, op_array->function_name->val);
+ } else {
+ fprintf(stderr, "%s", op_array->function_name->val);
+ }
+ } else {
+ fprintf(stderr, "%s", "$_main");
+ }
+ if (func_info && func_info->clone_num > 0) {
+ fprintf(stderr, "_@_clone_%d", func_info->clone_num);
+ }
+}
+
+void zend_dump_op_array(const zend_op_array *op_array, uint32_t dump_flags, const char *msg, const void *data)
+{
+ int i;
+ const zend_cfg *cfg = NULL;
+ const zend_ssa *ssa = NULL;
+ zend_func_info *func_info = NULL;
+ uint32_t func_flags = 0;
+
+ if (dump_flags & (ZEND_DUMP_CFG|ZEND_DUMP_SSA)) {
+ cfg = (const zend_cfg*)data;
+ if (!cfg->blocks) {
+ cfg = data = NULL;
+ }
+ }
+ if (dump_flags & ZEND_DUMP_SSA) {
+ ssa = (const zend_ssa*)data;
+ }
+
+ func_info = ZEND_FUNC_INFO(op_array);
+ if (func_info) {
+ func_flags = func_info->flags;
+ }
+
+ fprintf(stderr, "\n");
+ zend_dump_op_array_name(op_array);
+ fprintf(stderr, ": ; (lines=%d, args=%d",
+ op_array->last,
+ op_array->num_args);
+ if (func_info && func_info->num_args >= 0) {
+ fprintf(stderr, "/%d", func_info->num_args);
+ }
+ fprintf(stderr, ", vars=%d, tmps=%d", op_array->last_var, op_array->T);
+ if (ssa) {
+ fprintf(stderr, ", ssa_vars=%d", ssa->vars_count);
+ }
+ if (func_flags & ZEND_FUNC_INDIRECT_VAR_ACCESS) {
+ fprintf(stderr, ", dynamic");
+ }
+ if (func_flags & ZEND_FUNC_RECURSIVE) {
+ fprintf(stderr, ", recursive");
+ if (func_flags & ZEND_FUNC_RECURSIVE_DIRECTLY) {
+ fprintf(stderr, " directly");
+ }
+ if (func_flags & ZEND_FUNC_RECURSIVE_INDIRECTLY) {
+ fprintf(stderr, " indirectly");
+ }
+ }
+ if (func_flags & ZEND_FUNC_IRREDUCIBLE) {
+ fprintf(stderr, ", irreducable");
+ }
+ if (func_flags & ZEND_FUNC_NO_LOOPS) {
+ fprintf(stderr, ", no_loops");
+ }
+//TODO: this is useful only for JIT???
+#if 0
+ if (info->flags & ZEND_JIT_FUNC_NO_IN_MEM_CVS) {
+ fprintf(stderr, ", no_in_mem_cvs");
+ }
+ if (info->flags & ZEND_JIT_FUNC_NO_USED_ARGS) {
+ fprintf(stderr, ", no_used_args");
+ }
+ if (info->flags & ZEND_JIT_FUNC_NO_SYMTAB) {
+ fprintf(stderr, ", no_symtab");
+ }
+ if (info->flags & ZEND_JIT_FUNC_NO_FRAME) {
+ fprintf(stderr, ", no_frame");
+ }
+ if (info->flags & ZEND_JIT_FUNC_INLINE) {
+ fprintf(stderr, ", inline");
+ }
+#endif
+ if (func_info && func_info->return_value_used == 0) {
+ fprintf(stderr, ", no_return_value");
+ } else if (func_info && func_info->return_value_used == 1) {
+ fprintf(stderr, ", return_value");
+ }
+ fprintf(stderr, ")\n");
+ if (msg) {
+ fprintf(stderr, " ; (%s)\n", msg);
+ }
+ fprintf(stderr, " ; %s:%u-%u\n", op_array->filename->val, op_array->line_start, op_array->line_end);
+
+ if (func_info && func_info->num_args > 0) {
+ uint32_t j;
+
+ for (j = 0; j < MIN(op_array->num_args, func_info->num_args ); j++) {
+ fprintf(stderr, " ; arg %d ", j);
+ zend_dump_type_info(func_info->arg_info[j].info.type, func_info->arg_info[j].info.ce, func_info->arg_info[j].info.is_instanceof, dump_flags);
+ zend_dump_range(&func_info->arg_info[j].info.range);
+ fprintf(stderr, "\n");
+ }
+ }
+
+ if (func_info) {
+ fprintf(stderr, " ; return ");
+ zend_dump_type_info(func_info->return_info.type, func_info->return_info.ce, func_info->return_info.is_instanceof, dump_flags);
+ zend_dump_range(&func_info->return_info.range);
+ fprintf(stderr, "\n");
+ }
+
+ if (ssa && ssa->var_info) {
+ for (i = 0; i < op_array->last_var; i++) {
+ fprintf(stderr, " ; ");
+ zend_dump_ssa_var(op_array, ssa, i, IS_CV, i, dump_flags);
+ fprintf(stderr, "\n");
+ }
+ }
+
+ if (cfg) {
+ int n;
+ zend_basic_block *b;
+
+ for (n = 0; n < cfg->blocks_count; n++) {
+ b = cfg->blocks + n;
+ if (!(dump_flags & ZEND_DUMP_HIDE_UNREACHABLE) || (b->flags & ZEND_BB_REACHABLE)) {
+ const zend_op *opline;
+ const zend_op *end;
+
+ zend_dump_block_header(cfg, op_array, ssa, n, dump_flags);
+ opline = op_array->opcodes + b->start;
+ end = opline + b->len;
+ while (opline < end) {
+ zend_dump_op(op_array, b, opline, dump_flags, data);
+ opline++;
+ }
+ }
+ }
+ if (op_array->last_live_range) {
+ fprintf(stderr, "LIVE RANGES:\n");
+ for (i = 0; i < op_array->last_live_range; i++) {
+ if (cfg->split_at_live_ranges) {
+ fprintf(stderr, " %u: BB%u - BB%u ",
+ EX_VAR_TO_NUM(op_array->live_range[i].var & ~ZEND_LIVE_MASK),
+ cfg->map[op_array->live_range[i].start],
+ cfg->map[op_array->live_range[i].end]);
+ } else {
+ fprintf(stderr, " %u: L%u - L%u ",
+ EX_VAR_TO_NUM(op_array->live_range[i].var & ~ZEND_LIVE_MASK),
+ op_array->live_range[i].start,
+ op_array->live_range[i].end);
+ }
+ switch (op_array->live_range[i].var & ZEND_LIVE_MASK) {
+ case ZEND_LIVE_TMPVAR:
+ fprintf(stderr, "(tmp/var)\n");
+ break;
+ case ZEND_LIVE_LOOP:
+ fprintf(stderr, "(loop)\n");
+ break;
+ case ZEND_LIVE_SILENCE:
+ fprintf(stderr, "(silence)\n");
+ break;
+ case ZEND_LIVE_ROPE:
+ fprintf(stderr, "(rope)\n");
+ break;
+ }
+ }
+ }
+ if (op_array->last_try_catch) {
+ fprintf(stderr, "EXCEPTION TABLE:\n");
+ for (i = 0; i < op_array->last_try_catch; i++) {
+ fprintf(stderr, " BB%u",
+ cfg->map[op_array->try_catch_array[i].try_op]);
+ if (op_array->try_catch_array[i].catch_op) {
+ fprintf(stderr, ", BB%u",
+ cfg->map[op_array->try_catch_array[i].catch_op]);
+ } else {
+ fprintf(stderr, ", -");
+ }
+ if (op_array->try_catch_array[i].finally_op) {
+ fprintf(stderr, ", BB%u",
+ cfg->map[op_array->try_catch_array[i].finally_op]);
+ } else {
+ fprintf(stderr, ", -");
+ }
+ if (op_array->try_catch_array[i].finally_end) {
+ fprintf(stderr, ", BB%u\n",
+ cfg->map[op_array->try_catch_array[i].finally_end]);
+ } else {
+ fprintf(stderr, ", -\n");
+ }
+ }
+ }
+ } else {
+ const zend_op *opline = op_array->opcodes;
+ const zend_op *end = opline + op_array->last;
+
+ while (opline < end) {
+ zend_dump_op(op_array, NULL, opline, dump_flags, data);
+ opline++;
+ }
+ if (op_array->last_live_range) {
+ fprintf(stderr, "LIVE RANGES:\n");
+ for (i = 0; i < op_array->last_live_range; i++) {
+ fprintf(stderr, " %u: L%u - L%u ",
+ EX_VAR_TO_NUM(op_array->live_range[i].var & ~ZEND_LIVE_MASK),
+ op_array->live_range[i].start,
+ op_array->live_range[i].end);
+ switch (op_array->live_range[i].var & ZEND_LIVE_MASK) {
+ case ZEND_LIVE_TMPVAR:
+ fprintf(stderr, "(tmp/var)\n");
+ break;
+ case ZEND_LIVE_LOOP:
+ fprintf(stderr, "(loop)\n");
+ break;
+ case ZEND_LIVE_SILENCE:
+ fprintf(stderr, "(silence)\n");
+ break;
+ case ZEND_LIVE_ROPE:
+ fprintf(stderr, "(rope)\n");
+ break;
+ }
+ }
+ }
+ if (op_array->last_try_catch) {
+ fprintf(stderr, "EXCEPTION TABLE:\n");
+ for (i = 0; i < op_array->last_try_catch; i++) {
+ fprintf(stderr, " L%u",
+ op_array->try_catch_array[i].try_op);
+ if (op_array->try_catch_array[i].catch_op) {
+ fprintf(stderr, ", L%u",
+ op_array->try_catch_array[i].catch_op);
+ } else {
+ fprintf(stderr, ", -");
+ }
+ if (op_array->try_catch_array[i].finally_op) {
+ fprintf(stderr, ", L%u",
+ op_array->try_catch_array[i].finally_op);
+ } else {
+ fprintf(stderr, ", -");
+ }
+ if (op_array->try_catch_array[i].finally_end) {
+ fprintf(stderr, ", L%u\n",
+ op_array->try_catch_array[i].finally_end);
+ } else {
+ fprintf(stderr, ", -\n");
+ }
+ }
+ }
+ }
+}
+
+void zend_dump_dominators(const zend_op_array *op_array, const zend_cfg *cfg)
+{
+ int j;
+
+ fprintf(stderr, "\nDOMINATORS-TREE for \"");
+ zend_dump_op_array_name(op_array);
+ fprintf(stderr, "\"\n");
+ for (j = 0; j < cfg->blocks_count; j++) {
+ zend_basic_block *b = cfg->blocks + j;
+ if (b->flags & ZEND_BB_REACHABLE) {
+ zend_dump_block_info(cfg, j, 0);
+ }
+ }
+}
+
+void zend_dump_variables(const zend_op_array *op_array)
+{
+ int j;
+
+ fprintf(stderr, "\nCV Variables for \"");
+ zend_dump_op_array_name(op_array);
+ fprintf(stderr, "\"\n");
+ for (j = 0; j < op_array->last_var; j++) {
+ fprintf(stderr, " ");
+ zend_dump_var(op_array, IS_CV, j);
+ fprintf(stderr, "\n");
+ }
+}
+
+void zend_dump_ssa_variables(const zend_op_array *op_array, const zend_ssa *ssa, uint32_t dump_flags)
+{
+ int j;
+
+ if (ssa->vars) {
+ fprintf(stderr, "\nSSA Variable for \"");
+ zend_dump_op_array_name(op_array);
+ fprintf(stderr, "\"\n");
+
+ for (j = 0; j < ssa->vars_count; j++) {
+ fprintf(stderr, " ");
+ zend_dump_ssa_var(op_array, ssa, j, IS_CV, ssa->vars[j].var, dump_flags);
+ if (ssa->vars[j].scc >= 0) {
+ if (ssa->vars[j].scc_entry) {
+ fprintf(stderr, " *");
+ } else {
+ fprintf(stderr, " ");
+ }
+ fprintf(stderr, "SCC=%d", ssa->vars[j].scc);
+ }
+ fprintf(stderr, "\n");
+ }
+ }
+}
+
+static void zend_dump_var_set(const zend_op_array *op_array, const char *name, zend_bitset set)
+{
+ int first = 1;
+ uint32_t i;
+
+ fprintf(stderr, " ; %s = {", name);
+ for (i = 0; i < op_array->last_var + op_array->T; i++) {
+ if (zend_bitset_in(set, i)) {
+ if (first) {
+ first = 0;
+ } else {
+ fprintf(stderr, ", ");
+ }
+ zend_dump_var(op_array, IS_CV, i);
+ }
+ }
+ fprintf(stderr, "}\n");
+}
+
+void zend_dump_dfg(const zend_op_array *op_array, const zend_cfg *cfg, const zend_dfg *dfg)
+{
+ int j;
+ fprintf(stderr, "\nVariable Liveness for \"");
+ zend_dump_op_array_name(op_array);
+ fprintf(stderr, "\"\n");
+
+ for (j = 0; j < cfg->blocks_count; j++) {
+ fprintf(stderr, " BB%d:\n", j);
+ zend_dump_var_set(op_array, "def", DFG_BITSET(dfg->def, dfg->size, j));
+ zend_dump_var_set(op_array, "use", DFG_BITSET(dfg->use, dfg->size, j));
+ zend_dump_var_set(op_array, "in ", DFG_BITSET(dfg->in, dfg->size, j));
+ zend_dump_var_set(op_array, "out", DFG_BITSET(dfg->out, dfg->size, j));
+ }
+}
+
+void zend_dump_phi_placement(const zend_op_array *op_array, const zend_ssa *ssa)
+{
+ int j;
+ zend_ssa_block *ssa_blocks = ssa->blocks;
+ int blocks_count = ssa->cfg.blocks_count;
+
+ fprintf(stderr, "\nSSA Phi() Placement for \"");
+ zend_dump_op_array_name(op_array);
+ fprintf(stderr, "\"\n");
+ for (j = 0; j < blocks_count; j++) {
+ if (ssa_blocks && ssa_blocks[j].phis) {
+ zend_ssa_phi *p = ssa_blocks[j].phis;
+ int first = 1;
+
+ fprintf(stderr, " BB%d:\n", j);
+ if (p->pi >= 0) {
+ fprintf(stderr, " ; pi={");
+ } else {
+ fprintf(stderr, " ; phi={");
+ }
+ do {
+ if (first) {
+ first = 0;
+ } else {
+ fprintf(stderr, ", ");
+ }
+ zend_dump_var(op_array, IS_CV, p->var);
+ p = p->next;
+ } while (p);
+ fprintf(stderr, "}\n");
+ }
+ }
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * indent-tabs-mode: t
+ * End:
+ */
diff --git a/ext/opcache/Optimizer/zend_dump.h b/ext/opcache/Optimizer/zend_dump.h
new file mode 100644
index 0000000000..11646d9a82
--- /dev/null
+++ b/ext/opcache/Optimizer/zend_dump.h
@@ -0,0 +1,51 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine, Bytecode Visualisation |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1998-2017 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Dmitry Stogov <dmitry@zend.com> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef ZEND_DUMP_H
+#define ZEND_DUMP_H
+
+#include "zend_ssa.h"
+#include "zend_dfg.h"
+
+#define ZEND_DUMP_HIDE_UNREACHABLE (1<<0)
+#define ZEND_DUMP_RC_INFERENCE (1<<1)
+#define ZEND_DUMP_CFG (1<<2)
+#define ZEND_DUMP_SSA (1<<3)
+#define ZEND_DUMP_RT_CONSTANTS ZEND_RT_CONSTANTS
+
+BEGIN_EXTERN_C()
+
+void zend_dump_op_array(const zend_op_array *op_array, uint32_t dump_flags, const char *msg, const void *data);
+void zend_dump_dominators(const zend_op_array *op_array, const zend_cfg *cfg);
+void zend_dump_dfg(const zend_op_array *op_array, const zend_cfg *cfg, const zend_dfg *dfg);
+void zend_dump_phi_placement(const zend_op_array *op_array, const zend_ssa *ssa);
+void zend_dump_variables(const zend_op_array *op_array);
+void zend_dump_ssa_variables(const zend_op_array *op_array, const zend_ssa *ssa, uint32_t dump_flags);
+void zend_dump_var(const zend_op_array *op_array, zend_uchar var_type, int var_num);
+
+END_EXTERN_C()
+
+#endif /* ZEND_DUMP_H */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * indent-tabs-mode: t
+ * End:
+ */
diff --git a/ext/opcache/Optimizer/zend_func_info.c b/ext/opcache/Optimizer/zend_func_info.c
new file mode 100644
index 0000000000..7a1e65f625
--- /dev/null
+++ b/ext/opcache/Optimizer/zend_func_info.c
@@ -0,0 +1,1288 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine, Func Info |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1998-2017 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Dmitry Stogov <dmitry@zend.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id:$ */
+
+#include "php.h"
+#include "zend_compile.h"
+#include "zend_extensions.h"
+#include "zend_ssa.h"
+#include "zend_optimizer_internal.h"
+#include "zend_inference.h"
+#include "zend_call_graph.h"
+#include "zend_func_info.h"
+#include "zend_inference.h"
+
+typedef uint32_t (*info_func_t)(const zend_call_info *call_info, const zend_ssa *ssa);
+
+typedef struct _func_info_t {
+ const char *name;
+ int name_len;
+ uint32_t info;
+ info_func_t info_func;
+} func_info_t;
+
+#define F0(name, info) \
+ {name, sizeof(name)-1, (FUNC_MAY_WARN | (info)), NULL}
+#define F1(name, info) \
+ {name, sizeof(name)-1, (FUNC_MAY_WARN | MAY_BE_RC1 | (info)), NULL}
+#define FN(name, info) \
+ {name, sizeof(name)-1, (FUNC_MAY_WARN | MAY_BE_RC1 | MAY_BE_RCN | (info)), NULL}
+#define FR(name, info) \
+ {name, sizeof(name)-1, (FUNC_MAY_WARN | MAY_BE_REF | (info)), NULL}
+#define FX(name, info) \
+ {name, sizeof(name)-1, (FUNC_MAY_WARN | MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_REF | (info)), NULL}
+#define I0(name, info) \
+ {name, sizeof(name)-1, (info), NULL}
+#define I1(name, info) \
+ {name, sizeof(name)-1, (MAY_BE_RC1 | (info)), NULL}
+#define IN(name, info) \
+ {name, sizeof(name)-1, (MAY_BE_RC1 | MAY_BE_RCN | (info)), NULL}
+#define FC(name, callback) \
+ {name, sizeof(name)-1, 0, callback}
+
+static uint32_t zend_strlen_info(const zend_call_info *call_info, const zend_ssa *ssa)
+{
+ if (call_info->caller_init_opline->extended_value == (uint32_t)call_info->num_args &&
+ call_info->num_args == 1) {
+
+ uint32_t tmp = 0;
+ if (call_info->arg_info[0].opline) {
+ uint32_t arg_info = _ssa_op1_info(call_info->caller_op_array, ssa, call_info->arg_info[0].opline);
+
+ if (arg_info & (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_STRING|MAY_BE_OBJECT)) {
+ tmp |= MAY_BE_LONG;
+ }
+ if (arg_info & (MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
+ /* warning, and returns NULL */
+ tmp |= FUNC_MAY_WARN | MAY_BE_NULL;
+ }
+ } else {
+ tmp |= MAY_BE_LONG | FUNC_MAY_WARN | MAY_BE_NULL;
+ }
+ return tmp;
+ } else {
+ /* warning, and returns NULL */
+ return FUNC_MAY_WARN | MAY_BE_NULL;
+ }
+}
+
+static uint32_t zend_dechex_info(const zend_call_info *call_info, const zend_ssa *ssa)
+{
+ if (call_info->caller_init_opline->extended_value == (uint32_t)call_info->num_args &&
+ call_info->num_args == 1) {
+ return MAY_BE_RC1 | MAY_BE_STRING;
+ } else {
+ /* warning, and returns NULL */
+ return FUNC_MAY_WARN | MAY_BE_NULL;
+ }
+}
+
+static uint32_t zend_range_info(const zend_call_info *call_info, const zend_ssa *ssa)
+{
+ if (call_info->caller_init_opline->extended_value == (uint32_t)call_info->num_args &&
+ (call_info->num_args == 2 || call_info->num_args == 3)) {
+
+ uint32_t t1 = _ssa_op1_info(call_info->caller_op_array, ssa, call_info->arg_info[0].opline);
+ uint32_t t2 = _ssa_op1_info(call_info->caller_op_array, ssa, call_info->arg_info[1].opline);
+ uint32_t t3 = 0;
+ uint32_t tmp = FUNC_MAY_WARN | MAY_BE_RC1 | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG;
+
+ if (call_info->num_args == 3) {
+ t3 = _ssa_op1_info(call_info->caller_op_array, ssa, call_info->arg_info[2].opline);
+ }
+ if ((t1 & MAY_BE_STRING) && (t2 & MAY_BE_STRING)) {
+ tmp |= MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE | MAY_BE_ARRAY_OF_STRING;
+ }
+ if ((t1 & (MAY_BE_DOUBLE|MAY_BE_STRING))
+ || (t2 & (MAY_BE_DOUBLE|MAY_BE_STRING))
+ || (t3 & (MAY_BE_DOUBLE|MAY_BE_STRING))) {
+ tmp |= MAY_BE_ARRAY_OF_DOUBLE;
+ }
+ if ((t1 & (MAY_BE_ANY-(MAY_BE_STRING|MAY_BE_DOUBLE))) && (t2 & (MAY_BE_ANY-(MAY_BE_STRING|MAY_BE_DOUBLE)))) {
+ if ((t3 & MAY_BE_ANY) != MAY_BE_DOUBLE) {
+ tmp |= MAY_BE_ARRAY_OF_LONG;
+ }
+ }
+ return tmp;
+ } else {
+ /* may warning, and return FALSE */
+ return FUNC_MAY_WARN | MAY_BE_RC1 | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE | MAY_BE_ARRAY_OF_STRING;
+ }
+}
+
+static uint32_t zend_is_type_info(const zend_call_info *call_info, const zend_ssa *ssa)
+{
+ if (call_info->caller_init_opline->extended_value == (uint32_t)call_info->num_args &&
+ call_info->num_args == 1) {
+ return MAY_BE_FALSE | MAY_BE_TRUE;
+ } else {
+ return MAY_BE_FALSE | MAY_BE_TRUE | FUNC_MAY_WARN;
+ }
+}
+
+static uint32_t zend_l_ss_info(const zend_call_info *call_info, const zend_ssa *ssa)
+{
+ if (call_info->caller_init_opline->extended_value == (uint32_t)call_info->num_args &&
+ call_info->num_args == 2) {
+
+ uint32_t arg1_info = _ssa_op1_info(call_info->caller_op_array, ssa, call_info->arg_info[0].opline);
+ uint32_t arg2_info = _ssa_op1_info(call_info->caller_op_array, ssa, call_info->arg_info[1].opline);
+ uint32_t tmp = 0;
+
+ if ((arg1_info & (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_STRING|MAY_BE_OBJECT)) &&
+ (arg2_info & (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_STRING|MAY_BE_OBJECT))) {
+ tmp |= MAY_BE_LONG;
+ }
+ if ((arg1_info & (MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) ||
+ (arg2_info & (MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
+ /* warning, and returns NULL */
+ tmp |= FUNC_MAY_WARN | MAY_BE_NULL;
+ }
+ return tmp;
+ } else {
+ /* warning, and returns NULL */
+ return FUNC_MAY_WARN | MAY_BE_NULL | MAY_BE_LONG;
+ }
+}
+
+static uint32_t zend_lb_ssn_info(const zend_call_info *call_info, const zend_ssa *ssa)
+{
+ if (call_info->caller_init_opline->extended_value == (uint32_t)call_info->num_args &&
+ call_info->num_args == 3) {
+ uint32_t arg1_info = _ssa_op1_info(call_info->caller_op_array, ssa, call_info->arg_info[0].opline);
+ uint32_t arg2_info = _ssa_op1_info(call_info->caller_op_array, ssa, call_info->arg_info[1].opline);
+ uint32_t arg3_info = _ssa_op1_info(call_info->caller_op_array, ssa, call_info->arg_info[2].opline);
+ uint32_t tmp = 0;
+
+ if ((arg1_info & (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_STRING|MAY_BE_OBJECT)) &&
+ (arg2_info & (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_STRING|MAY_BE_OBJECT)) &&
+ (arg3_info & (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_STRING|MAY_BE_OBJECT))) {
+ tmp |= MAY_BE_LONG | MAY_BE_FALSE;
+ }
+ if ((arg1_info & (MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) ||
+ (arg2_info & (MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) ||
+ (arg3_info & (MAY_BE_STRING|MAY_BE_RESOURCE|MAY_BE_ARRAY|MAY_BE_OBJECT))) {
+ /* warning, and returns NULL */
+ tmp |= FUNC_MAY_WARN | MAY_BE_NULL;
+ }
+ return tmp;
+ } else {
+ /* warning, and returns NULL */
+ return FUNC_MAY_WARN | MAY_BE_NULL | MAY_BE_LONG;
+ }
+}
+
+static uint32_t zend_b_s_info(const zend_call_info *call_info, const zend_ssa *ssa)
+{
+ if (call_info->caller_init_opline->extended_value == (uint32_t)call_info->num_args &&
+ call_info->num_args == 1) {
+
+ uint32_t arg1_info = _ssa_op1_info(call_info->caller_op_array, ssa, call_info->arg_info[0].opline);
+ uint32_t tmp = 0;
+
+ if (arg1_info & (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_STRING|MAY_BE_OBJECT)) {
+ tmp |= MAY_BE_FALSE | MAY_BE_TRUE;
+ }
+ if (arg1_info & (MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
+ /* warning, and returns NULL */
+ tmp |= FUNC_MAY_WARN | MAY_BE_NULL;
+ }
+ return tmp;
+ } else {
+ /* warning, and returns NULL */
+ return FUNC_MAY_WARN | MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE;
+ }
+}
+
+#define UNKNOWN_INFO (MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF)
+
+static const func_info_t func_infos[] = {
+ /* zend */
+ I1("zend_version", MAY_BE_STRING),
+ I0("gc_collect_cycles", MAY_BE_LONG),
+ I0("gc_enabled", MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("gc_enable", MAY_BE_NULL),
+ F0("gc_disable", MAY_BE_NULL),
+ F0("func_num_args", MAY_BE_LONG),
+ FN("func_get_arg", UNKNOWN_INFO),
+ F1("func_get_args", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF),
+ FC("strlen", zend_strlen_info),
+ FC("strcmp", zend_l_ss_info),
+ FC("strncmp", zend_lb_ssn_info),
+ FC("strcasecmp", zend_l_ss_info),
+ FC("strncasecmp", zend_lb_ssn_info),
+ F1("each", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_KEY_ANY),
+ F0("error_reporting", MAY_BE_NULL | MAY_BE_LONG),
+ F0("define", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_NULL), // TODO: inline
+ FC("defined", zend_b_s_info), // TODO: inline
+ FN("get_class", MAY_BE_FALSE | MAY_BE_STRING),
+ FN("get_called_class", MAY_BE_FALSE | MAY_BE_STRING),
+ FN("get_parrent_class", MAY_BE_FALSE | MAY_BE_STRING),
+ F0("is_subclass_of", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), // TODO: inline
+ F0("is_a", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), // TODO: inline
+ F1("get_class_vars", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF),
+ FN("get_object_vars", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF),
+ F1("get_class_methods", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F0("method_exists", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("property_exists", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("class_exists", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("interface_exists", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("trait_exists", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ FC("function_exists", zend_b_s_info), // TODO: inline
+ F0("class_alias", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("get_included_files", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F0("trigger_error", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("user_error", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ FN("set_error_handler", MAY_BE_NULL | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_OBJECT | MAY_BE_OBJECT),
+ I0("restore_error_handler", MAY_BE_NULL | MAY_BE_TRUE),
+ F1("get_declared_traits", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("get_declared_classes", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("get_declared_interfaces", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("get_defined_functions", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ARRAY),
+ I1("get_defined_vars", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF),
+ FN("create_function", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING),
+ F1("get_resource_type", MAY_BE_NULL | MAY_BE_STRING),
+ F1("get_defined_constants", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_RESOURCE | MAY_BE_ARRAY_OF_ARRAY),
+ F0("debug_print_backtrace", MAY_BE_NULL),
+ F1("debug_backtrace", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ARRAY),
+ F1("get_loaded_extensions", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ FC("extension_loaded", zend_b_s_info),
+ F1("get_extension_funcs", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+
+ /* ext/statdard */
+ FN("constant", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_LONG | MAY_BE_DOUBLE | MAY_BE_STRING | MAY_BE_RESOURCE),
+ F1("bin2hex", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("hex2bin", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F0("sleep", MAY_BE_FALSE | MAY_BE_LONG),
+ F0("usleep", MAY_BE_NULL | MAY_BE_FALSE),
+#if HAVE_NANOSLEEP
+ F0("time_nanosleep", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("time_sleep_until", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+#endif
+#if HAVE_STRPTIME
+ F1("strptime", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
+#endif
+ F0("flush", MAY_BE_NULL),
+ F1("wordwrap", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("htmlspecialchars", MAY_BE_NULL | MAY_BE_STRING),
+ F1("htmlentities", MAY_BE_NULL | MAY_BE_STRING),
+ F1("html_entity_decode", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("htmlspecialchars_decode", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("get_html_translation_table", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING),
+ F1("sha1", MAY_BE_NULL | MAY_BE_STRING),
+ F1("sha1_file", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("md5", MAY_BE_NULL | MAY_BE_STRING),
+ F1("md5_file", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F0("crc32", MAY_BE_NULL | MAY_BE_LONG),
+ F1("iptcparse", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ARRAY),
+ F1("iptcembed", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("getimagesize", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("getimagesizefromstring", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("image_type_to_mime_type", MAY_BE_NULL | MAY_BE_STRING),
+ F1("image_type_to_extension", MAY_BE_FALSE | MAY_BE_STRING),
+ F0("phpinfo", MAY_BE_NULL | MAY_BE_TRUE),
+ F1("phpversion", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F0("phpcredits", MAY_BE_NULL | MAY_BE_TRUE),
+ F1("php_sapi_name", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("php_uname", MAY_BE_NULL | MAY_BE_STRING),
+ F1("php_ini_scanned_files", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("php_ini_loaded_file", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F0("strnatcmp", MAY_BE_NULL | MAY_BE_LONG),
+ F0("strnatcasecmp", MAY_BE_NULL | MAY_BE_LONG),
+ F0("substr_count", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F0("strspn", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F0("strcspn", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("strtok", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ FN("strtoupper", MAY_BE_NULL | MAY_BE_STRING),
+ FN("strtolower", MAY_BE_NULL | MAY_BE_STRING),
+ F0("strpos", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F0("stripos", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F0("strrpos", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F0("strripos", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("strrev", MAY_BE_NULL | MAY_BE_STRING),
+ F1("hebrev", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("hebrevc", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("nl2br", MAY_BE_NULL | MAY_BE_STRING),
+ F1("basename", MAY_BE_NULL | MAY_BE_STRING),
+ F1("dirname", MAY_BE_NULL | MAY_BE_STRING),
+ F1("pathinfo", MAY_BE_NULL | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING),
+ F1("stripslashes", MAY_BE_NULL | MAY_BE_STRING),
+ F1("stripcslashes", MAY_BE_NULL | MAY_BE_STRING),
+ F1("strstr", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("stristr", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("strrchr", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("str_shuffle", MAY_BE_NULL | MAY_BE_STRING),
+ F1("str_word_count", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("str_split", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("strpbrk", MAY_BE_FALSE | MAY_BE_STRING),
+ F0("substr_compare", MAY_BE_FALSE | MAY_BE_LONG),
+#ifdef HAVE_STRCOLL
+ F0("strcoll", MAY_BE_NULL | MAY_BE_LONG),
+#endif
+#ifdef HAVE_STRFMON
+ F1("money_format", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+#endif
+ F1("substr", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ FN("substr_replace", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING),
+ F1("quotemeta", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("ucfirst", MAY_BE_NULL | MAY_BE_STRING),
+ F1("lcfirst", MAY_BE_NULL | MAY_BE_STRING),
+ F1("ucwords", MAY_BE_NULL | MAY_BE_STRING),
+ FN("strtr", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ FN("addslashes", MAY_BE_NULL | MAY_BE_STRING),
+ F1("addcslashes", MAY_BE_NULL | MAY_BE_STRING),
+ FN("rtrim", MAY_BE_NULL | MAY_BE_STRING),
+ FN("str_replace", MAY_BE_NULL | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY | MAY_BE_ARRAY_OF_OBJECT),
+ FN("str_ireplace", MAY_BE_NULL | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY | MAY_BE_ARRAY_OF_OBJECT),
+ F1("str_repeat", MAY_BE_NULL | MAY_BE_STRING),
+ F1("count_chars", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG),
+ F1("chunk_split", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ FN("trim", MAY_BE_NULL | MAY_BE_STRING),
+ FN("ltrim", MAY_BE_NULL | MAY_BE_STRING),
+ F1("strip_tags", MAY_BE_NULL | MAY_BE_STRING),
+ F0("similar_text", MAY_BE_NULL | MAY_BE_LONG),
+ F1("explode", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ FN("implode", MAY_BE_NULL | MAY_BE_STRING),
+ FN("join", MAY_BE_NULL | MAY_BE_STRING),
+ FN("setlocale", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("localeconv", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
+#if HAVE_NL_LANGINFO
+ F1("nl_langinfo", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+#endif
+ F1("soundex", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F0("levenshtein", MAY_BE_NULL | MAY_BE_LONG),
+ F1("chr", MAY_BE_NULL | MAY_BE_STRING),
+ F0("ord", MAY_BE_NULL | MAY_BE_LONG),
+ F0("parse_str", MAY_BE_NULL),
+ F1("str_getcsv", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_STRING),
+ F1("str_pad", MAY_BE_NULL | MAY_BE_STRING),
+ F1("chop", MAY_BE_NULL | MAY_BE_STRING),
+ F1("strchr", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("sprintf", MAY_BE_FALSE | MAY_BE_STRING),
+ F0("printf", MAY_BE_FALSE | MAY_BE_LONG),
+ F0("vprintf", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("vsprintf", MAY_BE_FALSE | MAY_BE_STRING),
+ F0("fprintf", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F0("vfprintf", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("sscanf", MAY_BE_NULL | MAY_BE_LONG | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ANY),
+ F1("fscanf", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ANY),
+ F1("parse_url", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_LONG),
+ F1("urlencode", MAY_BE_NULL | MAY_BE_STRING),
+ F1("urldecode", MAY_BE_NULL | MAY_BE_STRING),
+ F1("rawurlencode", MAY_BE_NULL | MAY_BE_STRING),
+ F1("rawurldecode", MAY_BE_NULL | MAY_BE_STRING),
+ F1("http_build_query", MAY_BE_FALSE | MAY_BE_STRING),
+#if defined(HAVE_SYMLINK) || defined(PHP_WIN32)
+ F1("readlink", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F0("linkinfo", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F0("symlink", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("link", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+#endif
+ F0("unlink", MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("exec", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("system", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("escapeshellcmd", MAY_BE_NULL | MAY_BE_STRING),
+ F1("escapeshellarg", MAY_BE_NULL | MAY_BE_STRING),
+ F1("passthru", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("shell_exec", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+#ifdef PHP_CAN_SUPPORT_PROC_OPEN
+ F1("proc_open", MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F0("proc_close", MAY_BE_FALSE | MAY_BE_LONG),
+ F0("proc_terminate", MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("proc_get_status", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
+#endif
+#ifdef HAVE_NICE
+ F0("proc_nice", MAY_BE_FALSE | MAY_BE_TRUE),
+#endif
+ F0("rand", MAY_BE_NULL | MAY_BE_LONG),
+ F0("srand", MAY_BE_NULL),
+ F0("getrandmax", MAY_BE_NULL | MAY_BE_LONG),
+ F0("mt_rand", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F0("mt_srand", MAY_BE_NULL),
+ F0("mt_getrandmax", MAY_BE_NULL | MAY_BE_LONG),
+#if HAVE_GETSERVBYNAME
+ F0("getservbyname", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+#endif
+#if HAVE_GETSERVBYPORT
+ F1("getservbyport", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+#endif
+#if HAVE_GETPROTOBYNAME
+ F0("getprotobyname", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+#endif
+#if HAVE_GETPROTOBYNUMBER
+ F1("getprotobynumber", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+#endif
+ F0("getmyuid", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F0("getmygid", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F0("getmypid", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F0("getmyinode", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F0("getlastmod", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("base64_decode", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("base64_encode", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("password_hash", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("password_get_info", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
+ F0("password_needs_rehash", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("password_verify", MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("convert_uuencode", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("convert_uudecode", MAY_BE_FALSE | MAY_BE_STRING),
+ F0("abs", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_DOUBLE),
+ F0("ceil", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_DOUBLE),
+ F0("floor", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_DOUBLE),
+ F0("round", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_DOUBLE),
+ F0("sin", MAY_BE_NULL | MAY_BE_DOUBLE),
+ F0("cos", MAY_BE_NULL | MAY_BE_DOUBLE),
+ F0("tan", MAY_BE_NULL | MAY_BE_DOUBLE),
+ F0("asin", MAY_BE_NULL | MAY_BE_DOUBLE),
+ F0("acos", MAY_BE_NULL | MAY_BE_DOUBLE),
+ F0("atan", MAY_BE_NULL | MAY_BE_DOUBLE),
+ F0("atanh", MAY_BE_NULL | MAY_BE_DOUBLE),
+ F0("atan2", MAY_BE_NULL | MAY_BE_DOUBLE),
+ F0("sinh", MAY_BE_NULL | MAY_BE_DOUBLE),
+ F0("cosh", MAY_BE_NULL | MAY_BE_DOUBLE),
+ F0("tanh", MAY_BE_NULL | MAY_BE_DOUBLE),
+ F0("asinh", MAY_BE_NULL | MAY_BE_DOUBLE),
+ F0("acosh", MAY_BE_NULL | MAY_BE_DOUBLE),
+ F0("expm1", MAY_BE_NULL | MAY_BE_DOUBLE),
+ F0("log1p", MAY_BE_NULL | MAY_BE_DOUBLE),
+ F0("pi", MAY_BE_DOUBLE),
+ F0("is_finite", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("is_nan", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("is_infinite", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("pow", MAY_BE_NULL | MAY_BE_LONG | MAY_BE_DOUBLE),
+ F0("exp", MAY_BE_NULL | MAY_BE_DOUBLE),
+ F0("log", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_DOUBLE),
+ F0("log10", MAY_BE_NULL | MAY_BE_DOUBLE),
+ F0("sqrt", MAY_BE_NULL | MAY_BE_DOUBLE),
+ F0("hypot", MAY_BE_NULL | MAY_BE_DOUBLE),
+ F0("deg2rad", MAY_BE_NULL | MAY_BE_DOUBLE),
+ F0("rad2deg", MAY_BE_NULL | MAY_BE_DOUBLE),
+ F0("bindec", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_DOUBLE),
+ F0("hexdec", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_DOUBLE),
+ F0("octdec", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_DOUBLE),
+ F1("decbin", MAY_BE_NULL | MAY_BE_STRING),
+ F1("decoct", MAY_BE_NULL | MAY_BE_STRING),
+ FC("dechex", zend_dechex_info),
+ F1("base_convert", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("number_format", MAY_BE_NULL | MAY_BE_STRING),
+ F0("fmod", MAY_BE_NULL | MAY_BE_DOUBLE),
+#ifdef HAVE_INET_NTOP
+ F1("inet_ntop", MAY_BE_FALSE | MAY_BE_STRING),
+#endif
+#ifdef HAVE_INET_PTON
+ F1("inet_pton", MAY_BE_FALSE | MAY_BE_STRING),
+#endif
+ F0("ip2long", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("long2ip", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("getenv", MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING),
+#ifdef HAVE_PUTENV
+ F0("putenv", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+#endif
+ F1("getopt", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
+#ifdef HAVE_GETLOADAVG
+ F1("sys_getloadavg", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_DOUBLE),
+#endif
+#ifdef HAVE_GETTIMEOFDAY
+ F1("microtime", MAY_BE_NULL | MAY_BE_DOUBLE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG | MAY_BE_STRING),
+ F1("gettimeofday", MAY_BE_NULL | MAY_BE_DOUBLE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_STRING),
+#endif
+#ifdef HAVE_GETRUSAGE
+ F1("getrusage", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG),
+#endif
+#ifdef HAVE_GETTIMEOFDAY
+ F1("uniqid", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+#endif
+ F1("quoted_printable_decode", MAY_BE_NULL | MAY_BE_STRING),
+ F1("quoted_printable_encode", MAY_BE_NULL | MAY_BE_STRING),
+ F1("convert_cyr_string", MAY_BE_NULL | MAY_BE_STRING),
+ F1("get_current_user", MAY_BE_NULL | MAY_BE_STRING),
+ F0("set_time_limit", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("header_register_callback", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("get_cfg_var", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
+ F0("magic_quotes_runtime", MAY_BE_NULL | MAY_BE_FALSE),
+ F0("set_magic_quotes_runtime", MAY_BE_NULL | MAY_BE_FALSE),
+ F0("get_magic_quotes_gpc", MAY_BE_NULL | MAY_BE_FALSE),
+ F0("get_magic_quotes_runtime", MAY_BE_NULL | MAY_BE_FALSE),
+ F0("error_log", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("error_get_last", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
+ FN("call_user_func", UNKNOWN_INFO),
+ FN("call_user_func_array", UNKNOWN_INFO),
+ FN("call_user_method", UNKNOWN_INFO),
+ FN("call_user_method_array", UNKNOWN_INFO),
+ FN("forward_static_call", UNKNOWN_INFO),
+ FN("forward_static_call_array", UNKNOWN_INFO),
+ F1("serialize", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ FN("unserialize", UNKNOWN_INFO),
+ F0("var_dump", MAY_BE_NULL),
+ F1("var_export", MAY_BE_NULL | MAY_BE_STRING),
+ F0("debug_zval_dump", MAY_BE_NULL),
+ F1("print_r", MAY_BE_FALSE | MAY_BE_STRING),
+ F0("memory_get_usage", MAY_BE_FALSE | MAY_BE_LONG),
+ F0("memory_get_peak_usage", MAY_BE_FALSE | MAY_BE_LONG),
+ F0("register_shutdown_function", MAY_BE_NULL | MAY_BE_FALSE),
+ F0("register_tick_function", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("unregister_tick_function", MAY_BE_NULL),
+ F1("highlight_file", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING),
+ F1("show_source", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("highlight_string", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING),
+ F1("php_strip_whitespace", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("ini_get", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("ini_get_all", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
+ F1("ini_set", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("ini_alter", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F0("ini_restore", MAY_BE_NULL),
+ F1("get_include_path", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("set_include_path", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F0("restore_include_path", MAY_BE_NULL),
+ F0("setcookie", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("setrawcookie", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("header", MAY_BE_NULL),
+ F0("header_remove", MAY_BE_NULL),
+ F0("headers_sent", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("headers_list", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F0("http_response_code", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F0("connection_aborted", MAY_BE_LONG),
+ F0("connection_status", MAY_BE_LONG),
+ F0("ignore_user_abort", MAY_BE_NULL | MAY_BE_LONG),
+ F1("parse_ini_file", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
+ F1("parse_ini_string", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
+#if ZEND_DEBUG
+ F1("config_get_hash", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
+#endif
+ F0("is_uploaded_file", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("move_uploaded_file", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("gethostbyaddr", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("gethostbyname", MAY_BE_NULL | MAY_BE_STRING),
+ F1("gethostbynamel", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+#ifdef HAVE_GETHOSTNAME
+ F1("gethostname", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+#endif
+#if defined(PHP_WIN32) || (HAVE_DNS_SEARCH_FUNC && !(defined(__BEOS__) || defined(NETWARE)))
+ F0("dns_check_record", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("checkdnsrr", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+# if defined(PHP_WIN32) || HAVE_FULL_DNS_FUNCS
+ F0("dns_get_mx", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("getmxrr", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("dns_get_record", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ARRAY),
+# endif
+#endif
+ F0("intval", MAY_BE_NULL | MAY_BE_LONG),
+ F0("floatval", MAY_BE_NULL | MAY_BE_DOUBLE),
+ F0("doubleval", MAY_BE_NULL | MAY_BE_DOUBLE),
+ FN("strval", MAY_BE_NULL | MAY_BE_STRING),
+ F0("boolval", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("gettype", MAY_BE_NULL | MAY_BE_STRING),
+ F0("settype", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ FC("is_null", zend_is_type_info),
+ F0("is_resource", MAY_BE_FALSE | MAY_BE_TRUE), // TODO: inline with support for closed resources
+ FC("is_bool", zend_is_type_info),
+ FC("is_long", zend_is_type_info),
+ FC("is_float", zend_is_type_info),
+ FC("is_int", zend_is_type_info),
+ FC("is_integer", zend_is_type_info),
+ FC("is_double", zend_is_type_info),
+ FC("is_real", zend_is_type_info),
+ F0("is_numeric", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ FC("is_string", zend_is_type_info),
+ FC("is_array", zend_is_type_info),
+ F0("is_object", MAY_BE_FALSE | MAY_BE_TRUE), // TODO: inline with support for incomplete class
+ F0("is_scalar", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("is_callable", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("pclose", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("popen", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F0("readfile", MAY_BE_FALSE | MAY_BE_LONG),
+ F0("rewind", MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("rmdir", MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("umask", MAY_BE_FALSE | MAY_BE_LONG),
+ F0("fclose", MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("feof", MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("fgetc", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("fgets", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("fgetss", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("fread", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("fopen", MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F0("fpassthru", MAY_BE_FALSE | MAY_BE_LONG),
+ F0("ftruncate", MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("fstat", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG),
+ F0("fseek", MAY_BE_FALSE | MAY_BE_LONG),
+ F0("ftell", MAY_BE_FALSE | MAY_BE_LONG),
+ F0("fflush", MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("fwrite", MAY_BE_FALSE | MAY_BE_LONG),
+ F0("fputs", MAY_BE_FALSE | MAY_BE_LONG),
+ F0("mkdir", MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("rename", MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("copy", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("tempnam", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("tmpfile", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("file", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("file_get_contents", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F0("file_put_contents", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F0("stream_select", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("stream_context_create", MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F0("stream_context_set_params", MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("stream_context_get_params", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
+ F0("stream_context_set_option", MAY_BE_FALSE | MAY_BE_TRUE),
+ FN("stream_context_get_options", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
+ FN("stream_context_get_default", MAY_BE_FALSE | MAY_BE_RESOURCE),
+ FN("stream_context_set_default", MAY_BE_FALSE | MAY_BE_RESOURCE),
+ FN("stream_filter_prepend", MAY_BE_FALSE | MAY_BE_RESOURCE),
+ FN("stream_filter_append", MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F0("stream_filter_remove", MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("stream_socket_client", MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("stream_socket_server", MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("stream_socket_accept", MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("stream_socket_get_name", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("stream_socket_recvfrom", MAY_BE_FALSE | MAY_BE_STRING),
+ F0("stream_socket_sendto", MAY_BE_FALSE | MAY_BE_LONG),
+ F0("stream_socket_enable_crypto", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_LONG),
+#ifdef HAVE_SHUTDOWN
+ F0("stream_socket_shutdown", MAY_BE_FALSE | MAY_BE_TRUE),
+#endif
+#if HAVE_SOCKETPAIR
+ F1("stream_socket_pair", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_RESOURCE),
+#endif
+ F0("stream_copy_to_stream", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("stream_get_contents", MAY_BE_FALSE | MAY_BE_STRING),
+ F0("stream_supports_lock", MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("fgetcsv", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_STRING),
+ F0("fputcsv", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F0("flock", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("get_meta_tags", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING),
+ F0("stream_set_read_buffer", MAY_BE_FALSE | MAY_BE_LONG),
+ F0("stream_set_write_buffer", MAY_BE_FALSE | MAY_BE_LONG),
+ F0("set_file_buffer", MAY_BE_FALSE | MAY_BE_LONG),
+ F0("stream_set_chunk_size", MAY_BE_FALSE | MAY_BE_LONG),
+ F0("set_socket_blocking", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("stream_set_blocking", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("socket_set_blocking", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("stream_get_meta_data", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
+ F1("stream_get_line", MAY_BE_FALSE | MAY_BE_STRING),
+ F0("stream_wrapper_register", MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("stream_register_wrapper", MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("stream_wrapper_unregister", MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("stream_wrapper_restore", MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("stream_get_wrappers", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("stream_get_transports", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("stream_resolve_include_path", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F0("stream_is_local", MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("get_headers", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
+#if HAVE_SYS_TIME_H || defined(PHP_WIN32)
+ F0("stream_set_timeout", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("socket_set_timeout", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+#endif
+ F1("socket_get_status", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
+#if (!defined(__BEOS__) && !defined(NETWARE) && HAVE_REALPATH) || defined(ZTS)
+ F1("realpath", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+#endif
+#ifdef HAVE_FNMATCH
+ F0("fnmatch", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+#endif
+ F1("fsockopen", MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("pfsockopen", MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("pack", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("unpack", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY),
+ F1("get_browser", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_OBJECT | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
+ F1("crypt", MAY_BE_NULL | MAY_BE_STRING),
+ FN("opendir", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F0("closedir", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("chdir", MAY_BE_FALSE | MAY_BE_TRUE),
+#if defined(HAVE_CHROOT) && !defined(ZTS) && ENABLE_CHROOT_FUNC
+ F0("chroot", MAY_BE_FALSE | MAY_BE_TRUE),
+#endif
+ F1("getcwd", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F0("rewinddir", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("readdir", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("dir", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("scandir", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+#ifdef HAVE_GLOB
+ F1("glob", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+#endif
+ F0("fileatime", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F0("filectime", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F0("filegroup", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F0("fileinode", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F0("filemtime", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F0("fileowner", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F0("fileperms", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F0("filesize", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("filetype", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F0("file_exists", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("is_writable", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("is_writeable", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("is_readable", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("is_executable", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("is_file", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("is_dir", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("is_link", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("stat", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("lstat", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
+#ifndef NETWARE
+ F0("chown", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("chgrp", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+#endif
+#if HAVE_LCHOWN
+ F0("lchown", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+#endif
+#if HAVE_LCHOWN
+ F0("lchgrp", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+#endif
+ F0("chmod", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+#if HAVE_UTIME
+ F0("touch", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+#endif
+ F0("clearstatcache", MAY_BE_NULL),
+ F0("disk_total_space", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_DOUBLE),
+ F0("disk_free_space", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_DOUBLE),
+ F0("diskfreespace", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_DOUBLE),
+ F0("realpath_cache_size", MAY_BE_NULL | MAY_BE_LONG),
+ F1("realpath_cache_get", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ARRAY),
+ F0("mail", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("ezmlm_hash", MAY_BE_NULL | MAY_BE_LONG),
+#ifdef HAVE_SYSLOG_H
+ F0("openlog", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("syslog", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("closelog", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+#endif
+ F0("lcg_value", MAY_BE_DOUBLE),
+ F1("metaphone", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F0("ob_start", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("ob_flush", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("ob_clean", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("ob_end_flush", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("ob_end_clean", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("ob_get_flush", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("ob_get_clean", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F0("ob_get_length", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F0("ob_get_level", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("ob_get_status", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
+ FN("ob_get_contents", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F0("ob_implicit_flush", MAY_BE_NULL),
+ F1("ob_list_handlers", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F0("ksort", MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("krsort", MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("natsort", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("natcasesort", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("asort", MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("arsort", MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("sort", MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("rsort", MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("usort", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("uasort", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("uksort", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("shuffle", MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("array_walk", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("array_walk_recursive", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("count", MAY_BE_NULL | MAY_BE_LONG),
+ FN("end", UNKNOWN_INFO),
+ FN("prev", UNKNOWN_INFO),
+ FN("next", UNKNOWN_INFO),
+ FN("reset", UNKNOWN_INFO),
+ FN("current", UNKNOWN_INFO),
+ FN("key", MAY_BE_NULL | MAY_BE_LONG | MAY_BE_STRING),
+ FN("min", UNKNOWN_INFO),
+ FN("max", UNKNOWN_INFO),
+ F0("in_array", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ FN("array_search", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_STRING),
+ F0("extract", MAY_BE_NULL | MAY_BE_LONG),
+ F1("compact", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_fill", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ANY),
+ F1("array_fill_keys", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ FC("range", zend_range_info),
+ F0("array_multisort", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("array_push", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ FN("array_pop", UNKNOWN_INFO),
+ FN("array_shift", UNKNOWN_INFO),
+ F0("array_unshift", MAY_BE_NULL | MAY_BE_LONG),
+ F1("array_splice", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_slice", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_merge", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_merge_recursive", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_replace", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_replace_recursive", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_keys", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("array_values", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_count_values", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG),
+ F1("array_column", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_reverse", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_reduce", UNKNOWN_INFO),
+ FN("array_pad", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_flip", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("array_change_key_case", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_rand", UNKNOWN_INFO),
+ FN("array_unique", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_intersect", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_intersect_key", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_intersect_ukey", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_uintersect", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_intersect_assoc", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_uintersect_assoc", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_intersect_uassoc", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_uintersect_uassoc", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ FN("array_diff", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_diff_key", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_diff_ukey", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_udiff", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_diff_assoc", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_udiff_assoc", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_diff_uassoc", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_udiff_uassoc", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F0("array_sum", MAY_BE_NULL | MAY_BE_LONG | MAY_BE_DOUBLE),
+ F0("array_product", MAY_BE_NULL | MAY_BE_LONG | MAY_BE_DOUBLE),
+ F1("array_filter", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ FN("array_map", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_chunk", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_combine", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F0("array_key_exists", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("pos", UNKNOWN_INFO),
+ F0("sizeof", MAY_BE_NULL | MAY_BE_LONG),
+ F0("key_exists", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("assert", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("assert_options", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_OBJECT | MAY_BE_OBJECT),
+ F0("version_compare", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_LONG),
+#if HAVE_FTOK
+ F0("ftok", MAY_BE_NULL | MAY_BE_LONG),
+#endif
+ F1("str_rot13", MAY_BE_NULL | MAY_BE_STRING),
+ F1("stream_get_filters", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F0("stream_filter_register", MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("stream_bucket_make_writeable", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("stream_bucket_prepend", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("stream_bucket_append", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("stream_bucket_new", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F0("output_add_rewrite_var", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("output_reset_rewrite_vars", MAY_BE_FALSE),
+ F1("sys_get_temp_dir", MAY_BE_NULL | MAY_BE_STRING),
+
+ /* ext/date */
+ F0("strtotime", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("date", MAY_BE_FALSE | MAY_BE_STRING),
+ F0("idate", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("gmdate", MAY_BE_FALSE | MAY_BE_STRING),
+ F0("mktime", MAY_BE_FALSE | MAY_BE_LONG),
+ F0("gmmktime", MAY_BE_FALSE | MAY_BE_LONG),
+ F0("checkdate", MAY_BE_FALSE | MAY_BE_TRUE),
+#ifdef HAVE_STRFTIME
+ F1("strftime", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("gmstrftime", MAY_BE_FALSE | MAY_BE_STRING),
+#endif
+ F0("time", MAY_BE_LONG),
+ F1("localtime", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG),
+ F1("getdate", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("date_create", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("date_create_immutable", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("date_create_from_format", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("date_create_immutable_from_format", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("date_parse", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
+ F1("date_parse_from_format", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
+ F1("date_get_last_errors", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_ARRAY),
+ F1("date_format", MAY_BE_FALSE | MAY_BE_STRING),
+ FN("date_modify", MAY_BE_FALSE | MAY_BE_OBJECT),
+ FN("date_add", MAY_BE_FALSE | MAY_BE_OBJECT),
+ FN("date_sub", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("date_timezone_get", MAY_BE_FALSE | MAY_BE_OBJECT),
+ FN("date_timezone_set", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F0("date_offset_get", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("date_diff", MAY_BE_FALSE | MAY_BE_OBJECT),
+ FN("date_time_set", MAY_BE_FALSE | MAY_BE_OBJECT),
+ FN("date_date_set", MAY_BE_FALSE | MAY_BE_OBJECT),
+ FN("date_isodate_set", MAY_BE_FALSE | MAY_BE_OBJECT),
+ FN("date_timestamp_set", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F0("date_timestamp_get", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("timezone_open", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("timezone_name_get", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("timezone_name_from_abbr", MAY_BE_FALSE | MAY_BE_STRING),
+ F0("timezone_offset_get", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("timezone_transitions_get", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY),
+ F1("timezone_location_get", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_DOUBLE | MAY_BE_ARRAY_OF_STRING),
+ F1("timezone_identifiers_list", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("timezone_abbreviations_list", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ARRAY),
+ F1("timezone_version_get", MAY_BE_STRING),
+ F1("date_interval_create_from_date_string", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("date_interval_format", MAY_BE_FALSE | MAY_BE_STRING),
+ F0("date_default_timezone_set", MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("date_default_timezone_get", MAY_BE_STRING),
+ F1("date_sunrise", MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_DOUBLE | MAY_BE_STRING),
+ F1("date_sunset", MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_DOUBLE | MAY_BE_STRING),
+ F1("date_sun_info", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_LONG),
+
+ /* ext/preg */
+ F0("preg_match", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F0("preg_match_all", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ FN("preg_replace", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING),
+ FN("preg_replace_callback", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING),
+ F1("preg_filter", MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING),
+ F1("preg_split", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
+ F1("preg_quote", MAY_BE_NULL | MAY_BE_STRING),
+ F1("preg_grep", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F0("preg_last_error", MAY_BE_NULL | MAY_BE_LONG),
+
+ /* ext/ereg */
+ F0("ereg", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("ereg_replace", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F0("eregi", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("eregi_replace", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("split", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("spliti", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("sql_regcase", MAY_BE_NULL | MAY_BE_STRING),
+
+ /* ext/mysql */
+ F1("mysql_connect", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("mysql_pconnect", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F0("mysql_close", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("mysql_select_db", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("mysql_create_db", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("mysql_drop_db", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("mysql_query", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_RESOURCE),
+ F1("mysql_unbuffered_query", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_RESOURCE),
+ F1("mysql_db_query", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_RESOURCE),
+ F1("mysql_list_dbs", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("mysql_list_tables", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("mysql_list_fields", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("mysql_list_processes", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("mysql_error", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F0("mysql_errno", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F0("mysql_affected_rows", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F0("mysql_insert_id", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("mysql_result", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F0("mysql_num_rows", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F0("mysql_num_fields", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("mysql_fetch_row", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ANY),
+ F1("mysql_fetch_array", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY),
+ F1("mysql_fetch_assoc", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
+ F1("mysql_fetch_object", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_OBJECT),
+ F0("mysql_data_seek", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("mysql_fetch_lengths", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG),
+ F1("mysql_fetch_field", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_OBJECT),
+ F0("mysql_field_seek", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("mysql_free_result", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("mysql_field_name", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mysql_field_table", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F0("mysql_field_len", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("mysql_field_type", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mysql_field_flags", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mysql_escape_string", MAY_BE_NULL | MAY_BE_STRING),
+ F1("mysql_real_escape_string", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mysql_stat", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F0("mysql_thread_id", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("mysql_client_encoding", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F0("mysql_ping", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("mysql_get_client_info", MAY_BE_NULL | MAY_BE_STRING),
+ F1("mysql_get_host_info", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F0("mysql_get_proto_info", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("mysql_get_server_info", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mysql_info", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F0("mysql_set_charset", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("mysql", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("mysql_fieldname", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mysql_fieldtable", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F0("mysql_fieldlen", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("mysql_fieldtype", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mysql_fieldflags", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F0("mysql_selectdb", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("mysql_createdb", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("mysql_dropdb", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("mysql_freeresult", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("mysql_numfields", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F0("mysql_numrows", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("mysql_listdbs", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("mysql_listtables", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("mysql_listfields", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("mysql_db_name", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mysql_dbname", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mysql_tablename", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mysql_table_name", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+
+ /* ext/curl */
+ F1("curl_init", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("curl_copy_handle", MAY_BE_NULL | MAY_BE_RESOURCE),
+ F1("curl_version", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
+ F0("curl_setopt", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("curl_setopt_array", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ FN("curl_exec", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("curl_getinfo", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_LONG | MAY_BE_DOUBLE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY),
+ F1("curl_error", MAY_BE_NULL | MAY_BE_STRING),
+ F0("curl_errno", MAY_BE_NULL | MAY_BE_LONG),
+ F0("curl_close", MAY_BE_NULL),
+ F1("curl_strerror", MAY_BE_NULL | MAY_BE_STRING),
+ F1("curl_multi_strerror", MAY_BE_NULL | MAY_BE_STRING),
+ F0("curl_reset", MAY_BE_NULL),
+ F1("curl_escape", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("curl_unescape", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F0("curl_pause", MAY_BE_NULL | MAY_BE_LONG),
+ F1("curl_multi_init", MAY_BE_NULL | MAY_BE_RESOURCE),
+ F0("curl_multi_add_handle", MAY_BE_NULL | MAY_BE_LONG),
+ F0("curl_multi_remove_handle", MAY_BE_NULL | MAY_BE_LONG),
+ F0("curl_multi_select", MAY_BE_NULL | MAY_BE_LONG),
+ F0("curl_multi_exec", MAY_BE_NULL | MAY_BE_LONG),
+ FN("curl_multi_getcontent", MAY_BE_NULL | MAY_BE_STRING),
+ F1("curl_multi_info_read", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_RESOURCE),
+ F0("curl_multi_close", MAY_BE_NULL),
+ F0("curl_multi_setopt", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("curl_share_init", MAY_BE_NULL | MAY_BE_RESOURCE),
+ F0("curl_share_close", MAY_BE_NULL),
+ F0("curl_share_setopt", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("curl_file_create", MAY_BE_OBJECT),
+
+ /* ext/mbstring */
+ F1("mb_convert_case", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mb_strtoupper", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mb_strtolower", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mb_language", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING),
+ F1("mb_internal_encoding", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING),
+ F1("mb_http_input", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mb_http_output", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING),
+ F1("mb_detect_order", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("mb_substitute_character", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_LONG | MAY_BE_STRING),
+ F0("mb_parse_str", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("mb_output_handler", MAY_BE_NULL | MAY_BE_STRING),
+ F1("mb_preferred_mime_name", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F0("mb_strlen", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F0("mb_strpos", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F0("mb_strrpos", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F0("mb_stripos", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F0("mb_strripos", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("mb_strstr", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mb_strrchr", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mb_stristr", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mb_strrichr", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F0("mb_substr_count", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("mb_substr", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mb_strcut", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F0("mb_strwidth", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("mb_strimwidth", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mb_convert_encoding", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY),
+ F1("mb_detect_encoding", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mb_list_encodings", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("mb_encoding_aliases", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("mb_convert_kana", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mb_encode_mimeheader", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mb_decode_mimeheader", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mb_convert_variables", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mb_encode_numericentity", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mb_decode_numericentity", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F0("mb_send_mail", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("mb_get_info", MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
+ F0("mb_check_encoding", MAY_BE_FALSE | MAY_BE_TRUE),
+
+ F1("mb_regex_encoding", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING),
+ F1("mb_regex_set_options", MAY_BE_FALSE | MAY_BE_STRING),
+ F0("mb_ereg", MAY_BE_FALSE | MAY_BE_LONG),
+ F0("mb_eregi", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("mb_ereg_replace", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mb_eregi_replace", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mb_ereg_replace_callback", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mb_split", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F0("mb_ereg_match", MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("mb_ereg_search", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("mb_ereg_search_pos", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG),
+ F1("mb_ereg_search_regs", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_STRING),
+ F0("mb_ereg_search_init", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("mb_ereg_search_getregs", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_STRING),
+ F0("mb_ereg_search_getpos", MAY_BE_LONG),
+ F0("mb_ereg_search_setpos", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+
+ F0("mbregex_encoding", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("mbereg", MAY_BE_FALSE | MAY_BE_LONG),
+ F0("mberegi", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("mbereg_replace", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mberegi_replace", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mbsplit", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F0("mbereg_match", MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("mbereg_search", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("mbereg_search_pos", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG),
+ F1("mbereg_search_regs", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_STRING),
+ F0("mbereg_search_init", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("mbereg_search_getregs", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_STRING),
+ F0("mbereg_search_getpos", MAY_BE_LONG),
+ F0("mbereg_search_setpos", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+
+ /* ext/iconv */
+ F1("iconv", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("iconv_get_encoding", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING),
+ F0("iconv_set_encoding", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("iconv_strlen", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("iconv_substr", MAY_BE_FALSE | MAY_BE_STRING),
+ F0("iconv_strpos", MAY_BE_FALSE | MAY_BE_LONG),
+ F0("iconv_strrpos", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("iconv_mime_encode", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("iconv_mime_decode", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("iconv_mime_decode_headers", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
+
+ /* ext/json */
+ F1("json_encode", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("json_decode", MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY),
+ F0("json_last_error", MAY_BE_NULL | MAY_BE_LONG),
+ F1("json_last_error_msg", MAY_BE_NULL | MAY_BE_STRING),
+
+ /* ext/xml */
+ FN("xml_parser_create", MAY_BE_FALSE | MAY_BE_RESOURCE),
+ FN("xml_parser_create_ns", MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F0("xml_set_object", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("xml_set_element_handler", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("xml_set_character_data_handler", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("xml_set_processing_instruction_handler",MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("xml_set_default_handler", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("xml_set_unparsed_entity_decl_handler", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("xml_set_notation_decl_handler", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("xml_set_external_entity_ref_handler", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("xml_set_start_namespace_decl_handler", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("xml_set_end_namespace_decl_handler", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("xml_parse", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F0("xml_parse_into_struct", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F0("xml_get_error_code", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("xml_error_string", MAY_BE_NULL | MAY_BE_STRING),
+ F0("xml_get_current_line_number", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F0("xml_get_current_column_number", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F0("xml_get_current_byte_index", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F0("xml_parser_free", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("xml_parser_set_option", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("xml_parser_get_option", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_STRING),
+ F1("utf8_encode", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("utf8_decode", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+
+ /* ext/zlib */
+ F0("readgzfile", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F0("gzrewind", MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("gzclose", MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("gzeof", MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("gzgetc", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("gzgets", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("gzgetss", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("gzread", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("gzopen", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F0("gzpassthru", MAY_BE_FALSE | MAY_BE_LONG),
+ F0("gzseek", MAY_BE_FALSE | MAY_BE_LONG),
+ F0("gztell", MAY_BE_FALSE | MAY_BE_LONG),
+ F0("gzwrite", MAY_BE_FALSE | MAY_BE_LONG),
+ F0("gzputs", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("gzfile", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("gzcompress", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("gzuncompress", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("gzdeflate", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("gzinflate", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("gzencode", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("gzdecode", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("zlib_encode", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("zlib_decode", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("zlib_get_coding_type", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("ob_gzhandler", MAY_BE_FALSE | MAY_BE_STRING),
+
+ /* ext/hash */
+ F1("hash", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("hash_file", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("hash_hmac", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("hash_hmac_file", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("hash_init", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F0("hash_update", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F0("hash_update_stream", MAY_BE_NULL | MAY_BE_LONG),
+ F0("hash_update_file", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("hash_final", MAY_BE_NULL | MAY_BE_STRING),
+ F1("hash_copy", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("hash_algos", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("hash_pbkdf2", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mhash_keygen_s2k", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F0("mhash_get_block_size", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("mhash_get_hash_name", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F0("mhash_count", MAY_BE_NULL | MAY_BE_LONG),
+ F1("mhash", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+};
+
+static HashTable func_info;
+int zend_func_info_rid = -1;
+
+uint32_t zend_get_func_info(const zend_call_info *call_info, const zend_ssa *ssa)
+{
+ uint32_t ret = 0;
+
+ if (call_info->callee_func->type == ZEND_INTERNAL_FUNCTION) {
+ func_info_t *info;
+
+ if ((info = zend_hash_find_ptr(&func_info, Z_STR_P(CRT_CONSTANT_EX(call_info->caller_op_array, call_info->caller_init_opline->op2, ssa->rt_constants)))) != NULL) {
+ if (UNEXPECTED(zend_optimizer_is_disabled_func(info->name, info->name_len))) {
+ ret = MAY_BE_NULL;
+ } else if (info->info_func) {
+ ret = info->info_func(call_info, ssa);
+ } else {
+ ret = info->info;
+ }
+#if 0
+ } else {
+ fprintf(stderr, "Unknown internal function '%s'\n", func->common.function_name);
+#endif
+ }
+ } else {
+ // FIXME: the order of functions matters!!!
+ zend_func_info *info = ZEND_FUNC_INFO((zend_op_array*)call_info->callee_func);
+ if (info) {
+ ret = info->return_info.type;
+ }
+ }
+ if (!ret) {
+ ret = MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+ if (call_info->callee_func->type == ZEND_INTERNAL_FUNCTION) {
+ ret |= FUNC_MAY_WARN;
+ }
+ if (call_info->callee_func->common.fn_flags & ZEND_ACC_GENERATOR) {
+ ret = MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_OBJECT;
+ } else if (call_info->callee_func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) {
+ ret |= MAY_BE_REF;
+ } else {
+ ret |= MAY_BE_RC1 | MAY_BE_RCN;
+ }
+ }
+ return ret;
+}
+
+int zend_func_info_startup(void)
+{
+ zend_extension dummy;
+ size_t i;
+
+ if (zend_func_info_rid == -1) {
+ zend_func_info_rid = zend_get_resource_handle(&dummy);
+ if (zend_func_info_rid < 0) {
+ return FAILURE;
+ }
+
+ zend_hash_init(&func_info, sizeof(func_infos)/sizeof(func_info_t), NULL, NULL, 1);
+ for (i = 0; i < sizeof(func_infos)/sizeof(func_info_t); i++) {
+ if (zend_hash_str_add_ptr(&func_info, func_infos[i].name, func_infos[i].name_len, (void**)&func_infos[i]) == NULL) {
+ fprintf(stderr, "ERROR: Duplicate function info for \"%s\"\n", func_infos[i].name);
+ }
+ }
+ }
+
+ return SUCCESS;
+}
+
+int zend_func_info_shutdown(void)
+{
+ if (zend_func_info_rid != -1) {
+ zend_hash_destroy(&func_info);
+ zend_func_info_rid = -1;
+ }
+ return SUCCESS;
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * indent-tabs-mode: t
+ * End:
+ */
diff --git a/ext/opcache/Optimizer/zend_func_info.h b/ext/opcache/Optimizer/zend_func_info.h
new file mode 100644
index 0000000000..a126bef708
--- /dev/null
+++ b/ext/opcache/Optimizer/zend_func_info.h
@@ -0,0 +1,69 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine, Func Info |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1998-2017 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Dmitry Stogov <dmitry@zend.com> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef ZEND_FUNC_INFO_H
+#define ZEND_FUNC_INFO_H
+
+#include "zend_ssa.h"
+
+/* func flags */
+#define ZEND_FUNC_INDIRECT_VAR_ACCESS (1<<0)
+#define ZEND_FUNC_HAS_CALLS (1<<1)
+#define ZEND_FUNC_VARARG (1<<2)
+#define ZEND_FUNC_NO_LOOPS (1<<3)
+#define ZEND_FUNC_IRREDUCIBLE (1<<4)
+#define ZEND_FUNC_RECURSIVE (1<<7)
+#define ZEND_FUNC_RECURSIVE_DIRECTLY (1<<8)
+#define ZEND_FUNC_RECURSIVE_INDIRECTLY (1<<9)
+
+/* The following flags are valid only for return values of internal functions
+ * returned by zend_get_func_info()
+ */
+#define FUNC_MAY_WARN (1<<30)
+
+typedef struct _zend_func_info zend_func_info;
+typedef struct _zend_call_info zend_call_info;
+
+#define ZEND_FUNC_INFO(op_array) \
+ ((zend_func_info*)((op_array)->reserved[zend_func_info_rid]))
+
+#define ZEND_SET_FUNC_INFO(op_array, info) do { \
+ zend_func_info** pinfo = (zend_func_info**)&(op_array)->reserved[zend_func_info_rid]; \
+ *pinfo = info; \
+ } while (0)
+
+BEGIN_EXTERN_C()
+
+extern int zend_func_info_rid;
+
+uint32_t zend_get_func_info(const zend_call_info *call_info, const zend_ssa *ssa);
+
+int zend_func_info_startup(void);
+int zend_func_info_shutdown(void);
+
+END_EXTERN_C()
+
+#endif /* ZEND_FUNC_INFO_H */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * indent-tabs-mode: t
+ * End:
+ */
diff --git a/ext/opcache/Optimizer/zend_inference.c b/ext/opcache/Optimizer/zend_inference.c
new file mode 100644
index 0000000000..508ba6c3a1
--- /dev/null
+++ b/ext/opcache/Optimizer/zend_inference.c
@@ -0,0 +1,3932 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine, e-SSA based Type & Range Inference |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1998-2017 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Dmitry Stogov <dmitry@zend.com> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "php.h"
+#include "zend_compile.h"
+#include "zend_generators.h"
+#include "zend_inference.h"
+#include "zend_func_info.h"
+#include "zend_call_graph.h"
+#include "zend_worklist.h"
+
+/* The used range inference algorithm is described in:
+ * V. Campos, R. Rodrigues, I. de Assis Costa and F. Pereira.
+ * "Speed and Precision in Range Analysis", SBLP'12.
+ *
+ * There are a couple degrees of freedom, we use:
+ * * Propagation on SCCs.
+ * * e-SSA for live range splitting.
+ * * Only intra-procedural inference.
+ * * Widening with warmup passes, but without jump sets.
+ */
+
+/* Whether to handle symbolic range constraints */
+#define SYM_RANGE
+
+/* Whether to handle negative range constraints */
+#define NEG_RANGE
+
+/* Number of warmup passes to use prior to widening */
+#define RANGE_WARMUP_PASSES 16
+
+/* Logging for range inference in general */
+#if 0
+#define LOG_SSA_RANGE(...) fprintf(stderr, __VA_ARGS__)
+#else
+#define LOG_SSA_RANGE(...)
+#endif
+
+/* Logging for negative range constraints */
+#if 0
+#define LOG_NEG_RANGE(...) fprintf(stderr, __VA_ARGS__)
+#else
+#define LOG_NEG_RANGE(...)
+#endif
+
+/* Pop elements in unspecified order from worklist until it is empty */
+#define WHILE_WORKLIST(worklist, len, i) do { \
+ zend_bool _done = 0; \
+ while (!_done) { \
+ _done = 1; \
+ ZEND_BITSET_FOREACH(worklist, len, i) { \
+ zend_bitset_excl(worklist, i); \
+ _done = 0;
+
+#define WHILE_WORKLIST_END() \
+ } ZEND_BITSET_FOREACH_END(); \
+ } \
+} while (0)
+
+#define CHECK_SCC_VAR(var2) \
+ do { \
+ if (!ssa->vars[var2].no_val) { \
+ if (dfs[var2] < 0) { \
+ zend_ssa_check_scc_var(op_array, ssa, var2, index, dfs, root, stack); \
+ } \
+ if (ssa->vars[var2].scc < 0 && dfs[root[var]] >= dfs[root[var2]]) { \
+ root[var] = root[var2]; \
+ } \
+ } \
+ } while (0)
+
+#define CHECK_SCC_ENTRY(var2) \
+ do { \
+ if (ssa->vars[var2].scc != ssa->vars[var].scc) { \
+ ssa->vars[var2].scc_entry = 1; \
+ } \
+ } while (0)
+
+#define ADD_SCC_VAR(_var) \
+ do { \
+ if (ssa->vars[_var].scc == scc) { \
+ zend_bitset_incl(worklist, _var); \
+ } \
+ } while (0)
+
+#define ADD_SCC_VAR_1(_var) \
+ do { \
+ if (ssa->vars[_var].scc == scc && \
+ !zend_bitset_in(visited, _var)) { \
+ zend_bitset_incl(worklist, _var); \
+ } \
+ } while (0)
+
+#define FOR_EACH_DEFINED_VAR(line, MACRO) \
+ do { \
+ if (ssa->ops[line].op1_def >= 0) { \
+ MACRO(ssa->ops[line].op1_def); \
+ } \
+ if (ssa->ops[line].op2_def >= 0) { \
+ MACRO(ssa->ops[line].op2_def); \
+ } \
+ if (ssa->ops[line].result_def >= 0) { \
+ MACRO(ssa->ops[line].result_def); \
+ } \
+ if (op_array->opcodes[line].opcode == ZEND_OP_DATA) { \
+ if (ssa->ops[line-1].op1_def >= 0) { \
+ MACRO(ssa->ops[line-1].op1_def); \
+ } \
+ if (ssa->ops[line-1].op2_def >= 0) { \
+ MACRO(ssa->ops[line-1].op2_def); \
+ } \
+ if (ssa->ops[line-1].result_def >= 0) { \
+ MACRO(ssa->ops[line-1].result_def); \
+ } \
+ } else if ((uint32_t)line+1 < op_array->last && \
+ op_array->opcodes[line+1].opcode == ZEND_OP_DATA) { \
+ if (ssa->ops[line+1].op1_def >= 0) { \
+ MACRO(ssa->ops[line+1].op1_def); \
+ } \
+ if (ssa->ops[line+1].op2_def >= 0) { \
+ MACRO(ssa->ops[line+1].op2_def); \
+ } \
+ if (ssa->ops[line+1].result_def >= 0) { \
+ MACRO(ssa->ops[line+1].result_def); \
+ } \
+ } \
+ } while (0)
+
+
+#define FOR_EACH_VAR_USAGE(_var, MACRO) \
+ do { \
+ zend_ssa_phi *p = ssa->vars[_var].phi_use_chain; \
+ int use = ssa->vars[_var].use_chain; \
+ while (use >= 0) { \
+ FOR_EACH_DEFINED_VAR(use, MACRO); \
+ use = zend_ssa_next_use(ssa->ops, _var, use); \
+ } \
+ p = ssa->vars[_var].phi_use_chain; \
+ while (p) { \
+ MACRO(p->ssa_var); \
+ p = zend_ssa_next_use_phi(ssa, _var, p); \
+ } \
+ } while (0)
+
+static void zend_ssa_check_scc_var(const zend_op_array *op_array, zend_ssa *ssa, int var, int *index, int *dfs, int *root, zend_worklist_stack *stack) /* {{{ */
+{
+#ifdef SYM_RANGE
+ zend_ssa_phi *p;
+#endif
+
+ dfs[var] = *index;
+ (*index)++;
+ root[var] = var;
+
+ FOR_EACH_VAR_USAGE(var, CHECK_SCC_VAR);
+
+#ifdef SYM_RANGE
+ /* Process symbolic control-flow constraints */
+ p = ssa->vars[var].sym_use_chain;
+ while (p) {
+ CHECK_SCC_VAR(p->ssa_var);
+ p = p->sym_use_chain;
+ }
+#endif
+
+ if (root[var] == var) {
+ ssa->vars[var].scc = ssa->sccs;
+ while (stack->len > 0) {
+ int var2 = zend_worklist_stack_peek(stack);
+ if (dfs[var2] <= dfs[var]) {
+ break;
+ }
+ zend_worklist_stack_pop(stack);
+ ssa->vars[var2].scc = ssa->sccs;
+ }
+ ssa->sccs++;
+ } else {
+ zend_worklist_stack_push(stack, var);
+ }
+}
+/* }}} */
+
+int zend_ssa_find_sccs(const zend_op_array *op_array, zend_ssa *ssa) /* {{{ */
+{
+ int index = 0, *dfs, *root;
+ zend_worklist_stack stack;
+ int j;
+ ALLOCA_FLAG(dfs_use_heap)
+ ALLOCA_FLAG(root_use_heap)
+ ALLOCA_FLAG(stack_use_heap)
+
+ dfs = do_alloca(sizeof(int) * ssa->vars_count, dfs_use_heap);
+ memset(dfs, -1, sizeof(int) * ssa->vars_count);
+ root = do_alloca(sizeof(int) * ssa->vars_count, root_use_heap);
+ ZEND_WORKLIST_STACK_ALLOCA(&stack, ssa->vars_count, stack_use_heap);
+
+ /* Find SCCs using Tarjan's algorithm. */
+ for (j = 0; j < ssa->vars_count; j++) {
+ if (!ssa->vars[j].no_val && dfs[j] < 0) {
+ zend_ssa_check_scc_var(op_array, ssa, j, &index, dfs, root, &stack);
+ }
+ }
+
+ /* Revert SCC order. This results in a topological order. */
+ for (j = 0; j < ssa->vars_count; j++) {
+ if (ssa->vars[j].scc >= 0) {
+ ssa->vars[j].scc = ssa->sccs - (ssa->vars[j].scc + 1);
+ }
+ }
+
+ for (j = 0; j < ssa->vars_count; j++) {
+ if (ssa->vars[j].scc >= 0) {
+ int var = j;
+ if (root[j] == j) {
+ ssa->vars[j].scc_entry = 1;
+ }
+ FOR_EACH_VAR_USAGE(var, CHECK_SCC_ENTRY);
+ }
+ }
+
+ ZEND_WORKLIST_STACK_FREE_ALLOCA(&stack, stack_use_heap);
+ free_alloca(root, root_use_heap);
+ free_alloca(dfs, dfs_use_heap);
+
+ return SUCCESS;
+}
+/* }}} */
+
+static inline zend_bool is_no_val_use(const zend_op *opline, const zend_ssa_op *ssa_op, int var)
+{
+ if (opline->opcode == ZEND_ASSIGN ||
+ (opline->opcode == ZEND_UNSET_VAR && (opline->extended_value & ZEND_QUICK_SET))) {
+ return ssa_op->op1_use == var && ssa_op->op2_use != var;
+ }
+ if (opline->opcode == ZEND_FE_FETCH_R) {
+ return ssa_op->op2_use == var && ssa_op->op1_use != var;
+ }
+ return 0;
+}
+
+int zend_ssa_find_false_dependencies(const zend_op_array *op_array, zend_ssa *ssa) /* {{{ */
+{
+ zend_ssa_var *ssa_vars = ssa->vars;
+ zend_ssa_op *ssa_ops = ssa->ops;
+ int ssa_vars_count = ssa->vars_count;
+ zend_bitset worklist;
+ int i, j, use;
+ zend_ssa_phi *p;
+ ALLOCA_FLAG(use_heap);
+
+ if (!op_array->function_name || !ssa->vars || !ssa->ops) {
+ return SUCCESS;
+ }
+
+ worklist = do_alloca(sizeof(zend_ulong) * zend_bitset_len(ssa_vars_count), use_heap);
+ memset(worklist, 0, sizeof(zend_ulong) * zend_bitset_len(ssa_vars_count));
+
+ for (i = 0; i < ssa_vars_count; i++) {
+ ssa_vars[i].no_val = 1; /* mark as unused */
+ use = ssa->vars[i].use_chain;
+ while (use >= 0) {
+ if (!is_no_val_use(&op_array->opcodes[use], &ssa->ops[use], i)) {
+ ssa_vars[i].no_val = 0; /* used directly */
+ zend_bitset_incl(worklist, i);
+ break;
+ }
+ use = zend_ssa_next_use(ssa_ops, i, use);
+ }
+ }
+
+ WHILE_WORKLIST(worklist, zend_bitset_len(ssa_vars_count), i) {
+ if (ssa_vars[i].definition_phi) {
+ /* mark all possible sources as used */
+ p = ssa_vars[i].definition_phi;
+ if (p->pi >= 0) {
+ if (ssa_vars[p->sources[0]].no_val) {
+ ssa_vars[p->sources[0]].no_val = 0; /* used indirectly */
+ zend_bitset_incl(worklist, p->sources[0]);
+ }
+ } else {
+ for (j = 0; j < ssa->cfg.blocks[p->block].predecessors_count; j++) {
+ if (p->sources[j] >= 0 && ssa->vars[p->sources[j]].no_val) {
+ ssa_vars[p->sources[j]].no_val = 0; /* used indirectly */
+ zend_bitset_incl(worklist, p->sources[j]);
+ }
+ }
+ }
+ }
+ } WHILE_WORKLIST_END();
+
+ free_alloca(worklist, use_heap);
+
+ return SUCCESS;
+}
+/* }}} */
+
+/* From "Hacker's Delight" */
+zend_ulong minOR(zend_ulong a, zend_ulong b, zend_ulong c, zend_ulong d)
+{
+ zend_ulong m, temp;
+
+ m = 1L << (sizeof(zend_ulong) * 8 - 1);
+ while (m != 0) {
+ if (~a & c & m) {
+ temp = (a | m) & -m;
+ if (temp <= b) {
+ a = temp;
+ break;
+ }
+ } else if (a & ~c & m) {
+ temp = (c | m) & -m;
+ if (temp <= d) {
+ c = temp;
+ break;
+ }
+ }
+ m = m >> 1;
+ }
+ return a | c;
+}
+
+zend_ulong maxOR(zend_ulong a, zend_ulong b, zend_ulong c, zend_ulong d)
+{
+ zend_ulong m, temp;
+
+ m = 1L << (sizeof(zend_ulong) * 8 - 1);
+ while (m != 0) {
+ if (b & d & m) {
+ temp = (b - m) | (m - 1);
+ if (temp >= a) {
+ b = temp;
+ break;
+ }
+ temp = (d - m) | (m - 1);
+ if (temp >= c) {
+ d = temp;
+ break;
+ }
+ }
+ m = m >> 1;
+ }
+ return b | d;
+}
+
+zend_ulong minAND(zend_ulong a, zend_ulong b, zend_ulong c, zend_ulong d)
+{
+ zend_ulong m, temp;
+
+ m = 1L << (sizeof(zend_ulong) * 8 - 1);
+ while (m != 0) {
+ if (~a & ~c & m) {
+ temp = (a | m) & -m;
+ if (temp <= b) {
+ a = temp;
+ break;
+ }
+ temp = (c | m) & -m;
+ if (temp <= d) {
+ c = temp;
+ break;
+ }
+ }
+ m = m >> 1;
+ }
+ return a & c;
+}
+
+zend_ulong maxAND(zend_ulong a, zend_ulong b, zend_ulong c, zend_ulong d)
+{
+ zend_ulong m, temp;
+
+ m = 1L << (sizeof(zend_ulong) * 8 - 1);
+ while (m != 0) {
+ if (b & ~d & m) {
+ temp = (b | ~m) | (m - 1);
+ if (temp >= a) {
+ b = temp;
+ break;
+ }
+ } else if (~b & d & m) {
+ temp = (d | ~m) | (m - 1);
+ if (temp >= c) {
+ d = temp;
+ break;
+ }
+ }
+ m = m >> 1;
+ }
+ return b & d;
+}
+
+zend_ulong minXOR(zend_ulong a, zend_ulong b, zend_ulong c, zend_ulong d)
+{
+ return minAND(a, b, ~d, ~c) | minAND(~b, ~a, c, d);
+}
+
+zend_ulong maxXOR(zend_ulong a, zend_ulong b, zend_ulong c, zend_ulong d)
+{
+ return maxOR(0, maxAND(a, b, ~d, ~c), 0, maxAND(~b, ~a, c, d));
+}
+
+/* Based on "Hacker's Delight" */
+
+/*
+0: + + + + 0 0 0 0 => 0 0 + min/max
+2: + + - + 0 0 1 0 => 1 0 ? min(a,b,c,-1)/max(a,b,0,d)
+3: + + - - 0 0 1 1 => 1 1 - min/max
+8: - + + + 1 0 0 0 => 1 0 ? min(a,-1,b,d)/max(0,b,c,d)
+a: - + - + 1 0 1 0 => 1 0 ? MIN(a,c)/max(0,b,0,d)
+b: - + - - 1 0 1 1 => 1 1 - c/-1
+c: - - + + 1 1 0 0 => 1 1 - min/max
+e: - - - + 1 1 1 0 => 1 1 - a/-1
+f - - - - 1 1 1 1 => 1 1 - min/max
+*/
+static void zend_ssa_range_or(zend_long a, zend_long b, zend_long c, zend_long d, zend_ssa_range *tmp)
+{
+ int x = ((a < 0) ? 8 : 0) |
+ ((b < 0) ? 4 : 0) |
+ ((c < 0) ? 2 : 0) |
+ ((d < 0) ? 2 : 0);
+ switch (x) {
+ case 0x0:
+ case 0x3:
+ case 0xc:
+ case 0xf:
+ tmp->min = minOR(a, b, c, d);
+ tmp->max = maxOR(a, b, c, d);
+ break;
+ case 0x2:
+ tmp->min = minOR(a, b, c, -1);
+ tmp->max = maxOR(a, b, 0, d);
+ break;
+ case 0x8:
+ tmp->min = minOR(a, -1, c, d);
+ tmp->max = maxOR(0, b, c, d);
+ break;
+ case 0xa:
+ tmp->min = MIN(a, c);
+ tmp->max = maxOR(0, b, 0, d);
+ break;
+ case 0xb:
+ tmp->min = c;
+ tmp->max = -1;
+ break;
+ case 0xe:
+ tmp->min = a;
+ tmp->max = -1;
+ break;
+ }
+}
+
+/*
+0: + + + + 0 0 0 0 => 0 0 + min/max
+2: + + - + 0 0 1 0 => 0 0 + 0/b
+3: + + - - 0 0 1 1 => 0 0 + min/max
+8: - + + + 1 0 0 0 => 0 0 + 0/d
+a: - + - + 1 0 1 0 => 1 0 ? min(a,-1,c,-1)/NAX(b,d)
+b: - + - - 1 0 1 1 => 1 0 ? min(a,-1,c,d)/max(0,b,c,d)
+c: - - + + 1 1 0 0 => 1 1 - min/max
+e: - - - + 1 1 1 0 => 1 0 ? min(a,b,c,-1)/max(a,b,0,d)
+f - - - - 1 1 1 1 => 1 1 - min/max
+*/
+static void zend_ssa_range_and(zend_long a, zend_long b, zend_long c, zend_long d, zend_ssa_range *tmp)
+{
+ int x = ((a < 0) ? 8 : 0) |
+ ((b < 0) ? 4 : 0) |
+ ((c < 0) ? 2 : 0) |
+ ((d < 0) ? 2 : 0);
+ switch (x) {
+ case 0x0:
+ case 0x3:
+ case 0xc:
+ case 0xf:
+ tmp->min = minAND(a, b, c, d);
+ tmp->max = maxAND(a, b, c, d);
+ break;
+ case 0x2:
+ tmp->min = 0;
+ tmp->max = b;
+ break;
+ case 0x8:
+ tmp->min = 0;
+ tmp->max = d;
+ break;
+ case 0xa:
+ tmp->min = minAND(a, -1, c, -1);
+ tmp->max = MAX(b, d);
+ break;
+ case 0xb:
+ tmp->min = minAND(a, -1, c, d);
+ tmp->max = maxAND(0, b, c, d);
+ break;
+ case 0xe:
+ tmp->min = minAND(a, b, c, -1);
+ tmp->max = maxAND(a, b, 0, d);
+ break;
+ }
+}
+
+/* Get the normal op corresponding to a compound assignment op */
+static inline zend_uchar get_compound_assign_op(zend_uchar opcode) {
+ switch (opcode) {
+ case ZEND_ASSIGN_ADD: return ZEND_ADD;
+ case ZEND_ASSIGN_SUB: return ZEND_SUB;
+ case ZEND_ASSIGN_MUL: return ZEND_MUL;
+ case ZEND_ASSIGN_DIV: return ZEND_DIV;
+ case ZEND_ASSIGN_MOD: return ZEND_MOD;
+ case ZEND_ASSIGN_SL: return ZEND_SL;
+ case ZEND_ASSIGN_SR: return ZEND_SR;
+ case ZEND_ASSIGN_CONCAT: return ZEND_CONCAT;
+ case ZEND_ASSIGN_BW_OR: return ZEND_BW_OR;
+ case ZEND_ASSIGN_BW_AND: return ZEND_BW_AND;
+ case ZEND_ASSIGN_BW_XOR: return ZEND_BW_XOR;
+ case ZEND_ASSIGN_POW: return ZEND_POW;
+ EMPTY_SWITCH_DEFAULT_CASE()
+ }
+}
+
+static int zend_inference_calc_binary_op_range(
+ const zend_op_array *op_array, zend_ssa *ssa,
+ zend_op *opline, zend_ssa_op *ssa_op, zend_uchar opcode, zend_ssa_range *tmp) {
+ zend_long op1_min, op2_min, op1_max, op2_max, t1, t2, t3, t4;
+
+ switch (opcode) {
+ case ZEND_ADD:
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+ tmp->min = op1_min + op2_min;
+ tmp->max = op1_max + op2_max;
+ if (OP1_RANGE_UNDERFLOW() ||
+ OP2_RANGE_UNDERFLOW() ||
+ (op1_min < 0 && op2_min < 0 && tmp->min >= 0)) {
+ tmp->underflow = 1;
+ tmp->min = ZEND_LONG_MIN;
+ }
+ if (OP1_RANGE_OVERFLOW() ||
+ OP2_RANGE_OVERFLOW() ||
+ (op1_max > 0 && op2_max > 0 && tmp->max <= 0)) {
+ tmp->overflow = 1;
+ tmp->max = ZEND_LONG_MAX;
+ }
+ return 1;
+ }
+ break;
+ case ZEND_SUB:
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+ tmp->min = op1_min - op2_max;
+ tmp->max = op1_max - op2_min;
+ if (OP1_RANGE_UNDERFLOW() ||
+ OP2_RANGE_OVERFLOW() ||
+ (op1_min < 0 && op2_max > 0 && tmp->min >= 0)) {
+ tmp->underflow = 1;
+ tmp->min = ZEND_LONG_MIN;
+ }
+ if (OP1_RANGE_OVERFLOW() ||
+ OP2_RANGE_UNDERFLOW() ||
+ (op1_max > 0 && op2_min < 0 && tmp->max <= 0)) {
+ tmp->overflow = 1;
+ tmp->max = ZEND_LONG_MAX;
+ }
+ return 1;
+ }
+ break;
+ case ZEND_MUL:
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+ t1 = op1_min * op2_min;
+ t2 = op1_min * op2_max;
+ t3 = op1_max * op2_min;
+ t4 = op1_max * op2_max;
+ // FIXME: more careful overflow checks?
+ if (OP1_RANGE_UNDERFLOW() ||
+ OP2_RANGE_UNDERFLOW() ||
+ OP1_RANGE_OVERFLOW() ||
+ OP2_RANGE_OVERFLOW() ||
+ (double)t1 != (double)op1_min * (double)op2_min ||
+ (double)t2 != (double)op1_min * (double)op2_max ||
+ (double)t3 != (double)op1_max * (double)op2_min ||
+ (double)t4 != (double)op1_max * (double)op2_max) {
+ tmp->underflow = 1;
+ tmp->overflow = 1;
+ tmp->min = ZEND_LONG_MIN;
+ tmp->max = ZEND_LONG_MAX;
+ } else {
+ tmp->min = MIN(MIN(t1, t2), MIN(t3, t4));
+ tmp->max = MAX(MAX(t1, t2), MAX(t3, t4));
+ }
+ return 1;
+ }
+ break;
+ case ZEND_DIV:
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+ if (op2_min <= 0 && op2_max >= 0) {
+ break;
+ }
+ if (op1_min == ZEND_LONG_MIN && op2_max == -1) {
+ /* Avoid ill-defined division, which may trigger SIGFPE. */
+ break;
+ }
+ t1 = op1_min / op2_min;
+ t2 = op1_min / op2_max;
+ t3 = op1_max / op2_min;
+ t4 = op1_max / op2_max;
+ // FIXME: more careful overflow checks?
+ if (OP1_RANGE_UNDERFLOW() ||
+ OP2_RANGE_UNDERFLOW() ||
+ OP1_RANGE_OVERFLOW() ||
+ OP2_RANGE_OVERFLOW() ||
+ t1 != (zend_long)((double)op1_min / (double)op2_min) ||
+ t2 != (zend_long)((double)op1_min / (double)op2_max) ||
+ t3 != (zend_long)((double)op1_max / (double)op2_min) ||
+ t4 != (zend_long)((double)op1_max / (double)op2_max)) {
+ tmp->underflow = 1;
+ tmp->overflow = 1;
+ tmp->min = ZEND_LONG_MIN;
+ tmp->max = ZEND_LONG_MAX;
+ } else {
+ tmp->min = MIN(MIN(t1, t2), MIN(t3, t4));
+ tmp->max = MAX(MAX(t1, t2), MAX(t3, t4));
+ }
+ return 1;
+ }
+ break;
+ case ZEND_MOD:
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ if (OP1_RANGE_UNDERFLOW() ||
+ OP2_RANGE_UNDERFLOW() ||
+ OP1_RANGE_OVERFLOW() ||
+ OP2_RANGE_OVERFLOW()) {
+ tmp->min = ZEND_LONG_MIN;
+ tmp->max = ZEND_LONG_MAX;
+ } else {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+ if (op2_min == 0 || op2_max == 0) {
+ /* avoid division by zero */
+ break;
+ }
+ t1 = (op2_min == -1) ? 0 : (op1_min % op2_min);
+ t2 = (op2_max == -1) ? 0 : (op1_min % op2_max);
+ t3 = (op2_min == -1) ? 0 : (op1_max % op2_min);
+ t4 = (op2_max == -1) ? 0 : (op1_max % op2_max);
+ tmp->min = MIN(MIN(t1, t2), MIN(t3, t4));
+ tmp->max = MAX(MAX(t1, t2), MAX(t3, t4));
+ }
+ return 1;
+ }
+ break;
+ case ZEND_SL:
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ if (OP1_RANGE_UNDERFLOW() ||
+ OP2_RANGE_UNDERFLOW() ||
+ OP1_RANGE_OVERFLOW() ||
+ OP2_RANGE_OVERFLOW()) {
+ tmp->min = ZEND_LONG_MIN;
+ tmp->max = ZEND_LONG_MAX;
+ } else {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+ t1 = op1_min << op2_min;
+ t2 = op1_min << op2_max;
+ t3 = op1_max << op2_min;
+ t4 = op1_max << op2_max;
+ tmp->min = MIN(MIN(t1, t2), MIN(t3, t4));
+ tmp->max = MAX(MAX(t1, t2), MAX(t3, t4));
+ }
+ return 1;
+ }
+ break;
+ case ZEND_SR:
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ if (OP1_RANGE_UNDERFLOW() ||
+ OP2_RANGE_UNDERFLOW() ||
+ OP1_RANGE_OVERFLOW() ||
+ OP2_RANGE_OVERFLOW()) {
+ tmp->min = ZEND_LONG_MIN;
+ tmp->max = ZEND_LONG_MAX;
+ } else {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+ t1 = op1_min >> op2_min;
+ t2 = op1_min >> op2_max;
+ t3 = op1_max >> op2_min;
+ t4 = op1_max >> op2_max;
+ tmp->min = MIN(MIN(t1, t2), MIN(t3, t4));
+ tmp->max = MAX(MAX(t1, t2), MAX(t3, t4));
+ }
+ return 1;
+ }
+ break;
+ case ZEND_BW_OR:
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ if (OP1_RANGE_UNDERFLOW() ||
+ OP2_RANGE_UNDERFLOW() ||
+ OP1_RANGE_OVERFLOW() ||
+ OP2_RANGE_OVERFLOW()) {
+ tmp->min = ZEND_LONG_MIN;
+ tmp->max = ZEND_LONG_MAX;
+ } else {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+ zend_ssa_range_or(op1_min, op1_max, op2_min, op2_max, tmp);
+ }
+ return 1;
+ }
+ break;
+ case ZEND_BW_AND:
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ if (OP1_RANGE_UNDERFLOW() ||
+ OP2_RANGE_UNDERFLOW() ||
+ OP1_RANGE_OVERFLOW() ||
+ OP2_RANGE_OVERFLOW()) {
+ tmp->min = ZEND_LONG_MIN;
+ tmp->max = ZEND_LONG_MAX;
+ } else {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+ zend_ssa_range_and(op1_min, op1_max, op2_min, op2_max, tmp);
+ }
+ return 1;
+ }
+ break;
+ case ZEND_BW_XOR:
+ // TODO
+ break;
+ EMPTY_SWITCH_DEFAULT_CASE()
+ }
+ return 0;
+}
+
+int zend_inference_calc_range(const zend_op_array *op_array, zend_ssa *ssa, int var, int widening, int narrowing, zend_ssa_range *tmp)
+{
+ uint32_t line;
+ zend_op *opline;
+ zend_long op1_min, op2_min, op1_max, op2_max;
+
+ if (ssa->vars[var].definition_phi) {
+ zend_ssa_phi *p = ssa->vars[var].definition_phi;
+ int i;
+
+ tmp->underflow = 0;
+ tmp->min = ZEND_LONG_MAX;
+ tmp->max = ZEND_LONG_MIN;
+ tmp->overflow = 0;
+ if (p->pi >= 0 && p->has_range_constraint) {
+ zend_ssa_range_constraint *constraint = &p->constraint.range;
+ if (constraint->negative) {
+ if (ssa->var_info[p->sources[0]].has_range) {
+ *tmp = ssa->var_info[p->sources[0]].range;
+ } else if (narrowing) {
+ tmp->underflow = 1;
+ tmp->min = ZEND_LONG_MIN;
+ tmp->max = ZEND_LONG_MAX;
+ tmp->overflow = 1;
+ }
+
+#ifdef NEG_RANGE
+ if (constraint->min_ssa_var < 0 &&
+ constraint->max_ssa_var < 0 &&
+ ssa->var_info[p->ssa_var].has_range) {
+ LOG_NEG_RANGE("%s() #%d [%ld..%ld] -> [%ld..%ld]?\n",
+ ZSTR_VAL(op_array->function_name),
+ p->ssa_var,
+ ssa->var_info[p->ssa_var].range.min,
+ ssa->var_info[p->ssa_var].range.max,
+ tmp->min,
+ tmp->max);
+ if (constraint->negative == NEG_USE_LT &&
+ tmp->max >= constraint->range.min) {
+ tmp->overflow = 0;
+ tmp->max = constraint->range.min - 1;
+ LOG_NEG_RANGE(" => [%ld..%ld]\n", tmp->min, tmp->max);
+ } else if (constraint->negative == NEG_USE_GT &&
+ tmp->min <= constraint->range.max) {
+ tmp->underflow = 0;
+ tmp->min = constraint->range.max + 1;
+ LOG_NEG_RANGE(" => [%ld..%ld]\n", tmp->min, tmp->max);
+ }
+ }
+#endif
+ } else if (ssa->var_info[p->sources[0]].has_range) {
+ /* intersection */
+ *tmp = ssa->var_info[p->sources[0]].range;
+ if (constraint->min_ssa_var < 0) {
+ tmp->underflow = constraint->range.underflow && tmp->underflow;
+ tmp->min = MAX(constraint->range.min, tmp->min);
+#ifdef SYM_RANGE
+ } else if (narrowing && ssa->var_info[constraint->min_ssa_var].has_range) {
+ tmp->underflow = ssa->var_info[constraint->min_ssa_var].range.underflow && tmp->underflow;
+ tmp->min = MAX(ssa->var_info[constraint->min_ssa_var].range.min + constraint->range.min, tmp->min);
+#endif
+ }
+ if (constraint->max_ssa_var < 0) {
+ tmp->max = MIN(constraint->range.max, tmp->max);
+ tmp->overflow = constraint->range.overflow && tmp->overflow;
+#ifdef SYM_RANGE
+ } else if (narrowing && ssa->var_info[constraint->max_ssa_var].has_range) {
+ tmp->max = MIN(ssa->var_info[constraint->max_ssa_var].range.max + constraint->range.max, tmp->max);
+ tmp->overflow = ssa->var_info[constraint->max_ssa_var].range.overflow && tmp->overflow;
+#endif
+ }
+ } else if (narrowing) {
+ if (constraint->min_ssa_var < 0) {
+ tmp->underflow = constraint->range.underflow;
+ tmp->min = constraint->range.min;
+#ifdef SYM_RANGE
+ } else if (narrowing && ssa->var_info[constraint->min_ssa_var].has_range) {
+ tmp->underflow = ssa->var_info[constraint->min_ssa_var].range.underflow;
+ tmp->min = ssa->var_info[constraint->min_ssa_var].range.min + constraint->range.min;
+#endif
+ } else {
+ tmp->underflow = 1;
+ tmp->min = ZEND_LONG_MIN;
+ }
+ if (constraint->max_ssa_var < 0) {
+ tmp->max = constraint->range.max;
+ tmp->overflow = constraint->range.overflow;
+#ifdef SYM_RANGE
+ } else if (narrowing && ssa->var_info[constraint->max_ssa_var].has_range) {
+ tmp->max = ssa->var_info[constraint->max_ssa_var].range.max + constraint->range.max;
+ tmp->overflow = ssa->var_info[constraint->max_ssa_var].range.overflow;
+#endif
+ } else {
+ tmp->max = ZEND_LONG_MAX;
+ tmp->overflow = 1;
+ }
+ }
+ } else {
+ for (i = 0; i < ssa->cfg.blocks[p->block].predecessors_count; i++) {
+ if (p->sources[i] >= 0 && ssa->var_info[p->sources[i]].has_range) {
+ /* union */
+ tmp->underflow |= ssa->var_info[p->sources[i]].range.underflow;
+ tmp->min = MIN(tmp->min, ssa->var_info[p->sources[i]].range.min);
+ tmp->max = MAX(tmp->max, ssa->var_info[p->sources[i]].range.max);
+ tmp->overflow |= ssa->var_info[p->sources[i]].range.overflow;
+ } else if (narrowing) {
+ tmp->underflow = 1;
+ tmp->min = ZEND_LONG_MIN;
+ tmp->max = ZEND_LONG_MAX;
+ tmp->overflow = 1;
+ }
+ }
+ }
+ return (tmp->min <= tmp->max);
+ } else if (ssa->vars[var].definition < 0) {
+ if (var < op_array->last_var &&
+ op_array->function_name) {
+
+ tmp->min = 0;
+ tmp->max = 0;
+ tmp->underflow = 0;
+ tmp->overflow = 0;
+ return 1;
+ }
+ return 0;
+ }
+ line = ssa->vars[var].definition;
+ opline = op_array->opcodes + line;
+
+ tmp->underflow = 0;
+ tmp->overflow = 0;
+ switch (opline->opcode) {
+ case ZEND_ADD:
+ case ZEND_SUB:
+ case ZEND_MUL:
+ case ZEND_DIV:
+ case ZEND_MOD:
+ case ZEND_SL:
+ case ZEND_SR:
+ case ZEND_BW_OR:
+ case ZEND_BW_AND:
+ case ZEND_BW_XOR:
+ if (ssa->ops[line].result_def == var) {
+ return zend_inference_calc_binary_op_range(
+ op_array, ssa, opline, &ssa->ops[line], opline->opcode, tmp);
+ }
+ break;
+
+ case ZEND_BW_NOT:
+ if (ssa->ops[line].result_def == var) {
+ if (OP1_HAS_RANGE()) {
+ if (OP1_RANGE_UNDERFLOW() ||
+ OP1_RANGE_OVERFLOW()) {
+ tmp->min = ZEND_LONG_MIN;
+ tmp->max = ZEND_LONG_MAX;
+ } else {
+ op1_min = OP1_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ tmp->min = ~op1_max;
+ tmp->max = ~op1_min;
+ }
+ return 1;
+ }
+ }
+ break;
+ case ZEND_CAST:
+ if (ssa->ops[line].op1_def == var) {
+ if (ssa->ops[line].op1_def >= 0) {
+ if (OP1_HAS_RANGE()) {
+ tmp->underflow = OP1_RANGE_UNDERFLOW();
+ tmp->min = OP1_MIN_RANGE();
+ tmp->max = OP1_MAX_RANGE();
+ tmp->overflow = OP1_RANGE_OVERFLOW();
+ return 1;
+ }
+ }
+ } else if (ssa->ops[line].result_def == var) {
+ if (opline->extended_value == IS_NULL) {
+ tmp->min = 0;
+ tmp->max = 0;
+ return 1;
+ } else if (opline->extended_value == _IS_BOOL) {
+ if (OP1_HAS_RANGE()) {
+ op1_min = OP1_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ tmp->min = (op1_min > 0 || op1_max < 0);
+ tmp->max = (op1_min != 0 || op1_max != 0);
+ return 1;
+ } else {
+ tmp->min = 0;
+ tmp->max = 1;
+ return 1;
+ }
+ } else if (opline->extended_value == IS_LONG) {
+ if (OP1_HAS_RANGE()) {
+ tmp->min = OP1_MIN_RANGE();
+ tmp->max = OP1_MAX_RANGE();
+ return 1;
+ } else {
+ tmp->min = ZEND_LONG_MIN;
+ tmp->max = ZEND_LONG_MAX;
+ return 1;
+ }
+ }
+ }
+ break;
+ case ZEND_BOOL:
+ case ZEND_JMPZ_EX:
+ case ZEND_JMPNZ_EX:
+ if (ssa->ops[line].result_def == var) {
+ if (OP1_HAS_RANGE()) {
+ op1_min = OP1_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ tmp->min = (op1_min > 0 || op1_max < 0);
+ tmp->max = (op1_min != 0 || op1_max != 0);
+ return 1;
+ } else {
+ tmp->min = 0;
+ tmp->max = 1;
+ return 1;
+ }
+ }
+ break;
+ case ZEND_BOOL_NOT:
+ if (ssa->ops[line].result_def == var) {
+ if (OP1_HAS_RANGE()) {
+ op1_min = OP1_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ tmp->min = (op1_min == 0 && op1_max == 0);
+ tmp->max = (op1_min <= 0 && op1_max >= 0);
+ return 1;
+ } else {
+ tmp->min = 0;
+ tmp->max = 1;
+ return 1;
+ }
+ }
+ break;
+ case ZEND_BOOL_XOR:
+ if (ssa->ops[line].result_def == var) {
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+ op1_min = (op1_min > 0 || op1_max < 0);
+ op1_max = (op1_min != 0 || op1_max != 0);
+ op2_min = (op2_min > 0 || op2_max < 0);
+ op2_max = (op2_min != 0 || op2_max != 0);
+ tmp->min = 0;
+ tmp->max = 1;
+ if (op1_min == op1_max && op2_min == op2_max) {
+ if (op1_min == op2_min) {
+ tmp->max = 0;
+ } else {
+ tmp->min = 1;
+ }
+ }
+ return 1;
+ } else {
+ tmp->min = 0;
+ tmp->max = 1;
+ return 1;
+ }
+ }
+ break;
+ case ZEND_IS_IDENTICAL:
+ case ZEND_IS_EQUAL:
+ if (ssa->ops[line].result_def == var) {
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+
+ tmp->min = (op1_min == op1_max &&
+ op2_min == op2_max &&
+ op1_min == op2_max);
+ tmp->max = (op1_min <= op2_max && op1_max >= op2_min);
+ return 1;
+ } else {
+ tmp->min = 0;
+ tmp->max = 1;
+ return 1;
+ }
+ }
+ break;
+ case ZEND_IS_NOT_IDENTICAL:
+ case ZEND_IS_NOT_EQUAL:
+ if (ssa->ops[line].result_def == var) {
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+
+ tmp->min = (op1_min > op2_max || op1_max < op2_min);
+ tmp->max = (op1_min != op1_max ||
+ op2_min != op2_max ||
+ op1_min != op2_max);
+ return 1;
+ } else {
+ tmp->min = 0;
+ tmp->max = 1;
+ return 1;
+ }
+ }
+ break;
+ case ZEND_IS_SMALLER:
+ if (ssa->ops[line].result_def == var) {
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+
+ tmp->min = op1_max < op2_min;
+ tmp->max = op1_min < op2_max;
+ return 1;
+ } else {
+ tmp->min = 0;
+ tmp->max = 1;
+ return 1;
+ }
+ }
+ break;
+ case ZEND_IS_SMALLER_OR_EQUAL:
+ if (ssa->ops[line].result_def == var) {
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+
+ tmp->min = op1_max <= op2_min;
+ tmp->max = op1_min <= op2_max;
+ return 1;
+ } else {
+ tmp->min = 0;
+ tmp->max = 1;
+ return 1;
+ }
+ }
+ break;
+ case ZEND_QM_ASSIGN:
+ case ZEND_JMP_SET:
+ case ZEND_COALESCE:
+ if (ssa->ops[line].op1_def == var) {
+ if (ssa->ops[line].op1_def >= 0) {
+ if (OP1_HAS_RANGE()) {
+ tmp->underflow = OP1_RANGE_UNDERFLOW();
+ tmp->min = OP1_MIN_RANGE();
+ tmp->max = OP1_MAX_RANGE();
+ tmp->overflow = OP1_RANGE_OVERFLOW();
+ return 1;
+ }
+ }
+ }
+ if (ssa->ops[line].result_def == var) {
+ if (OP1_HAS_RANGE()) {
+ tmp->min = OP1_MIN_RANGE();
+ tmp->max = OP1_MAX_RANGE();
+ tmp->underflow = OP1_RANGE_UNDERFLOW();
+ tmp->overflow = OP1_RANGE_OVERFLOW();
+ return 1;
+ }
+ }
+ break;
+ case ZEND_ASSERT_CHECK:
+ if (ssa->ops[line].result_def == var) {
+ tmp->min = 0;
+ tmp->max = 1;
+ return 1;
+ }
+ break;
+ case ZEND_SEND_VAR:
+ if (ssa->ops[line].op1_def == var) {
+ if (ssa->ops[line].op1_def >= 0) {
+ if (OP1_HAS_RANGE()) {
+ tmp->underflow = OP1_RANGE_UNDERFLOW();
+ tmp->min = OP1_MIN_RANGE();
+ tmp->max = OP1_MAX_RANGE();
+ tmp->overflow = OP1_RANGE_OVERFLOW();
+ return 1;
+ }
+ }
+ }
+ break;
+ case ZEND_PRE_INC:
+ if (ssa->ops[line].op1_def == var || ssa->ops[line].result_def == var) {
+ if (OP1_HAS_RANGE()) {
+ tmp->min = OP1_MIN_RANGE();
+ tmp->max = OP1_MAX_RANGE();
+ tmp->underflow = OP1_RANGE_UNDERFLOW();
+ tmp->overflow = OP1_RANGE_OVERFLOW();
+ if (tmp->max < ZEND_LONG_MAX) {
+ tmp->max++;
+ } else {
+ tmp->overflow = 1;
+ }
+ if (tmp->min < ZEND_LONG_MAX && !tmp->underflow) {
+ tmp->min++;
+ }
+ return 1;
+ }
+ }
+ break;
+ case ZEND_PRE_DEC:
+ if (ssa->ops[line].op1_def == var || ssa->ops[line].result_def == var) {
+ if (OP1_HAS_RANGE()) {
+ tmp->min = OP1_MIN_RANGE();
+ tmp->max = OP1_MAX_RANGE();
+ tmp->underflow = OP1_RANGE_UNDERFLOW();
+ tmp->overflow = OP1_RANGE_OVERFLOW();
+ if (tmp->min > ZEND_LONG_MIN) {
+ tmp->min--;
+ } else {
+ tmp->underflow = 1;
+ }
+ if (tmp->max > ZEND_LONG_MIN && !tmp->overflow) {
+ tmp->max--;
+ }
+ return 1;
+ }
+ }
+ break;
+ case ZEND_POST_INC:
+ if (ssa->ops[line].op1_def == var || ssa->ops[line].result_def == var) {
+ if (OP1_HAS_RANGE()) {
+ tmp->min = OP1_MIN_RANGE();
+ tmp->max = OP1_MAX_RANGE();
+ tmp->underflow = OP1_RANGE_UNDERFLOW();
+ tmp->overflow = OP1_RANGE_OVERFLOW();
+ if (ssa->ops[line].result_def == var) {
+ return 1;
+ }
+ if (tmp->max < ZEND_LONG_MAX) {
+ tmp->max++;
+ } else {
+ tmp->overflow = 1;
+ }
+ if (tmp->min < ZEND_LONG_MAX && !tmp->underflow) {
+ tmp->min++;
+ }
+ return 1;
+ }
+ }
+ break;
+ case ZEND_POST_DEC:
+ if (ssa->ops[line].op1_def == var || ssa->ops[line].result_def == var) {
+ if (OP1_HAS_RANGE()) {
+ tmp->min = OP1_MIN_RANGE();
+ tmp->max = OP1_MAX_RANGE();
+ tmp->underflow = OP1_RANGE_UNDERFLOW();
+ tmp->overflow = OP1_RANGE_OVERFLOW();
+ if (ssa->ops[line].result_def == var) {
+ return 1;
+ }
+ if (tmp->min > ZEND_LONG_MIN) {
+ tmp->min--;
+ } else {
+ tmp->underflow = 1;
+ }
+ if (tmp->max > ZEND_LONG_MIN && !tmp->overflow) {
+ tmp->max--;
+ }
+ return 1;
+ }
+ }
+ break;
+ case ZEND_UNSET_DIM:
+ case ZEND_UNSET_OBJ:
+ if (ssa->ops[line].op1_def == var) {
+ /* If op1 is scalar, UNSET_DIM and UNSET_OBJ have no effect, so we can keep
+ * the previous ranges. */
+ if (OP1_HAS_RANGE()) {
+ tmp->min = OP1_MIN_RANGE();
+ tmp->max = OP1_MAX_RANGE();
+ tmp->underflow = OP1_RANGE_UNDERFLOW();
+ tmp->overflow = OP1_RANGE_OVERFLOW();
+ return 1;
+ }
+ }
+ break;
+ case ZEND_ASSIGN:
+ if (ssa->ops[line].op1_def == var || ssa->ops[line].op2_def == var || ssa->ops[line].result_def == var) {
+ if (OP2_HAS_RANGE()) {
+ tmp->min = OP2_MIN_RANGE();
+ tmp->max = OP2_MAX_RANGE();
+ tmp->underflow = OP2_RANGE_UNDERFLOW();
+ tmp->overflow = OP2_RANGE_OVERFLOW();
+ return 1;
+ }
+ }
+ break;
+ case ZEND_ASSIGN_DIM:
+ case ZEND_ASSIGN_OBJ:
+ if (ssa->ops[line+1].op1_def == var) {
+ if ((opline+1)->opcode == ZEND_OP_DATA) {
+ opline++;
+ tmp->min = OP1_MIN_RANGE();
+ tmp->max = OP1_MAX_RANGE();
+ tmp->underflow = OP1_RANGE_UNDERFLOW();
+ tmp->overflow = OP1_RANGE_OVERFLOW();
+ return 1;
+ }
+ }
+ break;
+ case ZEND_ASSIGN_ADD:
+ case ZEND_ASSIGN_SUB:
+ case ZEND_ASSIGN_MUL:
+ case ZEND_ASSIGN_DIV:
+ case ZEND_ASSIGN_MOD:
+ case ZEND_ASSIGN_SL:
+ case ZEND_ASSIGN_SR:
+ case ZEND_ASSIGN_BW_OR:
+ case ZEND_ASSIGN_BW_AND:
+ case ZEND_ASSIGN_BW_XOR:
+ if (opline->extended_value == 0) {
+ if (ssa->ops[line].op1_def == var || ssa->ops[line].result_def == var) {
+ return zend_inference_calc_binary_op_range(
+ op_array, ssa, opline, &ssa->ops[line],
+ get_compound_assign_op(opline->opcode), tmp);
+ }
+ } else if ((opline+1)->opcode == ZEND_OP_DATA) {
+ if (ssa->ops[line+1].op1_def == var) {
+ opline++;
+ if (OP1_HAS_RANGE()) {
+ tmp->min = OP1_MIN_RANGE();
+ tmp->max = OP1_MAX_RANGE();
+ tmp->underflow = OP1_RANGE_UNDERFLOW();
+ tmp->overflow = OP1_RANGE_OVERFLOW();
+ return 1;
+ }
+ }
+ }
+ break;
+// case ZEND_ASSIGN_CONCAT:
+ case ZEND_OP_DATA:
+ if ((opline-1)->opcode == ZEND_ASSIGN_DIM ||
+ (opline-1)->opcode == ZEND_ASSIGN_OBJ ||
+ (opline-1)->opcode == ZEND_ASSIGN_ADD ||
+ (opline-1)->opcode == ZEND_ASSIGN_SUB ||
+ (opline-1)->opcode == ZEND_ASSIGN_MUL) {
+ if (ssa->ops[line].op1_def == var) {
+ if (OP1_HAS_RANGE()) {
+ tmp->min = OP1_MIN_RANGE();
+ tmp->max = OP1_MAX_RANGE();
+ tmp->underflow = OP1_RANGE_UNDERFLOW();
+ tmp->overflow = OP1_RANGE_OVERFLOW();
+ return 1;
+ }
+ }
+ break;
+ }
+ break;
+ case ZEND_RECV:
+ case ZEND_RECV_INIT:
+ if (ssa->ops[line].result_def == var) {
+ zend_func_info *func_info = ZEND_FUNC_INFO(op_array);
+
+ if (func_info &&
+ (int)opline->op1.num-1 < func_info->num_args &&
+ func_info->arg_info[opline->op1.num-1].info.has_range) {
+ *tmp = func_info->arg_info[opline->op1.num-1].info.range;
+ return 1;
+ } else if (op_array->arg_info &&
+ opline->op1.num <= op_array->num_args) {
+ if (op_array->arg_info[opline->op1.num-1].type_hint == IS_LONG) {
+ tmp->underflow = 0;
+ tmp->min = ZEND_LONG_MIN;
+ tmp->max = ZEND_LONG_MAX;
+ tmp->overflow = 0;
+ return 1;
+ } else if (op_array->arg_info[opline->op1.num-1].type_hint == _IS_BOOL) {
+ tmp->underflow = 0;
+ tmp->min = 0;
+ tmp->max = 1;
+ tmp->overflow = 0;
+ return 1;
+ }
+ }
+ }
+ break;
+ case ZEND_STRLEN:
+ if (ssa->ops[line].result_def == var) {
+#if SIZEOF_ZEND_LONG == 4
+ /* The length of a string is a non-negative integer. However, on 32-bit
+ * platforms overflows into negative lengths may occur, so it's better
+ * to not assume any particular range. */
+ tmp->min = ZEND_LONG_MIN;
+#else
+ tmp->min = 0;
+#endif
+ tmp->max = ZEND_LONG_MAX;
+ return 1;
+ }
+ break;
+ case ZEND_DO_FCALL:
+ case ZEND_DO_ICALL:
+ case ZEND_DO_UCALL:
+ case ZEND_DO_FCALL_BY_NAME:
+ if (ssa->ops[line].result_def == var) {
+ zend_func_info *func_info = ZEND_FUNC_INFO(op_array);
+ zend_call_info *call_info;
+ if (!func_info || !func_info->call_map) {
+ break;
+ }
+
+ call_info = func_info->call_map[opline - op_array->opcodes];
+ if (!call_info) {
+ break;
+ }
+ if (call_info->callee_func->type == ZEND_USER_FUNCTION) {
+ func_info = ZEND_FUNC_INFO(&call_info->callee_func->op_array);
+ if (func_info && func_info->return_info.has_range) {
+ *tmp = func_info->return_info.range;
+ return 1;
+ }
+ }
+//TODO: we can't use type inference for internal functions at this point ???
+#if 0
+ uint32_t type;
+
+ type = zend_get_func_info(call_info, ssa);
+ if (!(type & (MAY_BE_ANY - (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)))) {
+ tmp->underflow = 0;
+ tmp->min = 0;
+ tmp->max = 0;
+ tmp->overflow = 0;
+ if (type & MAY_BE_LONG) {
+ tmp->min = ZEND_LONG_MIN;
+ tmp->max = ZEND_LONG_MAX;
+ } else if (type & MAY_BE_TRUE) {
+ if (!(type & (MAY_BE_NULL|MAY_BE_FALSE))) {
+ tmp->min = 1;
+ }
+ tmp->max = 1;
+ }
+ return 1;
+ }
+#endif
+ }
+ break;
+ // FIXME: support for more opcodes
+ default:
+ break;
+ }
+ return 0;
+}
+
+void zend_inference_init_range(const zend_op_array *op_array, zend_ssa *ssa, int var, zend_bool underflow, zend_long min, zend_long max, zend_bool overflow)
+{
+ if (underflow) {
+ min = ZEND_LONG_MIN;
+ }
+ if (overflow) {
+ max = ZEND_LONG_MAX;
+ }
+ ssa->var_info[var].has_range = 1;
+ ssa->var_info[var].range.underflow = underflow;
+ ssa->var_info[var].range.min = min;
+ ssa->var_info[var].range.max = max;
+ ssa->var_info[var].range.overflow = overflow;
+ LOG_SSA_RANGE(" change range (init SCC %2d) %2d [%s%ld..%ld%s]\n", ssa->vars[var].scc, var, (underflow?"-- ":""), min, max, (overflow?" ++":""));
+}
+
+int zend_inference_widening_meet(zend_ssa_var_info *var_info, zend_ssa_range *r)
+{
+ if (!var_info->has_range) {
+ var_info->has_range = 1;
+ } else {
+ if (r->underflow ||
+ var_info->range.underflow ||
+ r->min < var_info->range.min) {
+ r->underflow = 1;
+ r->min = ZEND_LONG_MIN;
+ }
+ if (r->overflow ||
+ var_info->range.overflow ||
+ r->max > var_info->range.max) {
+ r->overflow = 1;
+ r->max = ZEND_LONG_MAX;
+ }
+ if (var_info->range.min == r->min &&
+ var_info->range.max == r->max &&
+ var_info->range.underflow == r->underflow &&
+ var_info->range.overflow == r->overflow) {
+ return 0;
+ }
+ }
+ var_info->range = *r;
+ return 1;
+}
+
+static int zend_ssa_range_widening(const zend_op_array *op_array, zend_ssa *ssa, int var, int scc)
+{
+ zend_ssa_range tmp;
+
+ if (zend_inference_calc_range(op_array, ssa, var, 1, 0, &tmp)) {
+ if (zend_inference_widening_meet(&ssa->var_info[var], &tmp)) {
+ LOG_SSA_RANGE(" change range (widening SCC %2d) %2d [%s%ld..%ld%s]\n", scc, var, (tmp.underflow?"-- ":""), tmp.min, tmp.max, (tmp.overflow?" ++":""));
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int zend_inference_narrowing_meet(zend_ssa_var_info *var_info, zend_ssa_range *r)
+{
+ if (!var_info->has_range) {
+ var_info->has_range = 1;
+ } else {
+ if (!r->underflow &&
+ !var_info->range.underflow &&
+ var_info->range.min < r->min) {
+ r->min = var_info->range.min;
+ }
+ if (!r->overflow &&
+ !var_info->range.overflow &&
+ var_info->range.max > r->max) {
+ r->max = var_info->range.max;
+ }
+ if (r->underflow) {
+ r->min = ZEND_LONG_MIN;
+ }
+ if (r->overflow) {
+ r->max = ZEND_LONG_MAX;
+ }
+ if (var_info->range.min == r->min &&
+ var_info->range.max == r->max &&
+ var_info->range.underflow == r->underflow &&
+ var_info->range.overflow == r->overflow) {
+ return 0;
+ }
+ }
+ var_info->range = *r;
+ return 1;
+}
+
+static int zend_ssa_range_narrowing(const zend_op_array *op_array, zend_ssa *ssa, int var, int scc)
+{
+ zend_ssa_range tmp;
+
+ if (zend_inference_calc_range(op_array, ssa, var, 0, 1, &tmp)) {
+ if (zend_inference_narrowing_meet(&ssa->var_info[var], &tmp)) {
+ LOG_SSA_RANGE(" change range (narrowing SCC %2d) %2d [%s%ld..%ld%s]\n", scc, var, (tmp.underflow?"-- ":""), tmp.min, tmp.max, (tmp.overflow?" ++":""));
+ return 1;
+ }
+ }
+ return 0;
+}
+
+#ifdef NEG_RANGE
+# define CHECK_INNER_CYCLE(var2) \
+ do { \
+ if (ssa->vars[var2].scc == ssa->vars[var].scc && \
+ !ssa->vars[var2].scc_entry && \
+ !zend_bitset_in(visited, var2) && \
+ zend_check_inner_cycles(op_array, ssa, worklist, visited, var2)) { \
+ return 1; \
+ } \
+ } while (0)
+
+static int zend_check_inner_cycles(const zend_op_array *op_array, zend_ssa *ssa, zend_bitset worklist, zend_bitset visited, int var)
+{
+ if (zend_bitset_in(worklist, var)) {
+ return 1;
+ }
+ zend_bitset_incl(worklist, var);
+ FOR_EACH_VAR_USAGE(var, CHECK_INNER_CYCLE);
+ zend_bitset_incl(visited, var);
+ return 0;
+}
+#endif
+
+static void zend_infer_ranges_warmup(const zend_op_array *op_array, zend_ssa *ssa, int *scc_var, int *next_scc_var, int scc)
+{
+ int worklist_len = zend_bitset_len(ssa->vars_count);
+ int j, n;
+ zend_ssa_range tmp;
+ ALLOCA_FLAG(use_heap);
+ zend_bitset worklist = do_alloca(sizeof(zend_ulong) * worklist_len * 2, use_heap);
+ zend_bitset visited = worklist + worklist_len;
+#ifdef NEG_RANGE
+ int has_inner_cycles = 0;
+
+ memset(worklist, 0, sizeof(zend_ulong) * worklist_len);
+ memset(visited, 0, sizeof(zend_ulong) * worklist_len);
+ j = scc_var[scc];
+ while (j >= 0) {
+ if (!zend_bitset_in(visited, j) &&
+ zend_check_inner_cycles(op_array, ssa, worklist, visited, j)) {
+ has_inner_cycles = 1;
+ break;
+ }
+ j = next_scc_var[j];
+ }
+#endif
+
+ memset(worklist, 0, sizeof(zend_ulong) * worklist_len);
+
+ for (n = 0; n < RANGE_WARMUP_PASSES; n++) {
+ j= scc_var[scc];
+ while (j >= 0) {
+ if (ssa->vars[j].scc_entry) {
+ zend_bitset_incl(worklist, j);
+ }
+ j = next_scc_var[j];
+ }
+
+ memset(visited, 0, sizeof(zend_ulong) * worklist_len);
+
+ WHILE_WORKLIST(worklist, worklist_len, j) {
+ if (zend_inference_calc_range(op_array, ssa, j, 0, 0, &tmp)) {
+#ifdef NEG_RANGE
+ if (!has_inner_cycles &&
+ ssa->var_info[j].has_range &&
+ ssa->vars[j].definition_phi &&
+ ssa->vars[j].definition_phi->pi >= 0 &&
+ ssa->vars[j].definition_phi->has_range_constraint &&
+ ssa->vars[j].definition_phi->constraint.range.negative &&
+ ssa->vars[j].definition_phi->constraint.range.min_ssa_var < 0 &&
+ ssa->vars[j].definition_phi->constraint.range.min_ssa_var < 0) {
+ zend_ssa_range_constraint *constraint =
+ &ssa->vars[j].definition_phi->constraint.range;
+ if (tmp.min == ssa->var_info[j].range.min &&
+ tmp.max == ssa->var_info[j].range.max) {
+ if (constraint->negative == NEG_INIT) {
+ LOG_NEG_RANGE("#%d INVARIANT\n", j);
+ constraint->negative = NEG_INVARIANT;
+ }
+ } else if (tmp.min == ssa->var_info[j].range.min &&
+ tmp.max == ssa->var_info[j].range.max + 1 &&
+ tmp.max < constraint->range.min) {
+ if (constraint->negative == NEG_INIT ||
+ constraint->negative == NEG_INVARIANT) {
+ LOG_NEG_RANGE("#%d LT\n", j);
+ constraint->negative = NEG_USE_LT;
+//???NEG
+ } else if (constraint->negative == NEG_USE_GT) {
+ LOG_NEG_RANGE("#%d UNKNOWN\n", j);
+ constraint->negative = NEG_UNKNOWN;
+ }
+ } else if (tmp.max == ssa->var_info[j].range.max &&
+ tmp.min == ssa->var_info[j].range.min - 1 &&
+ tmp.min > constraint->range.max) {
+ if (constraint->negative == NEG_INIT ||
+ constraint->negative == NEG_INVARIANT) {
+ LOG_NEG_RANGE("#%d GT\n", j);
+ constraint->negative = NEG_USE_GT;
+//???NEG
+ } else if (constraint->negative == NEG_USE_LT) {
+ LOG_NEG_RANGE("#%d UNKNOWN\n", j);
+ constraint->negative = NEG_UNKNOWN;
+ }
+ } else {
+ LOG_NEG_RANGE("#%d UNKNOWN\n", j);
+ constraint->negative = NEG_UNKNOWN;
+ }
+ }
+#endif
+ if (zend_inference_narrowing_meet(&ssa->var_info[j], &tmp)) {
+ LOG_SSA_RANGE(" change range (warmup %2d SCC %2d) %2d [%s%ld..%ld%s]\n", n, scc, j, (tmp.underflow?"-- ":""), tmp.min, tmp.max, (tmp.overflow?" ++":""));
+ zend_bitset_incl(visited, j);
+ FOR_EACH_VAR_USAGE(j, ADD_SCC_VAR_1);
+ }
+ }
+ } WHILE_WORKLIST_END();
+ }
+ free_alloca(worklist, use_heap);
+}
+
+static int zend_infer_ranges(const zend_op_array *op_array, zend_ssa *ssa) /* {{{ */
+{
+ int worklist_len = zend_bitset_len(ssa->vars_count);
+ zend_bitset worklist;
+ int *next_scc_var;
+ int *scc_var;
+ zend_ssa_phi *p;
+ zend_ssa_range tmp;
+ int scc, j;
+ ALLOCA_FLAG(use_heap);
+
+ worklist = do_alloca(
+ ZEND_MM_ALIGNED_SIZE(sizeof(zend_ulong) * worklist_len) +
+ ZEND_MM_ALIGNED_SIZE(sizeof(int) * ssa->vars_count) +
+ sizeof(int) * ssa->sccs, use_heap);
+ next_scc_var = (int*)((char*)worklist + ZEND_MM_ALIGNED_SIZE(sizeof(zend_ulong) * worklist_len));
+ scc_var = (int*)((char*)next_scc_var + ZEND_MM_ALIGNED_SIZE(sizeof(int) * ssa->vars_count));
+
+ LOG_SSA_RANGE("Range Inference\n");
+
+ /* Create linked lists of SSA variables for each SCC */
+ memset(scc_var, -1, sizeof(int) * ssa->sccs);
+ for (j = 0; j < ssa->vars_count; j++) {
+ if (ssa->vars[j].scc >= 0) {
+ next_scc_var[j] = scc_var[ssa->vars[j].scc];
+ scc_var[ssa->vars[j].scc] = j;
+ }
+ }
+
+ for (scc = 0; scc < ssa->sccs; scc++) {
+ j = scc_var[scc];
+ if (next_scc_var[j] < 0) {
+ /* SCC with a single element */
+ if (zend_inference_calc_range(op_array, ssa, j, 0, 1, &tmp)) {
+ zend_inference_init_range(op_array, ssa, j, tmp.underflow, tmp.min, tmp.max, tmp.overflow);
+ } else {
+ zend_inference_init_range(op_array, ssa, j, 1, ZEND_LONG_MIN, ZEND_LONG_MAX, 1);
+ }
+ } else {
+ /* Find SCC entry points */
+ memset(worklist, 0, sizeof(zend_ulong) * worklist_len);
+ do {
+ if (ssa->vars[j].scc_entry) {
+ zend_bitset_incl(worklist, j);
+ }
+ j = next_scc_var[j];
+ } while (j >= 0);
+
+#if RANGE_WARMUP_PASSES > 0
+ zend_infer_ranges_warmup(op_array, ssa, scc_var, next_scc_var, scc);
+ j = scc_var[scc];
+ do {
+ zend_bitset_incl(worklist, j);
+ j = next_scc_var[j];
+ } while (j >= 0);
+#endif
+
+ /* widening */
+ WHILE_WORKLIST(worklist, worklist_len, j) {
+ if (zend_ssa_range_widening(op_array, ssa, j, scc)) {
+ FOR_EACH_VAR_USAGE(j, ADD_SCC_VAR);
+ }
+ } WHILE_WORKLIST_END();
+
+ /* Add all SCC entry variables into worklist for narrowing */
+ for (j = scc_var[scc]; j >= 0; j = next_scc_var[j]) {
+ if (!ssa->var_info[j].has_range) {
+ zend_inference_init_range(op_array, ssa, j, 1, ZEND_LONG_MIN, ZEND_LONG_MAX, 1);
+ }
+ zend_bitset_incl(worklist, j);
+ }
+
+ /* narrowing */
+ WHILE_WORKLIST(worklist, worklist_len, j) {
+ if (zend_ssa_range_narrowing(op_array, ssa, j, scc)) {
+ FOR_EACH_VAR_USAGE(j, ADD_SCC_VAR);
+#ifdef SYM_RANGE
+ /* Process symbolic control-flow constraints */
+ p = ssa->vars[j].sym_use_chain;
+ while (p) {
+ ADD_SCC_VAR(p->ssa_var);
+ p = p->sym_use_chain;
+ }
+#endif
+ }
+ } WHILE_WORKLIST_END();
+ }
+ }
+
+ free_alloca(worklist, use_heap);
+
+ return SUCCESS;
+}
+/* }}} */
+
+#define UPDATE_SSA_TYPE(_type, _var) \
+ do { \
+ uint32_t __type = (_type); \
+ int __var = (_var); \
+ if (__type & MAY_BE_REF) { \
+ __type |= MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF; \
+ } \
+ if (__var >= 0) { \
+ if (ssa_vars[__var].var < op_array->last_var) { \
+ if (__type & (MAY_BE_REF|MAY_BE_RCN)) { \
+ __type |= MAY_BE_RC1 | MAY_BE_RCN; \
+ } \
+ if ((__type & MAY_BE_RC1) && (__type & MAY_BE_STRING)) {\
+ /* TODO: support for array keys and ($str . "")*/ \
+ __type |= MAY_BE_RCN; \
+ } \
+ } \
+ if (ssa_var_info[__var].type != __type) { \
+ if (ssa_var_info[__var].type & ~__type) { \
+ handle_type_narrowing(op_array, ssa, worklist, \
+ __var, ssa_var_info[__var].type, __type); \
+ return FAILURE; \
+ } \
+ ssa_var_info[__var].type = __type; \
+ add_usages(op_array, ssa, worklist, __var); \
+ } \
+ /*zend_bitset_excl(worklist, var);*/ \
+ } \
+ } while (0)
+
+#define UPDATE_SSA_OBJ_TYPE(_ce, _is_instanceof, var) \
+ do { \
+ if (var >= 0) { \
+ if (ssa_var_info[var].ce != (_ce) || \
+ ssa_var_info[var].is_instanceof != (_is_instanceof)) { \
+ ssa_var_info[var].ce = (_ce); \
+ ssa_var_info[var].is_instanceof = (_is_instanceof); \
+ add_usages(op_array, ssa, worklist, var); \
+ } \
+ /*zend_bitset_excl(worklist, var);*/ \
+ } \
+ } while (0)
+
+#define COPY_SSA_OBJ_TYPE(from_var, to_var) do { \
+ if ((from_var) >= 0 && (ssa_var_info[(from_var)].type & MAY_BE_OBJECT) \
+ && ssa_var_info[(from_var)].ce) { \
+ UPDATE_SSA_OBJ_TYPE(ssa_var_info[(from_var)].ce, \
+ ssa_var_info[(from_var)].is_instanceof, (to_var)); \
+ } else { \
+ UPDATE_SSA_OBJ_TYPE(NULL, 0, (to_var)); \
+ } \
+} while (0)
+
+static void add_usages(const zend_op_array *op_array, zend_ssa *ssa, zend_bitset worklist, int var)
+{
+ if (ssa->vars[var].phi_use_chain) {
+ zend_ssa_phi *p = ssa->vars[var].phi_use_chain;
+ do {
+ zend_bitset_incl(worklist, p->ssa_var);
+ p = zend_ssa_next_use_phi(ssa, var, p);
+ } while (p);
+ }
+ if (ssa->vars[var].use_chain >= 0) {
+ int use = ssa->vars[var].use_chain;
+ zend_ssa_op *op;
+
+ do {
+ op = ssa->ops + use;
+ if (op->result_def >= 0) {
+ zend_bitset_incl(worklist, op->result_def);
+ }
+ if (op->op1_def >= 0) {
+ zend_bitset_incl(worklist, op->op1_def);
+ }
+ if (op->op2_def >= 0) {
+ zend_bitset_incl(worklist, op->op2_def);
+ }
+ if (op_array->opcodes[use].opcode == ZEND_OP_DATA) {
+ op--;
+ if (op->result_def >= 0) {
+ zend_bitset_incl(worklist, op->result_def);
+ }
+ if (op->op1_def >= 0) {
+ zend_bitset_incl(worklist, op->op1_def);
+ }
+ if (op->op2_def >= 0) {
+ zend_bitset_incl(worklist, op->op2_def);
+ }
+ }
+ use = zend_ssa_next_use(ssa->ops, var, use);
+ } while (use >= 0);
+ }
+}
+
+static void reset_dependent_vars(const zend_op_array *op_array, zend_ssa *ssa, zend_bitset worklist, int var)
+{
+ zend_ssa_op *ssa_ops = ssa->ops;
+ zend_ssa_var *ssa_vars = ssa->vars;
+ zend_ssa_var_info *ssa_var_info = ssa->var_info;
+ zend_ssa_phi *p;
+ int use;
+
+ p = ssa_vars[var].phi_use_chain;
+ while (p) {
+ if (ssa_var_info[p->ssa_var].type) {
+ ssa_var_info[p->ssa_var].type = 0;
+ zend_bitset_incl(worklist, p->ssa_var);
+ reset_dependent_vars(op_array, ssa, worklist, p->ssa_var);
+ }
+ p = zend_ssa_next_use_phi(ssa, var, p);
+ }
+ use = ssa_vars[var].use_chain;
+ while (use >= 0) {
+ if (ssa_ops[use].op1_def >= 0 && ssa_var_info[ssa_ops[use].op1_def].type) {
+ ssa_var_info[ssa_ops[use].op1_def].type = 0;
+ zend_bitset_incl(worklist, ssa_ops[use].op1_def);
+ reset_dependent_vars(op_array, ssa, worklist, ssa_ops[use].op1_def);
+ }
+ if (ssa_ops[use].op2_def >= 0 && ssa_var_info[ssa_ops[use].op2_def].type) {
+ ssa_var_info[ssa_ops[use].op2_def].type = 0;
+ zend_bitset_incl(worklist, ssa_ops[use].op2_def);
+ reset_dependent_vars(op_array, ssa, worklist, ssa_ops[use].op2_def);
+ }
+ if (ssa_ops[use].result_def >= 0 && ssa_var_info[ssa_ops[use].result_def].type) {
+ ssa_var_info[ssa_ops[use].result_def].type = 0;
+ zend_bitset_incl(worklist, ssa_ops[use].result_def);
+ reset_dependent_vars(op_array, ssa, worklist, ssa_ops[use].result_def);
+ }
+ if (op_array->opcodes[use+1].opcode == ZEND_OP_DATA) {
+ if (ssa_ops[use+1].op1_def >= 0 && ssa_var_info[ssa_ops[use+1].op1_def].type) {
+ ssa_var_info[ssa_ops[use+1].op1_def].type = 0;
+ zend_bitset_incl(worklist, ssa_ops[use+1].op1_def);
+ reset_dependent_vars(op_array, ssa, worklist, ssa_ops[use+1].op1_def);
+ }
+ if (ssa_ops[use+1].op2_def >= 0 && ssa_var_info[ssa_ops[use+1].op2_def].type) {
+ ssa_var_info[ssa_ops[use+1].op2_def].type = 0;
+ zend_bitset_incl(worklist, ssa_ops[use+1].op2_def);
+ reset_dependent_vars(op_array, ssa, worklist, ssa_ops[use+1].op2_def);
+ }
+ if (ssa_ops[use+1].result_def >= 0 && ssa_var_info[ssa_ops[use+1].result_def].type) {
+ ssa_var_info[ssa_ops[use+1].result_def].type = 0;
+ zend_bitset_incl(worklist, ssa_ops[use+1].result_def);
+ reset_dependent_vars(op_array, ssa, worklist, ssa_ops[use+1].result_def);
+ }
+ }
+ use = zend_ssa_next_use(ssa_ops, var, use);
+ }
+#ifdef SYM_RANGE
+ /* Process symbolic control-flow constraints */
+ p = ssa->vars[var].sym_use_chain;
+ while (p) {
+ ssa_var_info[p->ssa_var].type = 0;
+ zend_bitset_incl(worklist, p->ssa_var);
+ reset_dependent_vars(op_array, ssa, worklist, p->ssa_var);
+ p = p->sym_use_chain;
+ }
+#endif
+}
+
+static void handle_type_narrowing(const zend_op_array *op_array, zend_ssa *ssa, zend_bitset worklist, int var, uint32_t old_type, uint32_t new_type)
+{
+ if (1) {
+ /* Right now, this is always a bug */
+ zend_error(E_WARNING, "Narrowing occurred during type inference. Please file a bug report on bugs.php.net");
+ } else {
+ /* if new_type set resets some bits from old_type set
+ * We have completely recalculate types of some dependent SSA variables
+ * (this may occurs mainly because of incremental inter-precudure
+ * type inference)
+ */
+ reset_dependent_vars(op_array, ssa, worklist, var);
+ }
+}
+
+uint32_t zend_array_element_type(uint32_t t1, int write, int insert)
+{
+ uint32_t tmp = 0;
+
+ if (t1 & MAY_BE_OBJECT) {
+ tmp |= MAY_BE_ANY | MAY_BE_REF | MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+ }
+ if (t1 & MAY_BE_ARRAY) {
+ if (insert) {
+ tmp |= MAY_BE_NULL;
+ } else {
+ tmp |= MAY_BE_NULL | ((t1 & MAY_BE_ARRAY_OF_ANY) >> MAY_BE_ARRAY_SHIFT);
+ if (tmp & MAY_BE_ARRAY) {
+ tmp |= MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+ }
+ if (t1 & MAY_BE_ARRAY_OF_REF) {
+ tmp |= MAY_BE_REF | MAY_BE_RC1 | MAY_BE_RCN;
+ } else if (tmp & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
+ tmp |= MAY_BE_RC1 | MAY_BE_RCN;
+ }
+ }
+ }
+ if (t1 & MAY_BE_STRING) {
+ tmp |= MAY_BE_STRING | MAY_BE_RC1;
+ if (write) {
+ tmp |= MAY_BE_NULL;
+ }
+ }
+ if (t1 & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE)) {
+ tmp |= MAY_BE_NULL;
+ if (t1 & MAY_BE_ERROR) {
+ if (write) {
+ tmp |= MAY_BE_ERROR;
+ }
+ }
+ }
+ if (t1 & (MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_RESOURCE)) {
+ tmp |= MAY_BE_NULL;
+ if (write) {
+ tmp |= MAY_BE_ERROR;
+ }
+ }
+ return tmp;
+}
+
+static uint32_t assign_dim_result_type(
+ uint32_t arr_type, uint32_t dim_type, uint32_t value_type, zend_uchar dim_op_type) {
+ uint32_t tmp = arr_type & ~(MAY_BE_RC1|MAY_BE_RCN);
+
+ if (arr_type & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE)) {
+ tmp &= ~(MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE);
+ tmp |= MAY_BE_ARRAY|MAY_BE_RC1;
+ }
+ if (tmp & (MAY_BE_ARRAY|MAY_BE_STRING)) {
+ tmp |= MAY_BE_RC1;
+ }
+ if (tmp & (MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
+ tmp |= MAY_BE_RC1 | MAY_BE_RCN;
+ }
+ if (tmp & MAY_BE_ARRAY) {
+ tmp |= (value_type & MAY_BE_ANY) << MAY_BE_ARRAY_SHIFT;
+ if (value_type & MAY_BE_UNDEF) {
+ tmp |= MAY_BE_ARRAY_OF_NULL;
+ }
+ if (dim_op_type == IS_UNUSED) {
+ tmp |= MAY_BE_ARRAY_KEY_LONG;
+ } else {
+ if (dim_type & (MAY_BE_LONG|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_RESOURCE|MAY_BE_DOUBLE)) {
+ tmp |= MAY_BE_ARRAY_KEY_LONG;
+ }
+ if (dim_type & MAY_BE_STRING) {
+ tmp |= MAY_BE_ARRAY_KEY_STRING;
+ if (dim_op_type != IS_CONST) {
+ // FIXME: numeric string
+ tmp |= MAY_BE_ARRAY_KEY_LONG;
+ }
+ }
+ if (dim_type & (MAY_BE_UNDEF|MAY_BE_NULL)) {
+ tmp |= MAY_BE_ARRAY_KEY_STRING;
+ }
+ }
+ }
+ return tmp;
+}
+
+/* For binary ops that have compound assignment operators */
+static uint32_t binary_op_result_type(
+ zend_ssa *ssa, zend_uchar opcode, uint32_t t1, uint32_t t2, uint32_t result_var) {
+ uint32_t tmp = 0;
+ uint32_t t1_type = (t1 & MAY_BE_ANY) | (t1 & MAY_BE_UNDEF ? MAY_BE_NULL : 0);
+ uint32_t t2_type = (t2 & MAY_BE_ANY) | (t2 & MAY_BE_UNDEF ? MAY_BE_NULL : 0);
+ switch (opcode) {
+ case ZEND_ADD:
+ if (t1_type == MAY_BE_LONG && t2_type == MAY_BE_LONG) {
+ if (!ssa->var_info[result_var].has_range ||
+ ssa->var_info[result_var].range.underflow ||
+ ssa->var_info[result_var].range.overflow) {
+ /* may overflow */
+ tmp |= MAY_BE_LONG | MAY_BE_DOUBLE;
+ } else {
+ tmp |= MAY_BE_LONG;
+ }
+ } else if (t1_type == MAY_BE_DOUBLE || t2_type == MAY_BE_DOUBLE) {
+ tmp |= MAY_BE_DOUBLE;
+ } else if (t1_type == MAY_BE_ARRAY && t2_type == MAY_BE_ARRAY) {
+ tmp |= MAY_BE_ARRAY | MAY_BE_RC1;
+ tmp |= t1 & (MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF);
+ tmp |= t2 & (MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF);
+ } else {
+ tmp |= MAY_BE_LONG | MAY_BE_DOUBLE;
+ if ((t1_type & MAY_BE_ARRAY) && (t2_type & MAY_BE_ARRAY)) {
+ tmp |= MAY_BE_ARRAY | MAY_BE_RC1;
+ tmp |= t1 & (MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF);
+ tmp |= t2 & (MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF);
+ }
+ }
+ break;
+ case ZEND_SUB:
+ case ZEND_MUL:
+ if (t1_type == MAY_BE_LONG && t2_type == MAY_BE_LONG) {
+ if (!ssa->var_info[result_var].has_range ||
+ ssa->var_info[result_var].range.underflow ||
+ ssa->var_info[result_var].range.overflow) {
+ /* may overflow */
+ tmp |= MAY_BE_LONG | MAY_BE_DOUBLE;
+ } else {
+ tmp |= MAY_BE_LONG;
+ }
+ } else if (t1_type == MAY_BE_DOUBLE || t2_type == MAY_BE_DOUBLE) {
+ tmp |= MAY_BE_DOUBLE;
+ } else {
+ tmp |= MAY_BE_LONG | MAY_BE_DOUBLE;
+ }
+ break;
+ case ZEND_DIV:
+ case ZEND_POW:
+ if (t1_type == MAY_BE_DOUBLE || t2_type == MAY_BE_DOUBLE) {
+ tmp |= MAY_BE_DOUBLE;
+ } else {
+ tmp |= MAY_BE_LONG | MAY_BE_DOUBLE;
+ }
+ /* Division by zero results in Inf/-Inf/Nan (double), so it doesn't need any special
+ * handling */
+ break;
+ case ZEND_MOD:
+ tmp = MAY_BE_LONG;
+ /* Division by zero results in an exception, so it doesn't need any special handling */
+ break;
+ case ZEND_BW_OR:
+ case ZEND_BW_AND:
+ case ZEND_BW_XOR:
+ if ((t1_type & MAY_BE_STRING) && (t2_type & MAY_BE_STRING)) {
+ tmp |= MAY_BE_STRING | MAY_BE_RC1;
+ }
+ if ((t1_type & ~MAY_BE_STRING) || (t2_type & ~MAY_BE_STRING)) {
+ tmp |= MAY_BE_LONG;
+ }
+ break;
+ case ZEND_SL:
+ case ZEND_SR:
+ tmp = MAY_BE_LONG;
+ break;
+ case ZEND_CONCAT:
+ case ZEND_FAST_CONCAT:
+ /* TODO: +MAY_BE_OBJECT ??? */
+ tmp = MAY_BE_STRING | MAY_BE_RC1 | MAY_BE_RCN;
+ break;
+ EMPTY_SWITCH_DEFAULT_CASE()
+ }
+ return tmp;
+}
+
+static inline zend_class_entry *get_class_entry(const zend_script *script, zend_string *lcname) {
+ zend_class_entry *ce = script ? zend_hash_find_ptr(&script->class_table, lcname) : NULL;
+ if (ce) {
+ return ce;
+ }
+
+ ce = zend_hash_find_ptr(CG(class_table), lcname);
+ if (ce && ce->type == ZEND_INTERNAL_CLASS) {
+ return ce;
+ }
+
+ return NULL;
+}
+
+static uint32_t zend_fetch_arg_info(const zend_script *script, zend_arg_info *arg_info, zend_class_entry **pce)
+{
+ uint32_t tmp = 0;
+
+ *pce = NULL;
+ if (arg_info->class_name) {
+ // class type hinting...
+ zend_string *lcname = zend_string_tolower(arg_info->class_name);
+ tmp |= MAY_BE_OBJECT;
+ *pce = get_class_entry(script, lcname);
+ zend_string_release(lcname);
+ } else if (arg_info->type_hint != IS_UNDEF) {
+ if (arg_info->type_hint == IS_VOID) {
+ tmp |= MAY_BE_NULL;
+ } else if (arg_info->type_hint == IS_CALLABLE) {
+ tmp |= MAY_BE_STRING|MAY_BE_OBJECT|MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
+ } else if (arg_info->type_hint == IS_ITERABLE) {
+ tmp |= MAY_BE_OBJECT|MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
+ } else if (arg_info->type_hint == IS_ARRAY) {
+ tmp |= MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
+ } else if (arg_info->type_hint == _IS_BOOL) {
+ tmp |= MAY_BE_TRUE|MAY_BE_FALSE;
+ } else {
+ ZEND_ASSERT(arg_info->type_hint < IS_REFERENCE);
+ tmp |= 1 << arg_info->type_hint;
+ }
+ } else {
+ tmp |= MAY_BE_ANY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
+ }
+ if (arg_info->allow_null) {
+ tmp |= MAY_BE_NULL;
+ }
+ return tmp;
+}
+
+static int zend_update_type_info(const zend_op_array *op_array,
+ zend_ssa *ssa,
+ const zend_script *script,
+ zend_bitset worklist,
+ int i)
+{
+ uint32_t t1, t2;
+ uint32_t tmp, orig;
+ zend_op *opline = op_array->opcodes + i;
+ zend_ssa_op *ssa_ops = ssa->ops;
+ zend_ssa_var *ssa_vars = ssa->vars;
+ zend_ssa_var_info *ssa_var_info = ssa->var_info;
+ zend_class_entry *ce;
+ int j;
+
+ if (opline->opcode == ZEND_OP_DATA) {
+ opline--;
+ i--;
+ }
+
+ t1 = OP1_INFO();
+ t2 = OP2_INFO();
+
+ switch (opline->opcode) {
+ case ZEND_ADD:
+ case ZEND_SUB:
+ case ZEND_MUL:
+ case ZEND_DIV:
+ case ZEND_POW:
+ case ZEND_MOD:
+ case ZEND_BW_OR:
+ case ZEND_BW_AND:
+ case ZEND_BW_XOR:
+ case ZEND_SL:
+ case ZEND_SR:
+ case ZEND_CONCAT:
+ tmp = binary_op_result_type(ssa, opline->opcode, t1, t2, ssa_ops[i].result_def);
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+ break;
+ case ZEND_BW_NOT:
+ tmp = 0;
+ if (t1 & MAY_BE_STRING) {
+ tmp |= MAY_BE_STRING | MAY_BE_RC1;
+ }
+ if (t1 & (MAY_BE_ANY-MAY_BE_STRING)) {
+ tmp |= MAY_BE_LONG;
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+ break;
+ case ZEND_BEGIN_SILENCE:
+ UPDATE_SSA_TYPE(MAY_BE_LONG, ssa_ops[i].result_def);
+ break;
+ case ZEND_BOOL_NOT:
+ case ZEND_BOOL_XOR:
+ case ZEND_IS_IDENTICAL:
+ case ZEND_IS_NOT_IDENTICAL:
+ case ZEND_IS_EQUAL:
+ case ZEND_IS_NOT_EQUAL:
+ case ZEND_IS_SMALLER:
+ case ZEND_IS_SMALLER_OR_EQUAL:
+ case ZEND_INSTANCEOF:
+ case ZEND_JMPZ_EX:
+ case ZEND_JMPNZ_EX:
+ case ZEND_CASE:
+ case ZEND_BOOL:
+ case ZEND_ISSET_ISEMPTY_VAR:
+ case ZEND_ISSET_ISEMPTY_DIM_OBJ:
+ case ZEND_ISSET_ISEMPTY_PROP_OBJ:
+ case ZEND_ISSET_ISEMPTY_STATIC_PROP:
+ case ZEND_ASSERT_CHECK:
+ UPDATE_SSA_TYPE(MAY_BE_FALSE|MAY_BE_TRUE, ssa_ops[i].result_def);
+ break;
+ case ZEND_CAST:
+ if (ssa_ops[i].op1_def >= 0) {
+ tmp = t1;
+ if ((t1 & (MAY_BE_ARRAY|MAY_BE_OBJECT)) &&
+ (opline->op1_type == IS_CV) &&
+ (opline->extended_value == IS_ARRAY ||
+ opline->extended_value == IS_OBJECT)) {
+ tmp |= MAY_BE_RCN;
+ } else if ((t1 & MAY_BE_STRING) &&
+ (opline->op1_type == IS_CV) &&
+ opline->extended_value == IS_STRING) {
+ tmp |= MAY_BE_RCN;
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+ COPY_SSA_OBJ_TYPE(ssa_ops[i].op1_use, ssa_ops[i].op1_def);
+ }
+ tmp = 0;
+ if (opline->extended_value == _IS_BOOL) {
+ tmp |= MAY_BE_TRUE|MAY_BE_FALSE;
+ } else {
+ tmp |= 1 << opline->extended_value;
+ if (tmp & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
+ if ((tmp & MAY_BE_ANY) == (t1 & MAY_BE_ANY)) {
+ tmp |= (t1 & MAY_BE_RC1) | MAY_BE_RCN;
+ } else if ((opline->extended_value == IS_ARRAY ||
+ opline->extended_value == IS_OBJECT) &&
+ (t1 & (MAY_BE_ARRAY|MAY_BE_OBJECT))) {
+ tmp |= MAY_BE_RC1 | MAY_BE_RCN;
+ } else if (opline->extended_value == IS_STRING &&
+ (t1 & (MAY_BE_STRING|MAY_BE_OBJECT))) {
+ tmp |= MAY_BE_RC1 | MAY_BE_RCN;
+ } else {
+ tmp |= MAY_BE_RC1;
+ }
+ }
+ }
+ if (opline->extended_value == IS_ARRAY) {
+ if (t1 & MAY_BE_ARRAY) {
+ tmp |= t1 & (MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF);
+ }
+ if (t1 & MAY_BE_OBJECT) {
+ tmp |= MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+ } else {
+ tmp |= ((t1 & MAY_BE_ANY) << MAY_BE_ARRAY_SHIFT) | MAY_BE_ARRAY_KEY_LONG;
+ }
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+ break;
+ case ZEND_QM_ASSIGN:
+ case ZEND_JMP_SET:
+ case ZEND_COALESCE:
+ if (ssa_ops[i].op1_def >= 0) {
+ tmp = t1;
+ if ((t1 & (MAY_BE_RC1|MAY_BE_REF)) && (opline->op1_type == IS_CV)) {
+ tmp |= MAY_BE_RCN;
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+ COPY_SSA_OBJ_TYPE(ssa_ops[i].op1_use, ssa_ops[i].op1_def);
+ }
+ tmp = t1 & ~(MAY_BE_UNDEF|MAY_BE_REF);
+ if (t1 & MAY_BE_UNDEF) {
+ tmp |= MAY_BE_NULL;
+ }
+ if (t1 & (MAY_BE_RC1|MAY_BE_RCN)) {
+ tmp |= (t1 & (MAY_BE_RC1|MAY_BE_RCN));
+ if (opline->op1_type == IS_CV) {
+ tmp |= MAY_BE_RCN;
+ }
+ }
+ if (opline->opcode != ZEND_QM_ASSIGN) {
+ /* COALESCE and JMP_SET result can't be null */
+ tmp &= ~MAY_BE_NULL;
+ if (opline->opcode == ZEND_JMP_SET) {
+ /* JMP_SET result can't be false either */
+ tmp &= ~MAY_BE_FALSE;
+ }
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+ COPY_SSA_OBJ_TYPE(ssa_ops[i].op1_use, ssa_ops[i].result_def);
+ break;
+ case ZEND_ASSIGN_ADD:
+ case ZEND_ASSIGN_SUB:
+ case ZEND_ASSIGN_MUL:
+ case ZEND_ASSIGN_DIV:
+ case ZEND_ASSIGN_POW:
+ case ZEND_ASSIGN_MOD:
+ case ZEND_ASSIGN_SL:
+ case ZEND_ASSIGN_SR:
+ case ZEND_ASSIGN_BW_OR:
+ case ZEND_ASSIGN_BW_AND:
+ case ZEND_ASSIGN_BW_XOR:
+ case ZEND_ASSIGN_CONCAT:
+ orig = 0;
+ tmp = 0;
+ if (opline->extended_value == ZEND_ASSIGN_OBJ) {
+ tmp |= MAY_BE_REF;
+ orig = t1;
+ t1 = MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+ t2 = OP1_DATA_INFO();
+ } else if (opline->extended_value == ZEND_ASSIGN_DIM) {
+ if (t1 & MAY_BE_ARRAY_OF_REF) {
+ tmp |= MAY_BE_REF;
+ }
+ orig = t1;
+ t1 = zend_array_element_type(t1, 1, 0);
+ t2 = OP1_DATA_INFO();
+ } else {
+ if (t1 & MAY_BE_REF) {
+ tmp |= MAY_BE_REF;
+ }
+ }
+
+ tmp |= binary_op_result_type(
+ ssa, get_compound_assign_op(opline->opcode), t1, t2, ssa_ops[i].op1_def);
+ if (tmp & (MAY_BE_STRING|MAY_BE_ARRAY)) {
+ tmp |= MAY_BE_RC1;
+ }
+ if (tmp & (MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
+ tmp |= MAY_BE_RC1 | MAY_BE_RCN;
+ }
+
+ if (opline->extended_value == ZEND_ASSIGN_DIM) {
+ if (opline->op1_type == IS_CV) {
+ orig = assign_dim_result_type(orig, OP2_INFO(), tmp, opline->op1_type);
+ UPDATE_SSA_TYPE(orig, ssa_ops[i].op1_def);
+ COPY_SSA_OBJ_TYPE(ssa_ops[i].op1_use, ssa_ops[i].op1_def);
+ }
+ } else if (opline->extended_value == ZEND_ASSIGN_OBJ) {
+ if (opline->op1_type == IS_CV) {
+ if (orig & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE)) {
+ orig &= (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE);
+ orig |= MAY_BE_OBJECT | MAY_BE_RC1 | MAY_BE_RCN;
+ }
+ if (orig & MAY_BE_OBJECT) {
+ orig |= (MAY_BE_RC1|MAY_BE_RCN);
+ }
+ UPDATE_SSA_TYPE(orig, ssa_ops[i].op1_def);
+ COPY_SSA_OBJ_TYPE(ssa_ops[i].op1_use, ssa_ops[i].op1_def);
+ }
+ } else {
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+ }
+ if (ssa_ops[i].result_def >= 0) {
+ if (opline->extended_value == ZEND_ASSIGN_DIM) {
+ if (opline->op2_type == IS_UNUSED) {
+ /* When appending to an array and the LONG_MAX key is already used
+ * null will be returned. */
+ tmp |= MAY_BE_NULL;
+ }
+ if (t2 & (MAY_BE_ARRAY | MAY_BE_OBJECT)) {
+ /* Arrays and objects cannot be used as keys. */
+ tmp |= MAY_BE_NULL;
+ }
+ if (t1 & (MAY_BE_ANY - (MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY))) {
+ /* null and false are implicitly converted to array, anything else
+ * results in a null return value. */
+ tmp |= MAY_BE_NULL;
+ }
+ } else if (opline->extended_value == ZEND_ASSIGN_OBJ) {
+ if (orig & (MAY_BE_ANY - (MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_OBJECT))) {
+ /* null and false (and empty string) are implicitly converted to object,
+ * anything else results in a null return value. */
+ tmp |= MAY_BE_NULL;
+ }
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+ }
+ break;
+ case ZEND_PRE_INC:
+ case ZEND_PRE_DEC:
+ tmp = 0;
+ if (t1 & MAY_BE_REF) {
+ tmp |= MAY_BE_REF;
+ }
+ if (t1 & (MAY_BE_RC1|MAY_BE_RCN)) {
+ tmp |= MAY_BE_RC1;
+ if (ssa_ops[i].result_def >= 0) {
+ tmp |= MAY_BE_RCN;
+ }
+ }
+ if ((t1 & MAY_BE_ANY) == MAY_BE_LONG) {
+ if (!ssa_var_info[ssa_ops[i].op1_use].has_range ||
+ (opline->opcode == ZEND_PRE_DEC &&
+ (ssa_var_info[ssa_ops[i].op1_use].range.underflow ||
+ ssa_var_info[ssa_ops[i].op1_use].range.min == ZEND_LONG_MIN)) ||
+ (opline->opcode == ZEND_PRE_INC &&
+ (ssa_var_info[ssa_ops[i].op1_use].range.overflow ||
+ ssa_var_info[ssa_ops[i].op1_use].range.max == ZEND_LONG_MAX))) {
+ /* may overflow */
+ tmp |= MAY_BE_LONG | MAY_BE_DOUBLE;
+ } else {
+ tmp |= MAY_BE_LONG;
+ }
+ } else {
+ if (t1 & MAY_BE_ERROR) {
+ tmp |= MAY_BE_NULL;
+ }
+ if (t1 & (MAY_BE_UNDEF | MAY_BE_NULL)) {
+ if (opline->opcode == ZEND_PRE_INC) {
+ tmp |= MAY_BE_LONG;
+ } else {
+ tmp |= MAY_BE_NULL;
+ }
+ }
+ if (t1 & MAY_BE_LONG) {
+ tmp |= MAY_BE_LONG | MAY_BE_DOUBLE;
+ }
+ if (t1 & MAY_BE_DOUBLE) {
+ tmp |= MAY_BE_DOUBLE;
+ }
+ if (t1 & MAY_BE_STRING) {
+ tmp |= MAY_BE_STRING | MAY_BE_LONG | MAY_BE_DOUBLE;
+ }
+ tmp |= t1 & (MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_RESOURCE | MAY_BE_ARRAY | MAY_BE_OBJECT | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_KEY_ANY);
+ }
+ if (ssa_ops[i].op1_def >= 0) {
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+ }
+ if (ssa_ops[i].result_def >= 0) {
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+ }
+ break;
+ case ZEND_POST_INC:
+ case ZEND_POST_DEC:
+ if (ssa_ops[i].result_def >= 0) {
+ tmp = 0;
+ if (t1 & (MAY_BE_RC1|MAY_BE_RCN)) {
+ tmp |= MAY_BE_RC1|MAY_BE_RCN;
+ }
+ tmp |= t1 & ~(MAY_BE_UNDEF|MAY_BE_ERROR|MAY_BE_REF|MAY_BE_RCN);
+ if (t1 & MAY_BE_UNDEF) {
+ tmp |= MAY_BE_NULL;
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+ }
+ tmp = 0;
+ if (t1 & MAY_BE_REF) {
+ tmp |= MAY_BE_REF;
+ }
+ if (t1 & (MAY_BE_RC1|MAY_BE_RCN)) {
+ tmp |= MAY_BE_RC1;
+ }
+ if ((t1 & MAY_BE_ANY) == MAY_BE_LONG) {
+ if (!ssa_var_info[ssa_ops[i].op1_use].has_range ||
+ (opline->opcode == ZEND_PRE_DEC &&
+ (ssa_var_info[ssa_ops[i].op1_use].range.underflow ||
+ ssa_var_info[ssa_ops[i].op1_use].range.min == ZEND_LONG_MIN)) ||
+ (opline->opcode == ZEND_PRE_INC &&
+ (ssa_var_info[ssa_ops[i].op1_use].range.overflow ||
+ ssa_var_info[ssa_ops[i].op1_use].range.max == ZEND_LONG_MAX))) {
+ /* may overflow */
+ tmp |= MAY_BE_LONG | MAY_BE_DOUBLE;
+ } else {
+ tmp |= MAY_BE_LONG;
+ }
+ } else {
+ if (t1 & MAY_BE_ERROR) {
+ tmp |= MAY_BE_NULL;
+ }
+ if (t1 & (MAY_BE_UNDEF | MAY_BE_NULL)) {
+ if (opline->opcode == ZEND_POST_INC) {
+ tmp |= MAY_BE_LONG;
+ } else {
+ tmp |= MAY_BE_NULL;
+ }
+ }
+ if (t1 & MAY_BE_LONG) {
+ tmp |= MAY_BE_LONG | MAY_BE_DOUBLE;
+ }
+ if (t1 & MAY_BE_DOUBLE) {
+ tmp |= MAY_BE_DOUBLE;
+ }
+ if (t1 & MAY_BE_STRING) {
+ tmp |= MAY_BE_STRING | MAY_BE_LONG | MAY_BE_DOUBLE;
+ }
+ tmp |= t1 & (MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_RESOURCE | MAY_BE_ARRAY | MAY_BE_OBJECT | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_KEY_ANY);
+ }
+ if (ssa_ops[i].op1_def >= 0) {
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+ }
+ break;
+ case ZEND_ASSIGN_DIM:
+ if (opline->op1_type == IS_CV) {
+ tmp = assign_dim_result_type(t1, t2, OP1_DATA_INFO(), opline->op2_type);
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+ COPY_SSA_OBJ_TYPE(ssa_ops[i].op1_use, ssa_ops[i].op1_def);
+ }
+ if (ssa_ops[i].result_def >= 0) {
+ tmp = 0;
+ if (t1 & MAY_BE_STRING) {
+ tmp |= MAY_BE_STRING;
+ }
+ if (t1 & ((MAY_BE_ANY|MAY_BE_UNDEF) - MAY_BE_STRING)) {
+ tmp |= (OP1_DATA_INFO() & (MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF));
+
+ if (opline->op2_type == IS_UNUSED) {
+ /* When appending to an array and the LONG_MAX key is already used
+ * null will be returned. */
+ tmp |= MAY_BE_NULL;
+ }
+ if (t2 & (MAY_BE_ARRAY | MAY_BE_OBJECT)) {
+ /* Arrays and objects cannot be used as keys. */
+ tmp |= MAY_BE_NULL;
+ }
+ if (t1 & (MAY_BE_ANY - (MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY))) {
+ /* undef, null and false are implicitly converted to array, anything else
+ * results in a null return value. */
+ tmp |= MAY_BE_NULL;
+ }
+ }
+ tmp |= MAY_BE_RC1 | MAY_BE_RCN;
+ if (t1 & MAY_BE_OBJECT) {
+ tmp |= MAY_BE_REF;
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+ }
+ if ((opline+1)->op1_type == IS_CV && ssa_ops[i+1].op1_def >= 0) {
+ opline++;
+ i++;
+ tmp = OP1_INFO();
+ if (tmp & (MAY_BE_ANY | MAY_BE_REF)) {
+ if (tmp & MAY_BE_RC1) {
+ tmp |= MAY_BE_RCN;
+ }
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+ }
+ break;
+ case ZEND_ASSIGN_OBJ:
+ if (opline->op1_type == IS_CV) {
+ tmp = t1;
+ if (t1 & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE)) {
+ tmp &= ~(MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE);
+ tmp |= MAY_BE_OBJECT | MAY_BE_RC1 | MAY_BE_RCN;
+ }
+ if (tmp & MAY_BE_OBJECT) {
+ tmp |= MAY_BE_RC1 | MAY_BE_RCN;
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+ COPY_SSA_OBJ_TYPE(ssa_ops[i].op1_use, ssa_ops[i].op1_def);
+ }
+ if (ssa_ops[i].result_def >= 0) {
+ // TODO: ???
+ tmp = MAY_BE_REF | MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+ }
+ if ((opline+1)->op1_type == IS_CV) {
+ opline++;
+ i++;
+ tmp = OP1_INFO();
+ if (tmp & (MAY_BE_ANY | MAY_BE_REF)) {
+ if (tmp & MAY_BE_RC1) {
+ tmp |= MAY_BE_RCN;
+ }
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+ }
+ break;
+ case ZEND_ASSIGN:
+ if (opline->op2_type == IS_CV && ssa_ops[i].op2_def >= 0) {
+ tmp = t2;
+ if (tmp & (MAY_BE_ANY | MAY_BE_REF)) {
+ if (tmp & MAY_BE_RC1) {
+ tmp |= MAY_BE_RCN;
+ }
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].op2_def);
+ }
+ tmp = t2 & ~(MAY_BE_UNDEF|MAY_BE_REF|MAY_BE_RC1|MAY_BE_RCN);
+ if (t2 & MAY_BE_UNDEF) {
+ tmp |= MAY_BE_NULL;
+ }
+ if (t1 & MAY_BE_REF) {
+ tmp |= MAY_BE_REF;
+ }
+ if (t2 & MAY_BE_REF) {
+ tmp |= MAY_BE_RC1 | MAY_BE_RCN;
+ } else if (opline->op2_type & (IS_TMP_VAR|IS_VAR)) {
+ tmp |= t2 & (MAY_BE_RC1|MAY_BE_RCN);
+ } else if (t2 & (MAY_BE_RC1|MAY_BE_RCN)) {
+ tmp |= MAY_BE_RCN;
+ }
+ if (RETURN_VALUE_USED(opline) && (tmp & MAY_BE_RC1)) {
+ tmp |= MAY_BE_RCN;
+ }
+ if (ssa_ops[i].op1_def >= 0) {
+ if (ssa_var_info[ssa_ops[i].op1_def].use_as_double) {
+ tmp &= ~MAY_BE_LONG;
+ tmp |= MAY_BE_DOUBLE;
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+ COPY_SSA_OBJ_TYPE(ssa_ops[i].op2_use, ssa_ops[i].op1_def);
+ }
+ if (ssa_ops[i].result_def >= 0) {
+ UPDATE_SSA_TYPE(tmp & ~MAY_BE_REF, ssa_ops[i].result_def);
+ COPY_SSA_OBJ_TYPE(ssa_ops[i].op2_use, ssa_ops[i].result_def);
+ }
+ break;
+ case ZEND_ASSIGN_REF:
+// TODO: ???
+ if (opline->op2_type == IS_CV) {
+ tmp = (MAY_BE_REF | t2) & ~(MAY_BE_UNDEF|MAY_BE_RC1|MAY_BE_RCN);
+ if (t2 & MAY_BE_UNDEF) {
+ tmp |= MAY_BE_NULL;
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].op2_def);
+ }
+ if (opline->op2_type == IS_VAR && opline->extended_value == ZEND_RETURNS_FUNCTION) {
+ tmp = (MAY_BE_REF | MAY_BE_RCN | MAY_BE_RC1 | t2) & ~MAY_BE_UNDEF;
+ } else {
+ tmp = (MAY_BE_REF | t2) & ~(MAY_BE_UNDEF|MAY_BE_ERROR|MAY_BE_RC1|MAY_BE_RCN);
+ }
+ if (t2 & MAY_BE_UNDEF) {
+ tmp |= MAY_BE_NULL;
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+ if (ssa_ops[i].result_def >= 0) {
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+ }
+ break;
+ case ZEND_BIND_GLOBAL:
+ tmp = MAY_BE_REF | MAY_BE_ANY
+ | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+ break;
+ case ZEND_BIND_STATIC:
+ tmp = MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF
+ | (opline->extended_value ? MAY_BE_REF : (MAY_BE_RC1 | MAY_BE_RCN));
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+ break;
+ case ZEND_SEND_VAR:
+ if (ssa_ops[i].op1_def >= 0) {
+ tmp = t1;
+ if ((t1 & (MAY_BE_RC1|MAY_BE_REF)) && (opline->op1_type == IS_CV)) {
+ tmp |= MAY_BE_RCN;
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+ COPY_SSA_OBJ_TYPE(ssa_ops[i].op1_use, ssa_ops[i].op1_def);
+ }
+ break;
+ case ZEND_BIND_LEXICAL:
+ if (ssa_ops[i].op2_def >= 0) {
+ if (opline->extended_value) {
+ tmp = t2 | MAY_BE_REF;
+ } else {
+ tmp = t2 & ~(MAY_BE_RC1|MAY_BE_RCN);
+ if (t2 & (MAY_BE_RC1|MAY_BE_RCN)) {
+ tmp |= MAY_BE_RCN;
+ }
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].op2_def);
+ COPY_SSA_OBJ_TYPE(ssa_ops[i].op2_use, ssa_ops[i].op2_def);
+ }
+ break;
+ case ZEND_YIELD:
+ if (ssa_ops[i].op1_def >= 0) {
+ if (op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE) {
+ tmp = t1 | MAY_BE_REF;
+ } else {
+ tmp = t1 & ~(MAY_BE_RC1|MAY_BE_RCN);
+ if (t1 & (MAY_BE_RC1|MAY_BE_RCN)) {
+ tmp |= MAY_BE_RCN;
+ }
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+ COPY_SSA_OBJ_TYPE(ssa_ops[i].op1_use, ssa_ops[i].op1_def);
+ }
+ if (ssa_ops[i].result_def >= 0) {
+ tmp = MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF
+ | MAY_BE_RC1 | MAY_BE_RCN;
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+ }
+ break;
+ case ZEND_SEND_VAR_EX:
+ if (ssa_ops[i].op1_def >= 0) {
+ tmp = (t1 & MAY_BE_UNDEF)|MAY_BE_REF|MAY_BE_RC1|MAY_BE_RCN|MAY_BE_ANY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+ }
+ break;
+ case ZEND_SEND_REF:
+ if (ssa_ops[i].op1_def >= 0) {
+ tmp = MAY_BE_REF|MAY_BE_RC1|MAY_BE_RCN|MAY_BE_ANY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+ }
+ break;
+ case ZEND_SEND_UNPACK:
+ if (ssa_ops[i].op1_def >= 0) {
+ tmp = t1;
+ if (t1 & MAY_BE_ARRAY) {
+ tmp |= MAY_BE_RC1 | MAY_BE_RCN;
+ /* SEND_UNPACK may acquire references into the array */
+ tmp |= MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+ }
+ if (t1 & MAY_BE_OBJECT) {
+ tmp |= MAY_BE_RC1 | MAY_BE_RCN;
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+ }
+ break;
+ case ZEND_FAST_CONCAT:
+ case ZEND_ROPE_INIT:
+ case ZEND_ROPE_ADD:
+ case ZEND_ROPE_END:
+ UPDATE_SSA_TYPE(MAY_BE_STRING|MAY_BE_RC1|MAY_BE_RCN, ssa_ops[i].result_def);
+ break;
+ case ZEND_RECV:
+ case ZEND_RECV_INIT:
+ {
+ /* Typehinting */
+ zend_func_info *func_info;
+ zend_arg_info *arg_info = NULL;
+ if (op_array->arg_info && opline->op1.num <= op_array->num_args) {
+ arg_info = &op_array->arg_info[opline->op1.num-1];
+ }
+
+ ce = NULL;
+ if (arg_info) {
+ tmp = zend_fetch_arg_info(script, arg_info, &ce);
+ if (opline->opcode == ZEND_RECV_INIT &&
+ Z_CONSTANT_P(CRT_CONSTANT_EX(op_array, opline->op2, ssa->rt_constants))) {
+ /* The constant may resolve to NULL */
+ tmp |= MAY_BE_NULL;
+ }
+ if (arg_info->pass_by_reference) {
+ tmp |= MAY_BE_REF;
+ } else if (tmp & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
+ tmp |= MAY_BE_RC1|MAY_BE_RCN;
+ }
+ } else {
+ tmp = MAY_BE_REF|MAY_BE_RC1|MAY_BE_RCN|MAY_BE_ANY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
+ }
+ func_info = ZEND_FUNC_INFO(op_array);
+ if (func_info && (int)opline->op1.num-1 < func_info->num_args) {
+ tmp = (tmp & (MAY_BE_RC1|MAY_BE_RCN|MAY_BE_REF)) |
+ (tmp & func_info->arg_info[opline->op1.num-1].info.type);
+ }
+#if 0
+ /* We won't recieve unused arguments */
+ if (ssa_vars[ssa_ops[i].result_def].use_chain < 0 &&
+ ssa_vars[ssa_ops[i].result_def].phi_use_chain == NULL &&
+ op_array->arg_info &&
+ opline->op1.num <= op_array->num_args &&
+ op_array->arg_info[opline->op1.num-1].class_name == NULL &&
+ !op_array->arg_info[opline->op1.num-1].type_hint) {
+ tmp = MAY_BE_UNDEF|MAY_BE_RCN;
+ }
+#endif
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+ if (func_info &&
+ (int)opline->op1.num-1 < func_info->num_args &&
+ func_info->arg_info[opline->op1.num-1].info.ce) {
+ UPDATE_SSA_OBJ_TYPE(
+ func_info->arg_info[opline->op1.num-1].info.ce,
+ func_info->arg_info[opline->op1.num-1].info.is_instanceof,
+ ssa_ops[i].result_def);
+ } else if (ce) {
+ UPDATE_SSA_OBJ_TYPE(ce, 1, ssa_ops[i].result_def);
+ } else {
+ UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_ops[i].result_def);
+ }
+ break;
+ }
+ case ZEND_DECLARE_CLASS:
+ case ZEND_DECLARE_INHERITED_CLASS:
+ case ZEND_DECLARE_ANON_CLASS:
+ case ZEND_DECLARE_ANON_INHERITED_CLASS:
+ UPDATE_SSA_TYPE(MAY_BE_CLASS, ssa_ops[i].result_def);
+ if (script && (ce = zend_hash_find_ptr(&script->class_table, Z_STR_P(CRT_CONSTANT_EX(op_array, opline->op1, ssa->rt_constants)))) != NULL) {
+ UPDATE_SSA_OBJ_TYPE(ce, 0, ssa_ops[i].result_def);
+ }
+ break;
+ case ZEND_FETCH_CLASS:
+ UPDATE_SSA_TYPE(MAY_BE_CLASS, ssa_ops[i].result_def);
+ if (opline->op2_type == IS_UNUSED) {
+ switch (opline->extended_value & ZEND_FETCH_CLASS_MASK) {
+ case ZEND_FETCH_CLASS_SELF:
+ if (op_array->scope) {
+ UPDATE_SSA_OBJ_TYPE(op_array->scope, 0, ssa_ops[i].result_def);
+ } else {
+ UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_ops[i].result_def);
+ }
+ break;
+ case ZEND_FETCH_CLASS_PARENT:
+ if (op_array->scope && op_array->scope->parent) {
+ UPDATE_SSA_OBJ_TYPE(op_array->scope->parent, 0, ssa_ops[i].result_def);
+ } else {
+ UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_ops[i].result_def);
+ }
+ break;
+ case ZEND_FETCH_CLASS_STATIC:
+ default:
+ UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_ops[i].result_def);
+ break;
+ }
+ } else if (opline->op2_type == IS_CONST) {
+ zval *zv = CRT_CONSTANT_EX(op_array, opline->op2, ssa->rt_constants);
+ if (Z_TYPE_P(zv) == IS_STRING) {
+ ce = get_class_entry(script, Z_STR_P(zv+1));
+ UPDATE_SSA_OBJ_TYPE(ce, 0, ssa_ops[i].result_def);
+ } else {
+ UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_ops[i].result_def);
+ }
+ } else {
+ COPY_SSA_OBJ_TYPE(ssa_ops[i].op2_use, ssa_ops[i].result_def);
+ }
+ break;
+ case ZEND_NEW:
+ tmp = MAY_BE_RC1|MAY_BE_RCN|MAY_BE_OBJECT;
+ if (opline->op1_type == IS_CONST &&
+ (ce = get_class_entry(script, Z_STR_P(CRT_CONSTANT_EX(op_array, opline->op1, ssa->rt_constants)+1))) != NULL) {
+ UPDATE_SSA_OBJ_TYPE(ce, 0, ssa_ops[i].result_def);
+ } else if ((t1 & MAY_BE_CLASS) && ssa_ops[i].op1_use >= 0 && ssa_var_info[ssa_ops[i].op1_use].ce) {
+ UPDATE_SSA_OBJ_TYPE(ssa_var_info[ssa_ops[i].op1_use].ce, ssa_var_info[ssa_ops[i].op1_use].is_instanceof, ssa_ops[i].result_def);
+ } else {
+ UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_ops[i].result_def);
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+ break;
+ case ZEND_CLONE:
+ UPDATE_SSA_TYPE(MAY_BE_RC1|MAY_BE_RCN|MAY_BE_OBJECT, ssa_ops[i].result_def);
+ COPY_SSA_OBJ_TYPE(ssa_ops[i].op1_use, ssa_ops[i].result_def);
+ break;
+ case ZEND_INIT_ARRAY:
+ case ZEND_ADD_ARRAY_ELEMENT:
+ if (opline->op1_type == IS_CV && ssa_ops[i].op1_def >= 0) {
+ if (opline->extended_value & ZEND_ARRAY_ELEMENT_REF) {
+ tmp = (MAY_BE_REF | t1) & ~(MAY_BE_UNDEF|MAY_BE_RC1|MAY_BE_RCN);
+ if (t1 & MAY_BE_UNDEF) {
+ tmp |= MAY_BE_NULL;
+ }
+ } else if ((t1 & (MAY_BE_REF|MAY_BE_RC1|MAY_BE_RCN)) == MAY_BE_REF) {
+ tmp = (MAY_BE_REF | t1) & ~(MAY_BE_UNDEF|MAY_BE_RC1|MAY_BE_RCN);
+ if (t1 & MAY_BE_UNDEF) {
+ tmp |= MAY_BE_NULL;
+ }
+ } else if (t1 & MAY_BE_REF) {
+ tmp = (MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_REF | t1);
+ } else {
+ tmp = t1;
+ if (t1 & MAY_BE_RC1) {
+ tmp |= MAY_BE_RCN;
+ }
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+ }
+ if (ssa_ops[i].result_def >= 0) {
+ tmp = MAY_BE_RC1|MAY_BE_ARRAY;
+ if (opline->op1_type != IS_UNUSED) {
+ tmp |= (t1 & MAY_BE_ANY) << MAY_BE_ARRAY_SHIFT;
+ if (t1 & MAY_BE_UNDEF) {
+ tmp |= MAY_BE_ARRAY_OF_NULL;
+ }
+ if (opline->extended_value & ZEND_ARRAY_ELEMENT_REF) {
+ tmp |= MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
+ }
+ }
+ if (ssa_ops[i].result_use >= 0) {
+ tmp |= ssa_var_info[ssa_ops[i].result_use].type;
+ }
+ if (opline->op2_type == IS_UNUSED) {
+ tmp |= MAY_BE_ARRAY_KEY_LONG;
+ } else {
+ if (t2 & (MAY_BE_LONG|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_DOUBLE)) {
+ tmp |= MAY_BE_ARRAY_KEY_LONG;
+ }
+ if (t2 & (MAY_BE_STRING)) {
+ tmp |= MAY_BE_ARRAY_KEY_STRING;
+ if (opline->op2_type != IS_CONST) {
+ // FIXME: numeric string
+ tmp |= MAY_BE_ARRAY_KEY_LONG;
+ }
+ }
+ if (t2 & (MAY_BE_UNDEF | MAY_BE_NULL)) {
+ tmp |= MAY_BE_ARRAY_KEY_STRING;
+ }
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+ }
+ break;
+ case ZEND_UNSET_VAR:
+ ZEND_ASSERT(opline->extended_value & ZEND_QUICK_SET);
+ tmp = MAY_BE_UNDEF;
+ if (!op_array->function_name) {
+ /* In global scope, we know nothing */
+ tmp |= MAY_BE_REF;
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+ break;
+ case ZEND_UNSET_DIM:
+ case ZEND_UNSET_OBJ:
+ if (ssa_ops[i].op1_def >= 0) {
+ UPDATE_SSA_TYPE(t1, ssa_ops[i].op1_def);
+ COPY_SSA_OBJ_TYPE(ssa_ops[i].op1_use, ssa_ops[i].op1_def);
+ }
+ break;
+ case ZEND_FE_RESET_R:
+ case ZEND_FE_RESET_RW:
+ if (ssa_ops[i].op1_def >= 0) {
+ tmp = t1;
+ if (opline->opcode == ZEND_FE_RESET_RW) {
+ tmp |= MAY_BE_REF;
+ } else {
+ if ((t1 & MAY_BE_RC1) && opline->op1_type != IS_TMP_VAR) {
+ tmp |= MAY_BE_RCN;
+ }
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+ COPY_SSA_OBJ_TYPE(ssa_ops[i].op1_use, ssa_ops[i].op1_def);
+ }
+ if (opline->opcode == ZEND_FE_RESET_RW) {
+//???
+ tmp = MAY_BE_REF | (t1 & (MAY_BE_ARRAY | MAY_BE_OBJECT));
+ } else {
+ tmp = MAY_BE_RC1 | MAY_BE_RCN | (t1 & (MAY_BE_ARRAY | MAY_BE_OBJECT | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF));
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+ COPY_SSA_OBJ_TYPE(ssa_ops[i].op1_use, ssa_ops[i].result_def);
+ break;
+ case ZEND_FE_FETCH_R:
+ case ZEND_FE_FETCH_RW:
+ tmp = (t2 & MAY_BE_REF);
+ if (t1 & MAY_BE_OBJECT) {
+ if (opline->opcode == ZEND_FE_FETCH_RW) {
+ tmp |= MAY_BE_REF | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+ } else {
+ tmp |= MAY_BE_REF | MAY_BE_RCN | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+ }
+ }
+ if (t1 & MAY_BE_ARRAY) {
+ if (opline->opcode == ZEND_FE_FETCH_RW) {
+ tmp |= MAY_BE_REF | MAY_BE_RCN | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+ } else {
+ tmp |= ((t1 & MAY_BE_ARRAY_OF_ANY) >> MAY_BE_ARRAY_SHIFT);
+ if (tmp & MAY_BE_ARRAY) {
+ tmp |= MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+ }
+ if (t1 & MAY_BE_ARRAY_OF_REF) {
+ tmp |= MAY_BE_RC1 | MAY_BE_RCN;
+ } else if (tmp & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
+ tmp |= MAY_BE_RC1 | MAY_BE_RCN;
+ }
+ }
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].op2_def);
+ if (ssa_ops[i].result_def >= 0) {
+ tmp = 0;
+ if (t1 & MAY_BE_OBJECT) {
+ tmp |= MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+ }
+ if (t1 & MAY_BE_ARRAY) {
+ if (t1 & MAY_BE_ARRAY_KEY_LONG) {
+ tmp |= MAY_BE_LONG;
+ }
+ if (t1 & MAY_BE_ARRAY_KEY_STRING) {
+ tmp |= MAY_BE_STRING | MAY_BE_RCN;
+ }
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+ }
+ break;
+ case ZEND_FETCH_DIM_R:
+ case ZEND_FETCH_DIM_IS:
+ case ZEND_FETCH_DIM_RW:
+ case ZEND_FETCH_DIM_W:
+ case ZEND_FETCH_DIM_UNSET:
+ case ZEND_FETCH_DIM_FUNC_ARG:
+ case ZEND_FETCH_LIST:
+ if (ssa_ops[i].op1_def >= 0) {
+ tmp = t1 & ~(MAY_BE_RC1|MAY_BE_RCN);
+ if (opline->opcode == ZEND_FETCH_DIM_W ||
+ opline->opcode == ZEND_FETCH_DIM_RW ||
+ opline->opcode == ZEND_FETCH_DIM_FUNC_ARG) {
+ if (t1 & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE)) {
+ if (opline->opcode != ZEND_FETCH_DIM_FUNC_ARG) {
+ tmp &= ~(MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE);
+ }
+ tmp |= MAY_BE_ARRAY | MAY_BE_RC1;
+ }
+ if (t1 & (MAY_BE_STRING|MAY_BE_ARRAY)) {
+ tmp |= MAY_BE_RC1;
+ }
+ if (t1 & (MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
+ tmp |= t1 & (MAY_BE_RC1|MAY_BE_RCN);
+ }
+ if (opline->op2_type == IS_UNUSED) {
+ tmp |= MAY_BE_ARRAY_KEY_LONG;
+ } else {
+ if (t2 & (MAY_BE_LONG|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_RESOURCE|MAY_BE_DOUBLE)) {
+ tmp |= MAY_BE_ARRAY_KEY_LONG;
+ }
+ if (t2 & MAY_BE_STRING) {
+ tmp |= MAY_BE_ARRAY_KEY_STRING;
+ if (opline->op2_type != IS_CONST) {
+ // FIXME: numeric string
+ tmp |= MAY_BE_ARRAY_KEY_LONG;
+ }
+ }
+ if (t2 & (MAY_BE_UNDEF | MAY_BE_NULL)) {
+ tmp |= MAY_BE_ARRAY_KEY_STRING;
+ }
+ }
+ }
+ j = ssa_vars[ssa_ops[i].result_def].use_chain;
+ while (j >= 0) {
+ switch (op_array->opcodes[j].opcode) {
+ case ZEND_FETCH_DIM_W:
+ case ZEND_FETCH_DIM_RW:
+ case ZEND_FETCH_DIM_FUNC_ARG:
+ case ZEND_ASSIGN_ADD:
+ case ZEND_ASSIGN_SUB:
+ case ZEND_ASSIGN_MUL:
+ case ZEND_ASSIGN_DIV:
+ case ZEND_ASSIGN_MOD:
+ case ZEND_ASSIGN_SL:
+ case ZEND_ASSIGN_SR:
+ case ZEND_ASSIGN_CONCAT:
+ case ZEND_ASSIGN_BW_OR:
+ case ZEND_ASSIGN_BW_AND:
+ case ZEND_ASSIGN_BW_XOR:
+ case ZEND_ASSIGN_POW:
+ case ZEND_ASSIGN_DIM:
+ tmp |= MAY_BE_ARRAY | MAY_BE_ARRAY_OF_ARRAY;
+ break;
+ case ZEND_FETCH_OBJ_W:
+ case ZEND_FETCH_OBJ_RW:
+ case ZEND_FETCH_OBJ_FUNC_ARG:
+ case ZEND_ASSIGN_OBJ:
+ case ZEND_PRE_INC_OBJ:
+ case ZEND_PRE_DEC_OBJ:
+ case ZEND_POST_INC_OBJ:
+ case ZEND_POST_DEC_OBJ:
+ tmp |= MAY_BE_ARRAY_OF_OBJECT;
+ break;
+ case ZEND_SEND_VAR_EX:
+ case ZEND_SEND_VAR_NO_REF:
+ case ZEND_SEND_VAR_NO_REF_EX:
+ case ZEND_SEND_REF:
+ case ZEND_ASSIGN_REF:
+ case ZEND_YIELD:
+ case ZEND_INIT_ARRAY:
+ case ZEND_ADD_ARRAY_ELEMENT:
+ case ZEND_RETURN_BY_REF:
+ case ZEND_VERIFY_RETURN_TYPE:
+ case ZEND_MAKE_REF:
+ tmp |= MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+ break;
+ case ZEND_PRE_INC:
+ case ZEND_PRE_DEC:
+ case ZEND_POST_INC:
+ case ZEND_POST_DEC:
+ if (tmp & MAY_BE_ARRAY_OF_LONG) {
+ /* may overflow */
+ tmp |= MAY_BE_ARRAY_OF_DOUBLE;
+ } else if (!(tmp & (MAY_BE_ARRAY_OF_LONG|MAY_BE_ARRAY_OF_DOUBLE))) {
+ tmp |= MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE;
+ }
+ break;
+ case ZEND_UNSET_DIM:
+ case ZEND_UNSET_OBJ:
+ case ZEND_FETCH_DIM_UNSET:
+ case ZEND_FETCH_OBJ_UNSET:
+ break;
+ default :
+ break;
+ }
+ j = zend_ssa_next_use(ssa_ops, ssa_ops[i].result_def, j);
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+ COPY_SSA_OBJ_TYPE(ssa_ops[i].op1_use, ssa_ops[i].op1_def);
+ }
+ /* FETCH_LIST on a string behaves like FETCH_R on null */
+ tmp = zend_array_element_type(
+ opline->opcode != ZEND_FETCH_LIST ? t1 : ((t1 & ~MAY_BE_STRING) | MAY_BE_NULL),
+ opline->opcode != ZEND_FETCH_DIM_R && opline->opcode != ZEND_FETCH_DIM_IS
+ && opline->opcode != ZEND_FETCH_LIST,
+ opline->op2_type == IS_UNUSED);
+ if (opline->opcode == ZEND_FETCH_DIM_W ||
+ opline->opcode == ZEND_FETCH_DIM_RW ||
+ opline->opcode == ZEND_FETCH_DIM_FUNC_ARG) {
+ if (t1 & (MAY_BE_ERROR|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_RESOURCE|MAY_BE_OBJECT)) {
+ tmp |= MAY_BE_ERROR;
+ } else if (opline->op2_type == IS_UNUSED) {
+ tmp |= MAY_BE_ERROR;
+ } else if (t2 & (MAY_BE_ARRAY|MAY_BE_OBJECT)) {
+ tmp |= MAY_BE_ERROR;
+ }
+ } else if (opline->opcode == ZEND_FETCH_DIM_IS && (t1 & MAY_BE_STRING)) {
+ tmp |= MAY_BE_NULL;
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+ break;
+ case ZEND_FETCH_THIS:
+ UPDATE_SSA_OBJ_TYPE(op_array->scope, 1, ssa_ops[i].result_def);
+ UPDATE_SSA_TYPE(MAY_BE_RC1|MAY_BE_RCN|MAY_BE_OBJECT, ssa_ops[i].result_def);
+ break;
+ case ZEND_FETCH_OBJ_R:
+ case ZEND_FETCH_OBJ_IS:
+ case ZEND_FETCH_OBJ_RW:
+ case ZEND_FETCH_OBJ_W:
+ case ZEND_FETCH_OBJ_UNSET:
+ case ZEND_FETCH_OBJ_FUNC_ARG:
+ if (ssa_ops[i].op1_def >= 0) {
+ tmp = t1;
+ if (opline->opcode == ZEND_FETCH_OBJ_W ||
+ opline->opcode == ZEND_FETCH_OBJ_RW ||
+ opline->opcode == ZEND_FETCH_OBJ_FUNC_ARG) {
+ if (opline->opcode != ZEND_FETCH_DIM_FUNC_ARG) {
+ if (t1 & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE)) {
+ tmp &= ~(MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE);
+ tmp |= MAY_BE_OBJECT | MAY_BE_RC1 | MAY_BE_RCN;
+ }
+ }
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+ COPY_SSA_OBJ_TYPE(ssa_ops[i].op1_use, ssa_ops[i].op1_def);
+ }
+ if (ssa_ops[i].result_def >= 0) {
+ tmp = MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+ if (opline->opcode != ZEND_FETCH_OBJ_R && opline->opcode != ZEND_FETCH_OBJ_IS) {
+ tmp |= MAY_BE_ERROR;
+ }
+ if (opline->result_type == IS_TMP_VAR) {
+ tmp |= MAY_BE_RC1 | MAY_BE_RCN;
+ } else {
+ tmp |= MAY_BE_REF | MAY_BE_RC1 | MAY_BE_RCN;
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+ }
+ break;
+ case ZEND_DO_FCALL:
+ case ZEND_DO_ICALL:
+ case ZEND_DO_UCALL:
+ case ZEND_DO_FCALL_BY_NAME:
+ if (ssa_ops[i].result_def >= 0) {
+ zend_func_info *func_info = ZEND_FUNC_INFO(op_array);
+ zend_call_info *call_info;
+
+ if (!func_info || !func_info->call_map) {
+ goto unknown_opcode;
+ }
+ call_info = func_info->call_map[opline - op_array->opcodes];
+ if (!call_info) {
+ goto unknown_opcode;
+ }
+ tmp = zend_get_func_info(call_info, ssa) & ~FUNC_MAY_WARN;
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+ if (call_info->callee_func->type == ZEND_USER_FUNCTION) {
+ func_info = ZEND_FUNC_INFO(&call_info->callee_func->op_array);
+ if (func_info) {
+ UPDATE_SSA_OBJ_TYPE(
+ func_info->return_info.ce,
+ func_info->return_info.is_instanceof,
+ ssa_ops[i].result_def);
+ }
+ }
+ }
+ break;
+ case ZEND_FETCH_CONSTANT:
+ case ZEND_FETCH_CLASS_CONSTANT:
+ UPDATE_SSA_TYPE(MAY_BE_RC1|MAY_BE_RCN|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_STRING|MAY_BE_RESOURCE|MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY, ssa_ops[i].result_def);
+ break;
+ case ZEND_STRLEN:
+ tmp = MAY_BE_LONG;
+ if (t1 & (MAY_BE_ANY - (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_STRING))) {
+ tmp |= MAY_BE_NULL;
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+ break;
+ case ZEND_TYPE_CHECK:
+ case ZEND_DEFINED:
+ UPDATE_SSA_TYPE(MAY_BE_FALSE|MAY_BE_TRUE, ssa_ops[i].result_def);
+ break;
+ case ZEND_VERIFY_RETURN_TYPE:
+ if (t1 & MAY_BE_REF) {
+ tmp = t1;
+ ce = NULL;
+ } else {
+ zend_arg_info *ret_info = op_array->arg_info - 1;
+
+ tmp = zend_fetch_arg_info(script, ret_info, &ce);
+ if (tmp & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
+ tmp |= MAY_BE_RC1 | MAY_BE_RCN;
+ }
+ }
+ if (opline->op1_type & (IS_TMP_VAR|IS_VAR|IS_CV)) {
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+ if (ce) {
+ UPDATE_SSA_OBJ_TYPE(ce, 1, ssa_ops[i].op1_def);
+ } else {
+ UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_ops[i].op1_def);
+ }
+ } else {
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+ if (ce) {
+ UPDATE_SSA_OBJ_TYPE(ce, 1, ssa_ops[i].result_def);
+ } else {
+ UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_ops[i].result_def);
+ }
+ }
+ break;
+ case ZEND_CATCH:
+ case ZEND_INCLUDE_OR_EVAL:
+ /* Forbidden opcodes */
+ ZEND_ASSERT(0);
+ break;
+ default:
+unknown_opcode:
+ if (ssa_ops[i].op1_def >= 0) {
+ tmp = MAY_BE_ANY | MAY_BE_REF | MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+ }
+ if (ssa_ops[i].result_def >= 0) {
+ tmp = MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+ if (opline->result_type == IS_TMP_VAR) {
+ tmp |= MAY_BE_RC1 | MAY_BE_RCN;
+ } else {
+ tmp |= MAY_BE_REF | MAY_BE_RC1 | MAY_BE_RCN;
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+ }
+ break;
+ }
+
+ return SUCCESS;
+}
+
+static uint32_t get_class_entry_rank(zend_class_entry *ce) {
+ uint32_t rank = 0;
+ while (ce->parent) {
+ rank++;
+ ce = ce->parent;
+ }
+ return rank;
+}
+
+/* Compute least common ancestor on class inheritance tree only */
+static zend_class_entry *join_class_entries(
+ zend_class_entry *ce1, zend_class_entry *ce2, int *is_instanceof) {
+ uint32_t rank1, rank2;
+ if (ce1 == ce2) {
+ return ce1;
+ }
+ if (!ce1 || !ce2) {
+ return NULL;
+ }
+
+ rank1 = get_class_entry_rank(ce1);
+ rank2 = get_class_entry_rank(ce2);
+
+ while (rank1 != rank2) {
+ if (rank1 > rank2) {
+ ce1 = ce1->parent;
+ rank1--;
+ } else {
+ ce2 = ce2->parent;
+ rank2--;
+ }
+ }
+
+ while (ce1 != ce2) {
+ ce1 = ce1->parent;
+ ce2 = ce2->parent;
+ }
+
+ if (ce1) {
+ *is_instanceof = 1;
+ }
+ return ce1;
+}
+
+int zend_infer_types_ex(const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa, zend_bitset worklist)
+{
+ zend_basic_block *blocks = ssa->cfg.blocks;
+ zend_ssa_var *ssa_vars = ssa->vars;
+ zend_ssa_var_info *ssa_var_info = ssa->var_info;
+ int ssa_vars_count = ssa->vars_count;
+ int i, j;
+ uint32_t tmp, worklist_len = zend_bitset_len(ssa_vars_count);
+
+ while (!zend_bitset_empty(worklist, worklist_len)) {
+ j = zend_bitset_first(worklist, worklist_len);
+ zend_bitset_excl(worklist, j);
+ if (ssa_vars[j].definition_phi) {
+ zend_ssa_phi *p = ssa_vars[j].definition_phi;
+ if (p->pi >= 0) {
+ zend_class_entry *ce = ssa_var_info[p->sources[0]].ce;
+ int is_instanceof = ssa_var_info[p->sources[0]].is_instanceof;
+ tmp = get_ssa_var_info(ssa, p->sources[0]);
+
+ if (!p->has_range_constraint) {
+ zend_ssa_type_constraint *constraint = &p->constraint.type;
+ tmp &= constraint->type_mask;
+ if ((tmp & MAY_BE_OBJECT) && constraint->ce && ce != constraint->ce) {
+ if (!ce) {
+ ce = constraint->ce;
+ is_instanceof = 1;
+ } else if (is_instanceof && instanceof_function(constraint->ce, ce)) {
+ ce = constraint->ce;
+ } else {
+ /* Ignore the constraint (either ce instanceof constraint->ce or
+ * they are unrelated, as far as we can statically determine) */
+ }
+ }
+ }
+
+ UPDATE_SSA_TYPE(tmp, j);
+ UPDATE_SSA_OBJ_TYPE(ce, is_instanceof, j);
+ } else {
+ int first = 1;
+ int is_instanceof = 0;
+ zend_class_entry *ce = NULL;
+
+ tmp = 0;
+ for (i = 0; i < blocks[p->block].predecessors_count; i++) {
+ tmp |= get_ssa_var_info(ssa, p->sources[i]);
+ }
+ UPDATE_SSA_TYPE(tmp, j);
+ for (i = 0; i < blocks[p->block].predecessors_count; i++) {
+ if (p->sources[i] >= 0) {
+ zend_ssa_var_info *info = &ssa_var_info[p->sources[i]];
+ if (info->type & MAY_BE_OBJECT) {
+ if (first) {
+ ce = info->ce;
+ is_instanceof = info->is_instanceof;
+ first = 0;
+ } else {
+ is_instanceof |= info->is_instanceof;
+ ce = join_class_entries(ce, info->ce, &is_instanceof);
+ }
+ }
+ }
+ }
+ UPDATE_SSA_OBJ_TYPE(ce, ce ? is_instanceof : 0, j);
+ }
+ } else if (ssa_vars[j].definition >= 0) {
+ i = ssa_vars[j].definition;
+ if (zend_update_type_info(op_array, ssa, script, worklist, i) == FAILURE) {
+ return FAILURE;
+ }
+ }
+ }
+ return SUCCESS;
+}
+
+static zend_bool is_narrowable_instr(zend_op *opline) {
+ return opline->opcode == ZEND_ADD || opline->opcode == ZEND_SUB
+ || opline->opcode == ZEND_MUL || opline->opcode == ZEND_DIV;
+}
+
+static zend_bool is_effective_op1_double_cast(zend_op *opline, zval *op2) {
+ return (opline->opcode == ZEND_ADD && Z_LVAL_P(op2) == 0)
+ || (opline->opcode == ZEND_SUB && Z_LVAL_P(op2) == 0)
+ || (opline->opcode == ZEND_MUL && Z_LVAL_P(op2) == 1)
+ || (opline->opcode == ZEND_DIV && Z_LVAL_P(op2) == 1);
+}
+static zend_bool is_effective_op2_double_cast(zend_op *opline, zval *op1) {
+ /* In PHP it holds that (double)(0-$int) is bitwise identical to 0.0-(double)$int,
+ * so allowing SUB here is fine. */
+ return (opline->opcode == ZEND_ADD && Z_LVAL_P(op1) == 0)
+ || (opline->opcode == ZEND_SUB && Z_LVAL_P(op1) == 0)
+ || (opline->opcode == ZEND_MUL && Z_LVAL_P(op1) == 1);
+}
+
+/* This function recursively checks whether it's possible to convert an integer variable
+ * initialization to a double initialization. The basic idea is that if the value is used
+ * only in add/sub/mul/div ("narrowable" instructions) with a double result value, then it
+ * will be cast to double at that point anyway, so we may as well do it earlier already.
+ *
+ * The tricky case are chains of operations, where it's not necessarily a given that converting
+ * an integer to double before the chain of operations is the same as converting it after the
+ * chain. What this function does is detect two cases where it is safe:
+ * * If the operations only involve constants, then we can simply verify that performing the
+ * calculation on integers and doubles yields the same value.
+ * * Even if one operand is not known, we may be able to determine that the operations with the
+ * integer replaced by a double only acts as an effective double cast on the unknown operand.
+ * E.g. 0+$i and 0.0+$i only differ by that cast. If then the consuming instruction of this
+ * result will perform a double cast anyway, the conversion is safe.
+ *
+ * The checks happens recursively, while keeping track of which variables are already visisted to
+ * avoid infinite loops. An iterative, worklist driven approach would be possible, but the state
+ * management more cumbersome to implement, so we don't bother for now.
+ */
+static zend_bool can_convert_to_double(
+ const zend_op_array *op_array, zend_ssa *ssa, int var_num,
+ zval *value, zend_bitset visited) {
+ zend_ssa_var *var = &ssa->vars[var_num];
+ zend_ssa_phi *phi;
+ int use;
+ uint32_t type;
+
+ if (zend_bitset_in(visited, var_num)) {
+ return 1;
+ }
+ zend_bitset_incl(visited, var_num);
+
+ for (use = var->use_chain; use >= 0; use = zend_ssa_next_use(ssa->ops, var_num, use)) {
+ zend_op *opline = &op_array->opcodes[use];
+ zend_ssa_op *ssa_op = &ssa->ops[use];
+
+ if (is_no_val_use(opline, ssa_op, var_num)) {
+ continue;
+ }
+
+ if (!is_narrowable_instr(opline)) {
+ return 0;
+ }
+
+ /* Instruction always returns double, the conversion is certainly fine */
+ type = ssa->var_info[ssa_op->result_def].type;
+ if ((type & MAY_BE_ANY) == MAY_BE_DOUBLE) {
+ continue;
+ }
+
+ /* UNDEF signals that the previous result is an effective double cast, this is only allowed
+ * if this instruction would have done the cast anyway (previous check). */
+ if (Z_ISUNDEF_P(value)) {
+ return 0;
+ }
+
+ /* Check that narrowing can actually be useful */
+ if ((type & MAY_BE_ANY) & ~(MAY_BE_LONG|MAY_BE_DOUBLE)) {
+ return 0;
+ }
+
+ {
+ /* For calculation on original values */
+ zval orig_op1, orig_op2, orig_result;
+ /* For calculation with var_num cast to double */
+ zval dval_op1, dval_op2, dval_result;
+
+ ZVAL_UNDEF(&orig_op1);
+ ZVAL_UNDEF(&dval_op1);
+ if (ssa_op->op1_use == var_num) {
+ ZVAL_COPY_VALUE(&orig_op1, value);
+ ZVAL_DOUBLE(&dval_op1, (double) Z_LVAL_P(value));
+ } else if (opline->op1_type == IS_CONST) {
+ zval *zv = CRT_CONSTANT_EX(op_array, opline->op1, ssa->rt_constants);
+ if (Z_TYPE_P(zv) == IS_LONG || Z_TYPE_P(zv) == IS_DOUBLE) {
+ ZVAL_COPY_VALUE(&orig_op1, zv);
+ ZVAL_COPY_VALUE(&dval_op1, zv);
+ }
+ }
+
+ ZVAL_UNDEF(&orig_op2);
+ ZVAL_UNDEF(&dval_op2);
+ if (ssa_op->op2_use == var_num) {
+ ZVAL_COPY_VALUE(&orig_op2, value);
+ ZVAL_DOUBLE(&dval_op2, (double) Z_LVAL_P(value));
+ } else if (opline->op2_type == IS_CONST) {
+ zval *zv = CRT_CONSTANT_EX(op_array, opline->op2, ssa->rt_constants);
+ if (Z_TYPE_P(zv) == IS_LONG || Z_TYPE_P(zv) == IS_DOUBLE) {
+ ZVAL_COPY_VALUE(&orig_op2, zv);
+ ZVAL_COPY_VALUE(&dval_op2, zv);
+ }
+ }
+
+ ZEND_ASSERT(!Z_ISUNDEF(orig_op1) || !Z_ISUNDEF(orig_op2));
+ if (Z_ISUNDEF(orig_op1)) {
+ if (opline->opcode == ZEND_MUL && Z_LVAL(orig_op2) == 0) {
+ ZVAL_LONG(&orig_result, 0);
+ } else if (is_effective_op1_double_cast(opline, &orig_op2)) {
+ ZVAL_UNDEF(&orig_result);
+ } else {
+ return 0;
+ }
+ } else if (Z_ISUNDEF(orig_op2)) {
+ if (opline->opcode == ZEND_MUL && Z_LVAL(orig_op1) == 0) {
+ ZVAL_LONG(&orig_result, 0);
+ } else if (is_effective_op2_double_cast(opline, &orig_op1)) {
+ ZVAL_UNDEF(&orig_result);
+ } else {
+ return 0;
+ }
+ } else {
+ /* Avoid division by zero */
+ if (opline->opcode == ZEND_DIV && zval_get_double(&orig_op2) == 0.0) {
+ return 0;
+ }
+
+ get_binary_op(opline->opcode)(&orig_result, &orig_op1, &orig_op2);
+ get_binary_op(opline->opcode)(&dval_result, &dval_op1, &dval_op2);
+ ZEND_ASSERT(Z_TYPE(dval_result) == IS_DOUBLE);
+ if (zval_get_double(&orig_result) != Z_DVAL(dval_result)) {
+ return 0;
+ }
+ }
+
+ if (!can_convert_to_double(op_array, ssa, ssa_op->result_def, &orig_result, visited)) {
+ return 0;
+ }
+ }
+ }
+
+ for (phi = var->phi_use_chain; phi; phi = zend_ssa_next_use_phi(ssa, var_num, phi)) {
+ /* Check that narrowing can actually be useful */
+ type = ssa->var_info[phi->ssa_var].type;
+ if ((type & MAY_BE_ANY) & ~(MAY_BE_LONG|MAY_BE_DOUBLE)) {
+ return 0;
+ }
+
+ if (!can_convert_to_double(op_array, ssa, phi->ssa_var, value, visited)) {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static int zend_type_narrowing(const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa)
+{
+ uint32_t bitset_len = zend_bitset_len(ssa->vars_count);
+ zend_bitset visited, worklist;
+ int i, v;
+ zend_op *opline;
+ zend_bool narrowed = 0;
+ ALLOCA_FLAG(use_heap)
+
+ visited = ZEND_BITSET_ALLOCA(2 * bitset_len, use_heap);
+ worklist = visited + bitset_len;
+
+ zend_bitset_clear(worklist, bitset_len);
+
+ for (v = op_array->last_var; v < ssa->vars_count; v++) {
+ if ((ssa->var_info[v].type & (MAY_BE_REF | MAY_BE_ANY | MAY_BE_UNDEF)) != MAY_BE_LONG) continue;
+ if (ssa->vars[v].definition < 0) continue;
+ if (ssa->vars[v].no_val) continue;
+ opline = op_array->opcodes + ssa->vars[v].definition;
+ /* Go through assignments of literal integers and check if they can be converted to
+ * doubles instead, in the hope that we'll narrow long|double to double. */
+ if (opline->opcode == ZEND_ASSIGN && opline->result_type == IS_UNUSED &&
+ opline->op1_type == IS_CV && opline->op2_type == IS_CONST) {
+ zval *value = CRT_CONSTANT_EX(op_array, opline->op2, ssa->rt_constants);
+
+ zend_bitset_clear(visited, bitset_len);
+ if (can_convert_to_double(op_array, ssa, v, value, visited)) {
+ narrowed = 1;
+ ssa->var_info[v].use_as_double = 1;
+ /* The "visited" vars are exactly those which may change their type due to
+ * narrowing. Reset their types and add them to the type inference worklist */
+ ZEND_BITSET_FOREACH(visited, bitset_len, i) {
+ ssa->var_info[i].type &= ~MAY_BE_ANY;
+ } ZEND_BITSET_FOREACH_END();
+ zend_bitset_union(worklist, visited, bitset_len);
+ }
+ }
+ }
+
+ if (!narrowed) {
+ free_alloca(visited, use_heap);
+ return SUCCESS;
+ }
+
+ if (zend_infer_types_ex(op_array, script, ssa, worklist) != SUCCESS) {
+ free_alloca(visited, use_heap);
+ return FAILURE;
+ }
+
+ free_alloca(visited, use_heap);
+ return SUCCESS;
+}
+
+static int is_recursive_tail_call(const zend_op_array *op_array,
+ zend_op *opline)
+{
+ zend_func_info *info = ZEND_FUNC_INFO(op_array);
+
+ if (info->ssa.ops && info->ssa.vars && info->call_map &&
+ info->ssa.ops[opline - op_array->opcodes].op1_use >= 0 &&
+ info->ssa.vars[info->ssa.ops[opline - op_array->opcodes].op1_use].definition >= 0) {
+
+ zend_op *op = op_array->opcodes + info->ssa.vars[info->ssa.ops[opline - op_array->opcodes].op1_use].definition;
+
+ if (op->opcode == ZEND_DO_UCALL) {
+ zend_call_info *call_info = info->call_map[op - op_array->opcodes];
+ if (call_info && op_array == &call_info->callee_func->op_array) {
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+void zend_init_func_return_info(const zend_op_array *op_array,
+ const zend_script *script,
+ zend_ssa_var_info *ret)
+{
+ if (op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
+ zend_arg_info *ret_info = op_array->arg_info - 1;
+ zend_ssa_range tmp_range = {0, 0, 0, 0};
+
+ ret->type = zend_fetch_arg_info(script, ret_info, &ret->ce);
+ if (op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE) {
+ ret->type |= MAY_BE_REF;
+ }
+ ret->is_instanceof = (ret->ce) ? 1 : 0;
+ ret->range = tmp_range;
+ ret->has_range = 0;
+ }
+}
+
+void zend_func_return_info(const zend_op_array *op_array,
+ const zend_script *script,
+ int recursive,
+ int widening,
+ zend_ssa_var_info *ret)
+{
+ zend_func_info *info = ZEND_FUNC_INFO(op_array);
+ zend_ssa *ssa = &info->ssa;
+ int blocks_count = info->ssa.cfg.blocks_count;
+ zend_basic_block *blocks = info->ssa.cfg.blocks;
+ int j;
+ uint32_t t1;
+ uint32_t tmp = 0;
+ zend_class_entry *tmp_ce = NULL;
+ int tmp_is_instanceof = -1;
+ zend_class_entry *arg_ce;
+ int arg_is_instanceof;
+ zend_ssa_range tmp_range = {0, 0, 0, 0};
+ int tmp_has_range = -1;
+
+ if (op_array->fn_flags & ZEND_ACC_GENERATOR) {
+ ret->type = MAY_BE_OBJECT | MAY_BE_RC1 | MAY_BE_RCN;
+ ret->ce = zend_ce_generator;
+ ret->is_instanceof = 0;
+ ret->range = tmp_range;
+ ret->has_range = 0;
+ return;
+ }
+
+ for (j = 0; j < blocks_count; j++) {
+ if ((blocks[j].flags & ZEND_BB_REACHABLE) && blocks[j].len != 0) {
+ zend_op *opline = op_array->opcodes + blocks[j].start + blocks[j].len - 1;
+
+ if (opline->opcode == ZEND_RETURN || opline->opcode == ZEND_RETURN_BY_REF) {
+ if (!recursive &&
+ info->ssa.ops &&
+ info->ssa.var_info &&
+ info->ssa.ops[opline - op_array->opcodes].op1_use >= 0 &&
+ info->ssa.var_info[info->ssa.ops[opline - op_array->opcodes].op1_use].recursive) {
+ continue;
+ }
+ if (is_recursive_tail_call(op_array, opline)) {
+ continue;
+ }
+ t1 = OP1_INFO();
+ if (t1 & MAY_BE_UNDEF) {
+ t1 |= MAY_BE_NULL;
+ }
+ if (opline->opcode == ZEND_RETURN) {
+ if (t1 & MAY_BE_RC1) {
+ t1 |= MAY_BE_RCN;
+ }
+ t1 &= ~(MAY_BE_UNDEF | MAY_BE_REF);
+ } else {
+ t1 |= MAY_BE_REF;
+ t1 &= ~(MAY_BE_UNDEF | MAY_BE_RC1 | MAY_BE_RCN);
+ }
+ tmp |= t1;
+
+ if (info->ssa.ops &&
+ info->ssa.var_info &&
+ info->ssa.ops[opline - op_array->opcodes].op1_use >= 0 &&
+ info->ssa.var_info[info->ssa.ops[opline - op_array->opcodes].op1_use].ce) {
+ arg_ce = info->ssa.var_info[info->ssa.ops[opline - op_array->opcodes].op1_use].ce;
+ arg_is_instanceof = info->ssa.var_info[info->ssa.ops[opline - op_array->opcodes].op1_use].is_instanceof;
+ } else {
+ arg_ce = NULL;
+ arg_is_instanceof = 0;
+ }
+
+ if (tmp_is_instanceof < 0) {
+ tmp_ce = arg_ce;
+ tmp_is_instanceof = arg_is_instanceof;
+ } else if (arg_ce && arg_ce == tmp_ce) {
+ if (tmp_is_instanceof != arg_is_instanceof) {
+ tmp_is_instanceof = 1;
+ }
+ } else {
+ tmp_ce = NULL;
+ tmp_is_instanceof = 0;
+ }
+
+ if (opline->op1_type == IS_CONST) {
+ zval *zv = CRT_CONSTANT_EX(op_array, opline->op1, info->ssa.rt_constants);
+
+ if (Z_TYPE_P(zv) == IS_NULL) {
+ if (tmp_has_range < 0) {
+ tmp_has_range = 1;
+ tmp_range.underflow = 0;
+ tmp_range.min = 0;
+ tmp_range.max = 0;
+ tmp_range.overflow = 0;
+ } else if (tmp_has_range) {
+ if (!tmp_range.underflow) {
+ tmp_range.min = MIN(tmp_range.min, 0);
+ }
+ if (!tmp_range.overflow) {
+ tmp_range.max = MAX(tmp_range.max, 0);
+ }
+ }
+ } else if (Z_TYPE_P(zv) == IS_FALSE) {
+ if (tmp_has_range < 0) {
+ tmp_has_range = 1;
+ tmp_range.underflow = 0;
+ tmp_range.min = 0;
+ tmp_range.max = 0;
+ tmp_range.overflow = 0;
+ } else if (tmp_has_range) {
+ if (!tmp_range.underflow) {
+ tmp_range.min = MIN(tmp_range.min, 0);
+ }
+ if (!tmp_range.overflow) {
+ tmp_range.max = MAX(tmp_range.max, 0);
+ }
+ }
+ } else if (Z_TYPE_P(zv) == IS_TRUE) {
+ if (tmp_has_range < 0) {
+ tmp_has_range = 1;
+ tmp_range.underflow = 0;
+ tmp_range.min = 1;
+ tmp_range.max = 1;
+ tmp_range.overflow = 0;
+ } else if (tmp_has_range) {
+ if (!tmp_range.underflow) {
+ tmp_range.min = MIN(tmp_range.min, 1);
+ }
+ if (!tmp_range.overflow) {
+ tmp_range.max = MAX(tmp_range.max, 1);
+ }
+ }
+ } else if (Z_TYPE_P(zv) == IS_LONG) {
+ if (tmp_has_range < 0) {
+ tmp_has_range = 1;
+ tmp_range.underflow = 0;
+ tmp_range.min = Z_LVAL_P(zv);
+ tmp_range.max = Z_LVAL_P(zv);
+ tmp_range.overflow = 0;
+ } else if (tmp_has_range) {
+ if (!tmp_range.underflow) {
+ tmp_range.min = MIN(tmp_range.min, Z_LVAL_P(zv));
+ }
+ if (!tmp_range.overflow) {
+ tmp_range.max = MAX(tmp_range.max, Z_LVAL_P(zv));
+ }
+ }
+ } else {
+ tmp_has_range = 0;
+ }
+ } else if (info->ssa.ops &&
+ info->ssa.var_info &&
+ info->ssa.ops[opline - op_array->opcodes].op1_use >= 0) {
+ if (info->ssa.var_info[info->ssa.ops[opline - op_array->opcodes].op1_use].has_range) {
+ if (tmp_has_range < 0) {
+ tmp_has_range = 1;
+ tmp_range = info->ssa.var_info[info->ssa.ops[opline - op_array->opcodes].op1_use].range;
+ } else if (tmp_has_range) {
+ /* union */
+ if (info->ssa.var_info[info->ssa.ops[opline - op_array->opcodes].op1_use].range.underflow) {
+ tmp_range.underflow = 1;
+ tmp_range.min = ZEND_LONG_MIN;
+ } else {
+ tmp_range.min = MIN(tmp_range.min, info->ssa.var_info[info->ssa.ops[opline - op_array->opcodes].op1_use].range.min);
+ }
+ if (info->ssa.var_info[info->ssa.ops[opline - op_array->opcodes].op1_use].range.overflow) {
+ tmp_range.overflow = 1;
+ tmp_range.max = ZEND_LONG_MAX;
+ } else {
+ tmp_range.max = MAX(tmp_range.max, info->ssa.var_info[info->ssa.ops[opline - op_array->opcodes].op1_use].range.max);
+ }
+ }
+ } else if (!widening) {
+ tmp_has_range = 1;
+ tmp_range.underflow = 1;
+ tmp_range.min = ZEND_LONG_MIN;
+ tmp_range.max = ZEND_LONG_MAX;
+ tmp_range.overflow = 1;
+ }
+ } else {
+ tmp_has_range = 0;
+ }
+ }
+ }
+ }
+
+ if (!(op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE)) {
+ if (tmp_is_instanceof < 0) {
+ tmp_is_instanceof = 0;
+ tmp_ce = NULL;
+ }
+ if (tmp_has_range < 0) {
+ tmp_has_range = 0;
+ }
+ ret->type = tmp;
+ ret->ce = tmp_ce;
+ ret->is_instanceof = tmp_is_instanceof;
+ }
+ ret->range = tmp_range;
+ ret->has_range = tmp_has_range;
+}
+
+static int zend_infer_types(const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa)
+{
+ zend_ssa_var_info *ssa_var_info = ssa->var_info;
+ int ssa_vars_count = ssa->vars_count;
+ int j;
+ zend_bitset worklist;
+ ALLOCA_FLAG(use_heap);
+
+ worklist = do_alloca(sizeof(zend_ulong) * zend_bitset_len(ssa_vars_count), use_heap);
+ memset(worklist, 0, sizeof(zend_ulong) * zend_bitset_len(ssa_vars_count));
+
+ /* Type Inference */
+ for (j = op_array->last_var; j < ssa_vars_count; j++) {
+ zend_bitset_incl(worklist, j);
+ ssa_var_info[j].type = 0;
+ }
+
+ if (zend_infer_types_ex(op_array, script, ssa, worklist) != SUCCESS) {
+ free_alloca(worklist, use_heap);
+ return FAILURE;
+ }
+
+ /* Narrowing integer initialization to doubles */
+ zend_type_narrowing(op_array, script, ssa);
+
+ for (j = 0; j < op_array->last_var; j++) {
+ /* $php_errormsg and $http_response_header may be updated indirectly */
+ if (zend_string_equals_literal(op_array->vars[j], "php_errormsg")) {
+ int i;
+ for (i = 0; i < ssa_vars_count; i++) {
+ if (ssa->vars[i].var == j) {
+ ssa_var_info[i].type |= MAY_BE_STRING | MAY_BE_RC1 | MAY_BE_RCN;
+ }
+ }
+ } else if (zend_string_equals_literal(op_array->vars[j], "http_response_header")) {
+ int i;
+ for (i = 0; i < ssa_vars_count; i++) {
+ if (ssa->vars[i].var == j) {
+ ssa_var_info[i].type |= MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_RC1 | MAY_BE_RCN;
+ }
+ }
+ }
+ }
+
+ if (ZEND_FUNC_INFO(op_array)) {
+ zend_func_return_info(op_array, script, 1, 0, &ZEND_FUNC_INFO(op_array)->return_info);
+ }
+
+ free_alloca(worklist, use_heap);
+ return SUCCESS;
+}
+
+int zend_ssa_inference(zend_arena **arena, const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa) /* {{{ */
+{
+ zend_ssa_var_info *ssa_var_info;
+ int i;
+
+ if (!ssa->var_info) {
+ ssa->var_info = zend_arena_calloc(arena, ssa->vars_count, sizeof(zend_ssa_var_info));
+ }
+ ssa_var_info = ssa->var_info;
+
+ if (!op_array->function_name) {
+ for (i = 0; i < op_array->last_var; i++) {
+ ssa_var_info[i].type = MAY_BE_UNDEF | MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_REF | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+ ssa_var_info[i].has_range = 0;
+ }
+ } else {
+ for (i = 0; i < op_array->last_var; i++) {
+ ssa_var_info[i].type = MAY_BE_UNDEF;
+ ssa_var_info[i].has_range = 0;
+ }
+ }
+ for (i = op_array->last_var; i < ssa->vars_count; i++) {
+ ssa_var_info[i].type = 0;
+ ssa_var_info[i].has_range = 0;
+ }
+
+ if (zend_infer_ranges(op_array, ssa) != SUCCESS) {
+ return FAILURE;
+ }
+
+ if (zend_infer_types(op_array, script, ssa) != SUCCESS) {
+ return FAILURE;
+ }
+
+ return SUCCESS;
+}
+/* }}} */
+
+void zend_inference_check_recursive_dependencies(zend_op_array *op_array)
+{
+ zend_func_info *info = ZEND_FUNC_INFO(op_array);
+ zend_call_info *call_info;
+ zend_bitset worklist;
+ int worklist_len, i;
+ ALLOCA_FLAG(use_heap);
+
+ if (!info->ssa.var_info || !(info->flags & ZEND_FUNC_RECURSIVE)) {
+ return;
+ }
+ worklist_len = zend_bitset_len(info->ssa.vars_count);
+ worklist = do_alloca(sizeof(zend_ulong) * worklist_len, use_heap);
+ memset(worklist, 0, sizeof(zend_ulong) * worklist_len);
+ call_info = info->callee_info;
+ while (call_info) {
+ if (call_info->recursive &&
+ info->ssa.ops[call_info->caller_call_opline - op_array->opcodes].result_def >= 0) {
+ zend_bitset_incl(worklist, info->ssa.ops[call_info->caller_call_opline - op_array->opcodes].result_def);
+ }
+ call_info = call_info->next_callee;
+ }
+ WHILE_WORKLIST(worklist, worklist_len, i) {
+ if (!info->ssa.var_info[i].recursive) {
+ info->ssa.var_info[i].recursive = 1;
+ add_usages(op_array, &info->ssa, worklist, i);
+ }
+ } WHILE_WORKLIST_END();
+ free_alloca(worklist, use_heap);
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * indent-tabs-mode: t
+ * End:
+ */
diff --git a/ext/opcache/Optimizer/zend_inference.h b/ext/opcache/Optimizer/zend_inference.h
new file mode 100644
index 0000000000..25b5cba4ca
--- /dev/null
+++ b/ext/opcache/Optimizer/zend_inference.h
@@ -0,0 +1,275 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine, e-SSA based Type & Range Inference |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1998-2017 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Dmitry Stogov <dmitry@zend.com> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef ZEND_INFERENCE_H
+#define ZEND_INFERENCE_H
+
+#include "zend_optimizer.h"
+#include "zend_ssa.h"
+#include "zend_bitset.h"
+
+/* Bitmask for type inference (zend_ssa_var_info.type) */
+#include "zend_type_info.h"
+
+#define MAY_BE_IN_REG (1<<25) /* value allocated in CPU register */
+
+//TODO: remome MAY_BE_RC1, MAY_BE_RCN???
+#define MAY_BE_RC1 (1<<27) /* may be non-reference with refcount == 1 */
+#define MAY_BE_RCN (1<<28) /* may be non-reference with refcount > 1 */
+
+
+#define DEFINE_SSA_OP_HAS_RANGE(opN) \
+ static zend_always_inline zend_bool _ssa_##opN##_has_range(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline) \
+ { \
+ if (opline->opN##_type == IS_CONST) { \
+ zval *zv = CRT_CONSTANT_EX(op_array, opline->opN, ssa->rt_constants); \
+ return (Z_TYPE_P(zv) == IS_LONG || Z_TYPE_P(zv) == IS_TRUE || Z_TYPE_P(zv) == IS_FALSE || Z_TYPE_P(zv) == IS_NULL); \
+ } else { \
+ return (opline->opN##_type != IS_UNUSED && \
+ ssa->ops && \
+ ssa->var_info && \
+ ssa->ops[opline - op_array->opcodes].opN##_use >= 0 && \
+ ssa->var_info[ssa->ops[opline - op_array->opcodes].opN##_use].has_range); \
+ } \
+ return 0; \
+ }
+
+#define DEFINE_SSA_OP_MIN_RANGE(opN) \
+ static zend_always_inline zend_long _ssa_##opN##_min_range(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline) \
+ { \
+ if (opline->opN##_type == IS_CONST) { \
+ zval *zv = CRT_CONSTANT_EX(op_array, opline->opN, ssa->rt_constants); \
+ if (Z_TYPE_P(zv) == IS_LONG) { \
+ return Z_LVAL_P(zv); \
+ } else if (Z_TYPE_P(zv) == IS_TRUE) { \
+ return 1; \
+ } else if (Z_TYPE_P(zv) == IS_FALSE) { \
+ return 0; \
+ } else if (Z_TYPE_P(zv) == IS_NULL) { \
+ return 0; \
+ } \
+ } else if (opline->opN##_type != IS_UNUSED && \
+ ssa->ops && \
+ ssa->var_info && \
+ ssa->ops[opline - op_array->opcodes].opN##_use >= 0 && \
+ ssa->var_info[ssa->ops[opline - op_array->opcodes].opN##_use].has_range) { \
+ return ssa->var_info[ssa->ops[opline - op_array->opcodes].opN##_use].range.min; \
+ } \
+ return ZEND_LONG_MIN; \
+ }
+
+#define DEFINE_SSA_OP_MAX_RANGE(opN) \
+ static zend_always_inline zend_long _ssa_##opN##_max_range(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline) \
+ { \
+ if (opline->opN##_type == IS_CONST) { \
+ zval *zv = CRT_CONSTANT_EX(op_array, opline->opN, ssa->rt_constants); \
+ if (Z_TYPE_P(zv) == IS_LONG) { \
+ return Z_LVAL_P(zv); \
+ } else if (Z_TYPE_P(zv) == IS_TRUE) { \
+ return 1; \
+ } else if (Z_TYPE_P(zv) == IS_FALSE) { \
+ return 0; \
+ } else if (Z_TYPE_P(zv) == IS_NULL) { \
+ return 0; \
+ } \
+ } else if (opline->opN##_type != IS_UNUSED && \
+ ssa->ops && \
+ ssa->var_info && \
+ ssa->ops[opline - op_array->opcodes].opN##_use >= 0 && \
+ ssa->var_info[ssa->ops[opline - op_array->opcodes].opN##_use].has_range) { \
+ return ssa->var_info[ssa->ops[opline - op_array->opcodes].opN##_use].range.max; \
+ } \
+ return ZEND_LONG_MAX; \
+ }
+
+#define DEFINE_SSA_OP_RANGE_UNDERFLOW(opN) \
+ static zend_always_inline char _ssa_##opN##_range_underflow(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline) \
+ { \
+ if (opline->opN##_type == IS_CONST) { \
+ zval *zv = CRT_CONSTANT_EX(op_array, opline->opN, ssa->rt_constants); \
+ if (Z_TYPE_P(zv) == IS_LONG || Z_TYPE_P(zv) == IS_TRUE || Z_TYPE_P(zv) == IS_FALSE || Z_TYPE_P(zv) == IS_NULL) { \
+ return 0; \
+ } \
+ } else if (opline->opN##_type != IS_UNUSED && \
+ ssa->ops && \
+ ssa->var_info && \
+ ssa->ops[opline - op_array->opcodes].opN##_use >= 0 && \
+ ssa->var_info[ssa->ops[opline - op_array->opcodes].opN##_use].has_range) { \
+ return ssa->var_info[ssa->ops[opline - op_array->opcodes].opN##_use].range.underflow; \
+ } \
+ return 1; \
+ }
+
+#define DEFINE_SSA_OP_RANGE_OVERFLOW(opN) \
+ static zend_always_inline char _ssa_##opN##_range_overflow(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline) \
+ { \
+ if (opline->opN##_type == IS_CONST) { \
+ zval *zv = CRT_CONSTANT_EX(op_array, opline->opN, ssa->rt_constants); \
+ if (Z_TYPE_P(zv) == IS_LONG || Z_TYPE_P(zv) == IS_TRUE || Z_TYPE_P(zv) == IS_FALSE || Z_TYPE_P(zv) == IS_NULL) { \
+ return 0; \
+ } \
+ } else if (opline->opN##_type != IS_UNUSED && \
+ ssa->ops && \
+ ssa->var_info && \
+ ssa->ops[opline - op_array->opcodes].opN##_use >= 0 && \
+ ssa->var_info[ssa->ops[opline - op_array->opcodes].opN##_use].has_range) { \
+ return ssa->var_info[ssa->ops[opline - op_array->opcodes].opN##_use].range.overflow; \
+ } \
+ return 1; \
+ }
+
+DEFINE_SSA_OP_HAS_RANGE(op1)
+DEFINE_SSA_OP_MIN_RANGE(op1)
+DEFINE_SSA_OP_MAX_RANGE(op1)
+DEFINE_SSA_OP_RANGE_UNDERFLOW(op1)
+DEFINE_SSA_OP_RANGE_OVERFLOW(op1)
+DEFINE_SSA_OP_HAS_RANGE(op2)
+DEFINE_SSA_OP_MIN_RANGE(op2)
+DEFINE_SSA_OP_MAX_RANGE(op2)
+DEFINE_SSA_OP_RANGE_UNDERFLOW(op2)
+DEFINE_SSA_OP_RANGE_OVERFLOW(op2)
+
+#define OP1_HAS_RANGE() (_ssa_op1_has_range (op_array, ssa, opline))
+#define OP1_MIN_RANGE() (_ssa_op1_min_range (op_array, ssa, opline))
+#define OP1_MAX_RANGE() (_ssa_op1_max_range (op_array, ssa, opline))
+#define OP1_RANGE_UNDERFLOW() (_ssa_op1_range_underflow (op_array, ssa, opline))
+#define OP1_RANGE_OVERFLOW() (_ssa_op1_range_overflow (op_array, ssa, opline))
+#define OP2_HAS_RANGE() (_ssa_op2_has_range (op_array, ssa, opline))
+#define OP2_MIN_RANGE() (_ssa_op2_min_range (op_array, ssa, opline))
+#define OP2_MAX_RANGE() (_ssa_op2_max_range (op_array, ssa, opline))
+#define OP2_RANGE_UNDERFLOW() (_ssa_op2_range_underflow (op_array, ssa, opline))
+#define OP2_RANGE_OVERFLOW() (_ssa_op2_range_overflow (op_array, ssa, opline))
+
+static zend_always_inline uint32_t _const_op_type(const zval *zv) {
+ if (Z_TYPE_P(zv) == IS_CONSTANT) {
+ return MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY;
+ } else if (Z_TYPE_P(zv) == IS_CONSTANT_AST) {
+ return MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY;
+ } else if (Z_TYPE_P(zv) == IS_ARRAY) {
+ HashTable *ht = Z_ARRVAL_P(zv);
+ uint32_t tmp = MAY_BE_ARRAY;
+
+ if (Z_REFCOUNTED_P(zv)) {
+ tmp |= MAY_BE_RC1 | MAY_BE_RCN;
+ } else {
+ tmp |= MAY_BE_RCN;
+ }
+ zend_string *str;
+ zval *val;
+ ZEND_HASH_FOREACH_STR_KEY_VAL(ht, str, val) {
+ if (str) {
+ tmp |= MAY_BE_ARRAY_KEY_STRING;
+ } else {
+ tmp |= MAY_BE_ARRAY_KEY_LONG;
+ }
+ tmp |= 1 << (Z_TYPE_P(val) + MAY_BE_ARRAY_SHIFT);
+ } ZEND_HASH_FOREACH_END();
+ return tmp;
+ } else {
+ uint32_t tmp = (1 << Z_TYPE_P(zv));
+
+ if (Z_REFCOUNTED_P(zv)) {
+ tmp |= MAY_BE_RC1 | MAY_BE_RCN;
+ } else if (Z_TYPE_P(zv) == IS_STRING) {
+ tmp |= MAY_BE_RCN;
+ }
+ return tmp;
+ }
+}
+
+static zend_always_inline uint32_t get_ssa_var_info(const zend_ssa *ssa, int ssa_var_num)
+{
+ if (ssa->var_info && ssa_var_num >= 0) {
+ return ssa->var_info[ssa_var_num].type;
+ } else {
+ return MAY_BE_UNDEF | MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_REF | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ERROR;
+ }
+}
+
+#define DEFINE_SSA_OP_INFO(opN) \
+ static zend_always_inline uint32_t _ssa_##opN##_info(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline) \
+ { \
+ if (opline->opN##_type == IS_CONST) { \
+ return _const_op_type(CRT_CONSTANT_EX(op_array, opline->opN, ssa->rt_constants)); \
+ } else { \
+ return get_ssa_var_info(ssa, ssa->ops ? ssa->ops[opline - op_array->opcodes].opN##_use : -1); \
+ } \
+ }
+
+#define DEFINE_SSA_OP_DEF_INFO(opN) \
+ static zend_always_inline uint32_t _ssa_##opN##_def_info(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline) \
+ { \
+ return get_ssa_var_info(ssa, ssa->ops ? ssa->ops[opline - op_array->opcodes].opN##_def : -1); \
+ }
+
+
+DEFINE_SSA_OP_INFO(op1)
+DEFINE_SSA_OP_INFO(op2)
+DEFINE_SSA_OP_INFO(result)
+DEFINE_SSA_OP_DEF_INFO(op1)
+DEFINE_SSA_OP_DEF_INFO(op2)
+DEFINE_SSA_OP_DEF_INFO(result)
+
+#define OP1_INFO() (_ssa_op1_info(op_array, ssa, opline))
+#define OP2_INFO() (_ssa_op2_info(op_array, ssa, opline))
+#define OP1_DATA_INFO() (_ssa_op1_info(op_array, ssa, (opline+1)))
+#define OP2_DATA_INFO() (_ssa_op2_info(op_array, ssa, (opline+1)))
+#define RES_USE_INFO() (_ssa_result_info(op_array, ssa, opline))
+#define OP1_DEF_INFO() (_ssa_op1_def_info(op_array, ssa, opline))
+#define OP2_DEF_INFO() (_ssa_op2_def_info(op_array, ssa, opline))
+#define OP1_DATA_DEF_INFO() (_ssa_op1_def_info(op_array, ssa, (opline+1)))
+#define OP2_DATA_DEF_INFO() (_ssa_op2_def_info(op_array, ssa, (opline+1)))
+#define RES_INFO() (_ssa_result_def_info(op_array, ssa, opline))
+
+
+BEGIN_EXTERN_C()
+
+int zend_ssa_find_false_dependencies(const zend_op_array *op_array, zend_ssa *ssa);
+int zend_ssa_find_sccs(const zend_op_array *op_array, zend_ssa *ssa);
+int zend_ssa_inference(zend_arena **raena, const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa);
+
+uint32_t zend_array_element_type(uint32_t t1, int write, int insert);
+
+int zend_inference_calc_range(const zend_op_array *op_array, zend_ssa *ssa, int var, int widening, int narrowing, zend_ssa_range *tmp);
+void zend_inference_init_range(const zend_op_array *op_array, zend_ssa *ssa, int var, zend_bool underflow, zend_long min, zend_long max, zend_bool overflow);
+int zend_inference_narrowing_meet(zend_ssa_var_info *var_info, zend_ssa_range *r);
+int zend_inference_widening_meet(zend_ssa_var_info *var_info, zend_ssa_range *r);
+void zend_inference_check_recursive_dependencies(zend_op_array *op_array);
+
+int zend_infer_types_ex(const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa, zend_bitset worklist);
+
+void zend_init_func_return_info(const zend_op_array *op_array,
+ const zend_script *script,
+ zend_ssa_var_info *ret);
+void zend_func_return_info(const zend_op_array *op_array,
+ const zend_script *script,
+ int recursive,
+ int widening,
+ zend_ssa_var_info *ret);
+
+END_EXTERN_C()
+
+#endif /* ZEND_INFERENCE_H */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * indent-tabs-mode: t
+ * End:
+ */
diff --git a/ext/opcache/Optimizer/zend_optimizer.c b/ext/opcache/Optimizer/zend_optimizer.c
index 7ae0e06127..08ac084713 100644
--- a/ext/opcache/Optimizer/zend_optimizer.c
+++ b/ext/opcache/Optimizer/zend_optimizer.c
@@ -26,6 +26,15 @@
#include "zend_constants.h"
#include "zend_execute.h"
#include "zend_vm.h"
+#include "zend_cfg.h"
+#include "zend_func_info.h"
+#include "zend_call_graph.h"
+#include "zend_inference.h"
+#include "zend_dump.h"
+
+#ifndef HAVE_DFA_PASS
+# define HAVE_DFA_PASS 1
+#endif
static void zend_optimizer_zval_dtor_wrapper(zval *zvalue)
{
@@ -88,11 +97,6 @@ int zend_optimizer_lookup_cv(zend_op_array *op_array, zend_string* name)
if (opline->result_type & (IS_TMP_VAR|IS_VAR)) {
opline->result.var += sizeof(zval);
}
- if (opline->opcode == ZEND_DECLARE_INHERITED_CLASS ||
- opline->opcode == ZEND_DECLARE_ANON_INHERITED_CLASS ||
- opline->opcode == ZEND_DECLARE_INHERITED_CLASS_DELAYED) {
- opline->extended_value += sizeof(zval);
- }
opline++;
}
}
@@ -168,6 +172,7 @@ int zend_optimizer_update_op1_const(zend_op_array *op_array,
case ZEND_INIT_STATIC_METHOD_CALL:
case ZEND_CATCH:
case ZEND_FETCH_CONSTANT:
+ case ZEND_FETCH_CLASS_CONSTANT:
case ZEND_DEFINED:
case ZEND_NEW:
REQUIRES_STRING(val);
@@ -176,6 +181,32 @@ int zend_optimizer_update_op1_const(zend_op_array *op_array,
alloc_cache_slots_op1(op_array, opline, 1);
zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
break;
+ case ZEND_FETCH_STATIC_PROP_R:
+ case ZEND_FETCH_STATIC_PROP_W:
+ case ZEND_FETCH_STATIC_PROP_RW:
+ case ZEND_FETCH_STATIC_PROP_IS:
+ case ZEND_FETCH_STATIC_PROP_UNSET:
+ case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
+ TO_STRING_NOWARN(val);
+ opline->op1.constant = zend_optimizer_add_literal(op_array, val);
+ alloc_cache_slots_op1(op_array, opline, 2);
+ break;
+ case ZEND_SEND_VAR:
+ opline->opcode = ZEND_SEND_VAL;
+ opline->op1.constant = zend_optimizer_add_literal(op_array, val);
+ break;
+ case ZEND_SEPARATE:
+ case ZEND_SEND_VAR_NO_REF:
+ case ZEND_SEND_VAR_NO_REF_EX:
+ zval_ptr_dtor(val);
+ return 0;
+ case ZEND_VERIFY_RETURN_TYPE:
+ /* This would require a non-local change.
+ * zend_optimizer_replace_by_const() supports this. */
+ zval_ptr_dtor(val);
+ return 0;
+ case ZEND_CONCAT:
+ case ZEND_FAST_CONCAT:
case ZEND_FETCH_R:
case ZEND_FETCH_W:
case ZEND_FETCH_RW:
@@ -183,14 +214,6 @@ int zend_optimizer_update_op1_const(zend_op_array *op_array,
case ZEND_FETCH_UNSET:
case ZEND_FETCH_FUNC_ARG:
TO_STRING_NOWARN(val);
- opline->op1.constant = zend_optimizer_add_literal(op_array, val);
- if (opline->extended_value == ZEND_FETCH_STATIC_MEMBER) {
- alloc_cache_slots_op1(op_array, opline, 2);
- }
- break;
- case ZEND_CONCAT:
- case ZEND_FAST_CONCAT:
- TO_STRING_NOWARN(val);
/* break missing intentionally */
default:
opline->op1.constant = zend_optimizer_add_literal(op_array, val);
@@ -210,22 +233,23 @@ int zend_optimizer_update_op2_const(zend_op_array *op_array,
{
switch (opline->opcode) {
case ZEND_ASSIGN_REF:
+ case ZEND_FAST_CALL:
zval_dtor(val);
return 0;
- case ZEND_FETCH_R:
- case ZEND_FETCH_W:
- case ZEND_FETCH_RW:
- case ZEND_FETCH_IS:
- case ZEND_FETCH_UNSET:
- case ZEND_FETCH_FUNC_ARG:
case ZEND_FETCH_CLASS:
case ZEND_INIT_FCALL_BY_NAME:
/*case ZEND_INIT_NS_FCALL_BY_NAME:*/
- case ZEND_UNSET_VAR:
- case ZEND_ISSET_ISEMPTY_VAR:
case ZEND_ADD_INTERFACE:
case ZEND_ADD_TRAIT:
case ZEND_INSTANCEOF:
+ case ZEND_FETCH_STATIC_PROP_R:
+ case ZEND_FETCH_STATIC_PROP_W:
+ case ZEND_FETCH_STATIC_PROP_RW:
+ case ZEND_FETCH_STATIC_PROP_IS:
+ case ZEND_FETCH_STATIC_PROP_UNSET:
+ case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
+ case ZEND_UNSET_STATIC_PROP:
+ case ZEND_ISSET_ISEMPTY_STATIC_PROP:
REQUIRES_STRING(val);
drop_leading_backslash(val);
opline->op2.constant = zend_optimizer_add_literal(op_array, val);
@@ -245,6 +269,13 @@ int zend_optimizer_update_op2_const(zend_op_array *op_array,
return 0;
}
+ if (zend_optimizer_classify_function(Z_STR_P(val), opline->extended_value)) {
+ /* Dynamic call to various special functions must stay dynamic,
+ * otherwise would drop a warning */
+ zval_dtor(val);
+ return 0;
+ }
+
opline->opcode = ZEND_INIT_FCALL_BY_NAME;
drop_leading_backslash(val);
opline->op2.constant = zend_optimizer_add_literal(op_array, val);
@@ -261,7 +292,7 @@ int zend_optimizer_update_op2_const(zend_op_array *op_array,
zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
alloc_cache_slots_op2(op_array, opline, 2);
break;
- /*case ZEND_FETCH_CONSTANT:*/
+ /*case ZEND_FETCH_CLASS_CONSTANT:*/
case ZEND_ASSIGN_OBJ:
case ZEND_FETCH_OBJ_R:
case ZEND_FETCH_OBJ_W:
@@ -299,26 +330,6 @@ int zend_optimizer_update_op2_const(zend_op_array *op_array,
opline->op2.constant = zend_optimizer_add_literal(op_array, val);
}
break;
- case ZEND_OP_DATA:
- if ((opline-1)->opcode != ZEND_ASSIGN_DIM &&
- ((opline-1)->extended_value != ZEND_ASSIGN_DIM ||
- ((opline-1)->opcode != ZEND_ASSIGN_ADD &&
- (opline-1)->opcode != ZEND_ASSIGN_SUB &&
- (opline-1)->opcode != ZEND_ASSIGN_MUL &&
- (opline-1)->opcode != ZEND_ASSIGN_DIV &&
- (opline-1)->opcode != ZEND_ASSIGN_POW &&
- (opline-1)->opcode != ZEND_ASSIGN_MOD &&
- (opline-1)->opcode != ZEND_ASSIGN_SL &&
- (opline-1)->opcode != ZEND_ASSIGN_SR &&
- (opline-1)->opcode != ZEND_ASSIGN_CONCAT &&
- (opline-1)->opcode != ZEND_ASSIGN_BW_OR &&
- (opline-1)->opcode != ZEND_ASSIGN_BW_AND &&
- (opline-1)->opcode != ZEND_ASSIGN_BW_XOR))
- ) {
- opline->op2.constant = zend_optimizer_add_literal(op_array, val);
- break;
- }
- /* break missing intentionally */
case ZEND_ISSET_ISEMPTY_DIM_OBJ:
case ZEND_ADD_ARRAY_ELEMENT:
case ZEND_INIT_ARRAY:
@@ -359,6 +370,47 @@ int zend_optimizer_update_op2_const(zend_op_array *op_array,
return 1;
}
+void zend_optimizer_remove_live_range(zend_op_array *op_array, uint32_t var)
+{
+ if (op_array->last_live_range) {
+ int i = 0;
+ int j = 0;
+ uint32_t *map;
+ ALLOCA_FLAG(use_heap);
+
+ map = (uint32_t *)do_alloca(sizeof(uint32_t) * op_array->last_live_range, use_heap);
+
+ do {
+ if ((op_array->live_range[i].var & ~ZEND_LIVE_MASK) != var) {
+ map[i] = j;
+ if (i != j) {
+ op_array->live_range[j] = op_array->live_range[i];
+ }
+ j++;
+ }
+ i++;
+ } while (i < op_array->last_live_range);
+ if (i != j) {
+ if ((op_array->last_live_range = j)) {
+ zend_op *opline = op_array->opcodes;
+ zend_op *end = opline + op_array->last;
+
+ while (opline != end) {
+ if ((opline->opcode == ZEND_FREE || opline->opcode == ZEND_FE_FREE) &&
+ opline->extended_value == ZEND_FREE_ON_RETURN) {
+ opline->op2.num = map[opline->op2.num];
+ }
+ opline++;
+ }
+ } else {
+ efree(op_array->live_range);
+ op_array->live_range = NULL;
+ }
+ }
+ free_alloca(map, use_heap);
+ }
+}
+
int zend_optimizer_replace_by_const(zend_op_array *op_array,
zend_op *opline,
zend_uchar type,
@@ -389,17 +441,10 @@ int zend_optimizer_replace_by_const(zend_op_array *op_array,
opline->opcode = ZEND_SEND_VAL_EX;
break;
case ZEND_SEND_VAR_NO_REF:
- if (opline->extended_value & ZEND_ARG_COMPILE_TIME_BOUND) {
- if (opline->extended_value & ZEND_ARG_SEND_BY_REF) {
- zval_dtor(val);
- return 0;
- }
- opline->extended_value = 0;
- opline->opcode = ZEND_SEND_VAL_EX;
- } else {
- opline->extended_value = 0;
- opline->opcode = ZEND_SEND_VAL;
- }
+ zval_dtor(val);
+ return 0;
+ case ZEND_SEND_VAR_NO_REF_EX:
+ opline->opcode = ZEND_SEND_VAL;
break;
case ZEND_SEND_USER:
opline->opcode = ZEND_SEND_VAL_EX;
@@ -422,16 +467,17 @@ int zend_optimizer_replace_by_const(zend_op_array *op_array,
} while (m->opcode != ZEND_FREE || ZEND_OP1_TYPE(m) != type || ZEND_OP1(m).var != var);
ZEND_ASSERT(m->opcode == ZEND_FREE && ZEND_OP1_TYPE(m) == type && ZEND_OP1(m).var == var);
MAKE_NOP(m);
+ zend_optimizer_remove_live_range(op_array, var);
return 1;
}
case ZEND_CASE:
case ZEND_FREE: {
zend_op *m, *n;
- int brk = op_array->last_brk_cont;
+ int brk = op_array->last_live_range;
zend_bool in_switch = 0;
while (brk--) {
- if (op_array->brk_cont_array[brk].start <= (opline - op_array->opcodes) &&
- op_array->brk_cont_array[brk].brk > (opline - op_array->opcodes)) {
+ if (op_array->live_range[brk].start <= (uint32_t)(opline - op_array->opcodes) &&
+ op_array->live_range[brk].end > (uint32_t)(opline - op_array->opcodes)) {
in_switch = 1;
break;
}
@@ -445,7 +491,13 @@ int zend_optimizer_replace_by_const(zend_op_array *op_array,
}
m = opline;
- n = op_array->opcodes + op_array->brk_cont_array[brk].brk + 1;
+ n = op_array->opcodes + op_array->live_range[brk].end;
+ if (n->opcode == ZEND_FREE &&
+ !(n->extended_value & ZEND_FREE_ON_RETURN)) {
+ n++;
+ } else {
+ n = op_array->opcodes + op_array->last;
+ }
while (m < n) {
if (ZEND_OP1_TYPE(m) == type &&
ZEND_OP1(m).var == var) {
@@ -464,11 +516,11 @@ int zend_optimizer_replace_by_const(zend_op_array *op_array,
m++;
}
zval_dtor(val);
+ zend_optimizer_remove_live_range(op_array, var);
return 1;
}
case ZEND_VERIFY_RETURN_TYPE: {
zend_arg_info *ret_info = op_array->arg_info - 1;
- ZEND_ASSERT((opline + 1)->opcode == ZEND_RETURN || (opline + 1)->opcode == ZEND_RETURN_BY_REF);
if (ret_info->class_name
|| ret_info->type_hint == IS_CALLABLE
|| !ZEND_SAME_FAKE_TYPE(ret_info->type_hint, Z_TYPE_P(val))
@@ -477,18 +529,32 @@ int zend_optimizer_replace_by_const(zend_op_array *op_array,
return 0;
}
MAKE_NOP(opline);
- zend_optimizer_update_op1_const(op_array, opline + 1, val);
- return 1;
+
+ /* zend_handle_loops_and_finally may inserts other oplines */
+ do {
+ ++opline;
+ } while (opline->opcode != ZEND_RETURN && opline->opcode != ZEND_RETURN_BY_REF);
+ ZEND_ASSERT(ZEND_OP1(opline).var == var);
+
+ break;
}
default:
break;
}
- return zend_optimizer_update_op1_const(op_array, opline, val);
+ if (zend_optimizer_update_op1_const(op_array, opline, val)) {
+ zend_optimizer_remove_live_range(op_array, var);
+ return 1;
+ }
+ return 0;
}
if (ZEND_OP2_TYPE(opline) == type &&
ZEND_OP2(opline).var == var) {
- return zend_optimizer_update_op2_const(op_array, opline, val);
+ if (zend_optimizer_update_op2_const(op_array, opline, val)) {
+ zend_optimizer_remove_live_range(op_array, var);
+ return 1;
+ }
+ return 0;
}
opline++;
}
@@ -496,6 +562,139 @@ int zend_optimizer_replace_by_const(zend_op_array *op_array,
return 1;
}
+static zend_class_entry *get_class_entry_from_op1(
+ zend_script *script, zend_op_array *op_array, zend_op *opline, zend_bool rt_constants) {
+ if (opline->op1_type == IS_CONST) {
+ zval *op1 = CRT_CONSTANT_EX(op_array, opline->op1, rt_constants);
+ if (Z_TYPE_P(op1) == IS_STRING) {
+ zend_string *class_name = Z_STR_P(op1 + 1);
+ zend_class_entry *ce;
+ if (script && (ce = zend_hash_find_ptr(&script->class_table, class_name))) {
+ return ce;
+ } else if ((ce = zend_hash_find_ptr(EG(class_table), class_name))) {
+ if (ce->type == ZEND_INTERNAL_CLASS) {
+ return ce;
+ } else if (ce->type == ZEND_USER_CLASS &&
+ ce->info.user.filename &&
+ ce->info.user.filename == op_array->filename) {
+ return ce;
+ }
+ }
+ }
+ } else if (opline->op1_type == IS_UNUSED && op_array->scope
+ && !(op_array->scope->ce_flags & ZEND_ACC_TRAIT)
+ && (opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) {
+ return op_array->scope;
+ }
+ return NULL;
+}
+
+zend_function *zend_optimizer_get_called_func(
+ zend_script *script, zend_op_array *op_array, zend_op *opline, zend_bool rt_constants)
+{
+#define GET_OP(op) CRT_CONSTANT_EX(op_array, opline->op, rt_constants)
+ switch (opline->opcode) {
+ case ZEND_INIT_FCALL:
+ {
+ zend_string *function_name = Z_STR_P(GET_OP(op2));
+ zend_function *func;
+ if (script && (func = zend_hash_find_ptr(&script->function_table, function_name)) != NULL) {
+ return func;
+ } else if ((func = zend_hash_find_ptr(EG(function_table), function_name)) != NULL) {
+ if (func->type == ZEND_INTERNAL_FUNCTION) {
+ return func;
+ } else if (func->type == ZEND_USER_FUNCTION &&
+ func->op_array.filename &&
+ func->op_array.filename == op_array->filename) {
+ return func;
+ }
+ }
+ break;
+ }
+ case ZEND_INIT_FCALL_BY_NAME:
+ case ZEND_INIT_NS_FCALL_BY_NAME:
+ if (opline->op2_type == IS_CONST && Z_TYPE_P(GET_OP(op2)) == IS_STRING) {
+ zval *function_name = GET_OP(op2) + 1;
+ zend_function *func;
+ if (script && (func = zend_hash_find_ptr(&script->function_table, Z_STR_P(function_name)))) {
+ return func;
+ } else if ((func = zend_hash_find_ptr(EG(function_table), Z_STR_P(function_name))) != NULL) {
+ if (func->type == ZEND_INTERNAL_FUNCTION) {
+ return func;
+ } else if (func->type == ZEND_USER_FUNCTION &&
+ func->op_array.filename &&
+ func->op_array.filename == op_array->filename) {
+ return func;
+ }
+ }
+ }
+ break;
+ case ZEND_INIT_STATIC_METHOD_CALL:
+ if (opline->op2_type == IS_CONST && Z_TYPE_P(GET_OP(op2)) == IS_STRING) {
+ zend_class_entry *ce = get_class_entry_from_op1(
+ script, op_array, opline, rt_constants);
+ if (ce) {
+ zend_string *func_name = Z_STR_P(GET_OP(op2) + 1);
+ return zend_hash_find_ptr(&ce->function_table, func_name);
+ }
+ }
+ break;
+ case ZEND_INIT_METHOD_CALL:
+ if (opline->op1_type == IS_UNUSED
+ && opline->op2_type == IS_CONST && Z_TYPE_P(GET_OP(op2)) == IS_STRING
+ && op_array->scope && !(op_array->scope->ce_flags & ZEND_ACC_TRAIT)) {
+ zend_string *method_name = Z_STR_P(GET_OP(op2) + 1);
+ zend_function *fbc = zend_hash_find_ptr(
+ &op_array->scope->function_table, method_name);
+ if (fbc) {
+ zend_bool is_private = (fbc->common.fn_flags & ZEND_ACC_PRIVATE) != 0;
+ zend_bool is_final = (fbc->common.fn_flags & ZEND_ACC_FINAL) != 0;
+ zend_bool same_scope = fbc->common.scope == op_array->scope;
+ if ((is_private && same_scope)
+ || (is_final && (!is_private || same_scope))) {
+ return fbc;
+ }
+ }
+ }
+ break;
+ case ZEND_NEW:
+ {
+ zend_class_entry *ce = get_class_entry_from_op1(
+ script, op_array, opline, rt_constants);
+ if (ce && ce->type == ZEND_USER_CLASS) {
+ return ce->constructor;
+ }
+ break;
+ }
+ }
+ return NULL;
+#undef GET_OP
+}
+
+uint32_t zend_optimizer_classify_function(zend_string *name, uint32_t num_args) {
+ if (zend_string_equals_literal(name, "extract")) {
+ return ZEND_FUNC_INDIRECT_VAR_ACCESS;
+ } else if (zend_string_equals_literal(name, "compact")) {
+ return ZEND_FUNC_INDIRECT_VAR_ACCESS;
+ } else if (zend_string_equals_literal(name, "parse_str") && num_args <= 1) {
+ return ZEND_FUNC_INDIRECT_VAR_ACCESS;
+ } else if (zend_string_equals_literal(name, "mb_parse_str") && num_args <= 1) {
+ return ZEND_FUNC_INDIRECT_VAR_ACCESS;
+ } else if (zend_string_equals_literal(name, "get_defined_vars")) {
+ return ZEND_FUNC_INDIRECT_VAR_ACCESS;
+ } else if (zend_string_equals_literal(name, "assert")) {
+ return ZEND_FUNC_INDIRECT_VAR_ACCESS;
+ } else if (zend_string_equals_literal(name, "func_num_args")) {
+ return ZEND_FUNC_VARARG;
+ } else if (zend_string_equals_literal(name, "func_get_arg")) {
+ return ZEND_FUNC_VARARG;
+ } else if (zend_string_equals_literal(name, "func_get_args")) {
+ return ZEND_FUNC_VARARG;
+ } else {
+ return 0;
+ }
+}
+
static void zend_optimize(zend_op_array *op_array,
zend_optimizer_ctx *ctx)
{
@@ -503,24 +702,33 @@ static void zend_optimize(zend_op_array *op_array,
return;
}
+ if (ctx->debug_level & ZEND_DUMP_BEFORE_OPTIMIZER) {
+ zend_dump_op_array(op_array, 0, "before optimizer", NULL);
+ }
+
/* pass 1
* - substitute persistent constants (true, false, null, etc)
* - perform compile-time evaluation of constant binary and unary operations
* - optimize series of ADD_STRING and/or ADD_CHAR
* - convert CAST(IS_BOOL,x) into BOOL(x)
+ * - pre-evaluate constant function calls
*/
- if (ZEND_OPTIMIZER_PASS_1 & OPTIMIZATION_LEVEL) {
+ if (ZEND_OPTIMIZER_PASS_1 & ctx->optimization_level) {
zend_optimizer_pass1(op_array, ctx);
+ if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_1) {
+ zend_dump_op_array(op_array, 0, "after pass 1", NULL);
+ }
}
/* pass 2:
* - convert non-numeric constants to numeric constants in numeric operators
* - optimize constant conditional JMPs
- * - optimize static BRKs and CONTs
- * - pre-evaluate constant function calls
*/
- if (ZEND_OPTIMIZER_PASS_2 & OPTIMIZATION_LEVEL) {
+ if (ZEND_OPTIMIZER_PASS_2 & ctx->optimization_level) {
zend_optimizer_pass2(op_array);
+ if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_2) {
+ zend_dump_op_array(op_array, 0, "after pass 2", NULL);
+ }
}
/* pass 3:
@@ -528,52 +736,85 @@ static void zend_optimize(zend_op_array *op_array,
* - optimize series of JMPs
* - change $i++ to ++$i where possible
*/
- if (ZEND_OPTIMIZER_PASS_3 & OPTIMIZATION_LEVEL) {
+ if (ZEND_OPTIMIZER_PASS_3 & ctx->optimization_level) {
zend_optimizer_pass3(op_array);
+ if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_3) {
+ zend_dump_op_array(op_array, 0, "after pass 3", NULL);
+ }
}
/* pass 4:
* - INIT_FCALL_BY_NAME -> DO_FCALL
*/
- if (ZEND_OPTIMIZER_PASS_4 & OPTIMIZATION_LEVEL) {
- optimize_func_calls(op_array, ctx);
+ if (ZEND_OPTIMIZER_PASS_4 & ctx->optimization_level) {
+ zend_optimize_func_calls(op_array, ctx);
+ if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_4) {
+ zend_dump_op_array(op_array, 0, "after pass 4", NULL);
+ }
}
/* pass 5:
* - CFG optimization
*/
- if (ZEND_OPTIMIZER_PASS_5 & OPTIMIZATION_LEVEL) {
- optimize_cfg(op_array, ctx);
+ if (ZEND_OPTIMIZER_PASS_5 & ctx->optimization_level) {
+ zend_optimize_cfg(op_array, ctx);
+ if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_5) {
+ zend_dump_op_array(op_array, 0, "after pass 5", NULL);
+ }
+ }
+
+#if HAVE_DFA_PASS
+ /* pass 6:
+ * - DFA optimization
+ */
+ if ((ZEND_OPTIMIZER_PASS_6 & ctx->optimization_level) &&
+ !(ZEND_OPTIMIZER_PASS_7 & ctx->optimization_level)) {
+ zend_optimize_dfa(op_array, ctx);
+ if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_6) {
+ zend_dump_op_array(op_array, 0, "after pass 6", NULL);
+ }
}
+#endif
/* pass 9:
* - Optimize temp variables usage
*/
- if (ZEND_OPTIMIZER_PASS_9 & OPTIMIZATION_LEVEL) {
- optimize_temporary_variables(op_array, ctx);
+ if (ZEND_OPTIMIZER_PASS_9 & ctx->optimization_level) {
+ zend_optimize_temporary_variables(op_array, ctx);
+ if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_9) {
+ zend_dump_op_array(op_array, 0, "after pass 9", NULL);
+ }
}
/* pass 10:
* - remove NOPs
*/
- if (((ZEND_OPTIMIZER_PASS_10|ZEND_OPTIMIZER_PASS_5) & OPTIMIZATION_LEVEL) == ZEND_OPTIMIZER_PASS_10) {
+ if (((ZEND_OPTIMIZER_PASS_10|ZEND_OPTIMIZER_PASS_5) & ctx->optimization_level) == ZEND_OPTIMIZER_PASS_10) {
zend_optimizer_nop_removal(op_array);
+ if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_10) {
+ zend_dump_op_array(op_array, 0, "after pass 10", NULL);
+ }
}
/* pass 11:
* - Compact literals table
*/
- if (ZEND_OPTIMIZER_PASS_11 & OPTIMIZATION_LEVEL) {
+ if (ZEND_OPTIMIZER_PASS_11 & ctx->optimization_level) {
zend_optimizer_compact_literals(op_array, ctx);
+ if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_11) {
+ zend_dump_op_array(op_array, 0, "after pass 11", NULL);
+ }
+ }
+
+ if (ctx->debug_level & ZEND_DUMP_AFTER_OPTIMIZER) {
+ zend_dump_op_array(op_array, 0, "after optimizer", NULL);
}
}
-static void zend_accel_optimize(zend_op_array *op_array,
- zend_optimizer_ctx *ctx)
+static void zend_revert_pass_two(zend_op_array *op_array)
{
zend_op *opline, *end;
- /* Revert pass_two() */
opline = op_array->opcodes;
end = opline + op_array->last;
while (opline < end) {
@@ -583,41 +824,14 @@ static void zend_accel_optimize(zend_op_array *op_array,
if (opline->op2_type == IS_CONST) {
ZEND_PASS_TWO_UNDO_CONSTANT(op_array, opline->op2);
}
- switch (opline->opcode) {
- case ZEND_JMP:
- case ZEND_FAST_CALL:
- case ZEND_DECLARE_ANON_CLASS:
- case ZEND_DECLARE_ANON_INHERITED_CLASS:
- ZEND_PASS_TWO_UNDO_JMP_TARGET(op_array, opline, ZEND_OP1(opline));
- break;
- case ZEND_JMPZNZ:
- /* relative offset into absolute index */
- opline->extended_value = ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value);
- /* break omitted intentionally */
- case ZEND_JMPZ:
- case ZEND_JMPNZ:
- case ZEND_JMPZ_EX:
- case ZEND_JMPNZ_EX:
- case ZEND_JMP_SET:
- case ZEND_COALESCE:
- case ZEND_NEW:
- case ZEND_FE_RESET_R:
- case ZEND_FE_RESET_RW:
- case ZEND_ASSERT_CHECK:
- ZEND_PASS_TWO_UNDO_JMP_TARGET(op_array, opline, ZEND_OP2(opline));
- break;
- case ZEND_FE_FETCH_R:
- case ZEND_FE_FETCH_RW:
- opline->extended_value = ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value);
- break;
- }
opline++;
}
+}
- /* Do actual optimizations */
- zend_optimize(op_array, ctx);
+static void zend_redo_pass_two(zend_op_array *op_array)
+{
+ zend_op *opline, *end;
- /* Redo pass_two() */
opline = op_array->opcodes;
end = opline + op_array->last;
while (opline < end) {
@@ -627,40 +841,53 @@ static void zend_accel_optimize(zend_op_array *op_array,
if (opline->op2_type == IS_CONST) {
ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline->op2);
}
- switch (opline->opcode) {
- case ZEND_JMP:
- case ZEND_FAST_CALL:
- case ZEND_DECLARE_ANON_CLASS:
- case ZEND_DECLARE_ANON_INHERITED_CLASS:
- ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, ZEND_OP1(opline));
- break;
- case ZEND_JMPZNZ:
- /* absolute index to relative offset */
- opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, opline->extended_value);
- /* break omitted intentionally */
- case ZEND_JMPZ:
- case ZEND_JMPNZ:
- case ZEND_JMPZ_EX:
- case ZEND_JMPNZ_EX:
- case ZEND_JMP_SET:
- case ZEND_COALESCE:
- case ZEND_NEW:
- case ZEND_FE_RESET_R:
- case ZEND_FE_RESET_RW:
- case ZEND_ASSERT_CHECK:
- ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, ZEND_OP2(opline));
- break;
- case ZEND_FE_FETCH_R:
- case ZEND_FE_FETCH_RW:
- opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, opline->extended_value);
- break;
- }
ZEND_VM_SET_OPCODE_HANDLER(opline);
opline++;
}
}
-static void zend_accel_adjust_fcall_stack_size(zend_op_array *op_array, zend_optimizer_ctx *ctx)
+#if HAVE_DFA_PASS
+static void zend_redo_pass_two_ex(zend_op_array *op_array, zend_ssa *ssa)
+{
+ zend_op *opline, *end;
+
+ opline = op_array->opcodes;
+ end = opline + op_array->last;
+ while (opline < end) {
+ zend_vm_set_opcode_handler_ex(opline,
+ opline->op1_type == IS_UNUSED ? 0 : (OP1_INFO() & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_KEY_ANY)),
+ opline->op2_type == IS_UNUSED ? 0 : (OP2_INFO() & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_KEY_ANY)),
+ (opline->opcode == ZEND_PRE_INC ||
+ opline->opcode == ZEND_PRE_DEC ||
+ opline->opcode == ZEND_POST_INC ||
+ opline->opcode == ZEND_POST_DEC) ?
+ ((ssa->ops[opline - op_array->opcodes].op1_def >= 0) ? (OP1_DEF_INFO() & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_KEY_ANY)) : MAY_BE_ANY) :
+ (opline->result_type == IS_UNUSED ? 0 : (RES_INFO() & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_KEY_ANY))));
+ if (opline->op1_type == IS_CONST) {
+ ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline->op1);
+ }
+ if (opline->op2_type == IS_CONST) {
+ ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline->op2);
+ }
+ opline++;
+ }
+}
+#endif
+
+static void zend_optimize_op_array(zend_op_array *op_array,
+ zend_optimizer_ctx *ctx)
+{
+ /* Revert pass_two() */
+ zend_revert_pass_two(op_array);
+
+ /* Do actual optimizations */
+ zend_optimize(op_array, ctx);
+
+ /* Redo pass_two() */
+ zend_redo_pass_two(op_array);
+}
+
+static void zend_adjust_fcall_stack_size(zend_op_array *op_array, zend_optimizer_ctx *ctx)
{
zend_function *func;
zend_op *opline, *end;
@@ -680,78 +907,151 @@ static void zend_accel_adjust_fcall_stack_size(zend_op_array *op_array, zend_opt
}
}
-int zend_accel_script_optimize(zend_persistent_script *script)
+#if HAVE_DFA_PASS
+static void zend_adjust_fcall_stack_size_graph(zend_op_array *op_array)
+{
+ zend_func_info *func_info = ZEND_FUNC_INFO(op_array);
+
+ if (func_info) {
+ zend_call_info *call_info =func_info->callee_info;
+
+ while (call_info) {
+ zend_op *opline = call_info->caller_init_opline;
+
+ if (opline && call_info->callee_func && opline->opcode == ZEND_INIT_FCALL) {
+ opline->op1.num = zend_vm_calc_used_stack(opline->extended_value, call_info->callee_func);
+ }
+ call_info = call_info->next_callee;
+ }
+ }
+}
+#endif
+
+int zend_optimize_script(zend_script *script, zend_long optimization_level, zend_long debug_level)
{
- uint idx, j;
- Bucket *p, *q;
zend_class_entry *ce;
zend_op_array *op_array;
+ zend_string *name;
zend_optimizer_ctx ctx;
+#if HAVE_DFA_PASS
+ zend_call_graph call_graph;
+#endif
ctx.arena = zend_arena_create(64 * 1024);
ctx.script = script;
ctx.constants = NULL;
+ ctx.optimization_level = optimization_level;
+ ctx.debug_level = debug_level;
- zend_accel_optimize(&script->main_op_array, &ctx);
+ zend_optimize_op_array(&script->main_op_array, &ctx);
- for (idx = 0; idx < script->function_table.nNumUsed; idx++) {
- p = script->function_table.arData + idx;
- if (Z_TYPE(p->val) == IS_UNDEF) continue;
- op_array = (zend_op_array*)Z_PTR(p->val);
- zend_accel_optimize(op_array, &ctx);
- }
+ ZEND_HASH_FOREACH_PTR(&script->function_table, op_array) {
+ zend_optimize_op_array(op_array, &ctx);
+ } ZEND_HASH_FOREACH_END();
- for (idx = 0; idx < script->class_table.nNumUsed; idx++) {
- p = script->class_table.arData + idx;
- if (Z_TYPE(p->val) == IS_UNDEF) continue;
- ce = (zend_class_entry*)Z_PTR(p->val);
- for (j = 0; j < ce->function_table.nNumUsed; j++) {
- q = ce->function_table.arData + j;
- if (Z_TYPE(q->val) == IS_UNDEF) continue;
- op_array = (zend_op_array*)Z_PTR(q->val);
+ ZEND_HASH_FOREACH_PTR(&script->class_table, ce) {
+ ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->function_table, name, op_array) {
if (op_array->scope == ce) {
- zend_accel_optimize(op_array, &ctx);
+ zend_optimize_op_array(op_array, &ctx);
} else if (op_array->type == ZEND_USER_FUNCTION) {
zend_op_array *orig_op_array;
- if ((orig_op_array = zend_hash_find_ptr(&op_array->scope->function_table, q->key)) != NULL) {
+ if ((orig_op_array = zend_hash_find_ptr(&op_array->scope->function_table, name)) != NULL) {
HashTable *ht = op_array->static_variables;
*op_array = *orig_op_array;
op_array->static_variables = ht;
}
}
+ } ZEND_HASH_FOREACH_END();
+ } ZEND_HASH_FOREACH_END();
+
+#if HAVE_DFA_PASS
+ if ((ZEND_OPTIMIZER_PASS_6 & optimization_level) &&
+ (ZEND_OPTIMIZER_PASS_7 & optimization_level) &&
+ zend_build_call_graph(&ctx.arena, script, ZEND_RT_CONSTANTS, &call_graph) == SUCCESS) {
+ /* Optimize using call-graph */
+ void *checkpoint = zend_arena_checkpoint(ctx.arena);
+ int i;
+ zend_func_info *func_info;
+
+ for (i = 0; i < call_graph.op_arrays_count; i++) {
+ zend_revert_pass_two(call_graph.op_arrays[i]);
+ }
+
+ for (i = 0; i < call_graph.op_arrays_count; i++) {
+ func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
+ if (func_info) {
+ func_info->call_map = zend_build_call_map(&ctx.arena, func_info, call_graph.op_arrays[i]);
+ if (call_graph.op_arrays[i]->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
+ zend_init_func_return_info(call_graph.op_arrays[i], script, &func_info->return_info);
+ }
+ }
+ }
+
+ for (i = 0; i < call_graph.op_arrays_count; i++) {
+ func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
+ if (func_info) {
+ zend_dfa_analyze_op_array(call_graph.op_arrays[i], &ctx, &func_info->ssa, &func_info->flags);
+ }
+ }
+
+ //TODO: perform inner-script inference???
+ for (i = 0; i < call_graph.op_arrays_count; i++) {
+ func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
+ if (func_info) {
+ zend_dfa_optimize_op_array(call_graph.op_arrays[i], &ctx, &func_info->ssa);
+ }
+ }
+
+ if (debug_level & ZEND_DUMP_AFTER_PASS_7) {
+ for (i = 0; i < call_graph.op_arrays_count; i++) {
+ zend_dump_op_array(call_graph.op_arrays[i], 0, "after pass 7", NULL);
+ }
+ }
+
+ if (ZEND_OPTIMIZER_PASS_12 & optimization_level) {
+ for (i = 0; i < call_graph.op_arrays_count; i++) {
+ zend_adjust_fcall_stack_size_graph(call_graph.op_arrays[i]);
+ }
}
- }
- if (ZEND_OPTIMIZER_PASS_12 & OPTIMIZATION_LEVEL) {
- zend_accel_adjust_fcall_stack_size(&script->main_op_array, &ctx);
+ for (i = 0; i < call_graph.op_arrays_count; i++) {
+ func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
+ if (func_info && func_info->ssa.var_info) {
+ zend_redo_pass_two_ex(call_graph.op_arrays[i], &func_info->ssa);
+ } else {
+ zend_redo_pass_two(call_graph.op_arrays[i]);
+ }
+ }
- for (idx = 0; idx < script->function_table.nNumUsed; idx++) {
- p = script->function_table.arData + idx;
- if (Z_TYPE(p->val) == IS_UNDEF) continue;
- op_array = (zend_op_array*)Z_PTR(p->val);
- zend_accel_adjust_fcall_stack_size(op_array, &ctx);
+ for (i = 0; i < call_graph.op_arrays_count; i++) {
+ ZEND_SET_FUNC_INFO(call_graph.op_arrays[i], NULL);
}
- for (idx = 0; idx < script->class_table.nNumUsed; idx++) {
- p = script->class_table.arData + idx;
- if (Z_TYPE(p->val) == IS_UNDEF) continue;
- ce = (zend_class_entry*)Z_PTR(p->val);
- for (j = 0; j < ce->function_table.nNumUsed; j++) {
- q = ce->function_table.arData + j;
- if (Z_TYPE(q->val) == IS_UNDEF) continue;
- op_array = (zend_op_array*)Z_PTR(q->val);
+ zend_arena_release(&ctx.arena, checkpoint);
+ } else
+#endif
+
+ if (ZEND_OPTIMIZER_PASS_12 & optimization_level) {
+ zend_adjust_fcall_stack_size(&script->main_op_array, &ctx);
+
+ ZEND_HASH_FOREACH_PTR(&script->function_table, op_array) {
+ zend_adjust_fcall_stack_size(op_array, &ctx);
+ } ZEND_HASH_FOREACH_END();
+
+ ZEND_HASH_FOREACH_PTR(&script->class_table, ce) {
+ ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->function_table, name, op_array) {
if (op_array->scope == ce) {
- zend_accel_adjust_fcall_stack_size(op_array, &ctx);
+ zend_adjust_fcall_stack_size(op_array, &ctx);
} else if (op_array->type == ZEND_USER_FUNCTION) {
zend_op_array *orig_op_array;
- if ((orig_op_array = zend_hash_find_ptr(&op_array->scope->function_table, q->key)) != NULL) {
+ if ((orig_op_array = zend_hash_find_ptr(&op_array->scope->function_table, name)) != NULL) {
HashTable *ht = op_array->static_variables;
*op_array = *orig_op_array;
op_array->static_variables = ht;
}
}
- }
- }
+ } ZEND_HASH_FOREACH_END();
+ } ZEND_HASH_FOREACH_END();
}
if (ctx.constants) {
@@ -761,3 +1061,21 @@ int zend_accel_script_optimize(zend_persistent_script *script)
return 1;
}
+
+int zend_optimizer_startup(void)
+{
+ return zend_func_info_startup();
+}
+
+int zend_optimizer_shutdown(void)
+{
+ return zend_func_info_shutdown();
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * indent-tabs-mode: t
+ * End:
+ */
diff --git a/ext/opcache/Optimizer/zend_optimizer.h b/ext/opcache/Optimizer/zend_optimizer.h
index 76f6a24264..69c89d7234 100644
--- a/ext/opcache/Optimizer/zend_optimizer.h
+++ b/ext/opcache/Optimizer/zend_optimizer.h
@@ -30,8 +30,8 @@
#define ZEND_OPTIMIZER_PASS_3 (1<<2) /* ++, +=, series of jumps */
#define ZEND_OPTIMIZER_PASS_4 (1<<3) /* INIT_FCALL_BY_NAME -> DO_FCALL */
#define ZEND_OPTIMIZER_PASS_5 (1<<4) /* CFG based optimization */
-#define ZEND_OPTIMIZER_PASS_6 (1<<5)
-#define ZEND_OPTIMIZER_PASS_7 (1<<6)
+#define ZEND_OPTIMIZER_PASS_6 (1<<5) /* DFA based optimization */
+#define ZEND_OPTIMIZER_PASS_7 (1<<6) /* CALL GRAPH optimization */
#define ZEND_OPTIMIZER_PASS_8 (1<<7)
#define ZEND_OPTIMIZER_PASS_9 (1<<8) /* TMP VAR usage */
#define ZEND_OPTIMIZER_PASS_10 (1<<9) /* NOP removal */
@@ -40,9 +40,53 @@
#define ZEND_OPTIMIZER_PASS_13 (1<<12)
#define ZEND_OPTIMIZER_PASS_14 (1<<13)
#define ZEND_OPTIMIZER_PASS_15 (1<<14) /* Collect constants */
+#define ZEND_OPTIMIZER_PASS_16 (1<<15) /* Inline functions */
#define ZEND_OPTIMIZER_ALL_PASSES 0x7FFFFFFF
#define DEFAULT_OPTIMIZATION_LEVEL "0x7FFFBFFF"
+
+#define ZEND_DUMP_AFTER_PASS_1 ZEND_OPTIMIZER_PASS_1
+#define ZEND_DUMP_AFTER_PASS_2 ZEND_OPTIMIZER_PASS_2
+#define ZEND_DUMP_AFTER_PASS_3 ZEND_OPTIMIZER_PASS_3
+#define ZEND_DUMP_AFTER_PASS_4 ZEND_OPTIMIZER_PASS_4
+#define ZEND_DUMP_AFTER_PASS_5 ZEND_OPTIMIZER_PASS_5
+#define ZEND_DUMP_AFTER_PASS_6 ZEND_OPTIMIZER_PASS_6
+#define ZEND_DUMP_AFTER_PASS_7 ZEND_OPTIMIZER_PASS_7
+#define ZEND_DUMP_AFTER_PASS_8 ZEND_OPTIMIZER_PASS_8
+#define ZEND_DUMP_AFTER_PASS_9 ZEND_OPTIMIZER_PASS_9
+#define ZEND_DUMP_AFTER_PASS_10 ZEND_OPTIMIZER_PASS_10
+#define ZEND_DUMP_AFTER_PASS_11 ZEND_OPTIMIZER_PASS_11
+#define ZEND_DUMP_AFTER_PASS_12 ZEND_OPTIMIZER_PASS_12
+#define ZEND_DUMP_AFTER_PASS_13 ZEND_OPTIMIZER_PASS_13
+#define ZEND_DUMP_AFTER_PASS_14 ZEND_OPTIMIZER_PASS_14
+
+#define ZEND_DUMP_BEFORE_OPTIMIZER (1<<16)
+#define ZEND_DUMP_AFTER_OPTIMIZER (1<<17)
+
+#define ZEND_DUMP_BEFORE_BLOCK_PASS (1<<18)
+#define ZEND_DUMP_AFTER_BLOCK_PASS (1<<19)
+#define ZEND_DUMP_BLOCK_PASS_VARS (1<<20)
+
+#define ZEND_DUMP_BEFORE_DFA_PASS (1<<21)
+#define ZEND_DUMP_AFTER_DFA_PASS (1<<22)
+#define ZEND_DUMP_DFA_CFG (1<<23)
+#define ZEND_DUMP_DFA_DOMINATORS (1<<24)
+#define ZEND_DUMP_DFA_LIVENESS (1<<25)
+#define ZEND_DUMP_DFA_PHI (1<<26)
+#define ZEND_DUMP_DFA_SSA (1<<27)
+#define ZEND_DUMP_DFA_SSA_VARS (1<<28)
+
+typedef struct _zend_script {
+ zend_string *filename;
+ zend_op_array main_op_array;
+ HashTable function_table;
+ HashTable class_table;
+} zend_script;
+
+int zend_optimize_script(zend_script *script, zend_long optimization_level, zend_long debug_level);
+int zend_optimizer_startup(void);
+int zend_optimizer_shutdown(void);
+
#endif
diff --git a/ext/opcache/Optimizer/zend_optimizer_internal.h b/ext/opcache/Optimizer/zend_optimizer_internal.h
index 257a54ea93..90297ad816 100644
--- a/ext/opcache/Optimizer/zend_optimizer_internal.h
+++ b/ext/opcache/Optimizer/zend_optimizer_internal.h
@@ -22,7 +22,18 @@
#ifndef ZEND_OPTIMIZER_INTERNAL_H
#define ZEND_OPTIMIZER_INTERNAL_H
-#include "ZendAccelerator.h"
+#include "zend_ssa.h"
+
+#define ZEND_RESULT_TYPE(opline) (opline)->result_type
+#define ZEND_RESULT(opline) (opline)->result
+#define ZEND_OP1_TYPE(opline) (opline)->op1_type
+#define ZEND_OP1(opline) (opline)->op1
+#define ZEND_OP1_LITERAL(opline) (op_array)->literals[(opline)->op1.constant]
+#define ZEND_OP1_JMP_ADDR(opline) OP_JMP_ADDR(opline, (opline)->op1)
+#define ZEND_OP2_TYPE(opline) (opline)->op2_type
+#define ZEND_OP2(opline) (opline)->op2
+#define ZEND_OP2_LITERAL(opline) (op_array)->literals[(opline)->op2.constant]
+#define ZEND_OP2_JMP_ADDR(opline) OP_JMP_ADDR(opline, (opline)->op2)
#define VAR_NUM(v) EX_VAR_TO_NUM(v)
#define NUM_VAR(v) ((uint32_t)(zend_uintptr_t)ZEND_CALL_VAR_NUM(0, v))
@@ -32,65 +43,17 @@
#define INV_COND_EX(op) ((op) == ZEND_JMPZ ? ZEND_JMPNZ_EX : ZEND_JMPZ_EX)
#define INV_EX_COND_EX(op) ((op) == ZEND_JMPZ_EX ? ZEND_JMPNZ_EX : ZEND_JMPZ_EX)
-#undef MAKE_NOP
-
-#define MAKE_NOP(opline) do { \
- (opline)->op1.num = 0; \
- (opline)->op2.num = 0; \
- (opline)->result.num = 0; \
- (opline)->opcode = ZEND_NOP; \
- (opline)->op1_type = IS_UNUSED; \
- (opline)->op2_type = IS_UNUSED; \
- (opline)->result_type = IS_UNUSED; \
- zend_vm_set_opcode_handler(opline); \
-} while (0)
-
-#define RESULT_USED(op) (((op->result_type & IS_VAR) && !(op->result_type & EXT_TYPE_UNUSED)) || op->result_type == IS_TMP_VAR)
-#define RESULT_UNUSED(op) ((op->result_type & EXT_TYPE_UNUSED) != 0)
-#define SAME_VAR(op1, op2) ((((op1 ## _type & IS_VAR) && (op2 ## _type & IS_VAR)) || (op1 ## _type == IS_TMP_VAR && op2 ## _type == IS_TMP_VAR)) && op1.var == op2.var)
+#define RESULT_UNUSED(op) (op->result_type == IS_UNUSED)
+#define SAME_VAR(op1, op2) (op1 ## _type == op2 ## _type && op1.var == op2.var)
typedef struct _zend_optimizer_ctx {
zend_arena *arena;
- zend_persistent_script *script;
+ zend_script *script;
HashTable *constants;
+ zend_long optimization_level;
+ zend_long debug_level;
} zend_optimizer_ctx;
-typedef struct _zend_code_block zend_code_block;
-typedef struct _zend_block_source zend_block_source;
-
-struct _zend_code_block {
- int access;
- zend_op *start_opline;
- int start_opline_no;
- int len;
- zend_code_block *op1_to;
- zend_code_block *op2_to;
- zend_code_block *ext_to;
- zend_code_block *follow_to;
- zend_code_block *next;
- zend_block_source *sources;
- zend_bool protected; /* don't merge this block with others */
-};
-
-typedef struct _zend_cfg {
- zend_code_block *blocks;
- zend_code_block **try;
- zend_code_block **catch;
- zend_code_block **loop_start;
- zend_code_block **loop_cont;
- zend_code_block **loop_brk;
- zend_op **Tsource;
- char *same_t;
-} zend_cfg;
-
-struct _zend_block_source {
- zend_code_block *from;
- zend_block_source *next;
-};
-
-#define OPTIMIZATION_LEVEL \
- ZCG(accel_directives).optimization_level
-
#define LITERAL_LONG(op, val) do { \
zval _c; \
ZVAL_LONG(&_c, val); \
@@ -130,14 +93,21 @@ int zend_optimizer_replace_by_const(zend_op_array *op_array,
uint32_t var,
zval *val);
+void zend_optimizer_remove_live_range(zend_op_array *op_array, uint32_t var);
void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx);
void zend_optimizer_pass2(zend_op_array *op_array);
void zend_optimizer_pass3(zend_op_array *op_array);
-void optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx);
-void optimize_cfg(zend_op_array *op_array, zend_optimizer_ctx *ctx);
-void optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *ctx);
+void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx);
+void zend_optimize_cfg(zend_op_array *op_array, zend_optimizer_ctx *ctx);
+void zend_optimize_dfa(zend_op_array *op_array, zend_optimizer_ctx *ctx);
+int zend_dfa_analyze_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx, zend_ssa *ssa, uint32_t *flags);
+void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx, zend_ssa *ssa);
+void zend_optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *ctx);
void zend_optimizer_nop_removal(zend_op_array *op_array);
void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx *ctx);
int zend_optimizer_is_disabled_func(const char *name, size_t len);
+zend_function *zend_optimizer_get_called_func(
+ zend_script *script, zend_op_array *op_array, zend_op *opline, zend_bool rt_constants);
+uint32_t zend_optimizer_classify_function(zend_string *name, uint32_t num_args);
#endif
diff --git a/ext/opcache/Optimizer/zend_ssa.c b/ext/opcache/Optimizer/zend_ssa.c
new file mode 100644
index 0000000000..c902e51766
--- /dev/null
+++ b/ext/opcache/Optimizer/zend_ssa.c
@@ -0,0 +1,1155 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine, SSA - Static Single Assignment Form |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1998-2017 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Dmitry Stogov <dmitry@zend.com> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "php.h"
+#include "zend_compile.h"
+#include "zend_dfg.h"
+#include "zend_ssa.h"
+#include "zend_dump.h"
+#include "zend_inference.h"
+
+static zend_bool dominates(const zend_basic_block *blocks, int a, int b) {
+ while (blocks[b].level > blocks[a].level) {
+ b = blocks[b].idom;
+ }
+ return a == b;
+}
+
+static zend_bool dominates_other_predecessors(
+ const zend_cfg *cfg, const zend_basic_block *block, int check, int exclude) {
+ int i;
+ for (i = 0; i < block->predecessors_count; i++) {
+ int predecessor = cfg->predecessors[block->predecessor_offset + i];
+ if (predecessor != exclude && !dominates(cfg->blocks, check, predecessor)) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static zend_bool needs_pi(const zend_op_array *op_array, zend_dfg *dfg, zend_ssa *ssa, int from, int to, int var) /* {{{ */
+{
+ zend_basic_block *from_block, *to_block;
+ int other_successor;
+
+ if (!DFG_ISSET(dfg->in, dfg->size, to, var)) {
+ /* Variable is not live, certainly won't benefit from pi */
+ return 0;
+ }
+
+ to_block = &ssa->cfg.blocks[to];
+ if (to_block->predecessors_count == 1) {
+ /* Always place pi if one predecessor (an if branch) */
+ return 1;
+ }
+
+ /* Check that the other successor of the from block does not dominate all other predecessors.
+ * If it does, we'd probably end up annihilating a positive+negative pi assertion. */
+ from_block = &ssa->cfg.blocks[from];
+ other_successor = from_block->successors[0] == to
+ ? from_block->successors[1] : from_block->successors[0];
+ return !dominates_other_predecessors(&ssa->cfg, to_block, other_successor, from);
+}
+/* }}} */
+
+static zend_ssa_phi *add_pi(
+ zend_arena **arena, const zend_op_array *op_array, zend_dfg *dfg, zend_ssa *ssa,
+ int from, int to, int var) /* {{{ */
+{
+ zend_ssa_phi *phi;
+ if (!needs_pi(op_array, dfg, ssa, from, to, var)) {
+ return NULL;
+ }
+
+ phi = zend_arena_calloc(arena, 1,
+ ZEND_MM_ALIGNED_SIZE(sizeof(zend_ssa_phi)) +
+ ZEND_MM_ALIGNED_SIZE(sizeof(int) * ssa->cfg.blocks[to].predecessors_count) +
+ sizeof(void*) * ssa->cfg.blocks[to].predecessors_count);
+ phi->sources = (int*)(((char*)phi) + ZEND_MM_ALIGNED_SIZE(sizeof(zend_ssa_phi)));
+ memset(phi->sources, 0xff, sizeof(int) * ssa->cfg.blocks[to].predecessors_count);
+ phi->use_chains = (zend_ssa_phi**)(((char*)phi->sources) + ZEND_MM_ALIGNED_SIZE(sizeof(int) * ssa->cfg.blocks[to].predecessors_count));
+
+ phi->pi = from;
+ phi->var = var;
+ phi->ssa_var = -1;
+ phi->next = ssa->blocks[to].phis;
+ ssa->blocks[to].phis = phi;
+
+ /* Block "to" now defines "var" via the pi statement, so add it to the "def" set. Note that
+ * this is not entirely accurate, because the pi is actually placed along the edge from->to.
+ * If there is a back-edge to "to" this may result in non-minimal SSA form. */
+ DFG_SET(dfg->def, dfg->size, to, var);
+
+ /* If there are multiple predecessors in the target block, we need to place a phi there.
+ * However this can (generally) not be expressed in terms of dominance frontiers, so place it
+ * explicitly. dfg->use here really is dfg->phi, we're reusing the set. */
+ if (ssa->cfg.blocks[to].predecessors_count > 1) {
+ DFG_SET(dfg->use, dfg->size, to, var);
+ }
+
+ return phi;
+}
+/* }}} */
+
+static void pi_range(
+ zend_ssa_phi *phi, int min_var, int max_var, zend_long min, zend_long max,
+ char underflow, char overflow, char negative) /* {{{ */
+{
+ zend_ssa_range_constraint *constraint = &phi->constraint.range;
+ constraint->min_var = min_var;
+ constraint->max_var = max_var;
+ constraint->min_ssa_var = -1;
+ constraint->max_ssa_var = -1;
+ constraint->range.min = min;
+ constraint->range.max = max;
+ constraint->range.underflow = underflow;
+ constraint->range.overflow = overflow;
+ constraint->negative = negative ? NEG_INIT : NEG_NONE;
+ phi->has_range_constraint = 1;
+}
+/* }}} */
+
+static inline void pi_range_equals(zend_ssa_phi *phi, int var, zend_long val) {
+ pi_range(phi, var, var, val, val, 0, 0, 0);
+}
+static inline void pi_range_not_equals(zend_ssa_phi *phi, int var, zend_long val) {
+ pi_range(phi, var, var, val, val, 0, 0, 1);
+}
+static inline void pi_range_min(zend_ssa_phi *phi, int var, zend_long val) {
+ pi_range(phi, var, -1, val, ZEND_LONG_MAX, 0, 1, 0);
+}
+static inline void pi_range_max(zend_ssa_phi *phi, int var, zend_long val) {
+ pi_range(phi, -1, var, ZEND_LONG_MIN, val, 1, 0, 0);
+}
+
+static void pi_type_mask(zend_ssa_phi *phi, uint32_t type_mask) {
+ phi->has_range_constraint = 0;
+ phi->constraint.type.ce = NULL;
+ phi->constraint.type.type_mask = MAY_BE_REF|MAY_BE_RC1|MAY_BE_RCN;
+ phi->constraint.type.type_mask |= type_mask;
+ if (type_mask & MAY_BE_NULL) {
+ phi->constraint.type.type_mask |= MAY_BE_UNDEF;
+ }
+}
+static inline void pi_not_type_mask(zend_ssa_phi *phi, uint32_t type_mask) {
+ uint32_t relevant = MAY_BE_ANY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
+ pi_type_mask(phi, ~type_mask & relevant);
+}
+static inline uint32_t mask_for_type_check(uint32_t type) {
+ if (type == _IS_BOOL) {
+ return MAY_BE_TRUE|MAY_BE_FALSE;
+ } else if (type == IS_ARRAY) {
+ return MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
+ } else {
+ return 1 << type;
+ }
+}
+
+/* We can interpret $a + 5 == 0 as $a = 0 - 5, i.e. shift the adjustment to the other operand.
+ * This negated adjustment is what is written into the "adjustment" parameter. */
+static int find_adjusted_tmp_var(const zend_op_array *op_array, uint32_t build_flags, zend_op *opline, uint32_t var_num, zend_long *adjustment) /* {{{ */
+{
+ zend_op *op = opline;
+ zval *zv;
+
+ while (op != op_array->opcodes) {
+ op--;
+ if (op->result_type != IS_TMP_VAR || op->result.var != var_num) {
+ continue;
+ }
+
+ if (op->opcode == ZEND_POST_DEC) {
+ if (op->op1_type == IS_CV) {
+ *adjustment = -1;
+ return EX_VAR_TO_NUM(op->op1.var);
+ }
+ } else if (op->opcode == ZEND_POST_INC) {
+ if (op->op1_type == IS_CV) {
+ *adjustment = 1;
+ return EX_VAR_TO_NUM(op->op1.var);
+ }
+ } else if (op->opcode == ZEND_ADD) {
+ if (op->op1_type == IS_CV && op->op2_type == IS_CONST) {
+ zv = CRT_CONSTANT(op->op2);
+ if (Z_TYPE_P(zv) == IS_LONG
+ && Z_LVAL_P(zv) != ZEND_LONG_MIN) {
+ *adjustment = -Z_LVAL_P(zv);
+ return EX_VAR_TO_NUM(op->op1.var);
+ }
+ } else if (op->op2_type == IS_CV && op->op1_type == IS_CONST) {
+ zv = CRT_CONSTANT(op->op1);
+ if (Z_TYPE_P(zv) == IS_LONG
+ && Z_LVAL_P(zv) != ZEND_LONG_MIN) {
+ *adjustment = -Z_LVAL_P(zv);
+ return EX_VAR_TO_NUM(op->op2.var);
+ }
+ }
+ } else if (op->opcode == ZEND_SUB) {
+ if (op->op1_type == IS_CV && op->op2_type == IS_CONST) {
+ zv = CRT_CONSTANT(op->op2);
+ if (Z_TYPE_P(zv) == IS_LONG) {
+ *adjustment = Z_LVAL_P(zv);
+ return EX_VAR_TO_NUM(op->op1.var);
+ }
+ }
+ }
+ break;
+ }
+ return -1;
+}
+/* }}} */
+
+static inline zend_bool add_will_overflow(zend_long a, zend_long b) {
+ return (b > 0 && a > ZEND_LONG_MAX - b)
+ || (b < 0 && a < ZEND_LONG_MIN - b);
+}
+static inline zend_bool sub_will_overflow(zend_long a, zend_long b) {
+ return (b > 0 && a < ZEND_LONG_MIN + b)
+ || (b < 0 && a > ZEND_LONG_MAX + b);
+}
+
+/* e-SSA construction: Pi placement (Pi is actually a Phi with single
+ * source and constraint).
+ * Order of Phis is importent, Pis must be placed before Phis
+ */
+static void place_essa_pis(
+ zend_arena **arena, const zend_script *script, const zend_op_array *op_array,
+ uint32_t build_flags, zend_ssa *ssa, zend_dfg *dfg) /* {{{ */ {
+ zend_basic_block *blocks = ssa->cfg.blocks;
+ int j, blocks_count = ssa->cfg.blocks_count;
+ for (j = 0; j < blocks_count; j++) {
+ zend_ssa_phi *pi;
+ zend_op *opline = op_array->opcodes + blocks[j].start + blocks[j].len - 1;
+ int bt; /* successor block number if a condition is true */
+ int bf; /* successor block number if a condition is false */
+
+ if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0 || blocks[j].len == 0) {
+ continue;
+ }
+ /* the last instruction of basic block is conditional branch,
+ * based on comparison of CV(s)
+ */
+ switch (opline->opcode) {
+ case ZEND_JMPZ:
+ case ZEND_JMPZNZ:
+ bf = blocks[j].successors[0];
+ bt = blocks[j].successors[1];
+ break;
+ case ZEND_JMPNZ:
+ bt = blocks[j].successors[0];
+ bf = blocks[j].successors[1];
+ break;
+ default:
+ continue;
+ }
+ if (opline->op1_type == IS_TMP_VAR &&
+ ((opline-1)->opcode == ZEND_IS_EQUAL ||
+ (opline-1)->opcode == ZEND_IS_NOT_EQUAL ||
+ (opline-1)->opcode == ZEND_IS_SMALLER ||
+ (opline-1)->opcode == ZEND_IS_SMALLER_OR_EQUAL) &&
+ opline->op1.var == (opline-1)->result.var) {
+ int var1 = -1;
+ int var2 = -1;
+ zend_long val1 = 0;
+ zend_long val2 = 0;
+// long val = 0;
+
+ if ((opline-1)->op1_type == IS_CV) {
+ var1 = EX_VAR_TO_NUM((opline-1)->op1.var);
+ } else if ((opline-1)->op1_type == IS_TMP_VAR) {
+ var1 = find_adjusted_tmp_var(
+ op_array, build_flags, opline, (opline-1)->op1.var, &val2);
+ }
+
+ if ((opline-1)->op2_type == IS_CV) {
+ var2 = EX_VAR_TO_NUM((opline-1)->op2.var);
+ } else if ((opline-1)->op2_type == IS_TMP_VAR) {
+ var2 = find_adjusted_tmp_var(
+ op_array, build_flags, opline, (opline-1)->op2.var, &val1);
+ }
+
+ if (var1 >= 0 && var2 >= 0) {
+ if (!sub_will_overflow(val1, val2) && !sub_will_overflow(val2, val1)) {
+ zend_long tmp = val1;
+ val1 -= val2;
+ val2 -= tmp;
+ } else {
+ var1 = -1;
+ var2 = -1;
+ }
+ } else if (var1 >= 0 && var2 < 0) {
+ zend_long add_val2 = 0;
+ if ((opline-1)->op2_type == IS_CONST) {
+ zval *zv = CRT_CONSTANT((opline-1)->op2);
+
+ if (Z_TYPE_P(zv) == IS_LONG) {
+ add_val2 = Z_LVAL_P(zv);
+ } else if (Z_TYPE_P(zv) == IS_FALSE) {
+ add_val2 = 0;
+ } else if (Z_TYPE_P(zv) == IS_TRUE) {
+ add_val2 = 1;
+ } else {
+ var1 = -1;
+ }
+ } else {
+ var1 = -1;
+ }
+ if (!add_will_overflow(val2, add_val2)) {
+ val2 += add_val2;
+ } else {
+ var1 = -1;
+ }
+ } else if (var1 < 0 && var2 >= 0) {
+ zend_long add_val1 = 0;
+ if ((opline-1)->op1_type == IS_CONST) {
+ zval *zv = CRT_CONSTANT((opline-1)->op1);
+ if (Z_TYPE_P(zv) == IS_LONG) {
+ add_val1 = Z_LVAL_P(CRT_CONSTANT((opline-1)->op1));
+ } else if (Z_TYPE_P(zv) == IS_FALSE) {
+ add_val1 = 0;
+ } else if (Z_TYPE_P(zv) == IS_TRUE) {
+ add_val1 = 1;
+ } else {
+ var2 = -1;
+ }
+ } else {
+ var2 = -1;
+ }
+ if (!add_will_overflow(val1, add_val1)) {
+ val1 += add_val1;
+ } else {
+ var2 = -1;
+ }
+ }
+
+ if (var1 >= 0) {
+ if ((opline-1)->opcode == ZEND_IS_EQUAL) {
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var1))) {
+ pi_range_equals(pi, var2, val2);
+ }
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var1))) {
+ pi_range_not_equals(pi, var2, val2);
+ }
+ } else if ((opline-1)->opcode == ZEND_IS_NOT_EQUAL) {
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var1))) {
+ pi_range_equals(pi, var2, val2);
+ }
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var1))) {
+ pi_range_not_equals(pi, var2, val2);
+ }
+ } else if ((opline-1)->opcode == ZEND_IS_SMALLER) {
+ if (val2 > ZEND_LONG_MIN) {
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var1))) {
+ pi_range_max(pi, var2, val2-1);
+ }
+ }
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var1))) {
+ pi_range_min(pi, var2, val2);
+ }
+ } else if ((opline-1)->opcode == ZEND_IS_SMALLER_OR_EQUAL) {
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var1))) {
+ pi_range_max(pi, var2, val2);
+ }
+ if (val2 < ZEND_LONG_MAX) {
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var1))) {
+ pi_range_min(pi, var2, val2+1);
+ }
+ }
+ }
+ }
+ if (var2 >= 0) {
+ if((opline-1)->opcode == ZEND_IS_EQUAL) {
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var2))) {
+ pi_range_equals(pi, var1, val1);
+ }
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var2))) {
+ pi_range_not_equals(pi, var1, val1);
+ }
+ } else if ((opline-1)->opcode == ZEND_IS_NOT_EQUAL) {
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var2))) {
+ pi_range_equals(pi, var1, val1);
+ }
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var2))) {
+ pi_range_not_equals(pi, var1, val1);
+ }
+ } else if ((opline-1)->opcode == ZEND_IS_SMALLER) {
+ if (val1 < ZEND_LONG_MAX) {
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var2))) {
+ pi_range_min(pi, var1, val1+1);
+ }
+ }
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var2))) {
+ pi_range_max(pi, var1, val1);
+ }
+ } else if ((opline-1)->opcode == ZEND_IS_SMALLER_OR_EQUAL) {
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var2))) {
+ pi_range_min(pi, var1, val1);
+ }
+ if (val1 > ZEND_LONG_MIN) {
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var2))) {
+ pi_range_max(pi, var1, val1-1);
+ }
+ }
+ }
+ }
+ } else if (opline->op1_type == IS_TMP_VAR &&
+ ((opline-1)->opcode == ZEND_POST_INC ||
+ (opline-1)->opcode == ZEND_POST_DEC) &&
+ opline->op1.var == (opline-1)->result.var &&
+ (opline-1)->op1_type == IS_CV) {
+ int var = EX_VAR_TO_NUM((opline-1)->op1.var);
+
+ if ((opline-1)->opcode == ZEND_POST_DEC) {
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var))) {
+ pi_range_equals(pi, -1, -1);
+ }
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var))) {
+ pi_range_not_equals(pi, -1, -1);
+ }
+ } else if ((opline-1)->opcode == ZEND_POST_INC) {
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var))) {
+ pi_range_equals(pi, -1, 1);
+ }
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var))) {
+ pi_range_not_equals(pi, -1, 1);
+ }
+ }
+ } else if (opline->op1_type == IS_VAR &&
+ ((opline-1)->opcode == ZEND_PRE_INC ||
+ (opline-1)->opcode == ZEND_PRE_DEC) &&
+ opline->op1.var == (opline-1)->result.var &&
+ (opline-1)->op1_type == IS_CV) {
+ int var = EX_VAR_TO_NUM((opline-1)->op1.var);
+
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var))) {
+ pi_range_equals(pi, -1, 0);
+ }
+ /* speculative */
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var))) {
+ pi_range_not_equals(pi, -1, 0);
+ }
+ } else if (opline->op1_type == IS_TMP_VAR && (opline-1)->opcode == ZEND_TYPE_CHECK &&
+ opline->op1.var == (opline-1)->result.var && (opline-1)->op1_type == IS_CV) {
+ int var = EX_VAR_TO_NUM((opline-1)->op1.var);
+ uint32_t type = (opline-1)->extended_value;
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var))) {
+ pi_type_mask(pi, mask_for_type_check(type));
+ }
+ if (type != IS_OBJECT && type != IS_RESOURCE) {
+ /* is_object() and is_resource() may return false, even though the value is
+ * an object/resource. */
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var))) {
+ pi_not_type_mask(pi, mask_for_type_check(type));
+ }
+ }
+ } else if (opline->op1_type == IS_TMP_VAR &&
+ ((opline-1)->opcode == ZEND_IS_IDENTICAL
+ || (opline-1)->opcode == ZEND_IS_NOT_IDENTICAL) &&
+ opline->op1.var == (opline-1)->result.var) {
+ int var;
+ zval *val;
+ uint32_t type_mask;
+ if ((opline-1)->op1_type == IS_CV && (opline-1)->op2_type == IS_CONST) {
+ var = EX_VAR_TO_NUM((opline-1)->op1.var);
+ val = CRT_CONSTANT((opline-1)->op2);
+ } else if ((opline-1)->op1_type == IS_CONST && (opline-1)->op2_type == IS_CV) {
+ var = EX_VAR_TO_NUM((opline-1)->op2.var);
+ val = CRT_CONSTANT((opline-1)->op1);
+ } else {
+ continue;
+ }
+
+ /* We're interested in === null/true/false comparisons here, because they eliminate
+ * a type in the false-branch. Other === VAL comparisons are unlikely to be useful. */
+ if (Z_TYPE_P(val) != IS_NULL && Z_TYPE_P(val) != IS_TRUE && Z_TYPE_P(val) != IS_FALSE) {
+ continue;
+ }
+
+ type_mask = _const_op_type(val);
+ if ((opline-1)->opcode == ZEND_IS_IDENTICAL) {
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var))) {
+ pi_type_mask(pi, type_mask);
+ }
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var))) {
+ pi_not_type_mask(pi, type_mask);
+ }
+ } else {
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var))) {
+ pi_type_mask(pi, type_mask);
+ }
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var))) {
+ pi_not_type_mask(pi, type_mask);
+ }
+ }
+ } else if (opline->op1_type == IS_TMP_VAR && (opline-1)->opcode == ZEND_INSTANCEOF &&
+ opline->op1.var == (opline-1)->result.var && (opline-1)->op1_type == IS_CV &&
+ (opline-1)->op2_type == IS_CONST) {
+ int var = EX_VAR_TO_NUM((opline-1)->op1.var);
+ zend_string *lcname = Z_STR_P(CRT_CONSTANT((opline-1)->op2) + 1);
+ zend_class_entry *ce = script ? zend_hash_find_ptr(&script->class_table, lcname) : NULL;
+ if (!ce) {
+ ce = zend_hash_find_ptr(CG(class_table), lcname);
+ if (!ce || ce->type != ZEND_INTERNAL_CLASS) {
+ continue;
+ }
+ }
+
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var))) {
+ pi_type_mask(pi, MAY_BE_OBJECT);
+ pi->constraint.type.ce = ce;
+ }
+ }
+ }
+}
+/* }}} */
+
+static int zend_ssa_rename(const zend_op_array *op_array, uint32_t build_flags, zend_ssa *ssa, int *var, int n) /* {{{ */
+{
+ zend_basic_block *blocks = ssa->cfg.blocks;
+ zend_ssa_block *ssa_blocks = ssa->blocks;
+ zend_ssa_op *ssa_ops = ssa->ops;
+ int ssa_vars_count = ssa->vars_count;
+ int i, j;
+ zend_op *opline, *end;
+ int *tmp = NULL;
+ ALLOCA_FLAG(use_heap);
+
+ // FIXME: Can we optimize this copying out in some cases?
+ if (blocks[n].next_child >= 0) {
+ tmp = do_alloca(sizeof(int) * (op_array->last_var + op_array->T), use_heap);
+ memcpy(tmp, var, sizeof(int) * (op_array->last_var + op_array->T));
+ var = tmp;
+ }
+
+ if (ssa_blocks[n].phis) {
+ zend_ssa_phi *phi = ssa_blocks[n].phis;
+ do {
+ if (phi->ssa_var < 0) {
+ phi->ssa_var = ssa_vars_count;
+ var[phi->var] = ssa_vars_count;
+ ssa_vars_count++;
+ } else {
+ var[phi->var] = phi->ssa_var;
+ }
+ phi = phi->next;
+ } while (phi);
+ }
+
+ opline = op_array->opcodes + blocks[n].start;
+ end = opline + blocks[n].len;
+ for (; opline < end; opline++) {
+ uint32_t k = opline - op_array->opcodes;
+ if (opline->opcode != ZEND_OP_DATA) {
+ zend_op *next = opline + 1;
+ if (next < end && next->opcode == ZEND_OP_DATA) {
+ if (next->op1_type == IS_CV) {
+ ssa_ops[k + 1].op1_use = var[EX_VAR_TO_NUM(next->op1.var)];
+ //USE_SSA_VAR(next->op1.var);
+ } else if (next->op1_type & (IS_VAR|IS_TMP_VAR)) {
+ ssa_ops[k + 1].op1_use = var[EX_VAR_TO_NUM(next->op1.var)];
+ //USE_SSA_VAR(op_array->last_var + next->op1.var);
+ }
+ if (next->op2_type == IS_CV) {
+ ssa_ops[k + 1].op2_use = var[EX_VAR_TO_NUM(next->op2.var)];
+ //USE_SSA_VAR(next->op2.var);
+ } else if (next->op2_type & (IS_VAR|IS_TMP_VAR)) {
+ ssa_ops[k + 1].op2_use = var[EX_VAR_TO_NUM(next->op2.var)];
+ //USE_SSA_VAR(op_array->last_var + next->op2.var);
+ }
+ }
+ if (opline->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
+ ssa_ops[k].op1_use = var[EX_VAR_TO_NUM(opline->op1.var)];
+ //USE_SSA_VAR(op_array->last_var + opline->op1.var)
+ }
+ if (opline->opcode == ZEND_FE_FETCH_R || opline->opcode == ZEND_FE_FETCH_RW) {
+ if (opline->op2_type == IS_CV) {
+ ssa_ops[k].op2_use = var[EX_VAR_TO_NUM(opline->op2.var)];
+ }
+ ssa_ops[k].op2_def = ssa_vars_count;
+ var[EX_VAR_TO_NUM(opline->op2.var)] = ssa_vars_count;
+ ssa_vars_count++;
+ //NEW_SSA_VAR(opline->op2.var)
+ } else if (opline->op2_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
+ ssa_ops[k].op2_use = var[EX_VAR_TO_NUM(opline->op2.var)];
+ //USE_SSA_VAR(op_array->last_var + opline->op2.var)
+ }
+ switch (opline->opcode) {
+ case ZEND_ASSIGN:
+ if ((build_flags & ZEND_SSA_RC_INFERENCE) && opline->op2_type == IS_CV) {
+ ssa_ops[k].op2_def = ssa_vars_count;
+ var[EX_VAR_TO_NUM(opline->op2.var)] = ssa_vars_count;
+ ssa_vars_count++;
+ //NEW_SSA_VAR(opline->op2.var)
+ }
+ if (opline->op1_type == IS_CV) {
+ ssa_ops[k].op1_def = ssa_vars_count;
+ var[EX_VAR_TO_NUM(opline->op1.var)] = ssa_vars_count;
+ ssa_vars_count++;
+ //NEW_SSA_VAR(opline->op1.var)
+ }
+ break;
+ case ZEND_ASSIGN_REF:
+//TODO: ???
+ if (opline->op2_type == IS_CV) {
+ ssa_ops[k].op2_def = ssa_vars_count;
+ var[EX_VAR_TO_NUM(opline->op2.var)] = ssa_vars_count;
+ ssa_vars_count++;
+ //NEW_SSA_VAR(opline->op2.var)
+ }
+ if (opline->op1_type == IS_CV) {
+ ssa_ops[k].op1_def = ssa_vars_count;
+ var[EX_VAR_TO_NUM(opline->op1.var)] = ssa_vars_count;
+ ssa_vars_count++;
+ //NEW_SSA_VAR(opline->op1.var)
+ }
+ break;
+ case ZEND_BIND_GLOBAL:
+ case ZEND_BIND_STATIC:
+ if (opline->op1_type == IS_CV) {
+ ssa_ops[k].op1_def = ssa_vars_count;
+ var[EX_VAR_TO_NUM(opline->op1.var)] = ssa_vars_count;
+ ssa_vars_count++;
+ //NEW_SSA_VAR(opline->op1.var)
+ }
+ break;
+ case ZEND_ASSIGN_DIM:
+ case ZEND_ASSIGN_OBJ:
+ if (opline->op1_type == IS_CV) {
+ ssa_ops[k].op1_def = ssa_vars_count;
+ var[EX_VAR_TO_NUM(opline->op1.var)] = ssa_vars_count;
+ ssa_vars_count++;
+ //NEW_SSA_VAR(opline->op1.var)
+ }
+ if ((build_flags & ZEND_SSA_RC_INFERENCE) && next->op1_type == IS_CV) {
+ ssa_ops[k + 1].op1_def = ssa_vars_count;
+ var[EX_VAR_TO_NUM(next->op1.var)] = ssa_vars_count;
+ ssa_vars_count++;
+ //NEW_SSA_VAR(next->op1.var)
+ }
+ break;
+ case ZEND_ADD_ARRAY_ELEMENT:
+ ssa_ops[k].result_use = var[EX_VAR_TO_NUM(opline->result.var)];
+ case ZEND_INIT_ARRAY:
+ if (((build_flags & ZEND_SSA_RC_INFERENCE)
+ || (opline->extended_value & ZEND_ARRAY_ELEMENT_REF))
+ && opline->op1_type == IS_CV) {
+ ssa_ops[k].op1_def = ssa_vars_count;
+ var[EX_VAR_TO_NUM(opline->op1.var)] = ssa_vars_count;
+ ssa_vars_count++;
+ //NEW_SSA_VAR(opline+->op1.var)
+ }
+ break;
+ case ZEND_SEND_VAR:
+ case ZEND_CAST:
+ case ZEND_QM_ASSIGN:
+ case ZEND_JMP_SET:
+ case ZEND_COALESCE:
+ if ((build_flags & ZEND_SSA_RC_INFERENCE) && opline->op1_type == IS_CV) {
+ ssa_ops[k].op1_def = ssa_vars_count;
+ var[EX_VAR_TO_NUM(opline->op1.var)] = ssa_vars_count;
+ ssa_vars_count++;
+ //NEW_SSA_VAR(opline->op1.var)
+ }
+ break;
+ case ZEND_SEND_VAR_NO_REF:
+ case ZEND_SEND_VAR_NO_REF_EX:
+ case ZEND_SEND_VAR_EX:
+ case ZEND_SEND_REF:
+ case ZEND_SEND_UNPACK:
+ case ZEND_FE_RESET_RW:
+//TODO: ???
+ if (opline->op1_type == IS_CV) {
+ ssa_ops[k].op1_def = ssa_vars_count;
+ var[EX_VAR_TO_NUM(opline->op1.var)] = ssa_vars_count;
+ ssa_vars_count++;
+ //NEW_SSA_VAR(opline->op1.var)
+ }
+ break;
+ case ZEND_FE_RESET_R:
+ if ((build_flags & ZEND_SSA_RC_INFERENCE) && opline->op1_type == IS_CV) {
+ ssa_ops[k].op1_def = ssa_vars_count;
+ var[EX_VAR_TO_NUM(opline->op1.var)] = ssa_vars_count;
+ ssa_vars_count++;
+ //NEW_SSA_VAR(opline->op1.var)
+ }
+ break;
+ case ZEND_ASSIGN_ADD:
+ case ZEND_ASSIGN_SUB:
+ case ZEND_ASSIGN_MUL:
+ case ZEND_ASSIGN_DIV:
+ case ZEND_ASSIGN_MOD:
+ case ZEND_ASSIGN_SL:
+ case ZEND_ASSIGN_SR:
+ case ZEND_ASSIGN_CONCAT:
+ case ZEND_ASSIGN_BW_OR:
+ case ZEND_ASSIGN_BW_AND:
+ case ZEND_ASSIGN_BW_XOR:
+ case ZEND_ASSIGN_POW:
+ case ZEND_PRE_INC:
+ case ZEND_PRE_DEC:
+ case ZEND_POST_INC:
+ case ZEND_POST_DEC:
+ if (opline->op1_type == IS_CV) {
+ ssa_ops[k].op1_def = ssa_vars_count;
+ var[EX_VAR_TO_NUM(opline->op1.var)] = ssa_vars_count;
+ ssa_vars_count++;
+ //NEW_SSA_VAR(opline->op1.var)
+ }
+ break;
+ case ZEND_UNSET_VAR:
+ if (opline->extended_value & ZEND_QUICK_SET) {
+ ssa_ops[k].op1_def = ssa_vars_count;
+ var[EX_VAR_TO_NUM(opline->op1.var)] = ssa_vars_count;
+ ssa_vars_count++;
+ }
+ break;
+ case ZEND_UNSET_DIM:
+ case ZEND_UNSET_OBJ:
+ case ZEND_FETCH_DIM_W:
+ case ZEND_FETCH_DIM_RW:
+ case ZEND_FETCH_DIM_FUNC_ARG:
+ case ZEND_FETCH_DIM_UNSET:
+ case ZEND_FETCH_OBJ_W:
+ case ZEND_FETCH_OBJ_RW:
+ case ZEND_FETCH_OBJ_FUNC_ARG:
+ case ZEND_FETCH_OBJ_UNSET:
+ if (opline->op1_type == IS_CV) {
+ ssa_ops[k].op1_def = ssa_vars_count;
+ var[EX_VAR_TO_NUM(opline->op1.var)] = ssa_vars_count;
+ ssa_vars_count++;
+ //NEW_SSA_VAR(opline->op1.var)
+ }
+ break;
+ case ZEND_BIND_LEXICAL:
+ if (opline->extended_value || (build_flags & ZEND_SSA_RC_INFERENCE)) {
+ ssa_ops[k].op2_def = ssa_vars_count;
+ var[EX_VAR_TO_NUM(opline->op2.var)] = ssa_vars_count;
+ ssa_vars_count++;
+ }
+ break;
+ case ZEND_YIELD:
+ if (opline->op1_type == IS_CV
+ && ((op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE)
+ || (build_flags & ZEND_SSA_RC_INFERENCE))) {
+ ssa_ops[k].op1_def = ssa_vars_count;
+ var[EX_VAR_TO_NUM(opline->op1.var)] = ssa_vars_count;
+ ssa_vars_count++;
+ }
+ break;
+ case ZEND_VERIFY_RETURN_TYPE:
+ if (opline->op1_type & (IS_TMP_VAR|IS_VAR|IS_CV)) {
+ ssa_ops[k].op1_def = ssa_vars_count;
+ var[EX_VAR_TO_NUM(opline->op1.var)] = ssa_vars_count;
+ ssa_vars_count++;
+ //NEW_SSA_VAR(opline->op1.var)
+ }
+ break;
+ default:
+ break;
+ }
+ if (opline->result_type == IS_CV) {
+ ssa_ops[k].result_def = ssa_vars_count;
+ var[EX_VAR_TO_NUM(opline->result.var)] = ssa_vars_count;
+ ssa_vars_count++;
+ //NEW_SSA_VAR(opline->result.var)
+ } else if (opline->result_type & (IS_VAR|IS_TMP_VAR)) {
+ ssa_ops[k].result_def = ssa_vars_count;
+ var[EX_VAR_TO_NUM(opline->result.var)] = ssa_vars_count;
+ ssa_vars_count++;
+ //NEW_SSA_VAR(op_array->last_var + opline->result.var)
+ }
+ }
+ }
+
+ for (i = 0; i < 2; i++) {
+ int succ = blocks[n].successors[i];
+ if (succ >= 0) {
+ zend_ssa_phi *p;
+ for (p = ssa_blocks[succ].phis; p; p = p->next) {
+ if (p->pi == n) {
+ /* e-SSA Pi */
+ if (p->has_range_constraint) {
+ if (p->constraint.range.min_var >= 0) {
+ p->constraint.range.min_ssa_var = var[p->constraint.range.min_var];
+ }
+ if (p->constraint.range.max_var >= 0) {
+ p->constraint.range.max_ssa_var = var[p->constraint.range.max_var];
+ }
+ }
+ for (j = 0; j < blocks[succ].predecessors_count; j++) {
+ p->sources[j] = var[p->var];
+ }
+ if (p->ssa_var < 0) {
+ p->ssa_var = ssa_vars_count;
+ ssa_vars_count++;
+ }
+ } else if (p->pi < 0) {
+ /* Normal Phi */
+ for (j = 0; j < blocks[succ].predecessors_count; j++)
+ if (ssa->cfg.predecessors[blocks[succ].predecessor_offset + j] == n) {
+ break;
+ }
+ ZEND_ASSERT(j < blocks[succ].predecessors_count);
+ p->sources[j] = var[p->var];
+ }
+ }
+ for (p = ssa_blocks[succ].phis; p && (p->pi >= 0); p = p->next) {
+ if (p->pi == n) {
+ zend_ssa_phi *q = p->next;
+ while (q) {
+ if (q->pi < 0 && q->var == p->var) {
+ for (j = 0; j < blocks[succ].predecessors_count; j++) {
+ if (ssa->cfg.predecessors[blocks[succ].predecessor_offset + j] == n) {
+ break;
+ }
+ }
+ ZEND_ASSERT(j < blocks[succ].predecessors_count);
+ q->sources[j] = p->ssa_var;
+ }
+ q = q->next;
+ }
+ }
+ }
+ }
+ }
+
+ ssa->vars_count = ssa_vars_count;
+
+ j = blocks[n].children;
+ while (j >= 0) {
+ // FIXME: Tail call optimization?
+ if (zend_ssa_rename(op_array, build_flags, ssa, var, j) != SUCCESS)
+ return FAILURE;
+ j = blocks[j].next_child;
+ }
+
+ if (tmp) {
+ free_alloca(tmp, use_heap);
+ }
+
+ return SUCCESS;
+}
+/* }}} */
+
+int zend_build_ssa(zend_arena **arena, const zend_script *script, const zend_op_array *op_array, uint32_t build_flags, zend_ssa *ssa, uint32_t *func_flags) /* {{{ */
+{
+ zend_basic_block *blocks = ssa->cfg.blocks;
+ zend_ssa_block *ssa_blocks;
+ int blocks_count = ssa->cfg.blocks_count;
+ uint32_t set_size;
+ zend_bitset def, in, phi;
+ int *var = NULL;
+ int i, j, k, changed;
+ zend_dfg dfg;
+ ALLOCA_FLAG(dfg_use_heap)
+ ALLOCA_FLAG(var_use_heap)
+
+ if ((blocks_count * (op_array->last_var + op_array->T)) > 4 * 1024 * 1024) {
+ /* Don't buld SSA for very big functions */
+ return FAILURE;
+ }
+
+ ssa->rt_constants = (build_flags & ZEND_RT_CONSTANTS);
+ ssa_blocks = zend_arena_calloc(arena, blocks_count, sizeof(zend_ssa_block));
+ if (!ssa_blocks) {
+ return FAILURE;
+ }
+ ssa->blocks = ssa_blocks;
+
+ /* Compute Variable Liveness */
+ dfg.vars = op_array->last_var + op_array->T;
+ dfg.size = set_size = zend_bitset_len(dfg.vars);
+ dfg.tmp = do_alloca((set_size * sizeof(zend_ulong)) * (blocks_count * 4 + 1), dfg_use_heap);
+ memset(dfg.tmp, 0, (set_size * sizeof(zend_ulong)) * (blocks_count * 4 + 1));
+ dfg.def = dfg.tmp + set_size;
+ dfg.use = dfg.def + set_size * blocks_count;
+ dfg.in = dfg.use + set_size * blocks_count;
+ dfg.out = dfg.in + set_size * blocks_count;
+
+ if (zend_build_dfg(op_array, &ssa->cfg, &dfg, build_flags) != SUCCESS) {
+ free_alloca(dfg.tmp, dfg_use_heap);
+ return FAILURE;
+ }
+
+ if (build_flags & ZEND_SSA_DEBUG_LIVENESS) {
+ zend_dump_dfg(op_array, &ssa->cfg, &dfg);
+ }
+
+ def = dfg.def;
+ in = dfg.in;
+
+ /* Reuse the "use" set, as we no longer need it */
+ phi = dfg.use;
+ zend_bitset_clear(phi, set_size * blocks_count);
+
+ /* Place e-SSA pis. This will add additional "def" points, so it must
+ * happen before def propagation. */
+ place_essa_pis(arena, script, op_array, build_flags, ssa, &dfg);
+
+ /* SSA construction, Step 1: Propagate "def" sets in merge points */
+ do {
+ changed = 0;
+ for (j = 0; j < blocks_count; j++) {
+ zend_bitset def_j = def + j * set_size, phi_j = phi + j * set_size;
+ if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) {
+ continue;
+ }
+ if (blocks[j].predecessors_count > 1) {
+ if (blocks[j].flags & ZEND_BB_IRREDUCIBLE_LOOP) {
+ /* Prevent any values from flowing into irreducible loops by
+ replacing all incoming values with explicit phis. The
+ register allocator depends on this property. */
+ zend_bitset_union(phi_j, in + (j * set_size), set_size);
+ } else {
+ for (k = 0; k < blocks[j].predecessors_count; k++) {
+ i = ssa->cfg.predecessors[blocks[j].predecessor_offset + k];
+ while (i != -1 && i != blocks[j].idom) {
+ zend_bitset_union_with_intersection(
+ phi_j, phi_j, def + (i * set_size), in + (j * set_size), set_size);
+ i = blocks[i].idom;
+ }
+ }
+ }
+ if (!zend_bitset_subset(phi_j, def_j, set_size)) {
+ zend_bitset_union(def_j, phi_j, set_size);
+ changed = 1;
+ }
+ }
+ }
+ } while (changed);
+
+ /* SSA construction, Step 2: Phi placement based on Dominance Frontiers */
+ var = do_alloca(sizeof(int) * (op_array->last_var + op_array->T), var_use_heap);
+ if (!var) {
+ free_alloca(dfg.tmp, dfg_use_heap);
+ return FAILURE;
+ }
+
+ for (j = 0; j < blocks_count; j++) {
+ if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) {
+ continue;
+ }
+ if (!zend_bitset_empty(phi + j * set_size, set_size)) {
+ ZEND_BITSET_REVERSE_FOREACH(phi + j * set_size, set_size, i) {
+ zend_ssa_phi *phi = zend_arena_calloc(arena, 1,
+ ZEND_MM_ALIGNED_SIZE(sizeof(zend_ssa_phi)) +
+ ZEND_MM_ALIGNED_SIZE(sizeof(int) * blocks[j].predecessors_count) +
+ sizeof(void*) * blocks[j].predecessors_count);
+
+ phi->sources = (int*)(((char*)phi) + ZEND_MM_ALIGNED_SIZE(sizeof(zend_ssa_phi)));
+ memset(phi->sources, 0xff, sizeof(int) * blocks[j].predecessors_count);
+ phi->use_chains = (zend_ssa_phi**)(((char*)phi->sources) + ZEND_MM_ALIGNED_SIZE(sizeof(int) * ssa->cfg.blocks[j].predecessors_count));
+
+ phi->pi = -1;
+ phi->var = i;
+ phi->ssa_var = -1;
+
+ /* Place phis after pis */
+ {
+ zend_ssa_phi **pp = &ssa_blocks[j].phis;
+ while (*pp) {
+ if ((*pp)->pi < 0) {
+ break;
+ }
+ pp = &(*pp)->next;
+ }
+ phi->next = *pp;
+ *pp = phi;
+ }
+ } ZEND_BITSET_FOREACH_END();
+ }
+ }
+
+ if (build_flags & ZEND_SSA_DEBUG_PHI_PLACEMENT) {
+ zend_dump_phi_placement(op_array, ssa);
+ }
+
+ /* SSA construction, Step 3: Renaming */
+ ssa->ops = zend_arena_calloc(arena, op_array->last, sizeof(zend_ssa_op));
+ memset(ssa->ops, 0xff, op_array->last * sizeof(zend_ssa_op));
+ memset(var + op_array->last_var, 0xff, op_array->T * sizeof(int));
+ /* Create uninitialized SSA variables for each CV */
+ for (j = 0; j < op_array->last_var; j++) {
+ var[j] = j;
+ }
+ ssa->vars_count = op_array->last_var;
+ if (zend_ssa_rename(op_array, build_flags, ssa, var, 0) != SUCCESS) {
+ free_alloca(var, var_use_heap);
+ free_alloca(dfg.tmp, dfg_use_heap);
+ return FAILURE;
+ }
+
+ free_alloca(var, var_use_heap);
+ free_alloca(dfg.tmp, dfg_use_heap);
+
+ return SUCCESS;
+}
+/* }}} */
+
+int zend_ssa_compute_use_def_chains(zend_arena **arena, const zend_op_array *op_array, zend_ssa *ssa) /* {{{ */
+{
+ zend_ssa_var *ssa_vars;
+ int i;
+
+ if (!ssa->vars) {
+ ssa->vars = zend_arena_calloc(arena, ssa->vars_count, sizeof(zend_ssa_var));
+ }
+ ssa_vars = ssa->vars;
+
+ for (i = 0; i < op_array->last_var; i++) {
+ ssa_vars[i].var = i;
+ ssa_vars[i].scc = -1;
+ ssa_vars[i].definition = -1;
+ ssa_vars[i].use_chain = -1;
+ }
+ for (i = op_array->last_var; i < ssa->vars_count; i++) {
+ ssa_vars[i].var = -1;
+ ssa_vars[i].scc = -1;
+ ssa_vars[i].definition = -1;
+ ssa_vars[i].use_chain = -1;
+ }
+
+ for (i = op_array->last - 1; i >= 0; i--) {
+ zend_ssa_op *op = ssa->ops + i;
+
+ if (op->op1_use >= 0) {
+ op->op1_use_chain = ssa_vars[op->op1_use].use_chain;
+ ssa_vars[op->op1_use].use_chain = i;
+ }
+ if (op->op2_use >= 0 && op->op2_use != op->op1_use) {
+ op->op2_use_chain = ssa_vars[op->op2_use].use_chain;
+ ssa_vars[op->op2_use].use_chain = i;
+ }
+ if (op->result_use >= 0) {
+ op->res_use_chain = ssa_vars[op->result_use].use_chain;
+ ssa_vars[op->result_use].use_chain = i;
+ }
+ if (op->op1_def >= 0) {
+ ssa_vars[op->op1_def].var = EX_VAR_TO_NUM(op_array->opcodes[i].op1.var);
+ ssa_vars[op->op1_def].definition = i;
+ }
+ if (op->op2_def >= 0) {
+ ssa_vars[op->op2_def].var = EX_VAR_TO_NUM(op_array->opcodes[i].op2.var);
+ ssa_vars[op->op2_def].definition = i;
+ }
+ if (op->result_def >= 0) {
+ ssa_vars[op->result_def].var = EX_VAR_TO_NUM(op_array->opcodes[i].result.var);
+ ssa_vars[op->result_def].definition = i;
+ }
+ }
+
+ for (i = 0; i < ssa->cfg.blocks_count; i++) {
+ zend_ssa_phi *phi = ssa->blocks[i].phis;
+ while (phi) {
+ phi->block = i;
+ ssa_vars[phi->ssa_var].var = phi->var;
+ ssa_vars[phi->ssa_var].definition_phi = phi;
+ if (phi->pi >= 0) {
+ if (phi->sources[0] >= 0) {
+ zend_ssa_phi *p = ssa_vars[phi->sources[0]].phi_use_chain;
+ while (p && p != phi) {
+ p = zend_ssa_next_use_phi(ssa, phi->sources[0], p);
+ }
+ if (!p) {
+ phi->use_chains[0] = ssa_vars[phi->sources[0]].phi_use_chain;
+ ssa_vars[phi->sources[0]].phi_use_chain = phi;
+ }
+ }
+ if (phi->has_range_constraint) {
+ /* min and max variables can't be used together */
+ zend_ssa_range_constraint *constraint = &phi->constraint.range;
+ if (constraint->min_ssa_var >= 0) {
+ phi->sym_use_chain = ssa_vars[constraint->min_ssa_var].sym_use_chain;
+ ssa_vars[constraint->min_ssa_var].sym_use_chain = phi;
+ } else if (constraint->max_ssa_var >= 0) {
+ phi->sym_use_chain = ssa_vars[constraint->max_ssa_var].sym_use_chain;
+ ssa_vars[constraint->max_ssa_var].sym_use_chain = phi;
+ }
+ }
+ } else {
+ int j;
+
+ for (j = 0; j < ssa->cfg.blocks[i].predecessors_count; j++) {
+ if (phi->sources[j] >= 0) {
+ zend_ssa_phi *p = ssa_vars[phi->sources[j]].phi_use_chain;
+ while (p && p != phi) {
+ p = zend_ssa_next_use_phi(ssa, phi->sources[j], p);
+ }
+ if (!p) {
+ phi->use_chains[j] = ssa_vars[phi->sources[j]].phi_use_chain;
+ ssa_vars[phi->sources[j]].phi_use_chain = phi;
+ }
+ }
+ }
+ }
+ phi = phi->next;
+ }
+ }
+
+ return SUCCESS;
+}
+/* }}} */
+
+int zend_ssa_unlink_use_chain(zend_ssa *ssa, int op, int var) /* {{{ */
+{
+ if (ssa->vars[var].use_chain == op) {
+ ssa->vars[var].use_chain = zend_ssa_next_use(ssa->ops, var, op);
+ return 1;
+ } else {
+ int use = ssa->vars[var].use_chain;
+
+ while (use >= 0) {
+ if (ssa->ops[use].result_use == var) {
+ if (ssa->ops[use].res_use_chain == op) {
+ ssa->ops[use].res_use_chain = zend_ssa_next_use(ssa->ops, var, op);
+ return 1;
+ } else {
+ use = ssa->ops[use].res_use_chain;
+ }
+ } else if (ssa->ops[use].op1_use == var) {
+ if (ssa->ops[use].op1_use_chain == op) {
+ ssa->ops[use].op1_use_chain = zend_ssa_next_use(ssa->ops, var, op);
+ return 1;
+ } else {
+ use = ssa->ops[use].op1_use_chain;
+ }
+ } else if (ssa->ops[use].op2_use == var) {
+ if (ssa->ops[use].op2_use_chain == op) {
+ ssa->ops[use].op2_use_chain = zend_ssa_next_use(ssa->ops, var, op);
+ return 1;
+ } else {
+ use = ssa->ops[use].op2_use_chain;
+ }
+ } else {
+ break;
+ }
+ }
+ /* something wrong */
+ ZEND_ASSERT(0);
+ return 0;
+ }
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * indent-tabs-mode: t
+ * End:
+ */
diff --git a/ext/opcache/Optimizer/zend_ssa.h b/ext/opcache/Optimizer/zend_ssa.h
new file mode 100644
index 0000000000..5e03f8ba69
--- /dev/null
+++ b/ext/opcache/Optimizer/zend_ssa.h
@@ -0,0 +1,168 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine, SSA - Static Single Assignment Form |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1998-2017 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Dmitry Stogov <dmitry@zend.com> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef ZEND_SSA_H
+#define ZEND_SSA_H
+
+#include "zend_optimizer.h"
+#include "zend_cfg.h"
+
+typedef struct _zend_ssa_range {
+ zend_long min;
+ zend_long max;
+ zend_bool underflow;
+ zend_bool overflow;
+} zend_ssa_range;
+
+typedef enum _zend_ssa_negative_lat {
+ NEG_NONE = 0,
+ NEG_INIT = 1,
+ NEG_INVARIANT = 2,
+ NEG_USE_LT = 3,
+ NEG_USE_GT = 4,
+ NEG_UNKNOWN = 5
+} zend_ssa_negative_lat;
+
+/* Special kind of SSA Phi function used in eSSA */
+typedef struct _zend_ssa_range_constraint {
+ zend_ssa_range range; /* simple range constraint */
+ int min_var;
+ int max_var;
+ int min_ssa_var; /* ((min_var>0) ? MIN(ssa_var) : 0) + range.min */
+ int max_ssa_var; /* ((max_var>0) ? MAX(ssa_var) : 0) + range.max */
+ zend_ssa_negative_lat negative;
+} zend_ssa_range_constraint;
+
+typedef struct _zend_ssa_type_constraint {
+ uint32_t type_mask; /* Type mask to intersect with */
+ zend_class_entry *ce; /* Class entry for instanceof constraints */
+} zend_ssa_type_constraint;
+
+typedef union _zend_ssa_pi_constraint {
+ zend_ssa_range_constraint range;
+ zend_ssa_type_constraint type;
+} zend_ssa_pi_constraint;
+
+/* SSA Phi - ssa_var = Phi(source0, source1, ...sourceN) */
+typedef struct _zend_ssa_phi zend_ssa_phi;
+struct _zend_ssa_phi {
+ zend_ssa_phi *next; /* next Phi in the same BB */
+ int pi; /* if >= 0 this is actually a e-SSA Pi */
+ zend_ssa_pi_constraint constraint; /* e-SSA Pi constraint */
+ int var; /* Original CV, VAR or TMP variable index */
+ int ssa_var; /* SSA variable index */
+ int block; /* current BB index */
+ int visited : 1; /* flag to avoid recursive processing */
+ int has_range_constraint : 1;
+ zend_ssa_phi **use_chains;
+ zend_ssa_phi *sym_use_chain;
+ int *sources; /* Array of SSA IDs that produce this var.
+ As many as this block has
+ predecessors. */
+};
+
+typedef struct _zend_ssa_block {
+ zend_ssa_phi *phis;
+} zend_ssa_block;
+
+typedef struct _zend_ssa_op {
+ int op1_use;
+ int op2_use;
+ int result_use;
+ int op1_def;
+ int op2_def;
+ int result_def;
+ int op1_use_chain;
+ int op2_use_chain;
+ int res_use_chain;
+} zend_ssa_op;
+
+typedef struct _zend_ssa_var {
+ int var; /* original var number; op.var for CVs and following numbers for VARs and TMP_VARs */
+ int scc; /* strongly connected component */
+ int definition; /* opcode that defines this value */
+ zend_ssa_phi *definition_phi; /* phi that defines this value */
+ int use_chain; /* uses of this value, linked through opN_use_chain */
+ zend_ssa_phi *phi_use_chain; /* uses of this value in Phi, linked through use_chain */
+ zend_ssa_phi *sym_use_chain; /* uses of this value in Pi constaints */
+ unsigned int no_val : 1; /* value doesn't mater (used as op1 in ZEND_ASSIGN) */
+ unsigned int scc_entry : 1;
+} zend_ssa_var;
+
+typedef struct _zend_ssa_var_info {
+ uint32_t type; /* inferred type (see zend_inference.h) */
+ zend_ssa_range range;
+ zend_class_entry *ce;
+ unsigned int has_range : 1;
+ unsigned int is_instanceof : 1; /* 0 - class == "ce", 1 - may be child of "ce" */
+ unsigned int recursive : 1;
+ unsigned int use_as_double : 1;
+} zend_ssa_var_info;
+
+typedef struct _zend_ssa {
+ zend_cfg cfg; /* control flow graph */
+ int rt_constants; /* run-time or compile-time */
+ int vars_count; /* number of SSA variables */
+ zend_ssa_block *blocks; /* array of SSA blocks */
+ zend_ssa_op *ops; /* array of SSA instructions */
+ zend_ssa_var *vars; /* use/def chain of SSA variables */
+ int sccs; /* number of SCCs */
+ zend_ssa_var_info *var_info;
+} zend_ssa;
+
+BEGIN_EXTERN_C()
+
+int zend_build_ssa(zend_arena **arena, const zend_script *script, const zend_op_array *op_array, uint32_t build_flags, zend_ssa *ssa, uint32_t *func_flags);
+int zend_ssa_compute_use_def_chains(zend_arena **arena, const zend_op_array *op_array, zend_ssa *ssa);
+int zend_ssa_unlink_use_chain(zend_ssa *ssa, int op, int var);
+
+END_EXTERN_C()
+
+static zend_always_inline int zend_ssa_next_use(const zend_ssa_op *ssa_op, int var, int use)
+{
+ ssa_op += use;
+ if (ssa_op->result_use == var) {
+ return ssa_op->res_use_chain;
+ }
+ return (ssa_op->op1_use == var) ? ssa_op->op1_use_chain : ssa_op->op2_use_chain;
+}
+
+static zend_always_inline zend_ssa_phi* zend_ssa_next_use_phi(const zend_ssa *ssa, int var, const zend_ssa_phi *p)
+{
+ if (p->pi >= 0) {
+ return p->use_chains[0];
+ } else {
+ int j;
+ for (j = 0; j < ssa->cfg.blocks[p->block].predecessors_count; j++) {
+ if (p->sources[j] == var) {
+ return p->use_chains[j];
+ }
+ }
+ }
+ return NULL;
+}
+
+#endif /* ZEND_SSA_H */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * indent-tabs-mode: t
+ * End:
+ */
diff --git a/ext/opcache/Optimizer/zend_worklist.h b/ext/opcache/Optimizer/zend_worklist.h
new file mode 100644
index 0000000000..73c0bca854
--- /dev/null
+++ b/ext/opcache/Optimizer/zend_worklist.h
@@ -0,0 +1,137 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1998-2017 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Andy Wingo <wingo@igalia.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id:$ */
+
+#ifndef _ZEND_WORKLIST_H_
+#define _ZEND_WORKLIST_H_
+
+#include "zend_arena.h"
+#include "zend_bitset.h"
+
+typedef struct _zend_worklist_stack {
+ int *buf;
+ int len;
+ int capacity;
+} zend_worklist_stack;
+
+#define ZEND_WORKLIST_STACK_ALLOCA(s, _len, use_heap) do { \
+ (s)->buf = (int*)do_alloca(sizeof(int) * _len, use_heap); \
+ (s)->len = 0; \
+ (s)->capacity = _len; \
+ } while (0)
+
+#define ZEND_WORKLIST_STACK_FREE_ALLOCA(s, use_heap) \
+ free_alloca((s)->buf, use_heap)
+
+static inline int zend_worklist_stack_prepare(zend_arena **arena, zend_worklist_stack *stack, int len)
+{
+ ZEND_ASSERT(len >= 0);
+
+ stack->buf = (int*)zend_arena_calloc(arena, sizeof(*stack->buf), len);
+ if (!stack->buf) {
+ return FAILURE;
+ }
+ stack->len = 0;
+ stack->capacity = len;
+
+ return SUCCESS;
+}
+
+static inline void zend_worklist_stack_push(zend_worklist_stack *stack, int i)
+{
+ ZEND_ASSERT(stack->len < stack->capacity);
+ stack->buf[stack->len++] = i;
+}
+
+static inline int zend_worklist_stack_peek(zend_worklist_stack *stack)
+{
+ ZEND_ASSERT(stack->len);
+ return stack->buf[stack->len - 1];
+}
+
+static inline int zend_worklist_stack_pop(zend_worklist_stack *stack)
+{
+ ZEND_ASSERT(stack->len);
+ return stack->buf[--stack->len];
+}
+
+typedef struct _zend_worklist {
+ zend_bitset visited;
+ zend_worklist_stack stack;
+} zend_worklist;
+
+#define ZEND_WORKLIST_ALLOCA(w, _len, use_heap) do { \
+ (w)->stack.buf = (int*)do_alloca(ZEND_MM_ALIGNED_SIZE(sizeof(int) * _len) + sizeof(zend_ulong) * zend_bitset_len(_len), use_heap); \
+ (w)->stack.len = 0; \
+ (w)->stack.capacity = _len; \
+ (w)->visited = (zend_bitset)((char*)(w)->stack.buf + ZEND_MM_ALIGNED_SIZE(sizeof(int) * _len)); \
+ memset((w)->visited, 0, sizeof(zend_ulong) * zend_bitset_len(_len)); \
+ } while (0)
+
+#define ZEND_WORKLIST_FREE_ALLOCA(w, use_heap) \
+ free_alloca((w)->stack.buf, use_heap)
+
+static inline int zend_worklist_prepare(zend_arena **arena, zend_worklist *worklist, int len)
+{
+ ZEND_ASSERT(len >= 0);
+ worklist->visited = (zend_bitset)zend_arena_calloc(arena, sizeof(zend_ulong), zend_bitset_len(len));
+ if (!worklist->visited) {
+ return FAILURE;
+ }
+ return zend_worklist_stack_prepare(arena, &worklist->stack, len);
+}
+
+static inline int zend_worklist_len(zend_worklist *worklist)
+{
+ return worklist->stack.len;
+}
+
+static inline int zend_worklist_push(zend_worklist *worklist, int i)
+{
+ ZEND_ASSERT(i >= 0 && i < worklist->stack.capacity);
+
+ if (zend_bitset_in(worklist->visited, i)) {
+ return 0;
+ }
+
+ zend_bitset_incl(worklist->visited, i);
+ zend_worklist_stack_push(&worklist->stack, i);
+ return 1;
+}
+
+static inline int zend_worklist_peek(zend_worklist *worklist)
+{
+ return zend_worklist_stack_peek(&worklist->stack);
+}
+
+static inline int zend_worklist_pop(zend_worklist *worklist)
+{
+ /* Does not clear visited flag */
+ return zend_worklist_stack_pop(&worklist->stack);
+}
+
+#endif /* _ZEND_WORKLIST_H_ */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * indent-tabs-mode: t
+ * End:
+ */
diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c
index 4c57e5c91e..c77b2a7f72 100644
--- a/ext/opcache/ZendAccelerator.c
+++ b/ext/opcache/ZendAccelerator.c
@@ -525,6 +525,9 @@ static void accel_use_shm_interned_strings(void)
s[1] = 0;
CG(one_char_string)[j] = accel_new_interned_string(zend_string_init(s, 1, 0));
}
+ for (j = 0; j < CG(known_strings_count); j++) {
+ CG(known_strings)[j] = accel_new_interned_string(CG(known_strings)[j]);
+ }
/* function table hash keys */
for (idx = 0; idx < CG(function_table)->nNumUsed; idx++) {
@@ -697,7 +700,7 @@ static inline int accel_is_inactive(void)
if (ZCG(accel_directives).force_restart_timeout
&& ZCSG(force_restart_time)
&& time(NULL) >= ZCSG(force_restart_time)) {
- zend_accel_error(ACCEL_LOG_WARNING, "Forced restart at %d (after %d seconds), locked by %d", time(NULL), ZCG(accel_directives).force_restart_timeout, mem_usage_check.l_pid);
+ zend_accel_error(ACCEL_LOG_WARNING, "Forced restart at %ld (after " ZEND_LONG_FMT " seconds), locked by %d", time(NULL), ZCG(accel_directives).force_restart_timeout, mem_usage_check.l_pid);
kill_all_lockers(&mem_usage_check);
return FAILURE; /* next request should be able to restart it */
@@ -896,17 +899,17 @@ static inline int do_validate_timestamps(zend_persistent_script *persistent_scri
* See bug #15140
*/
if (file_handle->opened_path) {
- if (persistent_script->full_path != file_handle->opened_path &&
- (ZSTR_LEN(persistent_script->full_path) != ZSTR_LEN(file_handle->opened_path) ||
- memcmp(ZSTR_VAL(persistent_script->full_path), ZSTR_VAL(file_handle->opened_path), ZSTR_LEN(file_handle->opened_path)) != 0)) {
+ if (persistent_script->script.filename != file_handle->opened_path &&
+ (ZSTR_LEN(persistent_script->script.filename) != ZSTR_LEN(file_handle->opened_path) ||
+ memcmp(ZSTR_VAL(persistent_script->script.filename), ZSTR_VAL(file_handle->opened_path), ZSTR_LEN(file_handle->opened_path)) != 0)) {
return FAILURE;
}
} else {
full_path_ptr = accelerator_orig_zend_resolve_path(file_handle->filename, strlen(file_handle->filename));
if (full_path_ptr &&
- persistent_script->full_path != full_path_ptr &&
- (ZSTR_LEN(persistent_script->full_path) != ZSTR_LEN(full_path_ptr) ||
- memcmp(ZSTR_VAL(persistent_script->full_path), ZSTR_VAL(full_path_ptr), ZSTR_LEN(full_path_ptr)) != 0)) {
+ persistent_script->script.filename != full_path_ptr &&
+ (ZSTR_LEN(persistent_script->script.filename) != ZSTR_LEN(full_path_ptr) ||
+ memcmp(ZSTR_VAL(persistent_script->script.filename), ZSTR_VAL(full_path_ptr), ZSTR_LEN(full_path_ptr)) != 0)) {
zend_string_release(full_path_ptr);
return FAILURE;
}
@@ -934,8 +937,8 @@ static inline int do_validate_timestamps(zend_persistent_script *persistent_scri
}
ps_handle.type = ZEND_HANDLE_FILENAME;
- ps_handle.filename = ZSTR_VAL(persistent_script->full_path);
- ps_handle.opened_path = persistent_script->full_path;
+ ps_handle.filename = ZSTR_VAL(persistent_script->script.filename);
+ ps_handle.opened_path = persistent_script->script.filename;
if (zend_get_file_handle_timestamp(&ps_handle, NULL) == persistent_script->timestamp) {
return SUCCESS;
@@ -1014,6 +1017,7 @@ char *accel_make_persistent_key(const char *path, int path_length, int *key_len)
zend_string *str = accel_find_interned_string(cwd_str);
if (!str) {
+ HANDLE_BLOCK_INTERRUPTIONS();
SHM_UNPROTECT();
zend_shared_alloc_lock();
str = accel_new_interned_string(zend_string_copy(cwd_str));
@@ -1023,6 +1027,7 @@ char *accel_make_persistent_key(const char *path, int path_length, int *key_len)
}
zend_shared_alloc_unlock();
SHM_PROTECT();
+ HANDLE_UNBLOCK_INTERRUPTIONS();
}
if (str) {
char buf[32];
@@ -1054,6 +1059,7 @@ char *accel_make_persistent_key(const char *path, int path_length, int *key_len)
zend_string *str = accel_find_interned_string(ZCG(include_path));
if (!str) {
+ HANDLE_BLOCK_INTERRUPTIONS();
SHM_UNPROTECT();
zend_shared_alloc_lock();
str = accel_new_interned_string(zend_string_copy(ZCG(include_path)));
@@ -1062,6 +1068,7 @@ char *accel_make_persistent_key(const char *path, int path_length, int *key_len)
}
zend_shared_alloc_unlock();
SHM_PROTECT();
+ HANDLE_UNBLOCK_INTERRUPTIONS();
}
if (str) {
char buf[32];
@@ -1159,6 +1166,7 @@ int zend_accel_invalidate(const char *filename, int filename_len, zend_bool forc
if (force ||
!ZCG(accel_directives).validate_timestamps ||
do_validate_timestamps(persistent_script, &file_handle) == FAILURE) {
+ HANDLE_BLOCK_INTERRUPTIONS();
SHM_UNPROTECT();
zend_shared_alloc_lock();
if (!persistent_script->corrupted) {
@@ -1173,6 +1181,7 @@ int zend_accel_invalidate(const char *filename, int filename_len, zend_bool forc
}
zend_shared_alloc_unlock();
SHM_PROTECT();
+ HANDLE_UNBLOCK_INTERRUPTIONS();
}
}
@@ -1214,7 +1223,7 @@ static zend_persistent_script *cache_script_in_file_cache(zend_persistent_script
return new_persistent_script;
}
- if (!zend_accel_script_optimize(new_persistent_script)) {
+ if (!zend_optimize_script(&new_persistent_script->script, ZCG(accel_directives).optimization_level, ZCG(accel_directives).opt_debug_level)) {
return new_persistent_script;
}
@@ -1238,19 +1247,19 @@ static zend_persistent_script *cache_script_in_file_cache(zend_persistent_script
zend_shared_alloc_destroy_xlat_table();
new_persistent_script->is_phar =
- new_persistent_script->full_path &&
- strstr(ZSTR_VAL(new_persistent_script->full_path), ".phar") &&
- !strstr(ZSTR_VAL(new_persistent_script->full_path), "://");
+ new_persistent_script->script.filename &&
+ strstr(ZSTR_VAL(new_persistent_script->script.filename), ".phar") &&
+ !strstr(ZSTR_VAL(new_persistent_script->script.filename), "://");
/* Consistency check */
if ((char*)new_persistent_script->mem + new_persistent_script->size != (char*)ZCG(mem)) {
zend_accel_error(
((char*)new_persistent_script->mem + new_persistent_script->size < (char*)ZCG(mem)) ? ACCEL_LOG_ERROR : ACCEL_LOG_WARNING,
- "Internal error: wrong size calculation: %s start=0x%08x, end=0x%08x, real=0x%08x\n",
- ZSTR_VAL(new_persistent_script->full_path),
- new_persistent_script->mem,
- (char *)new_persistent_script->mem + new_persistent_script->size,
- ZCG(mem));
+ "Internal error: wrong size calculation: %s start=" ZEND_ADDR_FMT ", end=" ZEND_ADDR_FMT ", real=" ZEND_ADDR_FMT "\n",
+ ZSTR_VAL(new_persistent_script->script.filename),
+ (size_t)new_persistent_script->mem,
+ (size_t)((char *)new_persistent_script->mem + new_persistent_script->size),
+ (size_t)ZCG(mem));
}
new_persistent_script->dynamic_members.checksum = zend_accel_script_checksum(new_persistent_script);
@@ -1272,7 +1281,7 @@ static zend_persistent_script *cache_script_in_shared_memory(zend_persistent_scr
return new_persistent_script;
}
- if (!zend_accel_script_optimize(new_persistent_script)) {
+ if (!zend_optimize_script(&new_persistent_script->script, ZCG(accel_directives).optimization_level, ZCG(accel_directives).opt_debug_level)) {
return new_persistent_script;
}
@@ -1290,7 +1299,7 @@ static zend_persistent_script *cache_script_in_shared_memory(zend_persistent_scr
/* Check if we still need to put the file into the cache (may be it was
* already stored by another process. This final check is done under
* exclusive lock) */
- bucket = zend_accel_hash_find_entry(&ZCSG(hash), new_persistent_script->full_path);
+ bucket = zend_accel_hash_find_entry(&ZCSG(hash), new_persistent_script->script.filename);
if (bucket) {
zend_persistent_script *existing_persistent_script = (zend_persistent_script *)bucket->data;
@@ -1332,32 +1341,32 @@ static zend_persistent_script *cache_script_in_shared_memory(zend_persistent_scr
zend_shared_alloc_destroy_xlat_table();
new_persistent_script->is_phar =
- new_persistent_script->full_path &&
- strstr(ZSTR_VAL(new_persistent_script->full_path), ".phar") &&
- !strstr(ZSTR_VAL(new_persistent_script->full_path), "://");
+ new_persistent_script->script.filename &&
+ strstr(ZSTR_VAL(new_persistent_script->script.filename), ".phar") &&
+ !strstr(ZSTR_VAL(new_persistent_script->script.filename), "://");
/* Consistency check */
if ((char*)new_persistent_script->mem + new_persistent_script->size != (char*)ZCG(mem)) {
zend_accel_error(
((char*)new_persistent_script->mem + new_persistent_script->size < (char*)ZCG(mem)) ? ACCEL_LOG_ERROR : ACCEL_LOG_WARNING,
- "Internal error: wrong size calculation: %s start=0x%08x, end=0x%08x, real=0x%08x\n",
- ZSTR_VAL(new_persistent_script->full_path),
- new_persistent_script->mem,
- (char *)new_persistent_script->mem + new_persistent_script->size,
- ZCG(mem));
+ "Internal error: wrong size calculation: %s start=" ZEND_ADDR_FMT ", end=" ZEND_ADDR_FMT ", real=" ZEND_ADDR_FMT "\n",
+ ZSTR_VAL(new_persistent_script->script.filename),
+ (size_t)new_persistent_script->mem,
+ (size_t)((char *)new_persistent_script->mem + new_persistent_script->size),
+ (size_t)ZCG(mem));
}
new_persistent_script->dynamic_members.checksum = zend_accel_script_checksum(new_persistent_script);
/* store script structure in the hash table */
- bucket = zend_accel_hash_update(&ZCSG(hash), ZSTR_VAL(new_persistent_script->full_path), ZSTR_LEN(new_persistent_script->full_path), 0, new_persistent_script);
+ bucket = zend_accel_hash_update(&ZCSG(hash), ZSTR_VAL(new_persistent_script->script.filename), ZSTR_LEN(new_persistent_script->script.filename), 0, new_persistent_script);
if (bucket) {
- zend_accel_error(ACCEL_LOG_INFO, "Cached script '%s'", ZSTR_VAL(new_persistent_script->full_path));
+ zend_accel_error(ACCEL_LOG_INFO, "Cached script '%s'", ZSTR_VAL(new_persistent_script->script.filename));
if (key &&
/* key may contain non-persistent PHAR aliases (see issues #115 and #149) */
memcmp(key, "phar://", sizeof("phar://") - 1) != 0 &&
- (ZSTR_LEN(new_persistent_script->full_path) != key_length ||
- memcmp(ZSTR_VAL(new_persistent_script->full_path), key, key_length) != 0)) {
+ (ZSTR_LEN(new_persistent_script->script.filename) != key_length ||
+ memcmp(ZSTR_VAL(new_persistent_script->script.filename), key, key_length) != 0)) {
/* link key to the same persistent script in hash table */
if (zend_accel_hash_update(&ZCSG(hash), key, key_length, 1, bucket)) {
zend_accel_error(ACCEL_LOG_INFO, "Added key '%s'", key);
@@ -1446,7 +1455,7 @@ static void zend_accel_init_auto_globals(void)
}
}
-static zend_persistent_script *opcache_compile_file(zend_file_handle *file_handle, int type, char *key, unsigned int key_length, zend_op_array **op_array_p)
+static zend_persistent_script *opcache_compile_file(zend_file_handle *file_handle, int type, char *key, zend_op_array **op_array_p)
{
zend_persistent_script *new_persistent_script;
zend_op_array *orig_active_op_array;
@@ -1459,12 +1468,7 @@ static zend_persistent_script *opcache_compile_file(zend_file_handle *file_handl
/* Try to open file */
if (file_handle->type == ZEND_HANDLE_FILENAME) {
- if (accelerator_orig_zend_stream_open_function(file_handle->filename, file_handle) == SUCCESS) {
- /* key may be changed by zend_stream_open_function() */
- if (key == ZCG(key)) {
- key_length = ZCG(key_len);
- }
- } else {
+ if (accelerator_orig_zend_stream_open_function(file_handle->filename, file_handle) != SUCCESS) {
*op_array_p = NULL;
if (type == ZEND_REQUIRE) {
zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, file_handle->filename);
@@ -1503,7 +1507,7 @@ static zend_persistent_script *opcache_compile_file(zend_file_handle *file_handl
/* check if file is too new (may be it's not written completely yet) */
if (ZCG(accel_directives).file_update_protection &&
- (ZCG(request_time) - ZCG(accel_directives).file_update_protection < timestamp)) {
+ ((accel_time_t)(ZCG(request_time) - ZCG(accel_directives).file_update_protection) < timestamp)) {
*op_array_p = accelerator_orig_compile_file(file_handle, type);
return NULL;
}
@@ -1525,7 +1529,7 @@ static zend_persistent_script *opcache_compile_file(zend_file_handle *file_handl
/* Override them with ours */
CG(function_table) = &ZCG(function_table);
- EG(class_table) = CG(class_table) = &new_persistent_script->class_table;
+ EG(class_table) = CG(class_table) = &new_persistent_script->script.class_table;
ZVAL_UNDEF(&EG(user_error_handler));
zend_try {
@@ -1562,8 +1566,8 @@ static zend_persistent_script *opcache_compile_file(zend_file_handle *file_handl
Here we aren't sure we would store it, but we will need it
further anyway.
*/
- zend_accel_move_user_functions(&ZCG(function_table), &new_persistent_script->function_table);
- new_persistent_script->main_op_array = *op_array;
+ zend_accel_move_user_functions(&ZCG(function_table), &new_persistent_script->script.function_table);
+ new_persistent_script->script.main_op_array = *op_array;
efree(op_array); /* we have valid persistent_script, so it's safe to free op_array */
@@ -1584,11 +1588,11 @@ static zend_persistent_script *opcache_compile_file(zend_file_handle *file_handl
}
if (file_handle->opened_path) {
- new_persistent_script->full_path = zend_string_copy(file_handle->opened_path);
+ new_persistent_script->script.filename = zend_string_copy(file_handle->opened_path);
} else {
- new_persistent_script->full_path = zend_string_init(file_handle->filename, strlen(file_handle->filename), 0);
+ new_persistent_script->script.filename = zend_string_init(file_handle->filename, strlen(file_handle->filename), 0);
}
- zend_string_hash_val(new_persistent_script->full_path);
+ zend_string_hash_val(new_persistent_script->script.filename);
/* Now persistent_script structure is ready in process memory */
return new_persistent_script;
@@ -1619,26 +1623,28 @@ zend_op_array *file_cache_compile_file(zend_file_handle *file_handle, int type)
}
}
+ HANDLE_BLOCK_INTERRUPTIONS();
SHM_UNPROTECT();
persistent_script = zend_file_cache_script_load(file_handle);
SHM_PROTECT();
+ HANDLE_UNBLOCK_INTERRUPTIONS();
if (persistent_script) {
/* see bug #15471 (old BTS) */
- if (persistent_script->full_path) {
+ if (persistent_script->script.filename) {
if (!EG(current_execute_data) || !EG(current_execute_data)->opline ||
!EG(current_execute_data)->func ||
!ZEND_USER_CODE(EG(current_execute_data)->func->common.type) ||
EG(current_execute_data)->opline->opcode != ZEND_INCLUDE_OR_EVAL ||
(EG(current_execute_data)->opline->extended_value != ZEND_INCLUDE_ONCE &&
EG(current_execute_data)->opline->extended_value != ZEND_REQUIRE_ONCE)) {
- if (zend_hash_add_empty_element(&EG(included_files), persistent_script->full_path) != NULL) {
+ if (zend_hash_add_empty_element(&EG(included_files), persistent_script->script.filename) != NULL) {
/* ext/phar has to load phar's metadata into memory */
if (persistent_script->is_phar) {
php_stream_statbuf ssb;
- char *fname = emalloc(sizeof("phar://") + ZSTR_LEN(persistent_script->full_path));
+ char *fname = emalloc(sizeof("phar://") + ZSTR_LEN(persistent_script->script.filename));
memcpy(fname, "phar://", sizeof("phar://") - 1);
- memcpy(fname + sizeof("phar://") - 1, ZSTR_VAL(persistent_script->full_path), ZSTR_LEN(persistent_script->full_path) + 1);
+ memcpy(fname + sizeof("phar://") - 1, ZSTR_VAL(persistent_script->script.filename), ZSTR_LEN(persistent_script->script.filename) + 1);
php_stream_stat_path(fname, &ssb);
efree(fname);
}
@@ -1654,7 +1660,7 @@ zend_op_array *file_cache_compile_file(zend_file_handle *file_handle, int type)
return zend_accel_load_script(persistent_script, 1);
}
- persistent_script = opcache_compile_file(file_handle, type, NULL, 0, &op_array);
+ persistent_script = opcache_compile_file(file_handle, type, NULL, &op_array);
if (persistent_script) {
from_memory = 0;
@@ -1744,11 +1750,13 @@ zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type)
persistent_script = (zend_persistent_script *)bucket->data;
if (key && !persistent_script->corrupted) {
+ HANDLE_BLOCK_INTERRUPTIONS();
SHM_UNPROTECT();
zend_shared_alloc_lock();
zend_accel_add_key(key, key_length, bucket);
zend_shared_alloc_unlock();
SHM_PROTECT();
+ HANDLE_UNBLOCK_INTERRUPTIONS();
}
}
}
@@ -1783,7 +1791,7 @@ zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type)
if (EXPECTED(persistent_script != NULL) &&
UNEXPECTED(ZCG(accel_directives).validate_permission) &&
file_handle->type == ZEND_HANDLE_FILENAME &&
- UNEXPECTED(access(ZSTR_VAL(persistent_script->full_path), R_OK) != 0)) {
+ UNEXPECTED(access(ZSTR_VAL(persistent_script->script.filename), R_OK) != 0)) {
if (type == ZEND_REQUIRE) {
zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, file_handle->filename);
zend_bailout();
@@ -1793,6 +1801,7 @@ zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type)
return NULL;
}
+ HANDLE_BLOCK_INTERRUPTIONS();
SHM_UNPROTECT();
/* If script is found then validate_timestamps if option is enabled */
@@ -1821,8 +1830,8 @@ zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type)
unsigned int checksum = zend_accel_script_checksum(persistent_script);
if (checksum != persistent_script->dynamic_members.checksum ) {
/* The checksum is wrong */
- zend_accel_error(ACCEL_LOG_INFO, "Checksum failed for '%s': expected=0x%0.8X, found=0x%0.8X",
- ZSTR_VAL(persistent_script->full_path), persistent_script->dynamic_members.checksum, checksum);
+ zend_accel_error(ACCEL_LOG_INFO, "Checksum failed for '%s': expected=0x%08x, found=0x%08x",
+ ZSTR_VAL(persistent_script->script.filename), persistent_script->dynamic_members.checksum, checksum);
zend_shared_alloc_lock();
if (!persistent_script->corrupted) {
persistent_script->corrupted = 1;
@@ -1857,6 +1866,7 @@ zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type)
/* No memory left. Behave like without the Accelerator */
if (ZSMMG(memory_exhausted) || ZCSG(restart_pending)) {
SHM_PROTECT();
+ HANDLE_UNBLOCK_INTERRUPTIONS();
return accelerator_orig_compile_file(file_handle, type);
}
@@ -1864,7 +1874,7 @@ zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type)
* If it isn't compile_and_cache_file() changes the flag to 0
*/
from_shared_memory = 0;
- persistent_script = opcache_compile_file(file_handle, type, key, key ? key_length : 0, &op_array);
+ persistent_script = opcache_compile_file(file_handle, type, key, &op_array);
if (persistent_script) {
persistent_script = cache_script_in_shared_memory(persistent_script, key, key ? key_length : 0, &from_shared_memory);
}
@@ -1874,6 +1884,7 @@ zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type)
*/
if (!persistent_script) {
SHM_PROTECT();
+ HANDLE_UNBLOCK_INTERRUPTIONS();
return op_array;
}
if (from_shared_memory) {
@@ -1899,21 +1910,21 @@ zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type)
#endif
/* see bug #15471 (old BTS) */
- if (persistent_script->full_path) {
+ if (persistent_script->script.filename) {
if (!EG(current_execute_data) || !EG(current_execute_data)->opline ||
!EG(current_execute_data)->func ||
!ZEND_USER_CODE(EG(current_execute_data)->func->common.type) ||
EG(current_execute_data)->opline->opcode != ZEND_INCLUDE_OR_EVAL ||
(EG(current_execute_data)->opline->extended_value != ZEND_INCLUDE_ONCE &&
EG(current_execute_data)->opline->extended_value != ZEND_REQUIRE_ONCE)) {
- if (zend_hash_add_empty_element(&EG(included_files), persistent_script->full_path) != NULL) {
+ if (zend_hash_add_empty_element(&EG(included_files), persistent_script->script.filename) != NULL) {
/* ext/phar has to load phar's metadata into memory */
if (persistent_script->is_phar) {
php_stream_statbuf ssb;
- char *fname = emalloc(sizeof("phar://") + ZSTR_LEN(persistent_script->full_path));
+ char *fname = emalloc(sizeof("phar://") + ZSTR_LEN(persistent_script->script.filename));
memcpy(fname, "phar://", sizeof("phar://") - 1);
- memcpy(fname + sizeof("phar://") - 1, ZSTR_VAL(persistent_script->full_path), ZSTR_LEN(persistent_script->full_path) + 1);
+ memcpy(fname + sizeof("phar://") - 1, ZSTR_VAL(persistent_script->script.filename), ZSTR_LEN(persistent_script->script.filename) + 1);
php_stream_stat_path(fname, &ssb);
efree(fname);
}
@@ -1927,6 +1938,7 @@ zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type)
persistent_script->dynamic_members.last_used = ZCG(request_time);
SHM_PROTECT();
+ HANDLE_UNBLOCK_INTERRUPTIONS();
/* Fetch jit auto globals used in the script before execution */
if (persistent_script->ping_auto_globals_mask) {
@@ -1952,7 +1964,7 @@ static int persistent_stream_open_function(const char *filename, zend_file_handl
/* we are in include_once or FastCGI request */
handle->filename = (char*)filename;
handle->free_filename = 0;
- handle->opened_path = zend_string_copy(ZCG(cache_persistent_script)->full_path);
+ handle->opened_path = zend_string_copy(ZCG(cache_persistent_script)->script.filename);
handle->type = ZEND_HANDLE_FILENAME;
return SUCCESS;
}
@@ -1994,7 +2006,7 @@ static zend_string* persistent_zend_resolve_path(const char *filename, int filen
if (!persistent_script->corrupted) {
ZCG(cache_opline) = EG(current_execute_data) ? EG(current_execute_data)->opline : NULL;
ZCG(cache_persistent_script) = persistent_script;
- return zend_string_copy(persistent_script->full_path);
+ return zend_string_copy(persistent_script->script.filename);
}
}
} else {
@@ -2015,11 +2027,13 @@ static zend_string* persistent_zend_resolve_path(const char *filename, int filen
if (!persistent_script->corrupted) {
if (key) {
/* add another "key" for the same bucket */
+ HANDLE_BLOCK_INTERRUPTIONS();
SHM_UNPROTECT();
zend_shared_alloc_lock();
zend_accel_add_key(key, key_length, bucket);
zend_shared_alloc_unlock();
SHM_PROTECT();
+ HANDLE_UNBLOCK_INTERRUPTIONS();
} else {
ZCG(key_len) = 0;
}
@@ -2086,7 +2100,7 @@ static void accel_activate(void)
ZCG(include_path_check) = 1;
/* check if ZCG(function_table) wasn't somehow polluted on the way */
- if (ZCG(internal_functions_count) != zend_hash_num_elements(&ZCG(function_table))) {
+ if (ZCG(internal_functions_count) != (zend_long)zend_hash_num_elements(&ZCG(function_table))) {
zend_accel_error(ACCEL_LOG_WARNING, "Internal functions count changed - was %d, now %d", ZCG(internal_functions_count), zend_hash_num_elements(&ZCG(function_table)));
}
@@ -2123,6 +2137,7 @@ static void accel_activate(void)
}
#endif
+ HANDLE_BLOCK_INTERRUPTIONS();
SHM_UNPROTECT();
if (ZCG(counted)) {
@@ -2179,6 +2194,7 @@ static void accel_activate(void)
}
SHM_PROTECT();
+ HANDLE_UNBLOCK_INTERRUPTIONS();
if (ZCSG(last_restart_time) != ZCG(last_restart_time)) {
/* SHM was reinitialized. */
@@ -2497,7 +2513,7 @@ static int zend_accel_init_shm(void)
ZCSG(interned_strings_start) = ZCSG(interned_strings_end) = NULL;
# ifndef ZTS
- zend_hash_init(&ZCSG(interned_strings), (ZCG(accel_directives).interned_strings_buffer * 1024 * 1024) / (sizeof(Bucket) + sizeof(Bucket*) + 8 /* average string length */), NULL, NULL, 1);
+ zend_hash_init(&ZCSG(interned_strings), (ZCG(accel_directives).interned_strings_buffer * 1024 * 1024) / _ZSTR_STRUCT_SIZE(8 /* average string length */), NULL, NULL, 1);
if (ZCG(accel_directives).interned_strings_buffer) {
void *data;
@@ -2883,6 +2899,8 @@ file_cache_fallback:
zend_accel_blacklist_load(&accel_blacklist, ZCG(accel_directives.user_blacklist_filename));
}
+ zend_optimizer_startup();
+
return SUCCESS;
}
@@ -2900,6 +2918,8 @@ void accel_shutdown(void)
zend_ini_entry *ini_entry;
zend_bool file_cache_only = 0;
+ zend_optimizer_shutdown();
+
zend_accel_blacklist_shutdown(&accel_blacklist);
if (!ZCG(enabled) || !accel_startup_ok) {
@@ -2953,6 +2973,7 @@ void zend_accel_schedule_restart(zend_accel_restart_reason reason)
zend_accel_error(ACCEL_LOG_DEBUG, "Restart Scheduled! Reason: %s",
zend_accel_restart_reason_text[reason]);
+ HANDLE_BLOCK_INTERRUPTIONS();
SHM_UNPROTECT();
ZCSG(restart_pending) = 1;
ZCSG(restart_reason) = reason;
@@ -2965,6 +2986,7 @@ void zend_accel_schedule_restart(zend_accel_restart_reason reason)
ZCSG(force_restart_time) = 0;
}
SHM_PROTECT();
+ HANDLE_UNBLOCK_INTERRUPTIONS();
}
/* this is needed because on WIN32 lock is not decreased unless ZCG(counted) is set */
diff --git a/ext/opcache/ZendAccelerator.h b/ext/opcache/ZendAccelerator.h
index 8f2349a010..6faa263db2 100644
--- a/ext/opcache/ZendAccelerator.h
+++ b/ext/opcache/ZendAccelerator.h
@@ -75,7 +75,8 @@
#ifdef ZEND_WIN32
# ifndef MAXPATHLEN
-# define MAXPATHLEN _MAX_PATH
+# include "win32/ioutil.h"
+# define MAXPATHLEN PHP_WIN32_IOUTIL_MAXPATHLEN
# endif
# include <direct.h>
#else
@@ -85,14 +86,6 @@
# include <sys/param.h>
#endif
-#define PHP_5_0_X_API_NO 220040412
-#define PHP_5_1_X_API_NO 220051025
-#define PHP_5_2_X_API_NO 220060519
-#define PHP_5_3_X_API_NO 220090626
-#define PHP_5_4_X_API_NO 220100525
-#define PHP_5_5_X_API_NO 220121212
-#define PHP_5_6_X_API_NO 220131226
-
/*** file locking ***/
#ifndef ZEND_WIN32
extern int lock_file;
@@ -125,18 +118,6 @@ extern int lock_file;
# endif
#endif
-#define PZ_REFCOUNT_P(pz) (pz)->refcount__gc
-#define PZ_SET_REFCOUNT_P(pz, v) (pz)->refcount__gc = (v)
-#define PZ_ADDREF_P(pz) ++((pz)->refcount__gc)
-#define PZ_DELREF_P(pz) --((pz)->refcount__gc)
-#define PZ_ISREF_P(pz) (pz)->is_ref__gc
-#define PZ_SET_ISREF_P(pz) Z_SET_ISREF_TO_P(pz, 1)
-#define PZ_UNSET_ISREF_P(pz) Z_SET_ISREF_TO_P(pz, 0)
-#define PZ_SET_ISREF_TO_P(pz, isref) (pz)->is_ref__gc = (isref)
-
-#define DO_ALLOCA(x) do_alloca(x, use_heap)
-#define FREE_ALLOCA(x) free_alloca(x, use_heap)
-
#if defined(HAVE_OPCACHE_FILE_CACHE) && defined(ZEND_WIN32)
# define ENABLE_FILE_CACHE_FALLBACK 1
#endif
@@ -154,11 +135,8 @@ typedef enum _zend_accel_restart_reason {
} zend_accel_restart_reason;
typedef struct _zend_persistent_script {
- zend_string *full_path; /* full real path with resolved symlinks */
- zend_op_array main_op_array;
- HashTable function_table;
- HashTable class_table;
- zend_long compiler_halt_offset; /* position of __HALT_COMPILER or -1 */
+ zend_script script;
+ zend_long compiler_halt_offset; /* position of __HALT_COMPILER or -1 */
int ping_auto_globals_mask; /* which autoglobals are used by the script */
accel_time_t timestamp; /* the script modification time */
zend_bool corrupted;
@@ -216,6 +194,7 @@ typedef struct _zend_accel_directives {
zend_long log_verbosity_level;
zend_long optimization_level;
+ zend_long opt_debug_level;
zend_long max_file_size;
zend_long interned_strings_buffer;
char *restrict_api;
@@ -333,37 +312,15 @@ accel_time_t zend_get_file_handle_timestamp(zend_file_handle *file_handle, size_
int validate_timestamp_and_record(zend_persistent_script *persistent_script, zend_file_handle *file_handle);
int validate_timestamp_and_record_ex(zend_persistent_script *persistent_script, zend_file_handle *file_handle);
int zend_accel_invalidate(const char *filename, int filename_len, zend_bool force);
-int zend_accel_script_optimize(zend_persistent_script *persistent_script);
int accelerator_shm_read_lock(void);
void accelerator_shm_read_unlock(void);
char *accel_make_persistent_key(const char *path, int path_length, int *key_len);
zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type);
-#if !defined(ZEND_DECLARE_INHERITED_CLASS_DELAYED)
-# define ZEND_DECLARE_INHERITED_CLASS_DELAYED 145
-#endif
-
-#define ZEND_DECLARE_INHERITED_CLASS_DELAYED_FLAG 0x80
-
#define IS_ACCEL_INTERNED(str) \
((char*)(str) >= ZCSG(interned_strings_start) && (char*)(str) < ZCSG(interned_strings_end))
zend_string *accel_new_interned_string(zend_string *str);
-# define ZEND_RESULT_TYPE(opline) (opline)->result_type
-# define ZEND_RESULT(opline) (opline)->result
-# define ZEND_OP1_TYPE(opline) (opline)->op1_type
-# define ZEND_OP1(opline) (opline)->op1
-# define ZEND_OP1_CONST(opline) (*(opline)->op1.zv)
-# define ZEND_OP1_LITERAL(opline) (op_array)->literals[(opline)->op1.constant]
-# define ZEND_OP2_TYPE(opline) (opline)->op2_type
-# define ZEND_OP2(opline) (opline)->op2
-# define ZEND_OP2_CONST(opline) (*(opline)->op2.zv)
-# define ZEND_OP2_LITERAL(opline) (op_array)->literals[(opline)->op2.constant]
-# define ZEND_DONE_PASS_TWO(op_array) (((op_array)->fn_flags & ZEND_ACC_DONE_PASS_TWO) != 0)
-# define ZEND_CE_FILENAME(ce) (ce)->info.user.filename
-# define ZEND_CE_DOC_COMMENT(ce) (ce)->info.user.doc_comment
-# define ZEND_CE_DOC_COMMENT_LEN(ce) (ce)->info.user.doc_comment_len
-
#endif /* ZEND_ACCELERATOR_H */
diff --git a/ext/opcache/config.m4 b/ext/opcache/config.m4
index fbb9b21c94..ded7f3dab2 100644
--- a/ext/opcache/config.m4
+++ b/ext/opcache/config.m4
@@ -402,7 +402,15 @@ fi
Optimizer/block_pass.c \
Optimizer/optimize_temp_vars_5.c \
Optimizer/nop_removal.c \
- Optimizer/compact_literals.c,
+ Optimizer/compact_literals.c \
+ Optimizer/zend_cfg.c \
+ Optimizer/zend_dfg.c \
+ Optimizer/dfa_pass.c \
+ Optimizer/zend_ssa.c \
+ Optimizer/zend_inference.c \
+ Optimizer/zend_func_info.c \
+ Optimizer/zend_call_graph.c \
+ Optimizer/zend_dump.c,
shared,,-DZEND_ENABLE_STATIC_TSRMLS_CACHE=1,,yes)
PHP_ADD_BUILD_DIR([$ext_builddir/Optimizer], 1)
diff --git a/ext/opcache/config.w32 b/ext/opcache/config.w32
index ba6fd621bd..35c4645620 100644
--- a/ext/opcache/config.w32
+++ b/ext/opcache/config.w32
@@ -23,7 +23,7 @@ if (PHP_OPCACHE != "no") {
zend_shared_alloc.c \
shared_alloc_win32.c", true, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1");
- ADD_SOURCES(configure_module_dirname + "/Optimizer", "zend_optimizer.c pass1_5.c pass2.c pass3.c optimize_func_calls.c block_pass.c optimize_temp_vars_5.c nop_removal.c compact_literals.c", "opcache", "OptimizerObj");
+ ADD_SOURCES(configure_module_dirname + "/Optimizer", "zend_optimizer.c pass1_5.c pass2.c pass3.c optimize_func_calls.c block_pass.c optimize_temp_vars_5.c nop_removal.c compact_literals.c zend_cfg.c zend_dfg.c dfa_pass.c zend_ssa.c zend_inference.c zend_func_info.c zend_call_graph.c zend_dump.c", "opcache", "OptimizerObj");
ADD_FLAG('CFLAGS_OPCACHE', "/I " + configure_module_dirname);
diff --git a/ext/opcache/shared_alloc_win32.c b/ext/opcache/shared_alloc_win32.c
index a0398b2d90..13a4cf5e8c 100644
--- a/ext/opcache/shared_alloc_win32.c
+++ b/ext/opcache/shared_alloc_win32.c
@@ -77,28 +77,38 @@ static void zend_win_error_message(int type, char *msg, int err)
static char *create_name_with_username(char *name)
{
static char newname[MAXPATHLEN + UNLEN + 4 + 1 + 32];
- char uname[UNLEN + 1];
- DWORD unsize = UNLEN;
+ char *uname;
- GetUserName(uname, &unsize);
+ uname = php_win32_get_username();
+ if (!uname) {
+ return NULL;
+ }
snprintf(newname, sizeof(newname) - 1, "%s@%s@%.32s", name, uname, ZCG(system_id));
+
+ free(uname);
+
return newname;
}
static char *get_mmap_base_file(void)
{
static char windir[MAXPATHLEN+UNLEN + 3 + sizeof("\\\\@") + 1 + 32];
- char uname[UNLEN + 1];
- DWORD unsize = UNLEN;
+ char *uname;
int l;
+ uname = php_win32_get_username();
+ if (!uname) {
+ return NULL;
+ }
GetTempPath(MAXPATHLEN, windir);
- GetUserName(uname, &unsize);
l = strlen(windir);
if ('\\' == windir[l-1]) {
l--;
}
snprintf(windir + l, sizeof(windir) - l - 1, "\\%s@%s@%.32s", ACCEL_FILEMAP_BASE, uname, ZCG(system_id));
+
+ free(uname);
+
return windir;
}
diff --git a/ext/opcache/tests/blacklist-win32.phpt b/ext/opcache/tests/blacklist-win32.phpt
index 1e479b6c2e..fab0698f7f 100644
--- a/ext/opcache/tests/blacklist-win32.phpt
+++ b/ext/opcache/tests/blacklist-win32.phpt
@@ -18,7 +18,7 @@ $conf[4] = preg_replace("!^\\Q".dirname(__FILE__)."\\E!", "__DIR__", $conf[4]);
print_r($conf);
include("blacklist.inc");
$status = opcache_get_status();
-print_r(count($status['scripts']));
+print_r(count($status['scripts']) > 0);
?>
--EXPECTF--
Array
diff --git a/ext/opcache/tests/block_pass_001.phpt b/ext/opcache/tests/block_pass_001.phpt
new file mode 100644
index 0000000000..556e76f543
--- /dev/null
+++ b/ext/opcache/tests/block_pass_001.phpt
@@ -0,0 +1,10 @@
+--TEST--
+Block pass: Bugs in BOOL/QM_ASSIGN elision
+--FILE--
+<?php
+(bool) (true ? $x : $y);
+(bool) (true ? new stdClass : null);
+(bool) new stdClass;
+?>
+--EXPECTF--
+Notice: Undefined variable: x in %s on line %d
diff --git a/ext/opcache/tests/bug71127.phpt b/ext/opcache/tests/bug71127.phpt
index 5770aea1fb..0c606097fe 100644
--- a/ext/opcache/tests/bug71127.phpt
+++ b/ext/opcache/tests/bug71127.phpt
@@ -3,7 +3,7 @@ Bug #71127 (Define in auto_prepend_file is overwrite)
--INI--
opcache.enable=1
opcache.enable_cli=1
-opcache.optimization_level=0xFFFFBFFF
+opcache.optimization_level=0x7FFFBFFF
--SKIPIF--
<?php if (!extension_loaded('Zend OPcache')) die("skip"); ?>
--FILE--
diff --git a/ext/opcache/tests/bug71843.phpt b/ext/opcache/tests/bug71843.phpt
index 32af61bf74..73301a6e96 100644
--- a/ext/opcache/tests/bug71843.phpt
+++ b/ext/opcache/tests/bug71843.phpt
@@ -15,7 +15,11 @@ okey
--EXPECTF--
Notice: Use of undefined constant E - assumed 'E' in %sbug71843.php on line %d
+Warning: A non-numeric value encountered in %s on line %d
+
Notice: Use of undefined constant R - assumed 'R' in %sbug71843.php on line %d
+Warning: A non-numeric value encountered in %s on line %d
+
Notice: Use of undefined constant See - assumed 'See' in %sbug71843.php on line %d
okey
diff --git a/ext/opcache/tests/bug72762.phpt b/ext/opcache/tests/bug72762.phpt
new file mode 100644
index 0000000000..8ce98bf438
--- /dev/null
+++ b/ext/opcache/tests/bug72762.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Bug #72762: Infinite loop while parsing a file with opcache enabled
+--FILE--
+<?php
+
+class foo
+{
+ function bar()
+ {
+ $b = array();
+
+ foreach ($a as $a) {
+ foreach ($b as $k => $v) {
+ }
+ $b[$k] = $v;
+ }
+ }
+}
+
+?>
+===DONE===
+--EXPECT--
+===DONE===
diff --git a/ext/opcache/tests/bug73583.phpt b/ext/opcache/tests/bug73583.phpt
new file mode 100644
index 0000000000..e947b451c9
--- /dev/null
+++ b/ext/opcache/tests/bug73583.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Bug #73583 (Segfaults when conditionally declared class and function have the same name)
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.optimization_level=0x4ff
+opcache.file_update_protection=0
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--FILE--
+<?php
+if (true) {
+ class A { }
+ function A() { }
+ function A() { }
+}
+?>
+--EXPECTF--
+Fatal error: Cannot redeclare A() (previously declared in %sbug73583.php:4) in %sbug73583.php on line 5
diff --git a/ext/opcache/tests/bug73654.phpt b/ext/opcache/tests/bug73654.phpt
new file mode 100644
index 0000000000..164e10829c
--- /dev/null
+++ b/ext/opcache/tests/bug73654.phpt
@@ -0,0 +1,17 @@
+--TEST--
+Bug #73654: Segmentation fault in zend_call_function
+--FILE--
+<?php
+echo xyz();
+
+function x () : string {
+ return 'x';
+}
+
+function xyz() : string {
+ return x().'yz';
+}
+
+?>
+--EXPECT--
+xyz
diff --git a/ext/opcache/tests/bug73668.phpt b/ext/opcache/tests/bug73668.phpt
new file mode 100644
index 0000000000..aac5c9e65c
--- /dev/null
+++ b/ext/opcache/tests/bug73668.phpt
@@ -0,0 +1,8 @@
+--TEST--
+Bug #73668: "SIGFPE Arithmetic exception" in opcache when divide by minus 1
+--FILE--
+<?php
+$a/-1;
+?>
+--EXPECTF--
+Notice: Undefined variable: a in %s on line %d
diff --git a/ext/opcache/tests/bug73746.phpt b/ext/opcache/tests/bug73746.phpt
new file mode 100644
index 0000000000..c97833abcc
--- /dev/null
+++ b/ext/opcache/tests/bug73746.phpt
@@ -0,0 +1,28 @@
+--TEST--
+Bug #73746 (Method that returns string returns UNKNOWN:0 instead)
+--FILE--
+<?php
+namespace Core\Bundle\Service\Property\Room\Rooms;
+
+class CountryMapping
+{
+ const CZ = 'CZ';
+ const EN = 'EN';
+
+ public function get(string $countryIsoCode = null) : string // Works correctly if return type is removed
+ {
+ switch (strtoupper($countryIsoCode)) {
+ case 'CZ':
+ case 'SK':
+ return self::CZ; // Works correctly if changed to CountryMapping::CZ
+ default:
+ return self::EN; // Works correctly if changed to CountryMapping::EN
+ }
+ }
+}
+
+$mapping = new CountryMapping();
+var_dump($mapping->get('CZ'));
+?>
+--EXPECT--
+string(2) "CZ"
diff --git a/ext/opcache/tests/bug73789.phpt b/ext/opcache/tests/bug73789.phpt
new file mode 100644
index 0000000000..142d5229f9
--- /dev/null
+++ b/ext/opcache/tests/bug73789.phpt
@@ -0,0 +1,30 @@
+--TEST--
+Bug #73789 (Strange behavior of class constants in switch/case block)
+--FILE--
+<?php
+class Lexer
+{
+ const T_NONE = 1;
+ const T_STRING = 2;
+ const T_DOT = 8;
+ public function getType($value): int
+ {
+ $type = self::T_NONE;
+ switch (true) {
+ case ctype_alpha($value[0]):
+ $name = 'Lexer::T_' . strtoupper($value);
+ $type = constant($name);
+ if ($type > 100) {
+ return $type;
+ }
+ return self::T_STRING;
+ case $value === '.':
+ return self::T_DOT;
+ default:
+ }
+ return $type;
+ }
+}
+var_dump((new Lexer())->getType("dot"));
+--EXPECT--
+int(2)
diff --git a/ext/opcache/tests/bug73847.phpt b/ext/opcache/tests/bug73847.phpt
new file mode 100644
index 0000000000..7010dfbfb7
--- /dev/null
+++ b/ext/opcache/tests/bug73847.phpt
@@ -0,0 +1,44 @@
+--TEST--
+Bug #73847: Recursion when a variable is redefined as array
+--FILE--
+<?php
+function test() {
+ $a = 42;
+ $a = array($a);
+ var_dump($a);
+
+ $a = 42;
+ $a = array($a => 24);
+ var_dump($a);
+
+ $a = 42;
+ $a = array($a, 24);
+ var_dump($a);
+
+ $a = 42;
+ $a = array(24, $a);
+ var_dump($a);
+}
+test();
+?>
+--EXPECT--
+array(1) {
+ [0]=>
+ int(42)
+}
+array(1) {
+ [42]=>
+ int(24)
+}
+array(2) {
+ [0]=>
+ int(42)
+ [1]=>
+ int(24)
+}
+array(2) {
+ [0]=>
+ int(24)
+ [1]=>
+ int(42)
+}
diff --git a/ext/opcache/tests/bug74431.phpt b/ext/opcache/tests/bug74431.phpt
new file mode 100644
index 0000000000..40948bac21
--- /dev/null
+++ b/ext/opcache/tests/bug74431.phpt
@@ -0,0 +1,28 @@
+--TEST--
+Bug #74431 - foreach infinite loop
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.optimization_level=0xffffffff
+--FILE--
+<?php
+function test(){
+ $arr = [1,2];
+ $j = 0;
+ $cond = true;
+ foreach ($arr as $i => $v){
+ while(1){
+ if($cond){
+ break;
+ }
+ }
+ $j++;
+ echo $j."\n";
+ if ($j>10) break;
+ }
+}
+test();
+?>
+--EXPECT--
+1
+2
diff --git a/ext/opcache/tests/bug74442.phpt b/ext/opcache/tests/bug74442.phpt
new file mode 100644
index 0000000000..a4ad8e0540
--- /dev/null
+++ b/ext/opcache/tests/bug74442.phpt
@@ -0,0 +1,39 @@
+--TEST--
+Bug #74442: Opcached version produces a nested array
+--CREDITS--
+Eric Norris <erictnorris@gmail.com>
+--FILE--
+<?php
+class Schema_Base {
+ public function addField($typeclass, array $params = null) {
+ $field = new $typeclass($params);
+ return $field;
+ }
+}
+
+class Field_Base {
+ public function __construct(array $params = null) {
+ if (! is_array($params)) {
+ $params = (array) $params;
+ }
+ call_user_func_array(array($this, 'acceptParams'), $params);
+ }
+}
+
+class Field_Integer extends Field_Base {
+ protected function acceptParams($bytes = 4) {
+ echo print_r($bytes, true);
+ }
+}
+
+try {
+ $schema = new Schema_Base;
+ $schema->addField('Field_Integer');
+} catch (Throwable $ex) {
+ echo "CAUGHT EXCEPTION";
+ echo (string)$ex;
+}
+
+?>
+--EXPECT--
+4
diff --git a/ext/opcache/tests/bug74456.phpt b/ext/opcache/tests/bug74456.phpt
new file mode 100644
index 0000000000..9c9a286e2f
--- /dev/null
+++ b/ext/opcache/tests/bug74456.phpt
@@ -0,0 +1,24 @@
+--TEST--
+Bug #74456 (Segmentation error while running a script in CLI mode)
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.optimization_level=-1
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--FILE--
+<?php
+
+
+function small_numbers() {
+ return [0,1,2];
+}
+
+list ($zero, $one, $two) = small_numbers();
+
+var_dump($zero, $one, $two);
+?>
+--EXPECT--
+int(0)
+int(1)
+int(2)
diff --git a/ext/opcache/tests/bug74623.phpt b/ext/opcache/tests/bug74623.phpt
new file mode 100644
index 0000000000..4cd0b26135
--- /dev/null
+++ b/ext/opcache/tests/bug74623.phpt
@@ -0,0 +1,22 @@
+--TEST--
+Bug #74623: Infinite loop in type inference when using HTMLPurifier
+--FILE--
+<?php
+
+function crash($arr) {
+ $current_item = false;
+
+ foreach($arr as $item) {
+ if($item->name === 'string') {
+ $current_item = $item;
+ } else {
+ $current_item->a[] = '';
+ }
+ }
+
+}
+
+?>
+===DONE===
+--EXPECT--
+===DONE===
diff --git a/ext/opcache/tests/bug74980.phpt b/ext/opcache/tests/bug74980.phpt
new file mode 100644
index 0000000000..40fd3cd537
--- /dev/null
+++ b/ext/opcache/tests/bug74980.phpt
@@ -0,0 +1,30 @@
+--TEST--
+Bug #74980 (Narrowing occurred during type inference)
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--FILE--
+<?php
+
+class A
+{
+
+ static function foo()
+ {
+ while ($undef) {
+ $arr[][] = NULL;
+ }
+
+ foreach ($arr as $a) {
+ bar($a + []);
+ }
+ }
+
+}
+
+echo "okey";
+?>
+--EXPECT--
+okey
diff --git a/ext/opcache/tests/issue0140.phpt b/ext/opcache/tests/issue0140.phpt
index 98e0e45cc2..97fc11b3c7 100644
--- a/ext/opcache/tests/issue0140.phpt
+++ b/ext/opcache/tests/issue0140.phpt
@@ -8,6 +8,7 @@ opcache.file_update_protection=0
--SKIPIF--
<?php require_once('skipif.inc'); ?>
<?php if (php_sapi_name() != "cli") die("skip CLI only"); ?>
+<?php if (getenv("SKIP_SLOW_TESTS")) die("skip slow tests excluded by request") ?>
--FILE--
<?php
define("FILENAME", dirname(__FILE__) . "/issuer0140.inc.php");
diff --git a/ext/opcache/tests/ssa_bug_001.phpt b/ext/opcache/tests/ssa_bug_001.phpt
new file mode 100644
index 0000000000..56757f56a4
--- /dev/null
+++ b/ext/opcache/tests/ssa_bug_001.phpt
@@ -0,0 +1,19 @@
+--TEST--
+SSA constrution for CFG with unreachable basic blocks
+--FILE--
+<?php
+class X {
+ public function __get($n) {
+ if ($n === 'type') {
+ trigger_error('Deprecated type property called; use instanceof', E_USER_NOTICE);
+ switch (get_class($this)) {
+ case 'HTMLPurifier_Token_Start': return 'start';
+ default: return null;
+ }
+ }
+ }
+}
+?>
+OK
+--EXPECT--
+OK
diff --git a/ext/opcache/tests/ssa_bug_002.phpt b/ext/opcache/tests/ssa_bug_002.phpt
new file mode 100644
index 0000000000..9ff6f799f4
--- /dev/null
+++ b/ext/opcache/tests/ssa_bug_002.phpt
@@ -0,0 +1,16 @@
+--TEST--
+Incorrect NOP removal on jump to NOP
+--FILE--
+<?php
+
+function test(int $i) : int {
+ if ($i == 1) {
+ $x = $i + 1;
+ }
+ return $i;
+}
+var_dump(test(42));
+
+?>
+--EXPECT--
+int(42)
diff --git a/ext/opcache/tests/ssa_bug_003.phpt b/ext/opcache/tests/ssa_bug_003.phpt
new file mode 100644
index 0000000000..2895551c08
--- /dev/null
+++ b/ext/opcache/tests/ssa_bug_003.phpt
@@ -0,0 +1,38 @@
+--TEST--
+Incorrect elision of return type checks
+--FILE--
+<?php
+
+function test1($x) : callable {
+ if ($x == 1) {
+ $c = 'foo';
+ } elseif ($x == 2) {
+ $c = new stdClass;
+ } else {
+ $c = [$x => &$x];
+ }
+ return $c;
+}
+
+try {
+ test1(1);
+} catch (Error $e) {
+ echo "Error: {$e->getMessage()}\n";
+}
+
+class Foo {}
+function test2() : Foo {
+ $obj = new stdClass;
+ return $obj;
+}
+
+try {
+ test2();
+} catch (Error $e) {
+ echo "Error: {$e->getMessage()}\n";
+}
+
+?>
+--EXPECT--
+Error: Return value of test1() must be callable, string returned
+Error: Return value of test2() must be an instance of Foo, instance of stdClass returned
diff --git a/ext/opcache/tests/ssa_bug_004.phpt b/ext/opcache/tests/ssa_bug_004.phpt
new file mode 100644
index 0000000000..20e313045a
--- /dev/null
+++ b/ext/opcache/tests/ssa_bug_004.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Assign elision exception safety: ICALL
+--FILE--
+<?php
+
+set_error_handler(function() { throw new Exception; });
+
+function test() {
+ $x = str_replace(['foo'], [[]], ['foo']);
+}
+try {
+ test();
+} catch (Exception $e) {
+ echo "caught\n";
+}
+
+?>
+--EXPECT--
+caught
diff --git a/ext/opcache/tests/ssa_bug_005.phpt b/ext/opcache/tests/ssa_bug_005.phpt
new file mode 100644
index 0000000000..fb373e36b3
--- /dev/null
+++ b/ext/opcache/tests/ssa_bug_005.phpt
@@ -0,0 +1,24 @@
+--TEST--
+Assign elision exception safety: UCALL
+--FILE--
+<?php
+
+function test() {
+ $dtor = new class { function __destruct() { throw new Exception; } };
+ $a = 1;
+ return [0, $a];
+}
+
+function test2() {
+ $x = test();
+}
+
+try {
+ test2();
+} catch (Exception $e) {
+ echo "caught\n";
+}
+
+?>
+--EXPECT--
+caught
diff --git a/ext/opcache/tests/ssa_bug_006.phpt b/ext/opcache/tests/ssa_bug_006.phpt
new file mode 100644
index 0000000000..624c1e0047
--- /dev/null
+++ b/ext/opcache/tests/ssa_bug_006.phpt
@@ -0,0 +1,20 @@
+--TEST--
+Incorrect optimization of $i = $i++
+--FILE--
+<?php
+
+function test() {
+ $i = 1;
+ $i = $i++;
+ var_dump($i);
+
+ $i = 1;
+ $i = $i--;
+ var_dump($i);
+}
+test();
+
+?>
+--EXPECT--
+int(1)
+int(1)
diff --git a/ext/opcache/tests/verify_return_dfg.phpt b/ext/opcache/tests/verify_return_dfg.phpt
new file mode 100644
index 0000000000..f2e66511ed
--- /dev/null
+++ b/ext/opcache/tests/verify_return_dfg.phpt
@@ -0,0 +1,16 @@
+--TEST--
+Incorrect liveness computation for verify-return
+--FILE--
+<?php
+function test($foo): string
+{
+ switch ($foo) {
+ default: $bar = 'x'; break;
+ case 'z': $bar = 'y'; break;
+ }
+ return (string)$bar;
+}
+?>
+===DONE===
+--EXPECT--
+===DONE===
diff --git a/ext/opcache/tests/wrong_inlining_001.phpt b/ext/opcache/tests/wrong_inlining_001.phpt
new file mode 100644
index 0000000000..5cc515c4af
--- /dev/null
+++ b/ext/opcache/tests/wrong_inlining_001.phpt
@@ -0,0 +1,28 @@
+--TEST--
+Pass result of inlined function by reference
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.optimization_level=-1
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--FILE--
+<?php
+function get_const() {
+ return 42;
+}
+
+function test() {
+ foo(get_const());
+}
+
+if (true) {
+ function foo(&$ref) {}
+}
+
+test();
+?>
+OK
+--EXPECTF--
+Notice: Only variables should be passed by reference in %swrong_inlining_001.php on line 7
+OK \ No newline at end of file
diff --git a/ext/opcache/tests/wrong_inlining_002.phpt b/ext/opcache/tests/wrong_inlining_002.phpt
new file mode 100644
index 0000000000..4e71a96d10
--- /dev/null
+++ b/ext/opcache/tests/wrong_inlining_002.phpt
@@ -0,0 +1,29 @@
+--TEST--
+$this not in object context
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.optimization_level=-1
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--FILE--
+<?php
+class Foo {
+ private function getConst() {
+ return 42;
+ }
+ public function test() {
+ var_dump($this->getConst());
+ }
+}
+
+Foo::test();
+?>
+--EXPECTF--
+Deprecated: Non-static method Foo::test() should not be called statically in %swrong_inlining_002.php on line 11
+
+Fatal error: Uncaught Error: Using $this when not in object context in %swrong_inlining_002.php:7
+Stack trace:
+#0 %swrong_inlining_002.php(11): Foo::test()
+#1 {main}
+ thrown in %swrong_inlining_002.php on line 7
diff --git a/ext/opcache/tests/wrong_inlining_003.phpt b/ext/opcache/tests/wrong_inlining_003.phpt
new file mode 100644
index 0000000000..a7e4a11b76
--- /dev/null
+++ b/ext/opcache/tests/wrong_inlining_003.phpt
@@ -0,0 +1,23 @@
+--TEST--
+foo($bar) with undefined $bar
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.optimization_level=-1
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--FILE--
+<?php
+function get_const() {
+ return 42;
+}
+
+function test() {
+ var_dump(get_const($undef));
+}
+
+test();
+?>
+--EXPECTF--
+Notice: Undefined variable: undef in %swrong_inlining_003.php on line 7
+int(42)
diff --git a/ext/opcache/tests/wrong_inlining_004.phpt b/ext/opcache/tests/wrong_inlining_004.phpt
new file mode 100644
index 0000000000..d4b2d391a0
--- /dev/null
+++ b/ext/opcache/tests/wrong_inlining_004.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Inlining throgh call_user_func()
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.optimization_level=-1
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--FILE--
+<?php
+function get_const() {
+ return 42;
+}
+
+function test() {
+ $x = new stdClass;
+ var_dump(call_user_func('get_const', $x));
+}
+
+test();
+?>
+--EXPECTF--
+int(42)
diff --git a/ext/opcache/tests/wrong_inlining_005.phpt b/ext/opcache/tests/wrong_inlining_005.phpt
new file mode 100644
index 0000000000..b34cd1b12f
--- /dev/null
+++ b/ext/opcache/tests/wrong_inlining_005.phpt
@@ -0,0 +1,22 @@
+--TEST--
+Inlining of functions with ref arguments
+--FILE--
+<?php
+
+function by_ref(&$var)
+{
+}
+function &get_array() {
+ $array = [new stdClass];
+ return $array;
+}
+function test()
+{
+ by_ref(get_array()[0]);
+ print "ok!\n";
+}
+test();
+
+?>
+--EXPECT--
+ok!
diff --git a/ext/opcache/zend_accelerator_debug.h b/ext/opcache/zend_accelerator_debug.h
index fbe3127864..6445254232 100644
--- a/ext/opcache/zend_accelerator_debug.h
+++ b/ext/opcache/zend_accelerator_debug.h
@@ -28,6 +28,6 @@
#define ACCEL_LOG_INFO 3
#define ACCEL_LOG_DEBUG 4
-void zend_accel_error(int type, const char *format, ...);
+void zend_accel_error(int type, const char *format, ...) ZEND_ATTRIBUTE_FORMAT(printf, 2, 3);;
#endif /* _ZEND_ACCELERATOR_DEBUG_H */
diff --git a/ext/opcache/zend_accelerator_module.c b/ext/opcache/zend_accelerator_module.c
index 6112027cd8..259f75c2f2 100644
--- a/ext/opcache/zend_accelerator_module.c
+++ b/ext/opcache/zend_accelerator_module.c
@@ -80,13 +80,13 @@ static zend_function_entry accel_functions[] = {
/* Private functions */
ZEND_FE(opcache_get_configuration, arginfo_opcache_none)
ZEND_FE(opcache_get_status, arginfo_opcache_get_status)
- { NULL, NULL, NULL, 0, 0 }
+ ZEND_FE_END
};
static int validate_api_restriction(void)
{
if (ZCG(accel_directives).restrict_api && *ZCG(accel_directives).restrict_api) {
- int len = strlen(ZCG(accel_directives).restrict_api);
+ size_t len = strlen(ZCG(accel_directives).restrict_api);
if (!SG(request_info).path_translated ||
strlen(SG(request_info).path_translated) < len ||
@@ -204,7 +204,7 @@ static ZEND_INI_MH(OnUpdateMaxWastedPercentage)
percentage = 5;
zend_accel_error(ACCEL_LOG_WARNING, "opcache.max_wasted_percentage must be set between 1 and 50.\n");
- zend_accel_error(ACCEL_LOG_WARNING, ACCELERATOR_PRODUCT_NAME " will use 5%.\n");
+ zend_accel_error(ACCEL_LOG_WARNING, ACCELERATOR_PRODUCT_NAME " will use 5%%.\n");
if ((ini_entry = zend_hash_str_find_ptr(EG(ini_directives),
"opcache.max_wasted_percentage",
sizeof("opcache.max_wasted_percentage")-1)) == NULL) {
@@ -285,9 +285,9 @@ ZEND_INI_BEGIN()
STD_PHP_INI_BOOLEAN("opcache.revalidate_path" , "0", PHP_INI_ALL , OnUpdateBool, accel_directives.revalidate_path , zend_accel_globals, accel_globals)
STD_PHP_INI_ENTRY("opcache.log_verbosity_level" , "1" , PHP_INI_SYSTEM, OnUpdateLong, accel_directives.log_verbosity_level, zend_accel_globals, accel_globals)
- STD_PHP_INI_ENTRY("opcache.memory_consumption" , "64" , PHP_INI_SYSTEM, OnUpdateMemoryConsumption, accel_directives.memory_consumption, zend_accel_globals, accel_globals)
- STD_PHP_INI_ENTRY("opcache.interned_strings_buffer", "4" , PHP_INI_SYSTEM, OnUpdateLong, accel_directives.interned_strings_buffer, zend_accel_globals, accel_globals)
- STD_PHP_INI_ENTRY("opcache.max_accelerated_files" , "2000", PHP_INI_SYSTEM, OnUpdateMaxAcceleratedFiles, accel_directives.max_accelerated_files, zend_accel_globals, accel_globals)
+ STD_PHP_INI_ENTRY("opcache.memory_consumption" , "128" , PHP_INI_SYSTEM, OnUpdateMemoryConsumption, accel_directives.memory_consumption, zend_accel_globals, accel_globals)
+ STD_PHP_INI_ENTRY("opcache.interned_strings_buffer", "8" , PHP_INI_SYSTEM, OnUpdateLong, accel_directives.interned_strings_buffer, zend_accel_globals, accel_globals)
+ STD_PHP_INI_ENTRY("opcache.max_accelerated_files" , "10000", PHP_INI_SYSTEM, OnUpdateMaxAcceleratedFiles, accel_directives.max_accelerated_files, zend_accel_globals, accel_globals)
STD_PHP_INI_ENTRY("opcache.max_wasted_percentage" , "5" , PHP_INI_SYSTEM, OnUpdateMaxWastedPercentage, accel_directives.max_wasted_percentage, zend_accel_globals, accel_globals)
STD_PHP_INI_ENTRY("opcache.consistency_checks" , "0" , PHP_INI_ALL , OnUpdateLong, accel_directives.consistency_checks, zend_accel_globals, accel_globals)
STD_PHP_INI_ENTRY("opcache.force_restart_timeout" , "180" , PHP_INI_SYSTEM, OnUpdateLong, accel_directives.force_restart_timeout, zend_accel_globals, accel_globals)
@@ -302,6 +302,7 @@ ZEND_INI_BEGIN()
STD_PHP_INI_ENTRY("opcache.fast_shutdown" , "0" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.fast_shutdown, zend_accel_globals, accel_globals)
STD_PHP_INI_ENTRY("opcache.optimization_level" , DEFAULT_OPTIMIZATION_LEVEL , PHP_INI_SYSTEM, OnUpdateLong, accel_directives.optimization_level, zend_accel_globals, accel_globals)
+ STD_PHP_INI_ENTRY("opcache.opt_debug_level" , "0" , PHP_INI_SYSTEM, OnUpdateLong, accel_directives.opt_debug_level, zend_accel_globals, accel_globals)
STD_PHP_INI_BOOLEAN("opcache.enable_file_override" , "0" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.file_override_enabled, zend_accel_globals, accel_globals)
STD_PHP_INI_BOOLEAN("opcache.enable_cli" , "0" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.enable_cli, zend_accel_globals, accel_globals)
STD_PHP_INI_ENTRY("opcache.error_log" , "" , PHP_INI_SYSTEM, OnUpdateString, accel_directives.error_log, zend_accel_globals, accel_globals)
@@ -481,33 +482,33 @@ void zend_accel_info(ZEND_MODULE_INFO_FUNC_ARGS)
char buf[32];
php_info_print_table_row(2, "Startup", "OK");
php_info_print_table_row(2, "Shared memory model", zend_accel_get_shared_model());
- snprintf(buf, sizeof(buf), "%pd", (zend_ulong)ZCSG(hits));
+ snprintf(buf, sizeof(buf), ZEND_LONG_FMT, (zend_ulong)ZCSG(hits));
php_info_print_table_row(2, "Cache hits", buf);
- snprintf(buf, sizeof(buf), "%pd", ZSMMG(memory_exhausted)?ZCSG(misses):ZCSG(misses)-ZCSG(blacklist_misses));
+ snprintf(buf, sizeof(buf), ZEND_LONG_FMT, ZSMMG(memory_exhausted)?ZCSG(misses):ZCSG(misses)-ZCSG(blacklist_misses));
php_info_print_table_row(2, "Cache misses", buf);
snprintf(buf, sizeof(buf), ZEND_LONG_FMT, ZCG(accel_directives).memory_consumption-zend_shared_alloc_get_free_memory()-ZSMMG(wasted_shared_memory));
php_info_print_table_row(2, "Used memory", buf);
- snprintf(buf, sizeof(buf), "%pd", zend_shared_alloc_get_free_memory());
+ snprintf(buf, sizeof(buf), ZEND_LONG_FMT, zend_shared_alloc_get_free_memory());
php_info_print_table_row(2, "Free memory", buf);
- snprintf(buf, sizeof(buf), "%pd", ZSMMG(wasted_shared_memory));
+ snprintf(buf, sizeof(buf), ZEND_LONG_FMT, ZSMMG(wasted_shared_memory));
php_info_print_table_row(2, "Wasted memory", buf);
if (ZCSG(interned_strings_start) && ZCSG(interned_strings_end) && ZCSG(interned_strings_top)) {
- snprintf(buf, sizeof(buf), "%pd", ZCSG(interned_strings_top) - ZCSG(interned_strings_start));
+ snprintf(buf, sizeof(buf), ZEND_LONG_FMT, ZCSG(interned_strings_top) - ZCSG(interned_strings_start));
php_info_print_table_row(2, "Interned Strings Used memory", buf);
- snprintf(buf, sizeof(buf), "%pd", ZCSG(interned_strings_end) - ZCSG(interned_strings_top));
+ snprintf(buf, sizeof(buf), ZEND_LONG_FMT, ZCSG(interned_strings_end) - ZCSG(interned_strings_top));
php_info_print_table_row(2, "Interned Strings Free memory", buf);
}
- snprintf(buf, sizeof(buf), "%ld", ZCSG(hash).num_direct_entries);
+ snprintf(buf, sizeof(buf), "%d", ZCSG(hash).num_direct_entries);
php_info_print_table_row(2, "Cached scripts", buf);
- snprintf(buf, sizeof(buf), "%ld", ZCSG(hash).num_entries);
+ snprintf(buf, sizeof(buf), "%d", ZCSG(hash).num_entries);
php_info_print_table_row(2, "Cached keys", buf);
- snprintf(buf, sizeof(buf), "%pd", ZCSG(hash).max_num_entries);
+ snprintf(buf, sizeof(buf), "%d", ZCSG(hash).max_num_entries);
php_info_print_table_row(2, "Max keys", buf);
- snprintf(buf, sizeof(buf), "%pd", ZCSG(oom_restarts));
+ snprintf(buf, sizeof(buf), ZEND_LONG_FMT, ZCSG(oom_restarts));
php_info_print_table_row(2, "OOM restarts", buf);
- snprintf(buf, sizeof(buf), "%pd", ZCSG(hash_restarts));
+ snprintf(buf, sizeof(buf), ZEND_LONG_FMT, ZCSG(hash_restarts));
php_info_print_table_row(2, "Hash keys restarts", buf);
- snprintf(buf, sizeof(buf), "%pd", ZCSG(manual_restarts));
+ snprintf(buf, sizeof(buf), ZEND_LONG_FMT, ZCSG(manual_restarts));
php_info_print_table_row(2, "Manual restarts", buf);
}
}
@@ -563,7 +564,7 @@ static int accelerator_get_scripts(zval *return_value)
script = (zend_persistent_script *)cache_entry->data;
array_init(&persistent_script_report);
- add_assoc_str(&persistent_script_report, "full_path", zend_string_dup(script->full_path, 0));
+ add_assoc_str(&persistent_script_report, "full_path", zend_string_dup(script->script.filename, 0));
add_assoc_long(&persistent_script_report, "hits", (zend_long)script->dynamic_members.hits);
add_assoc_long(&persistent_script_report, "memory_consumption", script->dynamic_members.memory_consumption);
ta = localtime(&script->dynamic_members.last_used);
diff --git a/ext/opcache/zend_accelerator_util_funcs.c b/ext/opcache/zend_accelerator_util_funcs.c
index e527a7b83c..3d845f389e 100644
--- a/ext/opcache/zend_accelerator_util_funcs.c
+++ b/ext/opcache/zend_accelerator_util_funcs.c
@@ -40,8 +40,6 @@
typedef int (*id_function_t)(void *, void *);
typedef void (*unique_copy_ctor_func_t)(void *pElement);
-static zend_ast *zend_ast_clone(zend_ast *ast);
-
static void zend_accel_destroy_zend_function(zval *zv)
{
zend_function *function = Z_PTR_P(zv);
@@ -72,12 +70,12 @@ zend_persistent_script* create_persistent_script(void)
zend_persistent_script *persistent_script = (zend_persistent_script *) emalloc(sizeof(zend_persistent_script));
memset(persistent_script, 0, sizeof(zend_persistent_script));
- zend_hash_init(&persistent_script->function_table, 128, NULL, ZEND_FUNCTION_DTOR, 0);
+ zend_hash_init(&persistent_script->script.function_table, 128, NULL, ZEND_FUNCTION_DTOR, 0);
/* class_table is usually destroyed by free_persistent_script() that
* overrides destructor. ZEND_CLASS_DTOR may be used by standard
* PHP compiler
*/
- zend_hash_init(&persistent_script->class_table, 16, NULL, ZEND_CLASS_DTOR, 0);
+ zend_hash_init(&persistent_script->script.class_table, 16, NULL, ZEND_CLASS_DTOR, 0);
return persistent_script;
}
@@ -85,18 +83,18 @@ zend_persistent_script* create_persistent_script(void)
void free_persistent_script(zend_persistent_script *persistent_script, int destroy_elements)
{
if (destroy_elements) {
- persistent_script->function_table.pDestructor = zend_accel_destroy_zend_function;
- persistent_script->class_table.pDestructor = zend_accel_destroy_zend_class;
+ persistent_script->script.function_table.pDestructor = zend_accel_destroy_zend_function;
+ persistent_script->script.class_table.pDestructor = zend_accel_destroy_zend_class;
} else {
- persistent_script->function_table.pDestructor = NULL;
- persistent_script->class_table.pDestructor = NULL;
+ persistent_script->script.function_table.pDestructor = NULL;
+ persistent_script->script.class_table.pDestructor = NULL;
}
- zend_hash_destroy(&persistent_script->function_table);
- zend_hash_destroy(&persistent_script->class_table);
+ zend_hash_destroy(&persistent_script->script.function_table);
+ zend_hash_destroy(&persistent_script->script.class_table);
- if (persistent_script->full_path) {
- zend_string_release(persistent_script->full_path);
+ if (persistent_script->script.filename) {
+ zend_string_release(persistent_script->script.filename);
}
efree(persistent_script);
@@ -169,67 +167,13 @@ static inline void zend_clone_zval(zval *src)
src = Z_REFVAL_P(src);
}
}
- if (Z_TYPE_P(src) == IS_CONSTANT_AST) {
- if (Z_REFCOUNT_P(src) > 1 && (ptr = accel_xlat_get(Z_AST_P(src))) != NULL) {
- Z_AST_P(src) = ptr;
- } else {
- zend_ast_ref *old = Z_AST_P(src);
-
- ZVAL_NEW_AST(src, old->ast);
- Z_AST_P(src)->gc = old->gc;
- if (Z_REFCOUNT_P(src) > 1) {
- accel_xlat_set(old, Z_AST_P(src));
- }
- Z_ASTVAL_P(src) = zend_ast_clone(Z_ASTVAL_P(src));
- }
- }
-}
-
-static zend_ast *zend_ast_clone(zend_ast *ast)
-{
- uint32_t i;
-
- if (ast->kind == ZEND_AST_ZVAL) {
- zend_ast_zval *copy = emalloc(sizeof(zend_ast_zval));
- copy->kind = ZEND_AST_ZVAL;
- copy->attr = ast->attr;
- ZVAL_COPY_VALUE(&copy->val, zend_ast_get_zval(ast));
- return (zend_ast *) copy;
- } else if (zend_ast_is_list(ast)) {
- zend_ast_list *list = zend_ast_get_list(ast);
- zend_ast_list *copy = emalloc(
- sizeof(zend_ast_list) - sizeof(zend_ast *) + sizeof(zend_ast *) * list->children);
- copy->kind = list->kind;
- copy->attr = list->attr;
- copy->children = list->children;
- for (i = 0; i < list->children; i++) {
- if (list->child[i]) {
- copy->child[i] = zend_ast_clone(list->child[i]);
- } else {
- copy->child[i] = NULL;
- }
- }
- return (zend_ast *) copy;
- } else {
- uint32_t children = zend_ast_get_num_children(ast);
- zend_ast *copy = emalloc(sizeof(zend_ast) - sizeof(zend_ast *) + sizeof(zend_ast *) * children);
- copy->kind = ast->kind;
- copy->attr = ast->attr;
- for (i = 0; i < children; i++) {
- if (ast->child[i]) {
- copy->child[i] = zend_ast_clone(ast->child[i]);
- } else {
- copy->child[i] = NULL;
- }
- }
- return copy;
- }
}
static void zend_hash_clone_constants(HashTable *ht, HashTable *source)
{
Bucket *p, *q, *end;
zend_ulong nIndex;
+ zend_class_constant *c;
ht->nTableSize = source->nTableSize;
ht->nTableMask = source->nTableMask;
@@ -265,8 +209,14 @@ static void zend_hash_clone_constants(HashTable *ht, HashTable *source)
q->key = p->key;
/* Copy data */
- ZVAL_COPY_VALUE(&q->val, &p->val);
- zend_clone_zval(&q->val);
+ c = ARENA_REALLOC(Z_PTR(p->val));
+ ZVAL_PTR(&q->val, c);
+
+ zend_clone_zval(&c->value);
+ if ((void*)c->ce >= ZCG(current_persistent_script)->arena_mem &&
+ (void*)c->ce < (void*)((char*)ZCG(current_persistent_script)->arena_mem + ZCG(current_persistent_script)->arena_size)) {
+ c->ce = ARENA_REALLOC(c->ce);
+ }
}
}
@@ -681,7 +631,7 @@ zend_op_array* zend_accel_load_script(zend_persistent_script *persistent_script,
zend_op_array *op_array;
op_array = (zend_op_array *) emalloc(sizeof(zend_op_array));
- *op_array = persistent_script->main_op_array;
+ *op_array = persistent_script->script.main_op_array;
if (EXPECTED(from_shared_memory)) {
zend_hash_init(&ZCG(bind_hash), 10, NULL, NULL, 0);
@@ -701,22 +651,22 @@ zend_op_array* zend_accel_load_script(zend_persistent_script *persistent_script,
}
/* Copy all the necessary stuff from shared memory to regular memory, and protect the shared script */
- if (zend_hash_num_elements(&persistent_script->class_table) > 0) {
- zend_accel_class_hash_copy(CG(class_table), &persistent_script->class_table, (unique_copy_ctor_func_t) zend_class_copy_ctor);
+ if (zend_hash_num_elements(&persistent_script->script.class_table) > 0) {
+ zend_accel_class_hash_copy(CG(class_table), &persistent_script->script.class_table, (unique_copy_ctor_func_t) zend_class_copy_ctor);
}
/* we must first to copy all classes and then prepare functions, since functions may try to bind
classes - which depend on pre-bind class entries existent in the class table */
- if (zend_hash_num_elements(&persistent_script->function_table) > 0) {
- zend_accel_function_hash_copy_from_shm(CG(function_table), &persistent_script->function_table);
+ if (zend_hash_num_elements(&persistent_script->script.function_table) > 0) {
+ zend_accel_function_hash_copy_from_shm(CG(function_table), &persistent_script->script.function_table);
}
/* Register __COMPILER_HALT_OFFSET__ constant */
if (persistent_script->compiler_halt_offset != 0 &&
- persistent_script->full_path) {
+ persistent_script->script.filename) {
zend_string *name;
char haltoff[] = "__COMPILER_HALT_OFFSET__";
- name = zend_mangle_property_name(haltoff, sizeof(haltoff) - 1, ZSTR_VAL(persistent_script->full_path), ZSTR_LEN(persistent_script->full_path), 0);
+ name = zend_mangle_property_name(haltoff, sizeof(haltoff) - 1, ZSTR_VAL(persistent_script->script.filename), ZSTR_LEN(persistent_script->script.filename), 0);
if (!zend_hash_exists(EG(zend_constants), name)) {
zend_register_long_constant(ZSTR_VAL(name), ZSTR_LEN(name), persistent_script->compiler_halt_offset, CONST_CS, 0);
}
@@ -726,17 +676,17 @@ zend_op_array* zend_accel_load_script(zend_persistent_script *persistent_script,
zend_hash_destroy(&ZCG(bind_hash));
ZCG(current_persistent_script) = NULL;
} else /* if (!from_shared_memory) */ {
- if (zend_hash_num_elements(&persistent_script->function_table) > 0) {
- zend_accel_function_hash_copy(CG(function_table), &persistent_script->function_table);
+ if (zend_hash_num_elements(&persistent_script->script.function_table) > 0) {
+ zend_accel_function_hash_copy(CG(function_table), &persistent_script->script.function_table);
}
- if (zend_hash_num_elements(&persistent_script->class_table) > 0) {
- zend_accel_class_hash_copy(CG(class_table), &persistent_script->class_table, NULL);
+ if (zend_hash_num_elements(&persistent_script->script.class_table) > 0) {
+ zend_accel_class_hash_copy(CG(class_table), &persistent_script->script.class_table, NULL);
}
}
if (op_array->early_binding != (uint32_t)-1) {
zend_string *orig_compiled_filename = CG(compiled_filename);
- CG(compiled_filename) = persistent_script->full_path;
+ CG(compiled_filename) = persistent_script->script.filename;
zend_do_delayed_early_binding(op_array);
CG(compiled_filename) = orig_compiled_filename;
}
diff --git a/ext/opcache/zend_file_cache.c b/ext/opcache/zend_file_cache.c
index 2b65685d21..dea427fcaf 100644
--- a/ext/opcache/zend_file_cache.c
+++ b/ext/opcache/zend_file_cache.c
@@ -368,7 +368,7 @@ static void zend_file_cache_serialize_op_array(zend_op_array *op_arra
SERIALIZE_PTR(op_array->vars);
SERIALIZE_STR(op_array->function_name);
SERIALIZE_STR(op_array->filename);
- SERIALIZE_PTR(op_array->brk_cont_array);
+ SERIALIZE_PTR(op_array->live_range);
SERIALIZE_PTR(op_array->scope);
SERIALIZE_STR(op_array->doc_comment);
SERIALIZE_PTR(op_array->try_catch_array);
@@ -392,7 +392,6 @@ static void zend_file_cache_serialize_op_array(zend_op_array *op_arra
}
if (!IS_SERIALIZED(op_array->opcodes)) {
-#if ZEND_USE_ABS_CONST_ADDR || ZEND_USE_ABS_JMP_ADDR
zend_op *opline, *end;
SERIALIZE_PTR(op_array->opcodes);
@@ -400,20 +399,18 @@ static void zend_file_cache_serialize_op_array(zend_op_array *op_arra
UNSERIALIZE_PTR(opline);
end = opline + op_array->last;
while (opline < end) {
-# if ZEND_USE_ABS_CONST_ADDR
- if (ZEND_OP1_TYPE(opline) == IS_CONST) {
+#if ZEND_USE_ABS_CONST_ADDR
+ if (opline->op1_type == IS_CONST) {
SERIALIZE_PTR(opline->op1.zv);
}
- if (ZEND_OP2_TYPE(opline) == IS_CONST) {
+ if (opline->op2_type == IS_CONST) {
SERIALIZE_PTR(opline->op2.zv);
}
-# endif
-# if ZEND_USE_ABS_JMP_ADDR
+#endif
+#if ZEND_USE_ABS_JMP_ADDR
switch (opline->opcode) {
case ZEND_JMP:
case ZEND_FAST_CALL:
- case ZEND_DECLARE_ANON_CLASS:
- case ZEND_DECLARE_ANON_INHERITED_CLASS:
SERIALIZE_PTR(opline->op1.jmp_addr);
break;
case ZEND_JMPZNZ:
@@ -425,23 +422,22 @@ static void zend_file_cache_serialize_op_array(zend_op_array *op_arra
case ZEND_JMPNZ_EX:
case ZEND_JMP_SET:
case ZEND_COALESCE:
- case ZEND_NEW:
case ZEND_FE_RESET_R:
case ZEND_FE_RESET_RW:
case ZEND_ASSERT_CHECK:
SERIALIZE_PTR(opline->op2.jmp_addr);
break;
+ case ZEND_DECLARE_ANON_CLASS:
+ case ZEND_DECLARE_ANON_INHERITED_CLASS:
case ZEND_FE_FETCH_R:
case ZEND_FE_FETCH_RW:
/* relative extended_value don't have to be changed */
break;
}
-# endif
+#endif
+ zend_serialize_opcode_handler(opline);
opline++;
}
-#else
- SERIALIZE_PTR(op_array->opcodes);
-#endif
if (op_array->arg_info) {
zend_arg_info *p, *end;
@@ -483,7 +479,7 @@ static void zend_file_cache_serialize_op_array(zend_op_array *op_arra
SERIALIZE_STR(op_array->function_name);
SERIALIZE_STR(op_array->filename);
- SERIALIZE_PTR(op_array->brk_cont_array);
+ SERIALIZE_PTR(op_array->live_range);
SERIALIZE_PTR(op_array->scope);
SERIALIZE_STR(op_array->doc_comment);
SERIALIZE_PTR(op_array->try_catch_array);
@@ -528,6 +524,28 @@ static void zend_file_cache_serialize_prop_info(zval *zv,
}
}
+static void zend_file_cache_serialize_class_constant(zval *zv,
+ zend_persistent_script *script,
+ zend_file_cache_metainfo *info,
+ void *buf)
+{
+ if (!IS_SERIALIZED(Z_PTR_P(zv))) {
+ zend_class_constant *c;
+
+ SERIALIZE_PTR(Z_PTR_P(zv));
+ c = Z_PTR_P(zv);
+ UNSERIALIZE_PTR(c);
+
+ zend_file_cache_serialize_zval(&c->value, script, info, buf);
+ if (c->ce && !IS_SERIALIZED(c->ce)) {
+ SERIALIZE_PTR(c->ce);
+ }
+ if (c->doc_comment && !IS_SERIALIZED(c->doc_comment)) {
+ SERIALIZE_STR(c->doc_comment);
+ }
+ }
+}
+
static void zend_file_cache_serialize_class(zval *zv,
zend_persistent_script *script,
zend_file_cache_metainfo *info,
@@ -565,9 +583,9 @@ static void zend_file_cache_serialize_class(zval *zv,
p++;
}
}
- zend_file_cache_serialize_hash(&ce->constants_table, script, info, buf, zend_file_cache_serialize_zval);
- SERIALIZE_STR(ZEND_CE_FILENAME(ce));
- SERIALIZE_STR(ZEND_CE_DOC_COMMENT(ce));
+ zend_file_cache_serialize_hash(&ce->constants_table, script, info, buf, zend_file_cache_serialize_class_constant);
+ SERIALIZE_STR(ce->info.user.filename);
+ SERIALIZE_STR(ce->info.user.doc_comment);
zend_file_cache_serialize_hash(&ce->properties_info, script, info, buf, zend_file_cache_serialize_prop_info);
if (ce->trait_aliases) {
@@ -679,11 +697,11 @@ static void zend_file_cache_serialize(zend_persistent_script *script,
memcpy(buf, script->mem, script->size);
new_script = (zend_persistent_script*)((char*)buf + info->script_offset);
- SERIALIZE_STR(new_script->full_path);
+ SERIALIZE_STR(new_script->script.filename);
- zend_file_cache_serialize_hash(&new_script->class_table, script, info, buf, zend_file_cache_serialize_class);
- zend_file_cache_serialize_hash(&new_script->function_table, script, info, buf, zend_file_cache_serialize_func);
- zend_file_cache_serialize_op_array(&new_script->main_op_array, script, info, buf);
+ zend_file_cache_serialize_hash(&new_script->script.class_table, script, info, buf, zend_file_cache_serialize_class);
+ zend_file_cache_serialize_hash(&new_script->script.function_table, script, info, buf, zend_file_cache_serialize_func);
+ zend_file_cache_serialize_op_array(&new_script->script.main_op_array, script, info, buf);
SERIALIZE_PTR(new_script->arena_mem);
new_script->mem = NULL;
@@ -731,7 +749,7 @@ int zend_file_cache_script_store(zend_persistent_script *script, int in_shm)
#endif
void *mem, *buf;
- filename = zend_file_cache_get_bin_file_path(script->full_path);
+ filename = zend_file_cache_get_bin_file_path(script->script.filename);
if (zend_file_cache_mkdir(filename, strlen(ZCG(accel_directives).file_cache)) != SUCCESS) {
zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot create directory for file '%s'\n", filename);
@@ -946,7 +964,7 @@ static void zend_file_cache_unserialize_op_array(zend_op_array *op_arr
UNSERIALIZE_PTR(op_array->vars);
UNSERIALIZE_STR(op_array->function_name);
UNSERIALIZE_STR(op_array->filename);
- UNSERIALIZE_PTR(op_array->brk_cont_array);
+ UNSERIALIZE_PTR(op_array->live_range);
UNSERIALIZE_PTR(op_array->scope);
UNSERIALIZE_STR(op_array->doc_comment);
UNSERIALIZE_PTR(op_array->try_catch_array);
@@ -974,10 +992,10 @@ static void zend_file_cache_unserialize_op_array(zend_op_array *op_arr
end = opline + op_array->last;
while (opline < end) {
# if ZEND_USE_ABS_CONST_ADDR
- if (ZEND_OP1_TYPE(opline) == IS_CONST) {
+ if (opline->op1_type == IS_CONST) {
UNSERIALIZE_PTR(opline->op1.zv);
}
- if (ZEND_OP2_TYPE(opline) == IS_CONST) {
+ if (opline->op2_type == IS_CONST) {
UNSERIALIZE_PTR(opline->op2.zv);
}
# endif
@@ -985,8 +1003,6 @@ static void zend_file_cache_unserialize_op_array(zend_op_array *op_arr
switch (opline->opcode) {
case ZEND_JMP:
case ZEND_FAST_CALL:
- case ZEND_DECLARE_ANON_CLASS:
- case ZEND_DECLARE_ANON_INHERITED_CLASS:
UNSERIALIZE_PTR(opline->op1.jmp_addr);
break;
case ZEND_JMPZNZ:
@@ -998,19 +1014,20 @@ static void zend_file_cache_unserialize_op_array(zend_op_array *op_arr
case ZEND_JMPNZ_EX:
case ZEND_JMP_SET:
case ZEND_COALESCE:
- case ZEND_NEW:
case ZEND_FE_RESET_R:
case ZEND_FE_RESET_RW:
case ZEND_ASSERT_CHECK:
UNSERIALIZE_PTR(opline->op2.jmp_addr);
break;
+ case ZEND_DECLARE_ANON_CLASS:
+ case ZEND_DECLARE_ANON_INHERITED_CLASS:
case ZEND_FE_FETCH_R:
case ZEND_FE_FETCH_RW:
/* relative extended_value don't have to be changed */
break;
}
# endif
- ZEND_VM_SET_OPCODE_HANDLER(opline);
+ zend_deserialize_opcode_handler(opline);
opline++;
}
@@ -1052,7 +1069,7 @@ static void zend_file_cache_unserialize_op_array(zend_op_array *op_arr
UNSERIALIZE_STR(op_array->function_name);
UNSERIALIZE_STR(op_array->filename);
- UNSERIALIZE_PTR(op_array->brk_cont_array);
+ UNSERIALIZE_PTR(op_array->live_range);
UNSERIALIZE_PTR(op_array->scope);
UNSERIALIZE_STR(op_array->doc_comment);
UNSERIALIZE_PTR(op_array->try_catch_array);
@@ -1093,6 +1110,26 @@ static void zend_file_cache_unserialize_prop_info(zval *zv,
}
}
+static void zend_file_cache_unserialize_class_constant(zval *zv,
+ zend_persistent_script *script,
+ void *buf)
+{
+ if (!IS_UNSERIALIZED(Z_PTR_P(zv))) {
+ zend_class_constant *c;
+
+ UNSERIALIZE_PTR(Z_PTR_P(zv));
+ c = Z_PTR_P(zv);
+
+ zend_file_cache_unserialize_zval(&c->value, script, buf);
+ if (c->ce && !IS_UNSERIALIZED(c->ce)) {
+ UNSERIALIZE_PTR(c->ce);
+ }
+ if (c->doc_comment && !IS_UNSERIALIZED(c->doc_comment)) {
+ UNSERIALIZE_STR(c->doc_comment);
+ }
+ }
+}
+
static void zend_file_cache_unserialize_class(zval *zv,
zend_persistent_script *script,
void *buf)
@@ -1128,9 +1165,9 @@ static void zend_file_cache_unserialize_class(zval *zv,
}
}
zend_file_cache_unserialize_hash(&ce->constants_table,
- script, buf, zend_file_cache_unserialize_zval, NULL);
- UNSERIALIZE_STR(ZEND_CE_FILENAME(ce));
- UNSERIALIZE_STR(ZEND_CE_DOC_COMMENT(ce));
+ script, buf, zend_file_cache_unserialize_class_constant, NULL);
+ UNSERIALIZE_STR(ce->info.user.filename);
+ UNSERIALIZE_STR(ce->info.user.doc_comment);
zend_file_cache_unserialize_hash(&ce->properties_info,
script, buf, zend_file_cache_unserialize_prop_info, ZVAL_PTR_DTOR);
@@ -1230,13 +1267,13 @@ static void zend_file_cache_unserialize(zend_persistent_script *script,
{
script->mem = buf;
- UNSERIALIZE_STR(script->full_path);
+ UNSERIALIZE_STR(script->script.filename);
- zend_file_cache_unserialize_hash(&script->class_table,
+ zend_file_cache_unserialize_hash(&script->script.class_table,
script, buf, zend_file_cache_unserialize_class, ZEND_CLASS_DTOR);
- zend_file_cache_unserialize_hash(&script->function_table,
+ zend_file_cache_unserialize_hash(&script->script.function_table,
script, buf, zend_file_cache_unserialize_func, ZEND_FUNCTION_DTOR);
- zend_file_cache_unserialize_op_array(&script->main_op_array, script, buf);
+ zend_file_cache_unserialize_op_array(&script->script.main_op_array, script, buf);
UNSERIALIZE_PTR(script->arena_mem);
}
@@ -1399,7 +1436,7 @@ use_process_mem:
script->dynamic_members.checksum = zend_accel_script_checksum(script);
script->dynamic_members.last_used = ZCG(request_time);
- zend_accel_hash_update(&ZCSG(hash), ZSTR_VAL(script->full_path), ZSTR_LEN(script->full_path), 0, script);
+ zend_accel_hash_update(&ZCSG(hash), ZSTR_VAL(script->script.filename), ZSTR_LEN(script->script.filename), 0, script);
zend_shared_alloc_unlock();
zend_arena_release(&CG(arena), checkpoint);
diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c
index eca90b30ef..9574e43d6f 100644
--- a/ext/opcache/zend_persist.c
+++ b/ext/opcache/zend_persist.c
@@ -77,7 +77,6 @@
typedef void (*zend_persist_func_t)(zval*);
static void zend_persist_zval(zval *z);
-static void zend_persist_zval_const(zval *z);
static const uint32_t uninitialized_bucket[-HT_MIN_MASK] =
{HT_INVALID_IDX, HT_INVALID_IDX};
@@ -102,21 +101,21 @@ static void zend_hash_persist(HashTable *ht, zend_persist_func_t pPersistElement
void *data = HT_GET_DATA_ADDR(ht);
zend_accel_store(data, HT_USED_SIZE(ht));
HT_SET_DATA_ADDR(ht, data);
- } else if (ht->nNumUsed < -(int32_t)ht->nTableMask / 2) {
+ } else if (ht->nNumUsed < (uint32_t)(-(int32_t)ht->nTableMask) / 2) {
/* compact table */
void *old_data = HT_GET_DATA_ADDR(ht);
Bucket *old_buckets = ht->arData;
- int32_t hash_size;
+ uint32_t hash_size;
if (ht->nNumUsed <= HT_MIN_SIZE) {
hash_size = HT_MIN_SIZE;
} else {
- hash_size = -(int32_t)ht->nTableMask;
+ hash_size = (uint32_t)(-(int32_t)ht->nTableMask);
while (hash_size >> 1 > ht->nNumUsed) {
hash_size >>= 1;
}
}
- ht->nTableMask = -hash_size;
+ ht->nTableMask = (uint32_t)(-(int32_t)hash_size);
ZEND_ASSERT(((zend_uintptr_t)ZCG(mem) & 0x7) == 0); /* should be 8 byte aligned */
HT_SET_DATA_ADDR(ht, ZCG(mem));
ZCG(mem) = (void*)((char*)ZCG(mem) + ZEND_ALIGNED_SIZE((hash_size * sizeof(uint32_t)) + (ht->nNumUsed * sizeof(Bucket))));
@@ -184,21 +183,21 @@ static void zend_hash_persist_immutable(HashTable *ht)
}
if (ht->u.flags & HASH_FLAG_PACKED) {
HT_SET_DATA_ADDR(ht, zend_accel_memdup(HT_GET_DATA_ADDR(ht), HT_USED_SIZE(ht)));
- } else if (ht->nNumUsed < -(int32_t)ht->nTableMask / 2) {
+ } else if (ht->nNumUsed < (uint32_t)(-(int32_t)ht->nTableMask) / 2) {
/* compact table */
void *old_data = HT_GET_DATA_ADDR(ht);
Bucket *old_buckets = ht->arData;
- int32_t hash_size;
+ uint32_t hash_size;
if (ht->nNumUsed <= HT_MIN_SIZE) {
hash_size = HT_MIN_SIZE;
} else {
- hash_size = -(int32_t)ht->nTableMask;
+ hash_size = (uint32_t)(-(int32_t)ht->nTableMask);
while (hash_size >> 1 > ht->nNumUsed) {
hash_size >>= 1;
}
}
- ht->nTableMask = -hash_size;
+ ht->nTableMask = (uint32_t)(-(int32_t)hash_size);
ZEND_ASSERT(((zend_uintptr_t)ZCG(mem) & 0x7) == 0); /* should be 8 byte aligned */
HT_SET_DATA_ADDR(ht, ZCG(mem));
ZCG(mem) = (void*)((char*)ZCG(mem) + (hash_size * sizeof(uint32_t)) + (ht->nNumUsed * sizeof(Bucket)));
@@ -216,7 +215,7 @@ static void zend_hash_persist_immutable(HashTable *ht)
}
/* persist the data itself */
- zend_persist_zval_const(&p->val);
+ zend_persist_zval(&p->val);
nIndex = p->h | ht->nTableMask;
Z_NEXT(p->val) = HT_HASH(ht, nIndex);
@@ -241,7 +240,7 @@ static void zend_hash_persist_immutable(HashTable *ht)
}
/* persist the data itself */
- zend_persist_zval_const(&p->val);
+ zend_persist_zval(&p->val);
}
}
@@ -290,63 +289,10 @@ static void zend_persist_zval(zval *z)
zend_accel_store_interned_string(Z_STR_P(z));
Z_GC_FLAGS_P(z) |= flags;
Z_TYPE_FLAGS_P(z) &= ~(IS_TYPE_REFCOUNTED | IS_TYPE_COPYABLE);
- break;
- case IS_ARRAY:
- new_ptr = zend_shared_alloc_get_xlat_entry(Z_ARR_P(z));
- if (new_ptr) {
- Z_ARR_P(z) = new_ptr;
- Z_TYPE_FLAGS_P(z) = IS_TYPE_IMMUTABLE;
- } else {
- if (Z_IMMUTABLE_P(z)) {
- Z_ARR_P(z) = zend_accel_memdup(Z_ARR_P(z), sizeof(zend_array));
- zend_hash_persist_immutable(Z_ARRVAL_P(z));
- } else {
- GC_REMOVE_FROM_BUFFER(Z_ARR_P(z));
- zend_accel_store(Z_ARR_P(z), sizeof(zend_array));
- zend_hash_persist(Z_ARRVAL_P(z), zend_persist_zval);
- /* make immutable array */
- Z_TYPE_FLAGS_P(z) = IS_TYPE_IMMUTABLE;
- GC_REFCOUNT(Z_COUNTED_P(z)) = 2;
- GC_FLAGS(Z_COUNTED_P(z)) |= IS_ARRAY_IMMUTABLE;
- Z_ARRVAL_P(z)->u.flags |= HASH_FLAG_STATIC_KEYS;
- Z_ARRVAL_P(z)->u.flags &= ~HASH_FLAG_APPLY_PROTECTION;
- }
- }
- break;
- case IS_REFERENCE:
- new_ptr = zend_shared_alloc_get_xlat_entry(Z_REF_P(z));
- if (new_ptr) {
- Z_REF_P(z) = new_ptr;
- } else {
- zend_accel_store(Z_REF_P(z), sizeof(zend_reference));
- zend_persist_zval(Z_REFVAL_P(z));
+ if (Z_TYPE_P(z) == IS_CONSTANT) {
+ Z_TYPE_FLAGS_P(z) |= IS_TYPE_IMMUTABLE;
}
break;
- case IS_CONSTANT_AST:
- new_ptr = zend_shared_alloc_get_xlat_entry(Z_AST_P(z));
- if (new_ptr) {
- Z_AST_P(z) = new_ptr;
- } else {
- zend_accel_store(Z_AST_P(z), sizeof(zend_ast_ref));
- Z_ASTVAL_P(z) = zend_persist_ast(Z_ASTVAL_P(z));
- }
- break;
- }
-}
-
-static void zend_persist_zval_static(zval *z)
-{
- zend_uchar flags;
- void *new_ptr;
-
- switch (Z_TYPE_P(z)) {
- case IS_STRING:
- case IS_CONSTANT:
- flags = Z_GC_FLAGS_P(z) & ~ (IS_STR_PERSISTENT | IS_STR_INTERNED | IS_STR_PERMANENT);
- zend_accel_store_interned_string(Z_STR_P(z));
- Z_GC_FLAGS_P(z) |= flags;
- Z_TYPE_FLAGS_P(z) &= ~(IS_TYPE_REFCOUNTED | IS_TYPE_COPYABLE);
- break;
case IS_ARRAY:
new_ptr = zend_shared_alloc_get_xlat_entry(Z_ARR_P(z));
if (new_ptr) {
@@ -393,62 +339,6 @@ static void zend_persist_zval_static(zval *z)
}
}
-static void zend_persist_zval_const(zval *z)
-{
- zend_uchar flags;
- void *new_ptr;
-
- switch (Z_TYPE_P(z)) {
- case IS_STRING:
- case IS_CONSTANT:
- flags = Z_GC_FLAGS_P(z) & ~ (IS_STR_PERSISTENT | IS_STR_INTERNED | IS_STR_PERMANENT);
- zend_accel_memdup_interned_string(Z_STR_P(z));
- Z_GC_FLAGS_P(z) |= flags;
- Z_TYPE_FLAGS_P(z) &= ~(IS_TYPE_REFCOUNTED | IS_TYPE_COPYABLE);
- break;
- case IS_ARRAY:
- new_ptr = zend_shared_alloc_get_xlat_entry(Z_ARR_P(z));
- if (new_ptr) {
- Z_ARR_P(z) = new_ptr;
- Z_TYPE_FLAGS_P(z) = IS_TYPE_IMMUTABLE;
- } else {
- if (Z_IMMUTABLE_P(z)) {
- Z_ARR_P(z) = zend_accel_memdup(Z_ARR_P(z), sizeof(zend_array));
- zend_hash_persist_immutable(Z_ARRVAL_P(z));
- } else {
- GC_REMOVE_FROM_BUFFER(Z_ARR_P(z));
- zend_accel_store(Z_ARR_P(z), sizeof(zend_array));
- zend_hash_persist(Z_ARRVAL_P(z), zend_persist_zval);
- /* make immutable array */
- Z_TYPE_FLAGS_P(z) = IS_TYPE_IMMUTABLE;
- GC_REFCOUNT(Z_COUNTED_P(z)) = 2;
- GC_FLAGS(Z_COUNTED_P(z)) |= IS_ARRAY_IMMUTABLE;
- Z_ARRVAL_P(z)->u.flags |= HASH_FLAG_STATIC_KEYS;
- Z_ARRVAL_P(z)->u.flags &= ~HASH_FLAG_APPLY_PROTECTION;
- }
- }
- break;
- case IS_REFERENCE:
- new_ptr = zend_shared_alloc_get_xlat_entry(Z_REF_P(z));
- if (new_ptr) {
- Z_REF_P(z) = new_ptr;
- } else {
- zend_accel_store(Z_REF_P(z), sizeof(zend_reference));
- zend_persist_zval(Z_REFVAL_P(z));
- }
- break;
- case IS_CONSTANT_AST:
- new_ptr = zend_shared_alloc_get_xlat_entry(Z_AST_P(z));
- if (new_ptr) {
- Z_AST_P(z) = new_ptr;
- } else {
- zend_accel_store(Z_AST_P(z), sizeof(zend_ast_ref));
- Z_ASTVAL_P(z) = zend_persist_ast(Z_ASTVAL_P(z));
- }
- break;
- }
-}
-
static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_script* main_persistent_script)
{
int already_stored = 0;
@@ -484,7 +374,7 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc
if (stored) {
op_array->static_variables = stored;
} else {
- zend_hash_persist(op_array->static_variables, zend_persist_zval_static);
+ zend_hash_persist(op_array->static_variables, zend_persist_zval);
zend_accel_store(op_array->static_variables, sizeof(HashTable));
/* make immutable array */
GC_REFCOUNT(op_array->static_variables) = 2;
@@ -529,22 +419,20 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc
for (; opline < end ; opline++, offset++) {
# if ZEND_USE_ABS_CONST_ADDR
- if (ZEND_OP1_TYPE(opline) == IS_CONST) {
+ if (opline->op1_type == IS_CONST) {
opline->op1.zv = (zval*)((char*)opline->op1.zv + ((char*)op_array->literals - (char*)orig_literals));
}
- if (ZEND_OP2_TYPE(opline) == IS_CONST) {
+ if (opline->op2_type == IS_CONST) {
opline->op2.zv = (zval*)((char*)opline->op2.zv + ((char*)op_array->literals - (char*)orig_literals));
}
# endif
# if ZEND_USE_ABS_JMP_ADDR
- if (ZEND_DONE_PASS_TWO(op_array)) {
+ if (op_array->fn_flags & ZEND_ACC_DONE_PASS_TWO) {
/* fix jumps to point to new array */
switch (opline->opcode) {
case ZEND_JMP:
case ZEND_FAST_CALL:
- case ZEND_DECLARE_ANON_CLASS:
- case ZEND_DECLARE_ANON_INHERITED_CLASS:
- ZEND_OP1(opline).jmp_addr = &new_opcodes[ZEND_OP1(opline).jmp_addr - op_array->opcodes];
+ opline->op1.jmp_addr = &new_opcodes[opline->op1.jmp_addr - op_array->opcodes];
break;
case ZEND_JMPZNZ:
/* relative extended_value don't have to be changed */
@@ -555,12 +443,13 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc
case ZEND_JMPNZ_EX:
case ZEND_JMP_SET:
case ZEND_COALESCE:
- case ZEND_NEW:
case ZEND_FE_RESET_R:
case ZEND_FE_RESET_RW:
case ZEND_ASSERT_CHECK:
- ZEND_OP2(opline).jmp_addr = &new_opcodes[ZEND_OP2(opline).jmp_addr - op_array->opcodes];
+ opline->op2.jmp_addr = &new_opcodes[opline->op2.jmp_addr - op_array->opcodes];
break;
+ case ZEND_DECLARE_ANON_CLASS:
+ case ZEND_DECLARE_ANON_INHERITED_CLASS:
case ZEND_FE_FETCH_R:
case ZEND_FE_FETCH_RW:
/* relative extended_value don't have to be changed */
@@ -629,8 +518,8 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc
op_array->arg_info = arg_info;
}
- if (op_array->brk_cont_array) {
- zend_accel_store(op_array->brk_cont_array, sizeof(zend_brk_cont_element) * op_array->last_brk_cont);
+ if (op_array->live_range) {
+ zend_accel_store(op_array->live_range, sizeof(zend_live_range) * op_array->last_live_range);
}
if (op_array->scope) {
@@ -728,6 +617,39 @@ static void zend_persist_property_info(zval *zv)
}
}
+static void zend_persist_class_constant(zval *zv)
+{
+ zend_class_constant *c = zend_shared_alloc_get_xlat_entry(Z_PTR_P(zv));
+
+ if (c) {
+ Z_PTR_P(zv) = c;
+ return;
+ }
+ memcpy(ZCG(arena_mem), Z_PTR_P(zv), sizeof(zend_class_constant));
+ zend_shared_alloc_register_xlat_entry(Z_PTR_P(zv), ZCG(arena_mem));
+ c = Z_PTR_P(zv) = ZCG(arena_mem);
+ ZCG(arena_mem) = (void*)((char*)ZCG(arena_mem) + ZEND_ALIGNED_SIZE(sizeof(zend_class_constant)));
+ zend_persist_zval(&c->value);
+ c->ce = zend_shared_alloc_get_xlat_entry(c->ce);
+ if (c->doc_comment) {
+ if (ZCG(accel_directives).save_comments) {
+ zend_string *doc_comment = zend_shared_alloc_get_xlat_entry(c->doc_comment);
+ if (doc_comment) {
+ c->doc_comment = doc_comment;
+ } else {
+ zend_accel_store_string(c->doc_comment);
+ }
+ } else {
+ zend_string *doc_comment = zend_shared_alloc_get_xlat_entry(c->doc_comment);
+ if (!doc_comment) {
+ zend_shared_alloc_register_xlat_entry(c->doc_comment, c->doc_comment);
+ zend_string_release(c->doc_comment);
+ }
+ c->doc_comment = NULL;
+ }
+ }
+}
+
static void zend_persist_class_entry(zval *zv)
{
zend_class_entry *ce = Z_PTR_P(zv);
@@ -757,21 +679,21 @@ static void zend_persist_class_entry(zval *zv)
}
ce->static_members_table = NULL;
- zend_hash_persist(&ce->constants_table, zend_persist_zval);
+ zend_hash_persist(&ce->constants_table, zend_persist_class_constant);
- if (ZEND_CE_FILENAME(ce)) {
+ if (ce->info.user.filename) {
/* do not free! PHP has centralized filename storage, compiler will free it */
- zend_accel_memdup_string(ZEND_CE_FILENAME(ce));
+ zend_accel_memdup_string(ce->info.user.filename);
}
- if (ZEND_CE_DOC_COMMENT(ce)) {
+ if (ce->info.user.doc_comment) {
if (ZCG(accel_directives).save_comments) {
- zend_accel_store_string(ZEND_CE_DOC_COMMENT(ce));
+ zend_accel_store_string(ce->info.user.doc_comment);
} else {
- if (!zend_shared_alloc_get_xlat_entry(ZEND_CE_DOC_COMMENT(ce))) {
- zend_shared_alloc_register_xlat_entry(ZEND_CE_DOC_COMMENT(ce), ZEND_CE_DOC_COMMENT(ce));
- zend_string_release(ZEND_CE_DOC_COMMENT(ce));
+ if (!zend_shared_alloc_get_xlat_entry(ce->info.user.doc_comment)) {
+ zend_shared_alloc_register_xlat_entry(ce->info.user.doc_comment, ce->info.user.doc_comment);
+ zend_string_release(ce->info.user.doc_comment);
}
- ZEND_CE_DOC_COMMENT(ce) = NULL;
+ ce->info.user.doc_comment = NULL;
}
}
zend_hash_persist(&ce->properties_info, zend_persist_property_info);
@@ -918,7 +840,7 @@ zend_persistent_script *zend_accel_script_persist(zend_persistent_script *script
if (key && *key) {
*key = zend_accel_memdup(*key, key_length + 1);
}
- zend_accel_store_string(script->full_path);
+ zend_accel_store_string(script->script.filename);
#ifdef __SSE2__
/* Align to 64-byte boundary */
@@ -930,9 +852,9 @@ zend_persistent_script *zend_accel_script_persist(zend_persistent_script *script
script->arena_mem = ZCG(arena_mem) = ZCG(mem);
ZCG(mem) = (void*)((char*)ZCG(mem) + script->arena_size);
- zend_accel_persist_class_table(&script->class_table);
- zend_hash_persist(&script->function_table, zend_persist_op_array);
- zend_persist_op_array_ex(&script->main_op_array, script);
+ zend_accel_persist_class_table(&script->script.class_table);
+ zend_hash_persist(&script->script.function_table, zend_persist_op_array);
+ zend_persist_op_array_ex(&script->script.main_op_array, script);
return script;
}
diff --git a/ext/opcache/zend_persist_calc.c b/ext/opcache/zend_persist_calc.c
index 369c57cf84..d4f1e56c4f 100644
--- a/ext/opcache/zend_persist_calc.c
+++ b/ext/opcache/zend_persist_calc.c
@@ -60,14 +60,14 @@ static void zend_hash_persist_calc(HashTable *ht, void (*pPersistElement)(zval *
return;
}
- if (!(ht->u.flags & HASH_FLAG_PACKED) && ht->nNumUsed < -(int32_t)ht->nTableMask / 2) {
+ if (!(ht->u.flags & HASH_FLAG_PACKED) && ht->nNumUsed < (uint32_t)(-(int32_t)ht->nTableMask) / 2) {
/* compact table */
- int32_t hash_size;
+ uint32_t hash_size;
if (ht->nNumUsed <= HT_MIN_SIZE) {
hash_size = HT_MIN_SIZE;
} else {
- hash_size = -(int32_t)ht->nTableMask;
+ hash_size = (uint32_t)(-(int32_t)ht->nTableMask);
while (hash_size >> 1 > ht->nNumUsed) {
hash_size >>= 1;
}
@@ -236,8 +236,8 @@ static void zend_persist_op_array_calc_ex(zend_op_array *op_array)
}
}
- if (op_array->brk_cont_array) {
- ADD_DUP_SIZE(op_array->brk_cont_array, sizeof(zend_brk_cont_element) * op_array->last_brk_cont);
+ if (op_array->live_range) {
+ ADD_DUP_SIZE(op_array->live_range, sizeof(zend_live_range) * op_array->last_live_range);
}
if (ZCG(accel_directives).save_comments && op_array->doc_comment) {
@@ -294,6 +294,21 @@ static void zend_persist_property_info_calc(zval *zv)
}
}
+static void zend_persist_class_constant_calc(zval *zv)
+{
+ zend_class_constant *c = Z_PTR_P(zv);
+
+ if (!zend_shared_alloc_get_xlat_entry(c)) {
+ zend_shared_alloc_register_xlat_entry(c, c);
+ ADD_ARENA_SIZE(sizeof(zend_class_constant));
+ zend_persist_zval_calc(&c->value);
+ if (ZCG(accel_directives).save_comments && c->doc_comment) {
+ ADD_STRING(c->doc_comment);
+ }
+ }
+}
+
+
static void zend_persist_class_entry_calc(zval *zv)
{
zend_class_entry *ce = Z_PTR_P(zv);
@@ -318,13 +333,13 @@ static void zend_persist_class_entry_calc(zval *zv)
zend_persist_zval_calc(&ce->default_static_members_table[i]);
}
}
- zend_hash_persist_calc(&ce->constants_table, zend_persist_zval_calc);
+ zend_hash_persist_calc(&ce->constants_table, zend_persist_class_constant_calc);
- if (ZEND_CE_FILENAME(ce)) {
- ADD_STRING(ZEND_CE_FILENAME(ce));
+ if (ce->info.user.filename) {
+ ADD_STRING(ce->info.user.filename);
}
- if (ZCG(accel_directives).save_comments && ZEND_CE_DOC_COMMENT(ce)) {
- ADD_STRING(ZEND_CE_DOC_COMMENT(ce));
+ if (ZCG(accel_directives).save_comments && ce->info.user.doc_comment) {
+ ADD_STRING(ce->info.user.doc_comment);
}
zend_hash_persist_calc(&ce->properties_info, zend_persist_property_info_calc);
@@ -397,16 +412,16 @@ uint zend_accel_script_persist_calc(zend_persistent_script *new_persistent_scrip
/* script is not going to be saved in SHM */
new_persistent_script->corrupted = 1;
}
- ADD_STRING(new_persistent_script->full_path);
+ ADD_STRING(new_persistent_script->script.filename);
#ifdef __SSE2__
/* Align size to 64-byte boundary */
new_persistent_script->size = (new_persistent_script->size + 63) & ~63;
#endif
- zend_accel_persist_class_table_calc(&new_persistent_script->class_table);
- zend_hash_persist_calc(&new_persistent_script->function_table, zend_persist_op_array_calc);
- zend_persist_op_array_calc_ex(&new_persistent_script->main_op_array);
+ zend_accel_persist_class_table_calc(&new_persistent_script->script.class_table);
+ zend_hash_persist_calc(&new_persistent_script->script.function_table, zend_persist_op_array_calc);
+ zend_persist_op_array_calc_ex(&new_persistent_script->script.main_op_array);
#ifdef __SSE2__
/* Align size to 64-byte boundary */
diff --git a/ext/opcache/zend_shared_alloc.c b/ext/opcache/zend_shared_alloc.c
index 3f2f048178..b7940ad39b 100644
--- a/ext/opcache/zend_shared_alloc.c
+++ b/ext/opcache/zend_shared_alloc.c
@@ -99,7 +99,7 @@ void zend_shared_alloc_create_lock(char *lockfile_path)
static void no_memory_bailout(size_t allocate_size, char *error)
{
- zend_accel_error(ACCEL_LOG_FATAL, "Unable to allocate shared memory segment of %ld bytes: %s: %s (%d)", allocate_size, error?error:"unknown", strerror(errno), errno );
+ zend_accel_error(ACCEL_LOG_FATAL, "Unable to allocate shared memory segment of %zu bytes: %s: %s (%d)", allocate_size, error?error:"unknown", strerror(errno), errno );
}
static void copy_shared_segments(void *to, void *from, int count, int size)
diff --git a/ext/openssl/config.w32 b/ext/openssl/config.w32
index c9608531b7..ade17d402a 100644
--- a/ext/openssl/config.w32
+++ b/ext/openssl/config.w32
@@ -4,15 +4,22 @@
ARG_WITH("openssl", "OpenSSL support", "no,shared");
if (PHP_OPENSSL != "no") {
- if (CHECK_LIB("ssleay32.lib", "openssl", PHP_OPENSSL) &&
- CHECK_LIB("libeay32.lib", "openssl", PHP_OPENSSL) &&
- CHECK_LIB("crypt32.lib", "openssl") &&
- CHECK_HEADER_ADD_INCLUDE("openssl/ssl.h", "CFLAGS_OPENSSL")) {
- EXTENSION("openssl", "openssl.c xp_ssl.c");
+ var ret = SETUP_OPENSSL("openssl", PHP_OPENSSL);
+ if (ret > 0) {
+ EXTENSION("openssl", "openssl.c xp_ssl.c");
AC_DEFINE("HAVE_OPENSSL_EXT", PHP_OPENSSL_SHARED ? 0 : 1, "Have openssl");
AC_DEFINE("HAVE_OPENSSL", 1);
- AC_DEFINE("HAVE_DSA_DEFAULT_METHOD", 1);
+
+ switch (ret) {
+ /* Openssl 1.0.x and lower */
+ case 1:
+ AC_DEFINE("HAVE_DSA_DEFAULT_METHOD", 1);
+ break;
+ /* Openssl 1.1.x */
+ case 2:
+ break;
+ }
}
}
diff --git a/ext/openssl/config0.m4 b/ext/openssl/config0.m4
index 01fc89b28d..a7bbf05f62 100644
--- a/ext/openssl/config0.m4
+++ b/ext/openssl/config0.m4
@@ -3,7 +3,7 @@ dnl $Id$
dnl
PHP_ARG_WITH(openssl, for OpenSSL support,
-[ --with-openssl[=DIR] Include OpenSSL support (requires OpenSSL >= 0.9.8)])
+[ --with-openssl[=DIR] Include OpenSSL support (requires OpenSSL >= 1.0.1)])
PHP_ARG_WITH(kerberos, for Kerberos support,
[ --with-kerberos[=DIR] OPENSSL: Include Kerberos support], no, no)
diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c
index 2107b9ba59..13e298ec87 100644
--- a/ext/openssl/openssl.c
+++ b/ext/openssl/openssl.c
@@ -76,19 +76,19 @@
#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined (LIBRESSL_VERSION_NUMBER)
#define OPENSSL_ALGO_DSS1 5
#endif
-#if OPENSSL_VERSION_NUMBER >= 0x0090708fL
#define OPENSSL_ALGO_SHA224 6
#define OPENSSL_ALGO_SHA256 7
#define OPENSSL_ALGO_SHA384 8
#define OPENSSL_ALGO_SHA512 9
#define OPENSSL_ALGO_RMD160 10
-#endif
#define DEBUG_SMIME 0
#if !defined(OPENSSL_NO_EC) && defined(EVP_PKEY_EC)
#define HAVE_EVP_PKEY_EC 1
#endif
+ZEND_DECLARE_MODULE_GLOBALS(openssl)
+
/* FIXME: Use the openssl constants instead of
* enum. It is now impossible to match real values
* against php constants. Also sorry to break the
@@ -119,6 +119,9 @@ enum php_openssl_cipher_type {
PHP_FUNCTION(openssl_get_md_methods);
PHP_FUNCTION(openssl_get_cipher_methods);
+#ifdef HAVE_EVP_PKEY_EC
+PHP_FUNCTION(openssl_get_curve_names);
+#endif
PHP_FUNCTION(openssl_digest);
PHP_FUNCTION(openssl_encrypt);
@@ -265,7 +268,6 @@ ZEND_BEGIN_ARG_INFO(arginfo_openssl_pkey_get_details, 0)
ZEND_ARG_INFO(0, key)
ZEND_END_ARG_INFO()
-#if OPENSSL_VERSION_NUMBER >= 0x10000000L
ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pbkdf2, 0, 0, 4)
ZEND_ARG_INFO(0, password)
ZEND_ARG_INFO(0, salt)
@@ -273,7 +275,6 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pbkdf2, 0, 0, 4)
ZEND_ARG_INFO(0, iterations)
ZEND_ARG_INFO(0, digest_algorithm)
ZEND_END_ARG_INFO()
-#endif
ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pkcs7_verify, 0, 0, 2)
ZEND_ARG_INFO(0, filename)
@@ -380,6 +381,11 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_get_cipher_methods, 0, 0, 0)
ZEND_ARG_INFO(0, aliases)
ZEND_END_ARG_INFO()
+#ifdef HAVE_EVP_PKEY_EC
+ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_get_curve_names, 0, 0, 0)
+ZEND_END_ARG_INFO()
+#endif
+
ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_digest, 0, 0, 2)
ZEND_ARG_INFO(0, data)
ZEND_ARG_INFO(0, method)
@@ -392,6 +398,9 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_encrypt, 0, 0, 3)
ZEND_ARG_INFO(0, password)
ZEND_ARG_INFO(0, options)
ZEND_ARG_INFO(0, iv)
+ ZEND_ARG_INFO(1, tag)
+ ZEND_ARG_INFO(0, aad)
+ ZEND_ARG_INFO(0, tag_length)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_decrypt, 0, 0, 3)
@@ -400,6 +409,8 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_decrypt, 0, 0, 3)
ZEND_ARG_INFO(0, password)
ZEND_ARG_INFO(0, options)
ZEND_ARG_INFO(0, iv)
+ ZEND_ARG_INFO(0, tag)
+ ZEND_ARG_INFO(0, aad)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(arginfo_openssl_cipher_iv_length, 0)
@@ -494,9 +505,7 @@ const zend_function_entry openssl_functions[] = {
PHP_FE(openssl_seal, arginfo_openssl_seal)
PHP_FE(openssl_open, arginfo_openssl_open)
-#if OPENSSL_VERSION_NUMBER >= 0x10000000L
PHP_FE(openssl_pbkdf2, arginfo_openssl_pbkdf2)
-#endif
/* for S/MIME handling */
PHP_FE(openssl_pkcs7_verify, arginfo_openssl_pkcs7_verify)
@@ -511,6 +520,9 @@ const zend_function_entry openssl_functions[] = {
PHP_FE(openssl_get_md_methods, arginfo_openssl_get_md_methods)
PHP_FE(openssl_get_cipher_methods, arginfo_openssl_get_cipher_methods)
+#ifdef HAVE_EVP_PKEY_EC
+ PHP_FE(openssl_get_curve_names, arginfo_openssl_get_curve_names)
+#endif
PHP_FE(openssl_dh_compute_key, arginfo_openssl_dh_compute_key)
@@ -532,7 +544,11 @@ zend_module_entry openssl_module_entry = {
NULL,
PHP_MINFO(openssl),
PHP_OPENSSL_VERSION,
- STANDARD_MODULE_PROPERTIES
+ PHP_MODULE_GLOBALS(openssl),
+ PHP_GINIT(openssl),
+ PHP_GSHUTDOWN(openssl),
+ NULL,
+ STANDARD_MODULE_PROPERTIES_EX
};
/* }}} */
@@ -665,20 +681,6 @@ static int X509_get_signature_nid(const X509 *x)
return OBJ_obj2nid(x->sig_alg->algorithm);
}
-#if OPENSSL_VERSION_NUMBER < 0x10000000L
-
-int EVP_PKEY_id(const EVP_PKEY *pkey)
-{
- return pkey->type;
-}
-
-int EVP_PKEY_base_id(const EVP_PKEY *pkey)
-{
- return EVP_PKEY_type(pkey->type);
-}
-
-#endif
-
#endif
#endif
@@ -702,6 +704,32 @@ int EVP_PKEY_base_id(const EVP_PKEY *pkey)
#define PHP_OPENSSL_CHECK_LONG_TO_INT(_var, _name) \
PHP_OPENSSL_CHECK_NUMBER_CONVERSION(ZEND_LONG_EXCEEDS_INT(_var), _name)
+/* {{{ php_openssl_store_errors */
+void php_openssl_store_errors()
+{
+ struct php_openssl_errors *errors;
+ int error_code = ERR_get_error();
+
+ if (!error_code) {
+ return;
+ }
+
+ if (!OPENSSL_G(errors)) {
+ OPENSSL_G(errors) = pecalloc(1, sizeof(struct php_openssl_errors), 1);
+ }
+
+ errors = OPENSSL_G(errors);
+
+ do {
+ errors->top = (errors->top + 1) % ERR_NUM_ERRORS;
+ if (errors->top == errors->bottom) {
+ errors->bottom = (errors->bottom + 1) % ERR_NUM_ERRORS;
+ }
+ errors->buffer[errors->top] = error_code;
+ } while ((error_code = ERR_get_error()));
+
+}
+/* }}} */
static int le_key;
static int le_x509;
@@ -763,13 +791,8 @@ int php_openssl_get_ssl_stream_data_index()
static char default_ssl_conf_filename[MAXPATHLEN];
struct php_x509_request { /* {{{ */
-#if OPENSSL_VERSION_NUMBER >= 0x10000002L
LHASH_OF(CONF_VALUE) * global_config; /* Global SSL config */
LHASH_OF(CONF_VALUE) * req_config; /* SSL config for this request */
-#else
- LHASH * global_config; /* Global SSL config */
- LHASH * req_config; /* SSL config for this request */
-#endif
const EVP_MD * md_alg;
const EVP_MD * digest;
char * section_name,
@@ -782,6 +805,10 @@ struct php_x509_request { /* {{{ */
int priv_key_encrypt;
+#ifdef HAVE_EVP_PKEY_EC
+ int curve_name;
+#endif
+
EVP_PKEY * priv_key;
const EVP_CIPHER * priv_key_encrypt_cipher;
@@ -857,6 +884,8 @@ static void add_assoc_name_entry(zval * val, char * key, X509_NAME * name, int s
* but we need a test to cover this part first */
add_assoc_stringl(&subitem, sname, (char *)to_add, to_add_len);
}
+ } else {
+ php_openssl_store_errors();
}
if (to_add_buf != NULL) {
@@ -882,7 +911,7 @@ static time_t asn1_time_to_time_t(ASN1_UTCTIME * timestr) /* {{{ */
This is how the time string is formatted:
snprintf(p, sizeof(p), "%02d%02d%02d%02d%02d%02dZ",ts->tm_year%100,
- ts->tm_mon+1,ts->tm_mday,ts->tm_hour,ts->tm_min,ts->tm_sec);
+ ts->tm_mon+1,ts->tm_mday,ts->tm_hour,ts->tm_min,ts->tm_sec);
*/
time_t ret;
@@ -975,17 +1004,14 @@ static time_t asn1_time_to_time_t(ASN1_UTCTIME * timestr) /* {{{ */
}
/* }}} */
-#if OPENSSL_VERSION_NUMBER >= 0x10000002L
static inline int php_openssl_config_check_syntax(const char * section_label, const char * config_filename, const char * section, LHASH_OF(CONF_VALUE) * config) /* {{{ */
-#else
-static inline int php_openssl_config_check_syntax(const char * section_label, const char * config_filename, const char * section, LHASH * config)
-#endif
{
X509V3_CTX ctx;
X509V3_set_ctx_test(&ctx);
X509V3_set_conf_lhash(&ctx, config);
if (!X509V3_EXT_add_conf(config, &ctx, (char *)section, NULL)) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "Error loading %s section %s of %s",
section_label,
section,
@@ -1005,10 +1031,12 @@ static int add_oid_section(struct php_x509_request * req) /* {{{ */
str = CONF_get_string(req->req_config, NULL, "oid_section");
if (str == NULL) {
+ php_openssl_store_errors();
return SUCCESS;
}
sktmp = CONF_get_section(req->req_config, str);
if (sktmp == NULL) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "problem loading oid section %s", str);
return FAILURE;
}
@@ -1016,6 +1044,7 @@ static int add_oid_section(struct php_x509_request * req) /* {{{ */
cnf = sk_CONF_VALUE_value(sktmp, i);
if (OBJ_sn2nid(cnf->name) == NID_undef && OBJ_ln2nid(cnf->name) == NID_undef &&
OBJ_create(cnf->value, cnf->name, cnf->name) == NID_undef) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "problem creating object %s=%s", cnf->name, cnf->value);
return FAILURE;
}
@@ -1032,10 +1061,16 @@ static int add_oid_section(struct php_x509_request * req) /* {{{ */
req->config_filename, req->var, req->req_config) == FAILURE) return FAILURE
#define SET_OPTIONAL_STRING_ARG(key, varname, defval) \
- if (optional_args && (item = zend_hash_str_find(Z_ARRVAL_P(optional_args), key, sizeof(key)-1)) != NULL && Z_TYPE_P(item) == IS_STRING) \
- varname = Z_STRVAL_P(item); \
- else \
- varname = defval
+ do { \
+ if (optional_args && (item = zend_hash_str_find(Z_ARRVAL_P(optional_args), key, sizeof(key)-1)) != NULL && Z_TYPE_P(item) == IS_STRING) { \
+ varname = Z_STRVAL_P(item); \
+ } else { \
+ varname = defval; \
+ if (varname == NULL) { \
+ php_openssl_store_errors(); \
+ } \
+ } \
+ } while(0)
#define SET_OPTIONAL_LONG_ARG(key, varname, defval) \
if (optional_args && (item = zend_hash_str_find(Z_ARRVAL_P(optional_args), key, sizeof(key)-1)) != NULL && Z_TYPE_P(item) == IS_LONG) \
@@ -1072,19 +1107,25 @@ static int php_openssl_parse_config(struct php_x509_request * req, zval * option
SET_OPTIONAL_STRING_ARG("config", req->config_filename, default_ssl_conf_filename);
SET_OPTIONAL_STRING_ARG("config_section_name", req->section_name, "req");
req->global_config = CONF_load(NULL, default_ssl_conf_filename, NULL);
+ if (req->global_config == NULL) {
+ php_openssl_store_errors();
+ }
req->req_config = CONF_load(NULL, req->config_filename, NULL);
-
if (req->req_config == NULL) {
+ php_openssl_store_errors();
return FAILURE;
}
/* read in the oids */
str = CONF_get_string(req->req_config, NULL, "oid_file");
- if (str && !php_openssl_open_base_dir_chk(str)) {
- BIO *oid_bio = BIO_new_file(str, "r");
+ if (str == NULL) {
+ php_openssl_store_errors();
+ } else if (!php_openssl_open_base_dir_chk(str)) {
+ BIO *oid_bio = BIO_new_file(str, PHP_OPENSSL_BIO_MODE_R(PKCS7_BINARY));
if (oid_bio) {
OBJ_create_objects(oid_bio);
BIO_free(oid_bio);
+ php_openssl_store_errors();
}
}
if (add_oid_section(req) == FAILURE) {
@@ -1107,8 +1148,10 @@ static int php_openssl_parse_config(struct php_x509_request * req, zval * option
str = CONF_get_string(req->req_config, req->section_name, "encrypt_rsa_key");
if (str == NULL) {
str = CONF_get_string(req->req_config, req->section_name, "encrypt_key");
+ /* it is sure that there are some errrors as str was NULL for encrypt_rsa_key */
+ php_openssl_store_errors();
}
- if (str && strcmp(str, "no") == 0) {
+ if (str != NULL && strcmp(str, "no") == 0) {
req->priv_key_encrypt = 0;
} else {
req->priv_key_encrypt = 1;
@@ -1135,18 +1178,35 @@ static int php_openssl_parse_config(struct php_x509_request * req, zval * option
if (req->digest_name == NULL) {
req->digest_name = CONF_get_string(req->req_config, req->section_name, "default_md");
}
- if (req->digest_name) {
+ if (req->digest_name != NULL) {
req->digest = req->md_alg = EVP_get_digestbyname(req->digest_name);
+ } else {
+ php_openssl_store_errors();
}
if (req->md_alg == NULL) {
req->md_alg = req->digest = EVP_sha1();
+ php_openssl_store_errors();
}
PHP_SSL_CONFIG_SYNTAX_CHECK(extensions_section);
+#ifdef HAVE_EVP_PKEY_EC
+ /* set the ec group curve name */
+ req->curve_name = NID_undef;
+ if (optional_args && (item = zend_hash_str_find(Z_ARRVAL_P(optional_args), "curve_name", sizeof("curve_name")-1)) != NULL
+ && Z_TYPE_P(item) == IS_STRING) {
+ req->curve_name = OBJ_sn2nid(Z_STRVAL_P(item));
+ if (req->curve_name == NID_undef) {
+ php_error_docref(NULL, E_WARNING, "Unknown elliptic curve (short) name %s", Z_STRVAL_P(item));
+ return FAILURE;
+ }
+ }
+#endif
/* set the string mask */
str = CONF_get_string(req->req_config, req->section_name, "string_mask");
- if (str && !ASN1_STRING_set_default_mask_asc(str)) {
+ if (str == NULL) {
+ php_openssl_store_errors();
+ } else if (!ASN1_STRING_set_default_mask_asc(str)) {
php_error_docref(NULL, E_WARNING, "Invalid global string mask setting %s", str);
return FAILURE;
}
@@ -1174,7 +1234,7 @@ static void php_openssl_dispose_config(struct php_x509_request * req) /* {{{ */
}
/* }}} */
-#ifdef PHP_WIN32
+#if defined(PHP_WIN32) || (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER))
#define PHP_OPENSSL_RAND_ADD_TIME() ((void) 0)
#else
#define PHP_OPENSSL_RAND_ADD_TIME() php_openssl_rand_add_timeval()
@@ -1209,6 +1269,7 @@ static int php_openssl_load_rand_file(const char * file, int *egdsocket, int *se
}
if (file == NULL || !RAND_load_file(file, -1)) {
if (RAND_status() == 0) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "unable to load random state; not enough random data!");
return FAILURE;
}
@@ -1234,6 +1295,7 @@ static int php_openssl_write_rand_file(const char * file, int egdsocket, int see
}
PHP_OPENSSL_RAND_ADD_TIME();
if (file == NULL || !RAND_write_file(file)) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "unable to write random state");
return FAILURE;
}
@@ -1264,7 +1326,6 @@ static EVP_MD * php_openssl_get_evp_md_from_algo(zend_long algo) { /* {{{ */
mdtype = (EVP_MD *) EVP_dss1();
break;
#endif
-#if OPENSSL_VERSION_NUMBER >= 0x0090708fL
case OPENSSL_ALGO_SHA224:
mdtype = (EVP_MD *) EVP_sha224();
break;
@@ -1280,7 +1341,6 @@ static EVP_MD * php_openssl_get_evp_md_from_algo(zend_long algo) { /* {{{ */
case OPENSSL_ALGO_RMD160:
mdtype = (EVP_MD *) EVP_ripemd160();
break;
-#endif
default:
return NULL;
break;
@@ -1354,6 +1414,12 @@ PHP_MINIT_FUNCTION(openssl)
OpenSSL_add_all_digests();
OpenSSL_add_all_algorithms();
+#if !defined(OPENSSL_NO_AES) && defined(EVP_CIPH_CCM_MODE) && OPENSSL_VERSION_NUMBER < 0x100020000
+ EVP_add_cipher(EVP_aes_128_ccm());
+ EVP_add_cipher(EVP_aes_192_ccm());
+ EVP_add_cipher(EVP_aes_256_ccm());
+#endif
+
SSL_load_error_strings();
/* register a resource id number with OpenSSL so that we can map SSL -> stream structures in
@@ -1384,13 +1450,11 @@ PHP_MINIT_FUNCTION(openssl)
#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined (LIBRESSL_VERSION_NUMBER)
REGISTER_LONG_CONSTANT("OPENSSL_ALGO_DSS1", OPENSSL_ALGO_DSS1, CONST_CS|CONST_PERSISTENT);
#endif
-#if OPENSSL_VERSION_NUMBER >= 0x0090708fL
REGISTER_LONG_CONSTANT("OPENSSL_ALGO_SHA224", OPENSSL_ALGO_SHA224, CONST_CS|CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("OPENSSL_ALGO_SHA256", OPENSSL_ALGO_SHA256, CONST_CS|CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("OPENSSL_ALGO_SHA384", OPENSSL_ALGO_SHA384, CONST_CS|CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("OPENSSL_ALGO_SHA512", OPENSSL_ALGO_SHA512, CONST_CS|CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("OPENSSL_ALGO_RMD160", OPENSSL_ALGO_RMD160, CONST_CS|CONST_PERSISTENT);
-#endif
/* flags for S/MIME */
REGISTER_LONG_CONSTANT("PKCS7_DETACHED", PKCS7_DETACHED, CONST_CS|CONST_PERSISTENT);
@@ -1439,9 +1503,10 @@ PHP_MINIT_FUNCTION(openssl)
REGISTER_LONG_CONSTANT("OPENSSL_RAW_DATA", OPENSSL_RAW_DATA, CONST_CS|CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("OPENSSL_ZERO_PADDING", OPENSSL_ZERO_PADDING, CONST_CS|CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("OPENSSL_DONT_ZERO_PAD_KEY", OPENSSL_DONT_ZERO_PAD_KEY, CONST_CS|CONST_PERSISTENT);
-#if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT)
- /* SNI support included in OpenSSL >= 0.9.8j */
+#ifndef OPENSSL_NO_TLSEXT
+ /* SNI support included */
REGISTER_LONG_CONSTANT("OPENSSL_TLSEXT_SERVER_NAME", 1, CONST_CS|CONST_PERSISTENT);
#endif
@@ -1464,15 +1529,10 @@ PHP_MINIT_FUNCTION(openssl)
#ifndef OPENSSL_NO_SSL3
php_stream_xport_register("sslv3", php_openssl_ssl_socket_factory);
#endif
-#ifndef OPENSSL_NO_SSL2
- php_stream_xport_register("sslv2", php_openssl_ssl_socket_factory);
-#endif
php_stream_xport_register("tls", php_openssl_ssl_socket_factory);
php_stream_xport_register("tlsv1.0", php_openssl_ssl_socket_factory);
-#if OPENSSL_VERSION_NUMBER >= 0x10001001L
php_stream_xport_register("tlsv1.1", php_openssl_ssl_socket_factory);
php_stream_xport_register("tlsv1.2", php_openssl_ssl_socket_factory);
-#endif
/* override the default tcp socket provider */
php_stream_xport_register("tcp", php_openssl_ssl_socket_factory);
@@ -1486,6 +1546,27 @@ PHP_MINIT_FUNCTION(openssl)
}
/* }}} */
+/* {{{ PHP_GINIT_FUNCTION
+*/
+PHP_GINIT_FUNCTION(openssl)
+{
+#if defined(COMPILE_DL_OPENSSL) && defined(ZTS)
+ ZEND_TSRMLS_CACHE_UPDATE();
+#endif
+ openssl_globals->errors = NULL;
+}
+/* }}} */
+
+/* {{{ PHP_GSHUTDOWN_FUNCTION
+*/
+PHP_GSHUTDOWN_FUNCTION(openssl)
+{
+ if (openssl_globals->errors) {
+ pefree(openssl_globals->errors, 1);
+ }
+}
+/* }}} */
+
/* {{{ PHP_MINFO_FUNCTION
*/
PHP_MINFO_FUNCTION(openssl)
@@ -1506,29 +1587,22 @@ PHP_MSHUTDOWN_FUNCTION(openssl)
{
EVP_cleanup();
-#if OPENSSL_VERSION_NUMBER >= 0x00090805f
/* prevent accessing locking callback from unloaded extension */
CRYPTO_set_locking_callback(NULL);
/* free allocated error strings */
ERR_free_strings();
-#endif
php_unregister_url_stream_wrapper("https");
php_unregister_url_stream_wrapper("ftps");
php_stream_xport_unregister("ssl");
-#ifndef OPENSSL_NO_SSL2
- php_stream_xport_unregister("sslv2");
-#endif
#ifndef OPENSSL_NO_SSL3
php_stream_xport_unregister("sslv3");
#endif
php_stream_xport_unregister("tls");
php_stream_xport_unregister("tlsv1.0");
-#if OPENSSL_VERSION_NUMBER >= 0x10001001L
php_stream_xport_unregister("tlsv1.1");
php_stream_xport_unregister("tlsv1.2");
-#endif
/* reinstate the default tcp handler */
php_stream_xport_register("tcp", php_stream_generic_socket_factory);
@@ -1573,6 +1647,7 @@ PHP_FUNCTION(openssl_get_cert_locations)
static X509 * php_openssl_x509_from_zval(zval * val, int makeresource, zend_resource **resourceval)
{
X509 *cert = NULL;
+ BIO *in;
if (resourceval) {
*resourceval = NULL;
@@ -1602,24 +1677,23 @@ static X509 * php_openssl_x509_from_zval(zval * val, int makeresource, zend_reso
convert_to_string_ex(val);
if (Z_STRLEN_P(val) > 7 && memcmp(Z_STRVAL_P(val), "file://", sizeof("file://") - 1) == 0) {
- /* read cert from the named file */
- BIO *in;
if (php_openssl_open_base_dir_chk(Z_STRVAL_P(val) + (sizeof("file://") - 1))) {
return NULL;
}
- in = BIO_new_file(Z_STRVAL_P(val) + (sizeof("file://") - 1), "r");
+ in = BIO_new_file(Z_STRVAL_P(val) + (sizeof("file://") - 1), PHP_OPENSSL_BIO_MODE_R(PKCS7_BINARY));
if (in == NULL) {
+ php_openssl_store_errors();
return NULL;
}
cert = PEM_read_bio_X509(in, NULL, NULL, NULL);
- BIO_free(in);
+
} else {
- BIO *in;
in = BIO_new_mem_buf(Z_STRVAL_P(val), (int)Z_STRLEN_P(val));
if (in == NULL) {
+ php_openssl_store_errors();
return NULL;
}
#ifdef TYPEDEF_D2I_OF
@@ -1627,10 +1701,18 @@ static X509 * php_openssl_x509_from_zval(zval * val, int makeresource, zend_reso
#else
cert = (X509 *) PEM_ASN1_read_bio((char *(*)())d2i_X509, PEM_STRING_X509, in, NULL, NULL, NULL);
#endif
- BIO_free(in);
}
- if (cert && makeresource && resourceval) {
+ if (!BIO_free(in)) {
+ php_openssl_store_errors();
+ }
+
+ if (cert == NULL) {
+ php_openssl_store_errors();
+ return NULL;
+ }
+
+ if (makeresource && resourceval) {
*resourceval = zend_register_resource(cert, le_x509);
}
return cert;
@@ -1665,21 +1747,27 @@ PHP_FUNCTION(openssl_x509_export_to_file)
return;
}
- bio_out = BIO_new_file(filename, "w");
+ bio_out = BIO_new_file(filename, PHP_OPENSSL_BIO_MODE_W(PKCS7_BINARY));
if (bio_out) {
- if (!notext) {
- X509_print(bio_out, cert);
+ if (!notext && !X509_print(bio_out, cert)) {
+ php_openssl_store_errors();
+ }
+ if (!PEM_write_bio_X509(bio_out, cert)) {
+ php_openssl_store_errors();
}
- PEM_write_bio_X509(bio_out, cert);
RETVAL_TRUE;
} else {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "error opening file %s", filename);
}
if (certresource == NULL && cert) {
X509_free(cert);
}
- BIO_free(bio_out);
+
+ if (!BIO_free(bio_out)) {
+ php_openssl_store_errors();
+ }
}
/* }}} */
@@ -1730,29 +1818,34 @@ PHP_FUNCTION(openssl_spki_new)
}
if ((spki = NETSCAPE_SPKI_new()) == NULL) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "Unable to create new SPKAC");
goto cleanup;
}
if (challenge) {
if (!ASN1_STRING_set(spki->spkac->challenge, challenge, (int)challenge_len)) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "Unable to set challenge data");
goto cleanup;
}
}
if (!NETSCAPE_SPKI_set_pubkey(spki, pkey)) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "Unable to embed public key");
goto cleanup;
}
if (!NETSCAPE_SPKI_sign(spki, pkey, mdtype)) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "Unable to sign with specified algorithm");
goto cleanup;
}
spkstr = NETSCAPE_SPKI_b64_encode(spki);
if (!spkstr){
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "Unable to encode SPKAC");
goto cleanup;
}
@@ -1815,12 +1908,14 @@ PHP_FUNCTION(openssl_spki_verify)
spki = NETSCAPE_SPKI_b64_decode(spkstr_cleaned, spkstr_cleaned_len);
if (spki == NULL) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "Unable to decode supplied SPKAC");
goto cleanup;
}
pkey = X509_PUBKEY_get(spki->spkac->pubkey);
if (pkey == NULL) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "Unable to acquire signed public key");
goto cleanup;
}
@@ -1841,6 +1936,8 @@ cleanup:
if (i > 0) {
RETVAL_TRUE;
+ } else {
+ php_openssl_store_errors();
}
}
/* }}} */
@@ -1877,12 +1974,14 @@ PHP_FUNCTION(openssl_spki_export)
spki = NETSCAPE_SPKI_b64_decode(spkstr_cleaned, spkstr_cleaned_len);
if (spki == NULL) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "Unable to decode supplied SPKAC");
goto cleanup;
}
pkey = X509_PUBKEY_get(spki->spkac->pubkey);
if (pkey == NULL) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "Unable to acquire signed public key");
goto cleanup;
}
@@ -1893,6 +1992,8 @@ PHP_FUNCTION(openssl_spki_export)
BIO_get_mem_ptr(out, &bio_buf);
RETVAL_STRINGL((char *)bio_buf->data, bio_buf->length);
+ } else {
+ php_openssl_store_errors();
}
goto cleanup;
@@ -1946,6 +2047,7 @@ PHP_FUNCTION(openssl_spki_export_challenge)
spki = NETSCAPE_SPKI_b64_decode(spkstr_cleaned, spkstr_cleaned_len);
if (spki == NULL) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "Unable to decode SPKAC");
goto cleanup;
}
@@ -1982,8 +2084,12 @@ PHP_FUNCTION(openssl_x509_export)
}
bio_out = BIO_new(BIO_s_mem());
- if (!notext) {
- X509_print(bio_out, cert);
+ if (!bio_out) {
+ php_openssl_store_errors();
+ goto cleanup;
+ }
+ if (!notext && !X509_print(bio_out, cert)) {
+ php_openssl_store_errors();
}
if (PEM_write_bio_X509(bio_out, cert)) {
BUF_MEM *bio_buf;
@@ -1993,12 +2099,16 @@ PHP_FUNCTION(openssl_x509_export)
ZVAL_STRINGL(zout, bio_buf->data, bio_buf->length);
RETVAL_TRUE;
+ } else {
+ php_openssl_store_errors();
}
- if (certresource == NULL && cert) {
+ BIO_free(bio_out);
+
+cleanup:
+ if (certresource == NULL && cert != NULL) {
X509_free(cert);
}
- BIO_free(bio_out);
}
/* }}} */
@@ -2013,6 +2123,7 @@ zend_string* php_openssl_x509_fingerprint(X509 *peer, const char *method, zend_b
php_error_docref(NULL, E_WARNING, "Unknown signature algorithm");
return NULL;
} else if (!X509_digest(peer, mdtype, md, &n)) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_ERROR, "Could not generate signature");
return NULL;
}
@@ -2119,6 +2230,7 @@ static int openssl_x509v3_subjectAltName(BIO *bio, X509_EXTENSION *extension)
names = (GENERAL_NAMES*) (method->d2i(NULL, &p, length));
}
if (names == NULL) {
+ php_openssl_store_errors();
return -1;
}
@@ -2215,6 +2327,7 @@ PHP_FUNCTION(openssl_x509_parse)
bn_serial = ASN1_INTEGER_to_BN(asn1_serial, NULL);
/* Can return NULL on error or memory allocation failure */
if (!bn_serial) {
+ php_openssl_store_errors();
RETURN_FALSE;
}
@@ -2222,6 +2335,7 @@ PHP_FUNCTION(openssl_x509_parse)
BN_free(bn_serial);
/* Can return NULL on error or memory allocation failure */
if (!hex_serial) {
+ php_openssl_store_errors();
RETURN_FALSE;
}
@@ -2292,6 +2406,10 @@ PHP_FUNCTION(openssl_x509_parse)
extname = buf;
}
bio_out = BIO_new(BIO_s_mem());
+ if (bio_out == NULL) {
+ php_openssl_store_errors();
+ RETURN_FALSE;
+ }
if (nid == NID_subject_alt_name) {
if (openssl_x509v3_subjectAltName(bio_out, extension) == 0) {
BIO_get_mem_ptr(bio_out, &bio_buf);
@@ -2330,6 +2448,7 @@ static STACK_OF(X509) * load_all_certs_from_file(char *certfile)
X509_INFO *xi;
if(!(stack = sk_X509_new_null())) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_ERROR, "memory allocation failure");
goto end;
}
@@ -2339,7 +2458,8 @@ static STACK_OF(X509) * load_all_certs_from_file(char *certfile)
goto end;
}
- if(!(in=BIO_new_file(certfile, "r"))) {
+ if(!(in=BIO_new_file(certfile, PHP_OPENSSL_BIO_MODE_R(PKCS7_BINARY)))) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "error opening the file, %s", certfile);
sk_X509_free(stack);
goto end;
@@ -2347,6 +2467,7 @@ static STACK_OF(X509) * load_all_certs_from_file(char *certfile)
/* This loads from a file, a stack of x509/crl/pkey sets */
if(!(sk=PEM_X509_INFO_read_bio(in, NULL, NULL, NULL))) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "error reading the file, %s", certfile);
sk_X509_free(stack);
goto end;
@@ -2383,14 +2504,22 @@ static int check_cert(X509_STORE *ctx, X509 *x, STACK_OF(X509) *untrustedchain,
csc = X509_STORE_CTX_new();
if (csc == NULL) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_ERROR, "memory allocation failure");
return 0;
}
- X509_STORE_CTX_init(csc, ctx, x, untrustedchain);
- if(purpose >= 0) {
- X509_STORE_CTX_set_purpose(csc, purpose);
+ if (!X509_STORE_CTX_init(csc, ctx, x, untrustedchain)) {
+ php_openssl_store_errors();
+ php_error_docref(NULL, E_WARNING, "cert store initialization failed");
+ return 0;
+ }
+ if (purpose >= 0 && !X509_STORE_CTX_set_purpose(csc, purpose)) {
+ php_openssl_store_errors();
}
ret = X509_verify_cert(csc);
+ if (ret < 0) {
+ php_openssl_store_errors();
+ }
X509_STORE_CTX_free(csc);
return ret;
@@ -2468,6 +2597,7 @@ static X509_STORE * setup_verify(zval * calist)
store = X509_STORE_new();
if (store == NULL) {
+ php_openssl_store_errors();
return NULL;
}
@@ -2483,6 +2613,7 @@ static X509_STORE * setup_verify(zval * calist)
if ((sb.st_mode & S_IFREG) == S_IFREG) {
file_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
if (file_lookup == NULL || !X509_LOOKUP_load_file(file_lookup, Z_STRVAL_P(item), X509_FILETYPE_PEM)) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "error loading file %s", Z_STRVAL_P(item));
} else {
nfiles++;
@@ -2491,6 +2622,7 @@ static X509_STORE * setup_verify(zval * calist)
} else {
dir_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir());
if (dir_lookup == NULL || !X509_LOOKUP_add_dir(dir_lookup, Z_STRVAL_P(item), X509_FILETYPE_PEM)) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "error loading directory %s", Z_STRVAL_P(item));
} else {
ndirs++;
@@ -2501,14 +2633,14 @@ static X509_STORE * setup_verify(zval * calist)
}
if (nfiles == 0) {
file_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
- if (file_lookup) {
- X509_LOOKUP_load_file(file_lookup, NULL, X509_FILETYPE_DEFAULT);
+ if (file_lookup == NULL || !X509_LOOKUP_load_file(file_lookup, NULL, X509_FILETYPE_DEFAULT)) {
+ php_openssl_store_errors();
}
}
if (ndirs == 0) {
dir_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir());
- if (dir_lookup) {
- X509_LOOKUP_add_dir(dir_lookup, NULL, X509_FILETYPE_DEFAULT);
+ if (dir_lookup == NULL || !X509_LOOKUP_add_dir(dir_lookup, NULL, X509_FILETYPE_DEFAULT)) {
+ php_openssl_store_errors();
}
}
return store;
@@ -2588,6 +2720,7 @@ static STACK_OF(X509) * php_array_to_X509_sk(zval * zcerts) /* {{{ */
cert = X509_dup(cert);
if (cert == NULL) {
+ php_openssl_store_errors();
goto clean_exit;
}
@@ -2605,6 +2738,7 @@ static STACK_OF(X509) * php_array_to_X509_sk(zval * zcerts) /* {{{ */
if (certresource != NULL) {
cert = X509_dup(cert);
if (cert == NULL) {
+ php_openssl_store_errors();
goto clean_exit;
}
}
@@ -2650,6 +2784,7 @@ PHP_FUNCTION(openssl_pkcs12_export_to_file)
goto cleanup;
}
if (cert && !X509_check_private_key(cert, priv_key)) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "private key does not correspond to cert");
goto cleanup;
}
@@ -2673,19 +2808,24 @@ PHP_FUNCTION(openssl_pkcs12_export_to_file)
int nid_key, int nid_cert, int iter, int mac_iter, int keytype);*/
p12 = PKCS12_create(pass, friendly_name, priv_key, cert, ca, 0, 0, 0, 0, 0);
+ if (p12 != NULL) {
+ bio_out = BIO_new_file(filename, PHP_OPENSSL_BIO_MODE_W(PKCS7_BINARY));
+ if (bio_out != NULL) {
- bio_out = BIO_new_file(filename, "w");
- if (bio_out) {
+ i2d_PKCS12_bio(bio_out, p12);
+ BIO_free(bio_out);
- i2d_PKCS12_bio(bio_out, p12);
+ RETVAL_TRUE;
+ } else {
+ php_openssl_store_errors();
+ php_error_docref(NULL, E_WARNING, "error opening file %s", filename);
+ }
- RETVAL_TRUE;
+ PKCS12_free(p12);
} else {
- php_error_docref(NULL, E_WARNING, "error opening file %s", filename);
+ php_openssl_store_errors();
}
- BIO_free(bio_out);
- PKCS12_free(p12);
php_sk_X509_free(ca);
cleanup:
@@ -2745,19 +2885,25 @@ PHP_FUNCTION(openssl_pkcs12_export)
p12 = PKCS12_create(pass, friendly_name, priv_key, cert, ca, 0, 0, 0, 0, 0);
- bio_out = BIO_new(BIO_s_mem());
- if (i2d_PKCS12_bio(bio_out, p12)) {
- BUF_MEM *bio_buf;
+ if (p12 != NULL) {
+ bio_out = BIO_new(BIO_s_mem());
+ if (i2d_PKCS12_bio(bio_out, p12)) {
+ BUF_MEM *bio_buf;
- zval_dtor(zout);
- BIO_get_mem_ptr(bio_out, &bio_buf);
- ZVAL_STRINGL(zout, bio_buf->data, bio_buf->length);
+ zval_dtor(zout);
+ BIO_get_mem_ptr(bio_out, &bio_buf);
+ ZVAL_STRINGL(zout, bio_buf->data, bio_buf->length);
- RETVAL_TRUE;
- }
+ RETVAL_TRUE;
+ } else {
+ php_openssl_store_errors();
+ }
- BIO_free(bio_out);
- PKCS12_free(p12);
+ BIO_free(bio_out);
+ PKCS12_free(p12);
+ } else {
+ php_openssl_store_errors();
+ }
php_sk_X509_free(ca);
cleanup:
@@ -2794,78 +2940,74 @@ PHP_FUNCTION(openssl_pkcs12_read)
bio_in = BIO_new(BIO_s_mem());
- if(0 >= BIO_write(bio_in, zp12, (int)zp12_len))
+ if (0 >= BIO_write(bio_in, zp12, (int)zp12_len)) {
+ php_openssl_store_errors();
goto cleanup;
+ }
- if(d2i_PKCS12_bio(bio_in, &p12)) {
- if(PKCS12_parse(p12, pass, &pkey, &cert, &ca)) {
- BIO * bio_out;
- int cert_num;
+ if (d2i_PKCS12_bio(bio_in, &p12) && PKCS12_parse(p12, pass, &pkey, &cert, &ca)) {
+ BIO * bio_out;
+ int cert_num;
- zval_dtor(zout);
- array_init(zout);
+ zval_dtor(zout);
+ array_init(zout);
- if (cert) {
- bio_out = BIO_new(BIO_s_mem());
- if (PEM_write_bio_X509(bio_out, cert)) {
- BUF_MEM *bio_buf;
- BIO_get_mem_ptr(bio_out, &bio_buf);
- ZVAL_STRINGL(&zcert, bio_buf->data, bio_buf->length);
- add_assoc_zval(zout, "cert", &zcert);
- }
- BIO_free(bio_out);
+ if (cert) {
+ bio_out = BIO_new(BIO_s_mem());
+ if (PEM_write_bio_X509(bio_out, cert)) {
+ BUF_MEM *bio_buf;
+ BIO_get_mem_ptr(bio_out, &bio_buf);
+ ZVAL_STRINGL(&zcert, bio_buf->data, bio_buf->length);
+ add_assoc_zval(zout, "cert", &zcert);
+ } else {
+ php_openssl_store_errors();
}
+ BIO_free(bio_out);
+ }
- if (pkey) {
- bio_out = BIO_new(BIO_s_mem());
- if (PEM_write_bio_PrivateKey(bio_out, pkey, NULL, NULL, 0, 0, NULL)) {
- BUF_MEM *bio_buf;
- BIO_get_mem_ptr(bio_out, &bio_buf);
- ZVAL_STRINGL(&zpkey, bio_buf->data, bio_buf->length);
- add_assoc_zval(zout, "pkey", &zpkey);
- }
- BIO_free(bio_out);
+ if (pkey) {
+ bio_out = BIO_new(BIO_s_mem());
+ if (PEM_write_bio_PrivateKey(bio_out, pkey, NULL, NULL, 0, 0, NULL)) {
+ BUF_MEM *bio_buf;
+ BIO_get_mem_ptr(bio_out, &bio_buf);
+ ZVAL_STRINGL(&zpkey, bio_buf->data, bio_buf->length);
+ add_assoc_zval(zout, "pkey", &zpkey);
+ } else {
+ php_openssl_store_errors();
}
+ BIO_free(bio_out);
+ }
- cert_num = sk_X509_num(ca);
- if (ca && cert_num > 0) {
-
- array_init(&zextracerts);
-
- for (i=0; i < cert_num; i++) {
- zval zextracert;
- X509* aCA = sk_X509_pop(ca);
- if (!aCA) break;
-
- /* fix for bug 69882 */
- {
- int err = ERR_peek_error();
- if (err == OPENSSL_ERROR_X509_PRIVATE_KEY_VALUES_MISMATCH) {
- ERR_get_error();
- }
- }
-
- bio_out = BIO_new(BIO_s_mem());
- if (PEM_write_bio_X509(bio_out, aCA)) {
- BUF_MEM *bio_buf;
- BIO_get_mem_ptr(bio_out, &bio_buf);
- ZVAL_STRINGL(&zextracert, bio_buf->data, bio_buf->length);
- add_index_zval(&zextracerts, i, &zextracert);
+ cert_num = sk_X509_num(ca);
+ if (ca && cert_num) {
+ array_init(&zextracerts);
- }
- BIO_free(bio_out);
+ for (i = 0; i < cert_num; i++) {
+ zval zextracert;
+ X509* aCA = sk_X509_pop(ca);
+ if (!aCA) break;
- X509_free(aCA);
+ bio_out = BIO_new(BIO_s_mem());
+ if (PEM_write_bio_X509(bio_out, aCA)) {
+ BUF_MEM *bio_buf;
+ BIO_get_mem_ptr(bio_out, &bio_buf);
+ ZVAL_STRINGL(&zextracert, bio_buf->data, bio_buf->length);
+ add_index_zval(&zextracerts, i, &zextracert);
}
- sk_X509_free(ca);
- add_assoc_zval(zout, "extracerts", &zextracerts);
+ X509_free(aCA);
+ BIO_free(bio_out);
}
- RETVAL_TRUE;
-
- PKCS12_free(p12);
+ sk_X509_free(ca);
+ add_assoc_zval(zout, "extracerts", &zextracerts);
}
+
+ RETVAL_TRUE;
+
+ PKCS12_free(p12);
+ } else {
+ php_openssl_store_errors();
}
cleanup:
@@ -2891,18 +3033,22 @@ static int php_openssl_make_REQ(struct php_x509_request * req, X509_REQ * csr, z
dn_sect = CONF_get_string(req->req_config, req->section_name, "distinguished_name");
if (dn_sect == NULL) {
+ php_openssl_store_errors();
return FAILURE;
}
dn_sk = CONF_get_section(req->req_config, dn_sect);
if (dn_sk == NULL) {
+ php_openssl_store_errors();
return FAILURE;
}
attr_sect = CONF_get_string(req->req_config, req->section_name, "attributes");
if (attr_sect == NULL) {
+ php_openssl_store_errors();
attr_sk = NULL;
} else {
attr_sk = CONF_get_section(req->req_config, attr_sect);
if (attr_sk == NULL) {
+ php_openssl_store_errors();
return FAILURE;
}
}
@@ -2928,6 +3074,7 @@ static int php_openssl_make_REQ(struct php_x509_request * req, X509_REQ * csr, z
if (!X509_NAME_add_entry_by_NID(subj, nid, MBSTRING_UTF8,
(unsigned char*)Z_STRVAL_P(item), -1, -1, 0))
{
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING,
"dn: add_entry_by_NID %d -> %s (failed; check error"
" queue and value of string_mask OpenSSL option "
@@ -2943,13 +3090,13 @@ static int php_openssl_make_REQ(struct php_x509_request * req, X509_REQ * csr, z
/* Finally apply defaults from config file */
for(i = 0; i < sk_CONF_VALUE_num(dn_sk); i++) {
- int len;
+ size_t len;
char buffer[200 + 1]; /*200 + \0 !*/
v = sk_CONF_VALUE_value(dn_sk, i);
type = v->name;
- len = (int)strlen(type);
+ len = strlen(type);
if (len < sizeof("_default")) {
continue;
}
@@ -2981,6 +3128,7 @@ static int php_openssl_make_REQ(struct php_x509_request * req, X509_REQ * csr, z
continue;
}
if (!X509_NAME_add_entry_by_txt(subj, type, MBSTRING_UTF8, (unsigned char*)v->value, -1, -1, 0)) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "add_entry_by_txt %s -> %s (failed)", type, v->value);
return FAILURE;
}
@@ -3003,6 +3151,7 @@ static int php_openssl_make_REQ(struct php_x509_request * req, X509_REQ * csr, z
nid = OBJ_txt2nid(ZSTR_VAL(strindex));
if (nid != NID_undef) {
if (!X509_NAME_add_entry_by_NID(subj, nid, MBSTRING_UTF8, (unsigned char*)Z_STRVAL_P(item), -1, -1, 0)) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "attribs: add_entry_by_NID %d -> %s (failed)", nid, Z_STRVAL_P(item));
return FAILURE;
}
@@ -3018,6 +3167,7 @@ static int php_openssl_make_REQ(struct php_x509_request * req, X509_REQ * csr, z
continue;
}
if (!X509_REQ_add1_attr_by_txt(csr, v->name, MBSTRING_UTF8, (unsigned char*)v->value, -1)) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING,
"add1_attr_by_txt %s -> %s (failed; check error queue "
"and value of string_mask OpenSSL option if illegal "
@@ -3027,9 +3177,13 @@ static int php_openssl_make_REQ(struct php_x509_request * req, X509_REQ * csr, z
}
}
}
+ } else {
+ php_openssl_store_errors();
}
- X509_REQ_set_pubkey(csr, req->priv_key);
+ if (!X509_REQ_set_pubkey(csr, req->priv_key)) {
+ php_openssl_store_errors();
+ }
return SUCCESS;
}
/* }}} */
@@ -3068,11 +3222,21 @@ static X509_REQ * php_openssl_csr_from_zval(zval * val, int makeresource, zend_r
if (php_openssl_open_base_dir_chk(filename)) {
return NULL;
}
- in = BIO_new_file(filename, "r");
+ in = BIO_new_file(filename, PHP_OPENSSL_BIO_MODE_R(PKCS7_BINARY));
} else {
in = BIO_new_mem_buf(Z_STRVAL_P(val), (int)Z_STRLEN_P(val));
}
+
+ if (in == NULL) {
+ php_openssl_store_errors();
+ return NULL;
+ }
+
csr = PEM_read_bio_X509_REQ(in, NULL,NULL,NULL);
+ if (csr == NULL) {
+ php_openssl_store_errors();
+ }
+
BIO_free(in);
return csr;
@@ -3106,21 +3270,26 @@ PHP_FUNCTION(openssl_csr_export_to_file)
return;
}
- bio_out = BIO_new_file(filename, "w");
- if (bio_out) {
- if (!notext) {
- X509_REQ_print(bio_out, csr);
+ bio_out = BIO_new_file(filename, PHP_OPENSSL_BIO_MODE_W(PKCS7_BINARY));
+ if (bio_out != NULL) {
+ if (!notext && !X509_REQ_print(bio_out, csr)) {
+ php_openssl_store_errors();
}
- PEM_write_bio_X509_REQ(bio_out, csr);
- RETVAL_TRUE;
+ if (!PEM_write_bio_X509_REQ(bio_out, csr)) {
+ php_error_docref(NULL, E_WARNING, "error writing PEM to file %s", filename);
+ php_openssl_store_errors();
+ } else {
+ RETVAL_TRUE;
+ }
+ BIO_free(bio_out);
} else {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "error opening file %s", filename);
}
- if (csr_resource == NULL && csr) {
+ if (csr_resource == NULL && csr != NULL) {
X509_REQ_free(csr);
}
- BIO_free(bio_out);
}
/* }}} */
@@ -3149,8 +3318,8 @@ PHP_FUNCTION(openssl_csr_export)
/* export to a var */
bio_out = BIO_new(BIO_s_mem());
- if (!notext) {
- X509_REQ_print(bio_out, csr);
+ if (!notext && !X509_REQ_print(bio_out, csr)) {
+ php_openssl_store_errors();
}
if (PEM_write_bio_X509_REQ(bio_out, csr)) {
@@ -3161,6 +3330,8 @@ PHP_FUNCTION(openssl_csr_export)
ZVAL_STRINGL(zout, bio_buf->data, bio_buf->length);
RETVAL_TRUE;
+ } else {
+ php_openssl_store_errors();
}
if (csr_resource == NULL && csr) {
@@ -3208,6 +3379,7 @@ PHP_FUNCTION(openssl_csr_sign)
goto cleanup;
}
if (cert && !X509_check_private_key(cert, priv_key)) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "private key does not correspond to signing cert");
goto cleanup;
}
@@ -3218,12 +3390,14 @@ PHP_FUNCTION(openssl_csr_sign)
/* Check that the request matches the signature */
key = X509_REQ_get_pubkey(csr);
if (key == NULL) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "error unpacking public key");
goto cleanup;
}
i = X509_REQ_verify(csr, key);
if (i < 0) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "Signature verification problems");
goto cleanup;
}
@@ -3236,12 +3410,14 @@ PHP_FUNCTION(openssl_csr_sign)
new_cert = X509_new();
if (new_cert == NULL) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "No memory");
goto cleanup;
}
/* Version 3 cert */
- if (!X509_set_version(new_cert, 2))
+ if (!X509_set_version(new_cert, 2)) {
goto cleanup;
+ }
ASN1_INTEGER_set(X509_get_serialNumber(new_cert), (long)serial);
@@ -3252,12 +3428,14 @@ PHP_FUNCTION(openssl_csr_sign)
cert = new_cert;
}
if (!X509_set_issuer_name(new_cert, X509_get_subject_name(cert))) {
+ php_openssl_store_errors();
goto cleanup;
}
X509_gmtime_adj(X509_get_notBefore(new_cert), 0);
X509_gmtime_adj(X509_get_notAfter(new_cert), 60*60*24*(long)num_days);
i = X509_set_pubkey(new_cert, key);
if (!i) {
+ php_openssl_store_errors();
goto cleanup;
}
if (req.extensions_section) {
@@ -3266,12 +3444,14 @@ PHP_FUNCTION(openssl_csr_sign)
X509V3_set_ctx(&ctx, cert, new_cert, csr, NULL, 0);
X509V3_set_conf_lhash(&ctx, req.req_config);
if (!X509V3_EXT_add_conf(req.req_config, &ctx, req.extensions_section, new_cert)) {
+ php_openssl_store_errors();
goto cleanup;
}
}
/* Now sign it */
if (!X509_sign(new_cert, priv_key, req.digest)) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "failed to sign it");
goto cleanup;
}
@@ -3349,6 +3529,7 @@ PHP_FUNCTION(openssl_csr_new)
if (req.request_extensions_section && !X509V3_EXT_REQ_add_conf(req.req_config,
&ext_ctx, req.request_extensions_section, csr))
{
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "Error loading extension section %s", req.request_extensions_section);
} else {
RETVAL_TRUE;
@@ -3357,6 +3538,7 @@ PHP_FUNCTION(openssl_csr_new)
ZVAL_RES(return_value, zend_register_resource(csr, le_csr));
csr = NULL;
} else {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "Error signing request");
}
@@ -3376,7 +3558,10 @@ PHP_FUNCTION(openssl_csr_new)
req.priv_key = NULL;
}
}
+ } else {
+ php_openssl_store_errors();
}
+
}
}
if (csr) {
@@ -3452,6 +3637,7 @@ PHP_FUNCTION(openssl_csr_get_public_key)
#endif
if (tpubkey == NULL) {
+ php_openssl_store_errors();
RETURN_FALSE;
}
@@ -3609,11 +3795,12 @@ static EVP_PKEY * php_openssl_evp_from_zval(
/* not a X509 certificate, try to retrieve public key */
BIO* in;
if (filename) {
- in = BIO_new_file(filename, "r");
+ in = BIO_new_file(filename, PHP_OPENSSL_BIO_MODE_R(PKCS7_BINARY));
} else {
in = BIO_new_mem_buf(Z_STRVAL_P(val), (int)Z_STRLEN_P(val));
}
if (in == NULL) {
+ php_openssl_store_errors();
TMP_CLEAN;
}
key = PEM_read_bio_PUBKEY(in, NULL,NULL, NULL);
@@ -3627,7 +3814,7 @@ static EVP_PKEY * php_openssl_evp_from_zval(
if (php_openssl_open_base_dir_chk(filename)) {
TMP_CLEAN;
}
- in = BIO_new_file(filename, "r");
+ in = BIO_new_file(filename, PHP_OPENSSL_BIO_MODE_R(PKCS7_BINARY));
} else {
in = BIO_new_mem_buf(Z_STRVAL_P(val), (int)Z_STRLEN_P(val));
}
@@ -3647,9 +3834,16 @@ static EVP_PKEY * php_openssl_evp_from_zval(
}
}
- if (public_key && cert && key == NULL) {
- /* extract public key from X509 cert */
- key = (EVP_PKEY *) X509_get_pubkey(cert);
+ if (key == NULL) {
+ php_openssl_store_errors();
+
+ if (public_key && cert) {
+ /* extract public key from X509 cert */
+ key = (EVP_PKEY *) X509_get_pubkey(cert);
+ if (key == NULL) {
+ php_openssl_store_errors();
+ }
+ }
}
if (free_cert && cert) {
@@ -3679,6 +3873,9 @@ static EVP_PKEY * php_openssl_generate_private_key(struct php_x509_request * req
}
randfile = CONF_get_string(req->req_config, req->section_name, "RANDFILE");
+ if (randfile == NULL) {
+ php_openssl_store_errors();
+ }
php_openssl_load_rand_file(randfile, &egdsocket, &seeded);
if ((req->priv_key = EVP_PKEY_new()) != NULL) {
@@ -3700,12 +3897,16 @@ static EVP_PKEY * php_openssl_generate_private_key(struct php_x509_request * req
}
rsaparam = RSA_new();
PHP_OPENSSL_RAND_ADD_TIME();
- RSA_generate_key_ex(rsaparam, req->priv_key_bits, bne, NULL);
+ if (rsaparam == NULL || !RSA_generate_key_ex(rsaparam, req->priv_key_bits, bne, NULL)) {
+ php_openssl_store_errors();
+ }
BN_free(bne);
}
#endif
if (rsaparam && EVP_PKEY_assign_RSA(req->priv_key, rsaparam)) {
return_val = req->priv_key;
+ } else {
+ php_openssl_store_errors();
}
}
break;
@@ -3719,10 +3920,15 @@ static EVP_PKEY * php_openssl_generate_private_key(struct php_x509_request * req
if (DSA_generate_key(dsaparam)) {
if (EVP_PKEY_assign_DSA(req->priv_key, dsaparam)) {
return_val = req->priv_key;
+ } else {
+ php_openssl_store_errors();
}
} else {
+ php_openssl_store_errors();
DSA_free(dsaparam);
}
+ } else {
+ php_openssl_store_errors();
}
}
break;
@@ -3738,10 +3944,35 @@ static EVP_PKEY * php_openssl_generate_private_key(struct php_x509_request * req
if (DH_check(dhparam, &codes) && codes == 0 && DH_generate_key(dhparam)) {
if (EVP_PKEY_assign_DH(req->priv_key, dhparam)) {
return_val = req->priv_key;
+ } else {
+ php_openssl_store_errors();
}
} else {
+ php_openssl_store_errors();
DH_free(dhparam);
}
+ } else {
+ php_openssl_store_errors();
+ }
+ }
+ break;
+#endif
+#ifdef HAVE_EVP_PKEY_EC
+ case OPENSSL_KEYTYPE_EC:
+ {
+ if (req->curve_name == NID_undef) {
+ php_error_docref(NULL, E_WARNING, "Missing configuration value: 'curve_name' not set");
+ return NULL;
+ }
+ EC_KEY *eckey = EC_KEY_new_by_curve_name(req->curve_name);
+ if (eckey) {
+ EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE);
+ if (EC_KEY_generate_key(eckey) &&
+ EVP_PKEY_assign_EC_KEY(req->priv_key, eckey)) {
+ return_val = req->priv_key;
+ } else {
+ EC_KEY_free(eckey);
+ }
}
}
break;
@@ -3749,6 +3980,8 @@ static EVP_PKEY * php_openssl_generate_private_key(struct php_x509_request * req
default:
php_error_docref(NULL, E_WARNING, "Unsupported private key type");
}
+ } else {
+ php_openssl_store_errors();
}
php_openssl_write_rand_file(randfile, egdsocket, seeded);
@@ -3770,7 +4003,6 @@ static int php_openssl_is_private_key(EVP_PKEY* pkey)
assert(pkey != NULL);
switch (EVP_PKEY_id(pkey)) {
-#ifndef NO_RSA
case EVP_PKEY_RSA:
case EVP_PKEY_RSA2:
{
@@ -3785,8 +4017,6 @@ static int php_openssl_is_private_key(EVP_PKEY* pkey)
}
}
break;
-#endif
-#ifndef NO_DSA
case EVP_PKEY_DSA:
case EVP_PKEY_DSA1:
case EVP_PKEY_DSA2:
@@ -3809,8 +4039,6 @@ static int php_openssl_is_private_key(EVP_PKEY* pkey)
}
}
break;
-#endif
-#ifndef NO_DH
case EVP_PKEY_DH:
{
DH *dh = EVP_PKEY_get0_DH(pkey);
@@ -3829,7 +4057,6 @@ static int php_openssl_is_private_key(EVP_PKEY* pkey)
}
}
break;
-#endif
#ifdef HAVE_EVP_PKEY_EC
case EVP_PKEY_EC:
{
@@ -3898,6 +4125,7 @@ zend_bool php_openssl_pkey_init_and_assign_rsa(EVP_PKEY *pkey, RSA *rsa, zval *d
}
if (!EVP_PKEY_assign_RSA(pkey, rsa)) {
+ php_openssl_store_errors();
return 0;
}
@@ -3926,6 +4154,7 @@ zend_bool php_openssl_pkey_init_dsa(DSA *dsa, zval *data)
/* generate key */
PHP_OPENSSL_RAND_ADD_TIME();
if (!DSA_generate_key(dsa)) {
+ php_openssl_store_errors();
return 0;
}
@@ -3948,18 +4177,21 @@ static BIGNUM *php_openssl_dh_pub_from_priv(BIGNUM *priv_key, BIGNUM *g, BIGNUM
pub_key = BN_new();
if (pub_key == NULL) {
+ php_openssl_store_errors();
return NULL;
}
priv_key_const_time = BN_new();
if (priv_key_const_time == NULL) {
BN_free(pub_key);
+ php_openssl_store_errors();
return NULL;
}
ctx = BN_CTX_new();
if (ctx == NULL) {
BN_free(pub_key);
BN_free(priv_key_const_time);
+ php_openssl_store_errors();
return NULL;
}
@@ -3967,6 +4199,7 @@ static BIGNUM *php_openssl_dh_pub_from_priv(BIGNUM *priv_key, BIGNUM *g, BIGNUM
if (!BN_mod_exp_mont(pub_key, g, priv_key_const_time, p, ctx, NULL)) {
BN_free(pub_key);
+ php_openssl_store_errors();
pub_key = NULL;
}
@@ -4005,6 +4238,7 @@ zend_bool php_openssl_pkey_init_dh(DH *dh, zval *data)
/* generate key */
PHP_OPENSSL_RAND_ADD_TIME();
if (!DH_generate_key(dh)) {
+ php_openssl_store_errors();
return 0;
}
/* all good */
@@ -4038,8 +4272,12 @@ PHP_FUNCTION(openssl_pkey_new)
RETURN_RES(zend_register_resource(pkey, le_key));
}
RSA_free(rsa);
+ } else {
+ php_openssl_store_errors();
}
EVP_PKEY_free(pkey);
+ } else {
+ php_openssl_store_errors();
}
RETURN_FALSE;
} else if ((data = zend_hash_str_find(Z_ARRVAL_P(args), "dsa", sizeof("dsa") - 1)) != NULL &&
@@ -4051,11 +4289,17 @@ PHP_FUNCTION(openssl_pkey_new)
if (php_openssl_pkey_init_dsa(dsa, data)) {
if (EVP_PKEY_assign_DSA(pkey, dsa)) {
RETURN_RES(zend_register_resource(pkey, le_key));
+ } else {
+ php_openssl_store_errors();
}
}
DSA_free(dsa);
+ } else {
+ php_openssl_store_errors();
}
EVP_PKEY_free(pkey);
+ } else {
+ php_openssl_store_errors();
}
RETURN_FALSE;
} else if ((data = zend_hash_str_find(Z_ARRVAL_P(args), "dh", sizeof("dh") - 1)) != NULL &&
@@ -4068,13 +4312,131 @@ PHP_FUNCTION(openssl_pkey_new)
if (EVP_PKEY_assign_DH(pkey, dh)) {
ZVAL_COPY_VALUE(return_value, zend_list_insert(pkey, le_key));
return;
+ } else {
+ php_openssl_store_errors();
}
}
DH_free(dh);
+ } else {
+ php_openssl_store_errors();
}
EVP_PKEY_free(pkey);
+ } else {
+ php_openssl_store_errors();
}
RETURN_FALSE;
+#ifdef HAVE_EVP_PKEY_EC
+ } else if ((data = zend_hash_str_find(Z_ARRVAL_P(args), "ec", sizeof("ec") - 1)) != NULL &&
+ Z_TYPE_P(data) == IS_ARRAY) {
+ EC_KEY *eckey = NULL;
+ EC_GROUP *group = NULL;
+ EC_POINT *pnt = NULL;
+ const BIGNUM *d;
+ pkey = EVP_PKEY_new();
+ if (pkey) {
+ eckey = EC_KEY_new();
+ if (eckey) {
+ EC_GROUP *group = NULL;
+ zval *bn;
+ zval *x;
+ zval *y;
+
+ if ((bn = zend_hash_str_find(Z_ARRVAL_P(data), "curve_name", sizeof("curve_name") - 1)) != NULL &&
+ Z_TYPE_P(bn) == IS_STRING) {
+ int nid = OBJ_sn2nid(Z_STRVAL_P(bn));
+ if (nid != NID_undef) {
+ group = EC_GROUP_new_by_curve_name(nid);
+ if (!group) {
+ php_openssl_store_errors();
+ goto clean_exit;
+ }
+ EC_GROUP_set_asn1_flag(group, OPENSSL_EC_NAMED_CURVE);
+ EC_GROUP_set_point_conversion_form(group, POINT_CONVERSION_UNCOMPRESSED);
+ if (!EC_KEY_set_group(eckey, group)) {
+ php_openssl_store_errors();
+ goto clean_exit;
+ }
+ }
+ }
+
+ if (group == NULL) {
+ php_error_docref(NULL, E_WARNING, "Unknown curve_name");
+ goto clean_exit;
+ }
+
+ // The public key 'pnt' can be calculated from 'd' or is defined by 'x' and 'y'
+ if ((bn = zend_hash_str_find(Z_ARRVAL_P(data), "d", sizeof("d") - 1)) != NULL &&
+ Z_TYPE_P(bn) == IS_STRING) {
+ d = BN_bin2bn((unsigned char*) Z_STRVAL_P(bn), Z_STRLEN_P(bn), NULL);
+ if (!EC_KEY_set_private_key(eckey, d)) {
+ php_openssl_store_errors();
+ goto clean_exit;
+ }
+ // Calculate the public key by multiplying the Point Q with the public key
+ // P = d * Q
+ pnt = EC_POINT_new(group);
+ if (!pnt || !EC_POINT_mul(group, pnt, d, NULL, NULL, NULL)) {
+ php_openssl_store_errors();
+ goto clean_exit;
+ }
+ } else if ((x = zend_hash_str_find(Z_ARRVAL_P(data), "x", sizeof("x") - 1)) != NULL &&
+ Z_TYPE_P(x) == IS_STRING &&
+ (y = zend_hash_str_find(Z_ARRVAL_P(data), "y", sizeof("y") - 1)) != NULL &&
+ Z_TYPE_P(y) == IS_STRING) {
+ pnt = EC_POINT_new(group);
+ if (pnt == NULL) {
+ php_openssl_store_errors();
+ goto clean_exit;
+ }
+ if (!EC_POINT_set_affine_coordinates_GFp(
+ group, pnt, BN_bin2bn((unsigned char*) Z_STRVAL_P(x), Z_STRLEN_P(x), NULL),
+ BN_bin2bn((unsigned char*) Z_STRVAL_P(y), Z_STRLEN_P(y), NULL), NULL)) {
+ php_openssl_store_errors();
+ goto clean_exit;
+ }
+ }
+
+ if (pnt != NULL) {
+ if (!EC_KEY_set_public_key(eckey, pnt)) {
+ php_openssl_store_errors();
+ goto clean_exit;
+ }
+ EC_POINT_free(pnt);
+ pnt = NULL;
+ }
+
+ if (!EC_KEY_check_key(eckey)) {
+ PHP_OPENSSL_RAND_ADD_TIME();
+ EC_KEY_generate_key(eckey);
+ php_openssl_store_errors();
+ }
+ if (EC_KEY_check_key(eckey) && EVP_PKEY_assign_EC_KEY(pkey, eckey)) {
+ EC_GROUP_free(group);
+ RETURN_RES(zend_register_resource(pkey, le_key));
+ } else {
+ php_openssl_store_errors();
+ }
+ } else {
+ php_openssl_store_errors();
+ }
+ } else {
+ php_openssl_store_errors();
+ }
+clean_exit:
+ if (pnt != NULL) {
+ EC_POINT_free(pnt);
+ }
+ if (group != NULL) {
+ EC_GROUP_free(group);
+ }
+ if (eckey != NULL) {
+ EC_KEY_free(eckey);
+ }
+ if (pkey != NULL) {
+ EVP_PKEY_free(pkey);
+ }
+ RETURN_FALSE;
+#endif
}
}
@@ -4129,7 +4491,11 @@ PHP_FUNCTION(openssl_pkey_export_to_file)
PHP_SSL_REQ_INIT(&req);
if (PHP_SSL_REQ_PARSE(&req, args) == SUCCESS) {
- bio_out = BIO_new_file(filename, "w");
+ bio_out = BIO_new_file(filename, PHP_OPENSSL_BIO_MODE_W(PKCS7_BINARY));
+ if (bio_out == NULL) {
+ php_openssl_store_errors();
+ goto clean_exit;
+ }
if (passphrase && req.priv_key_encrypt) {
if (req.priv_key_encrypt_cipher) {
@@ -4144,7 +4510,7 @@ PHP_FUNCTION(openssl_pkey_export_to_file)
switch (EVP_PKEY_base_id(key)) {
#ifdef HAVE_EVP_PKEY_EC
case EVP_PKEY_EC:
- pem_write = PEM_write_bio_ECPrivateKey(bio_out, EVP_PKEY_get1_EC_KEY(key), cipher, (unsigned char *)passphrase, (int)passphrase_len, NULL, NULL);
+ pem_write = PEM_write_bio_ECPrivateKey(bio_out, EVP_PKEY_get0_EC_KEY(key), cipher, (unsigned char *)passphrase, (int)passphrase_len, NULL, NULL);
break;
#endif
default:
@@ -4156,8 +4522,12 @@ PHP_FUNCTION(openssl_pkey_export_to_file)
/* Success!
* If returning the output as a string, do so now */
RETVAL_TRUE;
+ } else {
+ php_openssl_store_errors();
}
}
+
+clean_exit:
PHP_SSL_REQ_DISPOSE(&req);
if (key_resource == NULL && key) {
@@ -4213,7 +4583,7 @@ PHP_FUNCTION(openssl_pkey_export)
switch (EVP_PKEY_base_id(key)) {
#ifdef HAVE_EVP_PKEY_EC
case EVP_PKEY_EC:
- pem_write = PEM_write_bio_ECPrivateKey(bio_out, EVP_PKEY_get1_EC_KEY(key), cipher, (unsigned char *)passphrase, (int)passphrase_len, NULL, NULL);
+ pem_write = PEM_write_bio_ECPrivateKey(bio_out, EVP_PKEY_get0_EC_KEY(key), cipher, (unsigned char *)passphrase, (int)passphrase_len, NULL, NULL);
break;
#endif
default:
@@ -4232,6 +4602,8 @@ PHP_FUNCTION(openssl_pkey_export)
bio_mem_len = BIO_get_mem_data(bio_out, &bio_mem_ptr);
zval_dtor(out);
ZVAL_STRINGL(out, bio_mem_ptr, bio_mem_len);
+ } else {
+ php_openssl_store_errors();
}
}
PHP_SSL_REQ_DISPOSE(&req);
@@ -4326,7 +4698,11 @@ PHP_FUNCTION(openssl_pkey_get_details)
RETURN_FALSE;
}
out = BIO_new(BIO_s_mem());
- PEM_write_bio_PUBKEY(out, pkey);
+ if (!PEM_write_bio_PUBKEY(out, pkey)) {
+ BIO_free(out);
+ php_openssl_store_errors();
+ RETURN_FALSE;
+ }
pbio_len = BIO_get_mem_data(out, &pbio);
array_init(return_value);
@@ -4362,7 +4738,6 @@ PHP_FUNCTION(openssl_pkey_get_details)
add_assoc_zval(return_value, "rsa", &z_rsa);
}
}
-
break;
case EVP_PKEY_DSA:
case EVP_PKEY_DSA2:
@@ -4409,7 +4784,6 @@ PHP_FUNCTION(openssl_pkey_get_details)
add_assoc_zval(return_value, "dh", &z_dh);
}
}
-
break;
#ifdef HAVE_EVP_PKEY_EC
case EVP_PKEY_EC:
@@ -4417,13 +4791,18 @@ PHP_FUNCTION(openssl_pkey_get_details)
if (EVP_PKEY_get0_EC_KEY(pkey) != NULL) {
zval ec;
const EC_GROUP *ec_group;
+ const EC_POINT *pub;
int nid;
char *crv_sn;
ASN1_OBJECT *obj;
// openssl recommends a buffer length of 80
char oir_buf[80];
+ const EC_KEY *ec_key = EVP_PKEY_get0_EC_KEY(pkey);
+ BIGNUM *x = BN_new();
+ BIGNUM *y = BN_new();
+ const BIGNUM *d;
- ec_group = EC_KEY_get0_group(EVP_PKEY_get1_EC_KEY(pkey));
+ ec_group = EC_KEY_get0_group(ec_key);
// Curve nid (numerical identifier) used for ASN1 mapping
nid = EC_GROUP_get_curve_name(ec_group);
@@ -4441,11 +4820,27 @@ PHP_FUNCTION(openssl_pkey_get_details)
obj = OBJ_nid2obj(nid);
if (obj != NULL) {
int oir_len = OBJ_obj2txt(oir_buf, sizeof(oir_buf), obj, 1);
- add_assoc_stringl(&ec, "curve_oid", (char*)oir_buf, oir_len);
+ add_assoc_stringl(&ec, "curve_oid", (char*) oir_buf, oir_len);
ASN1_OBJECT_free(obj);
}
+ pub = EC_KEY_get0_public_key(ec_key);
+
+ if (EC_POINT_get_affine_coordinates_GFp(ec_group, pub, x, y, NULL)) {
+ OPENSSL_GET_BN(ec, x, x);
+ OPENSSL_GET_BN(ec, y, y);
+ } else {
+ php_openssl_store_errors();
+ }
+
+ if ((d = EC_KEY_get0_private_key(EVP_PKEY_get0_EC_KEY(pkey))) != NULL) {
+ OPENSSL_GET_BN(ec, d, d);
+ }
+
add_assoc_zval(return_value, "ec", &ec);
+
+ BN_free(x);
+ BN_free(y);
}
break;
#endif
@@ -4459,9 +4854,54 @@ PHP_FUNCTION(openssl_pkey_get_details)
}
/* }}} */
+/* {{{ proto string openssl_dh_compute_key(string pub_key, resource dh_key)
+ Computes shared secret for public value of remote DH key and local DH key */
+PHP_FUNCTION(openssl_dh_compute_key)
+{
+ zval *key;
+ char *pub_str;
+ size_t pub_len;
+ DH *dh;
+ EVP_PKEY *pkey;
+ BIGNUM *pub;
+ zend_string *data;
+ int len;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "sr", &pub_str, &pub_len, &key) == FAILURE) {
+ return;
+ }
+ if ((pkey = (EVP_PKEY *)zend_fetch_resource(Z_RES_P(key), "OpenSSL key", le_key)) == NULL) {
+ RETURN_FALSE;
+ }
+ if (EVP_PKEY_base_id(pkey) != EVP_PKEY_DH) {
+ RETURN_FALSE;
+ }
+ dh = EVP_PKEY_get0_DH(pkey);
+ if (dh == NULL) {
+ RETURN_FALSE;
+ }
+
+ PHP_OPENSSL_CHECK_SIZE_T_TO_INT(pub_len, pub_key);
+ pub = BN_bin2bn((unsigned char*)pub_str, (int)pub_len, NULL);
+
+ data = zend_string_alloc(DH_size(dh), 0);
+ len = DH_compute_key((unsigned char*)ZSTR_VAL(data), pub, dh);
+
+ if (len >= 0) {
+ ZSTR_LEN(data) = len;
+ ZSTR_VAL(data)[len] = 0;
+ RETVAL_STR(data);
+ } else {
+ php_openssl_store_errors();
+ zend_string_release(data);
+ RETVAL_FALSE;
+ }
+
+ BN_free(pub);
+}
/* }}} */
-#if OPENSSL_VERSION_NUMBER >= 0x10000000L
+/* }}} */
/* {{{ proto string openssl_pbkdf2(string password, string salt, long key_length, long iterations [, string digest_method = "sha1"])
Generates a PKCS5 v2 PBKDF2 string, defaults to sha1 */
@@ -4512,14 +4952,13 @@ PHP_FUNCTION(openssl_pbkdf2)
ZSTR_VAL(out_buffer)[key_length] = 0;
RETURN_NEW_STR(out_buffer);
} else {
+ php_openssl_store_errors();
zend_string_release(out_buffer);
RETURN_FALSE;
}
}
/* }}} */
-#endif
-
/* {{{ PKCS7 S/MIME functions */
/* {{{ proto bool openssl_pkcs7_verify(string filename, long flags [, string signerscerts [, array cainfo [, string extracerts [, string content]]]])
@@ -4568,8 +5007,9 @@ PHP_FUNCTION(openssl_pkcs7_verify)
goto clean_exit;
}
- in = BIO_new_file(filename, (flags & PKCS7_BINARY) ? "rb" : "r");
+ in = BIO_new_file(filename, PHP_OPENSSL_BIO_MODE_R(flags));
if (in == NULL) {
+ php_openssl_store_errors();
goto clean_exit;
}
p7 = SMIME_read_PKCS7(in, &datain);
@@ -4577,6 +5017,7 @@ PHP_FUNCTION(openssl_pkcs7_verify)
#if DEBUG_SMIME
zend_printf("SMIME_read_PKCS7 failed\n");
#endif
+ php_openssl_store_errors();
goto clean_exit;
}
@@ -4586,8 +5027,9 @@ PHP_FUNCTION(openssl_pkcs7_verify)
goto clean_exit;
}
- dataout = BIO_new_file(datafilename, "w");
+ dataout = BIO_new_file(datafilename, PHP_OPENSSL_BIO_MODE_W(PKCS7_BINARY));
if (dataout == NULL) {
+ php_openssl_store_errors();
goto clean_exit;
}
}
@@ -4606,23 +5048,35 @@ PHP_FUNCTION(openssl_pkcs7_verify)
goto clean_exit;
}
- certout = BIO_new_file(signersfilename, "w");
+ certout = BIO_new_file(signersfilename, PHP_OPENSSL_BIO_MODE_W(PKCS7_BINARY));
if (certout) {
int i;
signers = PKCS7_get0_signers(p7, NULL, (int)flags);
+ if (signers != NULL) {
+
+ for (i = 0; i < sk_X509_num(signers); i++) {
+ if (!PEM_write_bio_X509(certout, sk_X509_value(signers, i))) {
+ php_openssl_store_errors();
+ RETVAL_LONG(-1);
+ php_error_docref(NULL, E_WARNING, "failed to write signer %d", i);
+ }
+ }
- for(i = 0; i < sk_X509_num(signers); i++) {
- PEM_write_bio_X509(certout, sk_X509_value(signers, i));
+ sk_X509_free(signers);
+ } else {
+ RETVAL_LONG(-1);
+ php_openssl_store_errors();
}
+
BIO_free(certout);
- sk_X509_free(signers);
} else {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "signature OK, but cannot open %s for writing", signersfilename);
RETVAL_LONG(-1);
}
}
- goto clean_exit;
} else {
+ php_openssl_store_errors();
RETVAL_FALSE;
}
clean_exit:
@@ -4665,13 +5119,15 @@ PHP_FUNCTION(openssl_pkcs7_encrypt)
return;
}
- infile = BIO_new_file(infilename, "r");
+ infile = BIO_new_file(infilename, PHP_OPENSSL_BIO_MODE_R(flags));
if (infile == NULL) {
+ php_openssl_store_errors();
goto clean_exit;
}
- outfile = BIO_new_file(outfilename, "w");
+ outfile = BIO_new_file(outfilename, PHP_OPENSSL_BIO_MODE_W(flags));
if (outfile == NULL) {
+ php_openssl_store_errors();
goto clean_exit;
}
@@ -4692,6 +5148,7 @@ PHP_FUNCTION(openssl_pkcs7_encrypt)
make a copy and push that on the stack instead */
cert = X509_dup(cert);
if (cert == NULL) {
+ php_openssl_store_errors();
goto clean_exit;
}
}
@@ -4711,6 +5168,7 @@ PHP_FUNCTION(openssl_pkcs7_encrypt)
make a copy and push that on the stack instead */
cert = X509_dup(cert);
if (cert == NULL) {
+ php_openssl_store_errors();
goto clean_exit;
}
}
@@ -4728,6 +5186,7 @@ PHP_FUNCTION(openssl_pkcs7_encrypt)
p7 = PKCS7_encrypt(recipcerts, infile, (EVP_CIPHER*)cipher, (int)flags);
if (p7 == NULL) {
+ php_openssl_store_errors();
goto clean_exit;
}
@@ -4747,7 +5206,10 @@ PHP_FUNCTION(openssl_pkcs7_encrypt)
(void)BIO_reset(infile);
/* write the encrypted data */
- SMIME_write_PKCS7(outfile, p7, infile, (int)flags);
+ if (!SMIME_write_PKCS7(outfile, p7, infile, (int)flags)) {
+ php_openssl_store_errors();
+ goto clean_exit;
+ }
RETVAL_TRUE;
@@ -4815,20 +5277,23 @@ PHP_FUNCTION(openssl_pkcs7_sign)
goto clean_exit;
}
- infile = BIO_new_file(infilename, "r");
+ infile = BIO_new_file(infilename, PHP_OPENSSL_BIO_MODE_R(flags));
if (infile == NULL) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "error opening input file %s!", infilename);
goto clean_exit;
}
- outfile = BIO_new_file(outfilename, "w");
+ outfile = BIO_new_file(outfilename, PHP_OPENSSL_BIO_MODE_W(PKCS7_BINARY));
if (outfile == NULL) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "error opening output file %s!", outfilename);
goto clean_exit;
}
p7 = PKCS7_sign(cert, privkey, others, infile, (int)flags);
if (p7 == NULL) {
+ php_openssl_store_errors();
php_error_docref(NULL, E_WARNING, "error creating PKCS7 structure!");
goto clean_exit;
}
@@ -4837,18 +5302,26 @@ PHP_FUNCTION(openssl_pkcs7_sign)
/* tack on extra headers */
if (zheaders) {
+ int ret;
+
ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(zheaders), strindex, hval) {
convert_to_string_ex(hval);
if (strindex) {
- BIO_printf(outfile, "%s: %s\n", ZSTR_VAL(strindex), Z_STRVAL_P(hval));
+ ret = BIO_printf(outfile, "%s: %s\n", ZSTR_VAL(strindex), Z_STRVAL_P(hval));
} else {
- BIO_printf(outfile, "%s\n", Z_STRVAL_P(hval));
+ ret = BIO_printf(outfile, "%s\n", Z_STRVAL_P(hval));
+ }
+ if (ret < 0) {
+ php_openssl_store_errors();
}
} ZEND_HASH_FOREACH_END();
}
/* write the signed data */
- SMIME_write_PKCS7(outfile, p7, infile, (int)flags);
+ if (!SMIME_write_PKCS7(outfile, p7, infile, (int)flags)) {
+ php_openssl_store_errors();
+ goto clean_exit;
+ }
RETVAL_TRUE;
@@ -4907,22 +5380,27 @@ PHP_FUNCTION(openssl_pkcs7_decrypt)
goto clean_exit;
}
- in = BIO_new_file(infilename, "r");
+ in = BIO_new_file(infilename, PHP_OPENSSL_BIO_MODE_R(PKCS7_BINARY));
if (in == NULL) {
+ php_openssl_store_errors();
goto clean_exit;
}
- out = BIO_new_file(outfilename, "w");
+ out = BIO_new_file(outfilename, PHP_OPENSSL_BIO_MODE_W(PKCS7_BINARY));
if (out == NULL) {
+ php_openssl_store_errors();
goto clean_exit;
}
p7 = SMIME_read_PKCS7(in, &datain);
if (p7 == NULL) {
+ php_openssl_store_errors();
goto clean_exit;
}
if (PKCS7_decrypt(p7, key, cert, out, PKCS7_DETACHED)) {
RETVAL_TRUE;
+ } else {
+ php_openssl_store_errors();
}
clean_exit:
PKCS7_free(p7);
@@ -4990,6 +5468,8 @@ PHP_FUNCTION(openssl_private_encrypt)
ZVAL_NEW_STR(crypted, cryptedbuf);
cryptedbuf = NULL;
RETVAL_TRUE;
+ } else {
+ php_openssl_store_errors();
}
if (cryptedbuf) {
zend_string_release(cryptedbuf);
@@ -5057,6 +5537,8 @@ PHP_FUNCTION(openssl_private_decrypt)
ZVAL_NEW_STR(crypted, cryptedbuf);
cryptedbuf = NULL;
RETVAL_TRUE;
+ } else {
+ php_openssl_store_errors();
}
if (keyresource == NULL) {
@@ -5117,6 +5599,8 @@ PHP_FUNCTION(openssl_public_encrypt)
ZVAL_NEW_STR(crypted, cryptedbuf);
cryptedbuf = NULL;
RETVAL_TRUE;
+ } else {
+ php_openssl_store_errors();
}
if (keyresource == NULL) {
EVP_PKEY_free(pkey);
@@ -5186,6 +5670,8 @@ PHP_FUNCTION(openssl_public_decrypt)
ZVAL_NEW_STR(crypted, cryptedbuf);
cryptedbuf = NULL;
RETVAL_TRUE;
+ } else {
+ php_openssl_store_errors();
}
if (cryptedbuf) {
@@ -5201,16 +5687,25 @@ PHP_FUNCTION(openssl_public_decrypt)
Returns a description of the last error, and alters the index of the error messages. Returns false when the are no more messages */
PHP_FUNCTION(openssl_error_string)
{
- char buf[512];
+ char buf[256];
unsigned long val;
if (zend_parse_parameters_none() == FAILURE) {
return;
}
- val = ERR_get_error();
+ php_openssl_store_errors();
+
+ if (OPENSSL_G(errors) == NULL || OPENSSL_G(errors)->top == OPENSSL_G(errors)->bottom) {
+ RETURN_FALSE;
+ }
+
+ OPENSSL_G(errors)->bottom = (OPENSSL_G(errors)->bottom + 1) % ERR_NUM_ERRORS;
+ val = OPENSSL_G(errors)->buffer[OPENSSL_G(errors)->bottom];
+
if (val) {
- RETURN_STRING(ERR_error_string(val, buf));
+ ERR_error_string_n(val, buf, 256);
+ RETURN_STRING(buf);
} else {
RETURN_FALSE;
}
@@ -5272,6 +5767,7 @@ PHP_FUNCTION(openssl_sign)
ZVAL_NEW_STR(signature, sigbuf);
RETVAL_TRUE;
} else {
+ php_openssl_store_errors();
efree(sigbuf);
RETVAL_FALSE;
}
@@ -5328,10 +5824,11 @@ PHP_FUNCTION(openssl_verify)
}
md_ctx = EVP_MD_CTX_create();
- if (md_ctx != NULL) {
- EVP_VerifyInit(md_ctx, mdtype);
- EVP_VerifyUpdate (md_ctx, data, data_len);
- err = EVP_VerifyFinal(md_ctx, (unsigned char *)signature, (unsigned int)signature_len, pkey);
+ if (md_ctx == NULL ||
+ !EVP_VerifyInit (md_ctx, mdtype) ||
+ !EVP_VerifyUpdate (md_ctx, data, data_len) ||
+ (err = EVP_VerifyFinal(md_ctx, (unsigned char *)signature, (unsigned int)signature_len, pkey)) < 0) {
+ php_openssl_store_errors();
}
EVP_MD_CTX_destroy(md_ctx);
@@ -5413,6 +5910,7 @@ PHP_FUNCTION(openssl_seal)
ctx = EVP_CIPHER_CTX_new();
if (ctx == NULL || !EVP_EncryptInit(ctx,cipher,NULL,NULL)) {
EVP_CIPHER_CTX_free(ctx);
+ php_openssl_store_errors();
RETVAL_FALSE;
goto clean_exit;
}
@@ -5424,9 +5922,10 @@ PHP_FUNCTION(openssl_seal)
if (EVP_SealInit(ctx, cipher, eks, eksl, &iv_buf[0], pkeys, nkeys) <= 0 ||
!EVP_SealUpdate(ctx, buf, &len1, (unsigned char *)data, (int)data_len) ||
!EVP_SealFinal(ctx, buf + len1, &len2)) {
- RETVAL_FALSE;
efree(buf);
EVP_CIPHER_CTX_free(ctx);
+ php_openssl_store_errors();
+ RETVAL_FALSE;
goto clean_exit;
}
@@ -5520,7 +6019,7 @@ PHP_FUNCTION(openssl_open)
"Cipher algorithm requires an IV to be supplied as a sixth parameter");
RETURN_FALSE;
}
- if (cipher_iv_len != iv_len) {
+ if ((size_t)cipher_iv_len != iv_len) {
php_error_docref(NULL, E_WARNING, "IV length is invalid");
RETURN_FALSE;
}
@@ -5540,6 +6039,7 @@ PHP_FUNCTION(openssl_open)
ZVAL_NEW_STR(opendata, zend_string_init((char*)buf, len1 + len2, 0));
RETVAL_TRUE;
} else {
+ php_openssl_store_errors();
RETVAL_FALSE;
}
@@ -5597,6 +6097,33 @@ PHP_FUNCTION(openssl_get_cipher_methods)
}
/* }}} */
+/* {{{ proto array openssl_get_curve_names()
+ Return array of available elliptic curves */
+#ifdef HAVE_EVP_PKEY_EC
+PHP_FUNCTION(openssl_get_curve_names)
+{
+ EC_builtin_curve *curves = NULL;
+ const char *sname;
+ size_t i;
+ size_t len = EC_get_builtin_curves(NULL, 0);
+
+ curves = emalloc(sizeof(EC_builtin_curve) * len);
+ if (!EC_get_builtin_curves(curves, len)) {
+ RETURN_FALSE;
+ }
+
+ array_init(return_value);
+ for (i = 0; i < len; i++) {
+ sname = OBJ_nid2sn(curves[i].nid);
+ if (sname != NULL) {
+ add_next_index_string(return_value, sname);
+ }
+ }
+ efree(curves);
+}
+#endif
+/* }}} */
+
/* {{{ proto string openssl_digest(string data, string method [, bool raw_output=false])
Computes digest hash value for given data using given method, returns raw or binhex encoded string */
PHP_FUNCTION(openssl_digest)
@@ -5639,6 +6166,7 @@ PHP_FUNCTION(openssl_digest)
RETVAL_STR(digest_str);
}
} else {
+ php_openssl_store_errors();
zend_string_release(sigbuf);
RETVAL_FALSE;
}
@@ -5647,13 +6175,58 @@ PHP_FUNCTION(openssl_digest)
}
/* }}} */
-static zend_bool php_openssl_validate_iv(char **piv, size_t *piv_len, size_t iv_required_len)
+/* Cipher mode info */
+struct php_openssl_cipher_mode {
+ zend_bool is_aead;
+ zend_bool is_single_run_aead;
+ int aead_get_tag_flag;
+ int aead_set_tag_flag;
+ int aead_ivlen_flag;
+};
+
+static void php_openssl_load_cipher_mode(struct php_openssl_cipher_mode *mode, const EVP_CIPHER *cipher_type) /* {{{ */
+{
+ switch (EVP_CIPHER_mode(cipher_type)) {
+#ifdef EVP_CIPH_GCM_MODE
+ case EVP_CIPH_GCM_MODE:
+ mode->is_aead = 1;
+ mode->is_single_run_aead = 0;
+ mode->aead_get_tag_flag = EVP_CTRL_GCM_GET_TAG;
+ mode->aead_set_tag_flag = EVP_CTRL_GCM_SET_TAG;
+ mode->aead_ivlen_flag = EVP_CTRL_GCM_SET_IVLEN;
+ break;
+#endif
+#ifdef EVP_CIPH_CCM_MODE
+ case EVP_CIPH_CCM_MODE:
+ mode->is_aead = 1;
+ mode->is_single_run_aead = 1;
+ mode->aead_get_tag_flag = EVP_CTRL_CCM_GET_TAG;
+ mode->aead_set_tag_flag = EVP_CTRL_CCM_SET_TAG;
+ mode->aead_ivlen_flag = EVP_CTRL_CCM_SET_IVLEN;
+ break;
+#endif
+ default:
+ memset(mode, 0, sizeof(struct php_openssl_cipher_mode));
+ }
+}
+/* }}} */
+
+static int php_openssl_validate_iv(char **piv, size_t *piv_len, size_t iv_required_len,
+ zend_bool *free_iv, EVP_CIPHER_CTX *cipher_ctx, struct php_openssl_cipher_mode *mode) /* {{{ */
{
char *iv_new;
/* Best case scenario, user behaved */
if (*piv_len == iv_required_len) {
- return 0;
+ return SUCCESS;
+ }
+
+ if (mode->is_aead) {
+ if (EVP_CIPHER_CTX_ctrl(cipher_ctx, mode->aead_ivlen_flag, *piv_len, NULL) != 1) {
+ php_error_docref(NULL, E_WARNING, "Setting of IV length for AEAD mode failed");
+ return FAILURE;
+ }
+ return SUCCESS;
}
iv_new = ecalloc(1, iv_required_len + 1);
@@ -5662,89 +6235,189 @@ static zend_bool php_openssl_validate_iv(char **piv, size_t *piv_len, size_t iv_
/* BC behavior */
*piv_len = iv_required_len;
*piv = iv_new;
- return 1;
+ *free_iv = 1;
+ return SUCCESS;
+
}
if (*piv_len < iv_required_len) {
- php_error_docref(NULL, E_WARNING, "IV passed is only %zd bytes long, cipher expects an IV of precisely %zd bytes, padding with \\0", *piv_len, iv_required_len);
+ php_error_docref(NULL, E_WARNING,
+ "IV passed is only %zd bytes long, cipher expects an IV of precisely %zd bytes, padding with \\0",
+ *piv_len, iv_required_len);
memcpy(iv_new, *piv, *piv_len);
*piv_len = iv_required_len;
*piv = iv_new;
- return 1;
+ *free_iv = 1;
+ return SUCCESS;
}
- php_error_docref(NULL, E_WARNING, "IV passed is %zd bytes long which is longer than the %zd expected by selected cipher, truncating", *piv_len, iv_required_len);
+ php_error_docref(NULL, E_WARNING,
+ "IV passed is %zd bytes long which is longer than the %zd expected by selected cipher, truncating",
+ *piv_len, iv_required_len);
memcpy(iv_new, *piv, iv_required_len);
*piv_len = iv_required_len;
*piv = iv_new;
- return 1;
+ *free_iv = 1;
+ return SUCCESS;
+
+}
+/* }}} */
+
+static int php_openssl_cipher_init(const EVP_CIPHER *cipher_type,
+ EVP_CIPHER_CTX *cipher_ctx, struct php_openssl_cipher_mode *mode,
+ char **ppassword, size_t *ppassword_len, zend_bool *free_password,
+ char **piv, size_t *piv_len, zend_bool *free_iv,
+ char *tag, int tag_len, zend_long options, int enc) /* {{{ */
+{
+ unsigned char *key;
+ int key_len, password_len;
+ size_t max_iv_len;
+
+ *free_password = 0;
+
+ max_iv_len = EVP_CIPHER_iv_length(cipher_type);
+ if (enc && *piv_len == 0 && max_iv_len > 0 && !mode->is_aead) {
+ php_error_docref(NULL, E_WARNING,
+ "Using an empty Initialization Vector (iv) is potentially insecure and not recommended");
+ }
+
+ if (!EVP_CipherInit_ex(cipher_ctx, cipher_type, NULL, NULL, NULL, enc)) {
+ php_openssl_store_errors();
+ return FAILURE;
+ }
+ if (php_openssl_validate_iv(piv, piv_len, max_iv_len, free_iv, cipher_ctx, mode) == FAILURE) {
+ return FAILURE;
+ }
+ if (mode->is_single_run_aead && enc) {
+ EVP_CIPHER_CTX_ctrl(cipher_ctx, mode->aead_set_tag_flag, tag_len, NULL);
+ } else if (!enc && tag && tag_len > 0) {
+ if (!mode->is_aead) {
+ php_error_docref(NULL, E_WARNING, "The tag cannot be used because the cipher method does not support AEAD");
+ } else if (!EVP_CIPHER_CTX_ctrl(cipher_ctx, mode->aead_set_tag_flag, tag_len, (unsigned char *) tag)) {
+ php_error_docref(NULL, E_WARNING, "Setting tag for AEAD cipher decryption failed");
+ return FAILURE;
+ }
+ }
+ /* check and set key */
+ password_len = (int) *ppassword_len;
+ key_len = EVP_CIPHER_key_length(cipher_type);
+ if (key_len > password_len) {
+ if ((OPENSSL_DONT_ZERO_PAD_KEY & options) && !EVP_CIPHER_CTX_set_key_length(cipher_ctx, password_len)) {
+ php_openssl_store_errors();
+ php_error_docref(NULL, E_WARNING, "Key length cannot be set for the cipher method");
+ return FAILURE;
+ }
+ key = emalloc(key_len);
+ memset(key, 0, key_len);
+ memcpy(key, *ppassword, password_len);
+ *ppassword = (char *) key;
+ *ppassword_len = key_len;
+ *free_password = 1;
+ } else {
+ if (password_len > key_len && !EVP_CIPHER_CTX_set_key_length(cipher_ctx, password_len)) {
+ php_openssl_store_errors();
+ }
+ key = (unsigned char*)*ppassword;
+ }
+
+ if (!EVP_CipherInit_ex(cipher_ctx, NULL, NULL, key, (unsigned char *)*piv, enc)) {
+ php_openssl_store_errors();
+ return FAILURE;
+ }
+ if (options & OPENSSL_ZERO_PADDING) {
+ EVP_CIPHER_CTX_set_padding(cipher_ctx, 0);
+ }
+
+ return SUCCESS;
+}
+/* }}} */
+
+static int php_openssl_cipher_update(const EVP_CIPHER *cipher_type,
+ EVP_CIPHER_CTX *cipher_ctx, struct php_openssl_cipher_mode *mode,
+ zend_string **poutbuf, int *poutlen, char *data, size_t data_len,
+ char *aad, size_t aad_len, int enc) /* {{{ */
+{
+ int i = 0;
+
+ if (mode->is_single_run_aead && !EVP_EncryptUpdate(cipher_ctx, NULL, &i, NULL, (int)data_len)) {
+ php_openssl_store_errors();
+ php_error_docref(NULL, E_WARNING, "Setting of data length failed");
+ return FAILURE;
+ }
+
+ if (mode->is_aead && !EVP_CipherUpdate(cipher_ctx, NULL, &i, (unsigned char *)aad, (int)aad_len)) {
+ php_openssl_store_errors();
+ php_error_docref(NULL, E_WARNING, "Setting of additional application data failed");
+ return FAILURE;
+ }
+ *poutbuf = zend_string_alloc((int)data_len + EVP_CIPHER_block_size(cipher_type), 0);
+
+ if (!EVP_CipherUpdate(cipher_ctx, (unsigned char*)ZSTR_VAL(*poutbuf),
+ &i, (unsigned char *)data, (int)data_len)) {
+ /* we don't show warning when we fail but if we ever do, then it should look like this:
+ if (mode->is_single_run_aead && !enc) {
+ php_error_docref(NULL, E_WARNING, "Tag verifycation failed");
+ } else {
+ php_error_docref(NULL, E_WARNING, enc ? "Encryption failed" : "Decryption failed");
+ }
+ */
+ php_openssl_store_errors();
+ zend_string_release(*poutbuf);
+ return FAILURE;
+ }
+
+ *poutlen = i;
+
+ return SUCCESS;
}
+/* }}} */
-/* {{{ proto string openssl_encrypt(string data, string method, string password [, long options=0 [, string $iv='']])
+/* {{{ proto string openssl_encrypt(string data, string method, string password [, long options=0 [, string $iv=''[, string &$tag = ''[, string $aad = ''[, long $tag_length = 16]]]]])
Encrypts given data with given method and key, returns raw or base64 encoded string */
PHP_FUNCTION(openssl_encrypt)
{
- zend_long options = 0;
- char *data, *method, *password, *iv = "";
- size_t data_len, method_len, password_len, iv_len = 0, max_iv_len;
+ zend_long options = 0, tag_len = 16;
+ char *data, *method, *password, *iv = "", *aad = "";
+ size_t data_len, method_len, password_len, iv_len = 0, aad_len = 0;
+ zval *tag = NULL;
const EVP_CIPHER *cipher_type;
EVP_CIPHER_CTX *cipher_ctx;
- int i=0, keylen;
- size_t outlen;
+ struct php_openssl_cipher_mode mode;
+ int i=0, outlen;
zend_string *outbuf;
- unsigned char *key;
- zend_bool free_iv;
+ zend_bool free_iv = 0, free_password = 0;
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss|ls", &data, &data_len, &method, &method_len, &password, &password_len, &options, &iv, &iv_len) == FAILURE) {
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss|lsz/sl", &data, &data_len, &method, &method_len,
+ &password, &password_len, &options, &iv, &iv_len, &tag, &aad, &aad_len, &tag_len) == FAILURE) {
return;
}
+
+ PHP_OPENSSL_CHECK_SIZE_T_TO_INT(data_len, data);
+ PHP_OPENSSL_CHECK_SIZE_T_TO_INT(password_len, password);
+ PHP_OPENSSL_CHECK_SIZE_T_TO_INT(aad_len, aad);
+ PHP_OPENSSL_CHECK_LONG_TO_INT(tag_len, tag_len);
+
cipher_type = EVP_get_cipherbyname(method);
if (!cipher_type) {
php_error_docref(NULL, E_WARNING, "Unknown cipher algorithm");
RETURN_FALSE;
}
- PHP_OPENSSL_CHECK_SIZE_T_TO_INT(data_len, data);
- PHP_OPENSSL_CHECK_SIZE_T_TO_INT(password_len, password);
-
cipher_ctx = EVP_CIPHER_CTX_new();
if (!cipher_ctx) {
php_error_docref(NULL, E_WARNING, "Failed to create cipher context");
RETURN_FALSE;
}
+ php_openssl_load_cipher_mode(&mode, cipher_type);
- keylen = EVP_CIPHER_key_length(cipher_type);
- if (keylen > password_len) {
- key = emalloc(keylen);
- memset(key, 0, keylen);
- memcpy(key, password, password_len);
- } else {
- key = (unsigned char*)password;
- }
-
- max_iv_len = EVP_CIPHER_iv_length(cipher_type);
- if (iv_len == 0 && max_iv_len > 0) {
- php_error_docref(NULL, E_WARNING, "Using an empty Initialization Vector (iv) is potentially insecure and not recommended");
- }
- free_iv = php_openssl_validate_iv(&iv, &iv_len, max_iv_len);
-
- outlen = data_len + EVP_CIPHER_block_size(cipher_type);
- outbuf = zend_string_alloc(outlen, 0);
-
- EVP_EncryptInit(cipher_ctx, cipher_type, NULL, NULL);
- if (password_len > keylen) {
- EVP_CIPHER_CTX_set_key_length(cipher_ctx, (int)password_len);
- }
- EVP_EncryptInit_ex(cipher_ctx, NULL, NULL, key, (unsigned char *)iv);
- if (options & OPENSSL_ZERO_PADDING) {
- EVP_CIPHER_CTX_set_padding(cipher_ctx, 0);
- }
- if (data_len > 0) {
- EVP_EncryptUpdate(cipher_ctx, (unsigned char*)ZSTR_VAL(outbuf), &i, (unsigned char *)data, (int)data_len);
- }
- outlen = i;
- if (EVP_EncryptFinal(cipher_ctx, (unsigned char *)ZSTR_VAL(outbuf) + i, &i)) {
+ if (php_openssl_cipher_init(cipher_type, cipher_ctx, &mode,
+ &password, &password_len, &free_password,
+ &iv, &iv_len, &free_iv, NULL, tag_len, options, 1) == FAILURE ||
+ php_openssl_cipher_update(cipher_type, cipher_ctx, &mode, &outbuf, &outlen,
+ data, data_len, aad, aad_len, 1) == FAILURE) {
+ RETVAL_FALSE;
+ } else if (EVP_EncryptFinal(cipher_ctx, (unsigned char *)ZSTR_VAL(outbuf) + outlen, &i)) {
outlen += i;
if (options & OPENSSL_RAW_DATA) {
ZSTR_VAL(outbuf)[outlen] = '\0';
@@ -5755,39 +6428,67 @@ PHP_FUNCTION(openssl_encrypt)
base64_str = php_base64_encode((unsigned char*)ZSTR_VAL(outbuf), outlen);
zend_string_release(outbuf);
+ outbuf = base64_str;
RETVAL_STR(base64_str);
}
+ if (mode.is_aead && tag) {
+ zend_string *tag_str = zend_string_alloc(tag_len, 0);
+
+ if (EVP_CIPHER_CTX_ctrl(cipher_ctx, mode.aead_get_tag_flag, tag_len, ZSTR_VAL(tag_str)) == 1) {
+ zval_dtor(tag);
+ ZSTR_VAL(tag_str)[tag_len] = '\0';
+ ZSTR_LEN(tag_str) = tag_len;
+ ZVAL_NEW_STR(tag, tag_str);
+ } else {
+ php_error_docref(NULL, E_WARNING, "Retrieving verification tag failed");
+ zend_string_release(tag_str);
+ zend_string_release(outbuf);
+ RETVAL_FALSE;
+ }
+ } else if (tag) {
+ zval_dtor(tag);
+ ZVAL_NULL(tag);
+ php_error_docref(NULL, E_WARNING,
+ "The authenticated tag cannot be provided for cipher that doesn not support AEAD");
+ } else if (mode.is_aead) {
+ php_error_docref(NULL, E_WARNING, "A tag should be provided when using AEAD mode");
+ zend_string_release(outbuf);
+ RETVAL_FALSE;
+ }
} else {
+ php_openssl_store_errors();
zend_string_release(outbuf);
RETVAL_FALSE;
}
- if (key != (unsigned char*)password) {
- efree(key);
+
+ if (free_password) {
+ efree(password);
}
if (free_iv) {
efree(iv);
}
+ EVP_CIPHER_CTX_cleanup(cipher_ctx);
EVP_CIPHER_CTX_free(cipher_ctx);
}
/* }}} */
-/* {{{ proto string openssl_decrypt(string data, string method, string password [, long options=0 [, string $iv = '']])
+/* {{{ proto string openssl_decrypt(string data, string method, string password [, long options=0 [, string $iv = ''[, string $tag = ''[, string $aad = '']]]])
Takes raw or base64 encoded string and decrypts it using given method and key */
PHP_FUNCTION(openssl_decrypt)
{
zend_long options = 0;
- char *data, *method, *password, *iv = "";
- size_t data_len, method_len, password_len, iv_len = 0;
+ char *data, *method, *password, *iv = "", *tag = NULL, *aad = "";
+ size_t data_len, method_len, password_len, iv_len = 0, tag_len = 0, aad_len = 0;
const EVP_CIPHER *cipher_type;
EVP_CIPHER_CTX *cipher_ctx;
- int i, keylen;
- size_t outlen;
+ struct php_openssl_cipher_mode mode;
+ int i = 0, outlen;
zend_string *outbuf;
- unsigned char *key;
zend_string *base64_str = NULL;
- zend_bool free_iv;
+ zend_bool free_iv = 0, free_password = 0;
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss|ls", &data, &data_len, &method, &method_len, &password, &password_len, &options, &iv, &iv_len) == FAILURE) {
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss|lsss", &data, &data_len, &method, &method_len,
+ &password, &password_len, &options, &iv, &iv_len, &tag, &tag_len, &aad, &aad_len) == FAILURE) {
return;
}
@@ -5798,6 +6499,8 @@ PHP_FUNCTION(openssl_decrypt)
PHP_OPENSSL_CHECK_SIZE_T_TO_INT(data_len, data);
PHP_OPENSSL_CHECK_SIZE_T_TO_INT(password_len, password);
+ PHP_OPENSSL_CHECK_SIZE_T_TO_INT(aad_len, aad);
+ PHP_OPENSSL_CHECK_SIZE_T_TO_INT(tag_len, tag);
cipher_type = EVP_get_cipherbyname(method);
if (!cipher_type) {
@@ -5811,6 +6514,8 @@ PHP_FUNCTION(openssl_decrypt)
RETURN_FALSE;
}
+ php_openssl_load_cipher_mode(&mode, cipher_type);
+
if (!(options & OPENSSL_RAW_DATA)) {
base64_str = php_base64_decode((unsigned char*)data, data_len);
if (!base64_str) {
@@ -5822,41 +6527,26 @@ PHP_FUNCTION(openssl_decrypt)
data = ZSTR_VAL(base64_str);
}
- keylen = EVP_CIPHER_key_length(cipher_type);
- if (keylen > password_len) {
- key = emalloc(keylen);
- memset(key, 0, keylen);
- memcpy(key, password, password_len);
- } else {
- key = (unsigned char*)password;
- }
-
- free_iv = php_openssl_validate_iv(&iv, &iv_len, EVP_CIPHER_iv_length(cipher_type));
-
- outlen = data_len + EVP_CIPHER_block_size(cipher_type);
- outbuf = zend_string_alloc(outlen, 0);
-
- EVP_DecryptInit(cipher_ctx, cipher_type, NULL, NULL);
- if (password_len > keylen) {
- EVP_CIPHER_CTX_set_key_length(cipher_ctx, (int)password_len);
- }
- EVP_DecryptInit_ex(cipher_ctx, NULL, NULL, key, (unsigned char *)iv);
- if (options & OPENSSL_ZERO_PADDING) {
- EVP_CIPHER_CTX_set_padding(cipher_ctx, 0);
- }
- EVP_DecryptUpdate(cipher_ctx, (unsigned char*)ZSTR_VAL(outbuf), &i, (unsigned char *)data, (int)data_len);
- outlen = i;
- if (EVP_DecryptFinal(cipher_ctx, (unsigned char *)ZSTR_VAL(outbuf) + i, &i)) {
+ if (php_openssl_cipher_init(cipher_type, cipher_ctx, &mode,
+ &password, &password_len, &free_password,
+ &iv, &iv_len, &free_iv, tag, tag_len, options, 0) == FAILURE ||
+ php_openssl_cipher_update(cipher_type, cipher_ctx, &mode, &outbuf, &outlen,
+ data, data_len, aad, aad_len, 0) == FAILURE) {
+ RETVAL_FALSE;
+ } else if (mode.is_single_run_aead ||
+ EVP_DecryptFinal(cipher_ctx, (unsigned char *)ZSTR_VAL(outbuf) + outlen, &i)) {
outlen += i;
ZSTR_VAL(outbuf)[outlen] = '\0';
ZSTR_LEN(outbuf) = outlen;
RETVAL_STR(outbuf);
} else {
+ php_openssl_store_errors();
zend_string_release(outbuf);
RETVAL_FALSE;
}
- if (key != (unsigned char*)password) {
- efree(key);
+
+ if (free_password) {
+ efree(password);
}
if (free_iv) {
efree(iv);
@@ -5864,6 +6554,7 @@ PHP_FUNCTION(openssl_decrypt)
if (base64_str) {
zend_string_release(base64_str);
}
+ EVP_CIPHER_CTX_cleanup(cipher_ctx);
EVP_CIPHER_CTX_free(cipher_ctx);
}
/* }}} */
@@ -5895,52 +6586,6 @@ PHP_FUNCTION(openssl_cipher_iv_length)
/* }}} */
-/* {{{ proto string openssl_dh_compute_key(string pub_key, resource dh_key)
- Computes shared secret for public value of remote DH key and local DH key */
-PHP_FUNCTION(openssl_dh_compute_key)
-{
- zval *key;
- char *pub_str;
- size_t pub_len;
- DH *dh;
- EVP_PKEY *pkey;
- BIGNUM *pub;
- zend_string *data;
- int len;
-
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "sr", &pub_str, &pub_len, &key) == FAILURE) {
- return;
- }
- if ((pkey = (EVP_PKEY *)zend_fetch_resource(Z_RES_P(key), "OpenSSL key", le_key)) == NULL) {
- RETURN_FALSE;
- }
- if (EVP_PKEY_base_id(pkey) != EVP_PKEY_DH) {
- RETURN_FALSE;
- }
- dh = EVP_PKEY_get0_DH(pkey);
- if (dh == NULL) {
- RETURN_FALSE;
- }
-
- PHP_OPENSSL_CHECK_SIZE_T_TO_INT(pub_len, pub_key);
- pub = BN_bin2bn((unsigned char*)pub_str, (int)pub_len, NULL);
-
- data = zend_string_alloc(DH_size(dh), 0);
- len = DH_compute_key((unsigned char*)ZSTR_VAL(data), pub, dh);
-
- if (len >= 0) {
- ZSTR_LEN(data) = len;
- ZSTR_VAL(data)[len] = 0;
- RETVAL_STR(data);
- } else {
- zend_string_release(data);
- RETVAL_FALSE;
- }
-
- BN_free(pub);
-}
-/* }}} */
-
/* {{{ proto string openssl_random_pseudo_bytes(integer length [, &bool returned_strong_result])
Returns a string of the length specified filled with random pseudo bytes */
PHP_FUNCTION(openssl_random_pseudo_bytes)
@@ -5987,6 +6632,8 @@ PHP_FUNCTION(openssl_random_pseudo_bytes)
ZVAL_FALSE(zstrong_result_returned);
}
RETURN_FALSE;
+ } else {
+ php_openssl_store_errors();
}
#endif
diff --git a/ext/openssl/php_openssl.h b/ext/openssl/php_openssl.h
index a324a73305..ba8e6bd6b7 100644
--- a/ext/openssl/php_openssl.h
+++ b/ext/openssl/php_openssl.h
@@ -31,6 +31,7 @@ extern zend_module_entry openssl_module_entry;
#define OPENSSL_RAW_DATA 1
#define OPENSSL_ZERO_PADDING 2
+#define OPENSSL_DONT_ZERO_PAD_KEY 4
#define OPENSSL_ERROR_X509_PRIVATE_KEY_VALUES_MISMATCH 0x0B080074
@@ -46,11 +47,33 @@ extern zend_module_entry openssl_module_entry;
"DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:" \
"AES256-GCM-SHA384:AES128:AES256:HIGH:!SSLv2:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!RC4:!ADH"
+#include <openssl/err.h>
+
+struct php_openssl_errors {
+ int buffer[ERR_NUM_ERRORS];
+ int top;
+ int bottom;
+};
+
+ZEND_BEGIN_MODULE_GLOBALS(openssl)
+ struct php_openssl_errors *errors;
+ZEND_END_MODULE_GLOBALS(openssl)
+
+#define OPENSSL_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(openssl, v)
+
+#if defined(ZTS) && defined(COMPILE_DL_OPENSSL)
+ZEND_TSRMLS_CACHE_EXTERN();
+#endif
+
php_stream_transport_factory_func php_openssl_ssl_socket_factory;
+void php_openssl_store_errors();
+
PHP_MINIT_FUNCTION(openssl);
PHP_MSHUTDOWN_FUNCTION(openssl);
PHP_MINFO_FUNCTION(openssl);
+PHP_GINIT_FUNCTION(openssl);
+PHP_GSHUTDOWN_FUNCTION(openssl);
PHP_FUNCTION(openssl_pkey_get_private);
PHP_FUNCTION(openssl_pkey_get_public);
@@ -104,12 +127,22 @@ PHP_FUNCTION(openssl_spki_export);
PHP_FUNCTION(openssl_spki_export_challenge);
PHP_FUNCTION(openssl_get_cert_locations);
+
+#ifdef PHP_WIN32
+#define PHP_OPENSSL_BIO_MODE_R(flags) (((flags) & PKCS7_BINARY) ? "rb" : "r")
+#define PHP_OPENSSL_BIO_MODE_W(flags) (((flags) & PKCS7_BINARY) ? "wb" : "w")
+#else
+#define PHP_OPENSSL_BIO_MODE_R(flags) "r"
+#define PHP_OPENSSL_BIO_MODE_W(flags) "w"
+#endif
+
#else
#define phpext_openssl_ptr NULL
#endif
+
#endif
/*
diff --git a/ext/openssl/tests/001.phpt b/ext/openssl/tests/001.phpt
index 627077e8f4..2d0aa907fe 100644
--- a/ext/openssl/tests/001.phpt
+++ b/ext/openssl/tests/001.phpt
@@ -25,7 +25,7 @@ if ($privkey === false)
die("failed to create private key");
$passphrase = "banana";
-$key_file_name = tempnam("/tmp", "ssl");
+$key_file_name = tempnam(sys_get_temp_dir(), "ssl");
if ($key_file_name === false)
die("failed to get a temporary filename!");
diff --git a/ext/openssl/tests/bug38261.phpt b/ext/openssl/tests/bug38261.phpt
index b06fa4f8fe..66de8b876c 100644
--- a/ext/openssl/tests/bug38261.phpt
+++ b/ext/openssl/tests/bug38261.phpt
@@ -31,4 +31,4 @@ Warning: openssl_x509_parse() expects at least 1 parameter, 0 given in %sbug3826
NULL
bool(false)
-Catchable fatal error: Object of class stdClass could not be converted to string in %sbug38261.php on line %d
+Recoverable fatal error: Object of class stdClass could not be converted to string in %sbug38261.php on line %d
diff --git a/ext/openssl/tests/bug41033.phpt b/ext/openssl/tests/bug41033.phpt
index c665be5273..4e9bea3e3e 100644
--- a/ext/openssl/tests/bug41033.phpt
+++ b/ext/openssl/tests/bug41033.phpt
@@ -10,14 +10,14 @@ if (OPENSSL_VERSION_NUMBER < 0x009070af) die("skip");
$prv = 'file://' . dirname(__FILE__) . '/' . 'bug41033.pem';
$pub = 'file://' . dirname(__FILE__) . '/' . 'bug41033pub.pem';
-$alg = (OPENSSL_VERSION_NUMBER < 0x10000000) ? OPENSSL_ALGO_DSS1 : OPENSSL_ALGO_SHA1;
+
$prkeyid = openssl_get_privatekey($prv, "1234");
$ct = "Hello I am some text!";
-openssl_sign($ct, $signature, $prkeyid, $alg);
+openssl_sign($ct, $signature, $prkeyid, OPENSSL_ALGO_SHA1);
echo "Signature: ".base64_encode($signature) . "\n";
$pukeyid = openssl_get_publickey($pub);
-$valid = openssl_verify($ct, $signature, $pukeyid, $alg);
+$valid = openssl_verify($ct, $signature, $pukeyid, OPENSSL_ALGO_SHA1);
echo "Signature validity: " . $valid . "\n";
diff --git a/ext/openssl/tests/bug61124.phpt b/ext/openssl/tests/bug61124.phpt
index 2fc192d434..9b21da5048 100644
--- a/ext/openssl/tests/bug61124.phpt
+++ b/ext/openssl/tests/bug61124.phpt
@@ -8,5 +8,5 @@ if (!extension_loaded("openssl")) die("skip");
var_dump(openssl_decrypt('kzo w2RMExUTYQXW2Xzxmg==', 'aes-128-cbc', 'pass', false, 'pass'));
--EXPECTF--
-Warning: openssl_decrypt(): Failed to base64 decode the input in %s on line %s
-bool(false) \ No newline at end of file
+Warning: openssl_decrypt(): IV passed is only 4 bytes long, cipher expects an IV of precisely 16 bytes, padding with \0 in %s on line %d
+bool(false)
diff --git a/ext/openssl/tests/bug66501.phpt b/ext/openssl/tests/bug66501.phpt
index a9d1359cd8..99ac4f55de 100644
--- a/ext/openssl/tests/bug66501.phpt
+++ b/ext/openssl/tests/bug66501.phpt
@@ -16,8 +16,7 @@ AwEHoUQDQgAEPq4hbIWHvB51rdWr8ejrjWo4qVNWVugYFtPg/xLQw0mHkIPZ4DvK
sqOTOnMoezkbSmVVMuwz9flvnqHGmQvmug==
-----END EC PRIVATE KEY-----';
$key = openssl_pkey_get_private($pkey);
-$sigalg = (OPENSSL_VERSION_NUMBER < 0x10000000) ? 'ecdsa-with-SHA1' : 'SHA1';
-$res = openssl_sign($data ='alpha', $sign, $key, $sigalg);
+$res = openssl_sign($data ='alpha', $sign, $key, 'SHA1');
var_dump($res);
--EXPECTF--
bool(true)
diff --git a/ext/openssl/tests/bug69882.phpt b/ext/openssl/tests/bug69882.phpt
deleted file mode 100644
index 6963f8db79..0000000000
--- a/ext/openssl/tests/bug69882.phpt
+++ /dev/null
@@ -1,17 +0,0 @@
---TEST--
-Bug #69882: OpenSSL error "key values mismatch" after openssl_pkcs12_read with extra certs
---SKIPIF--
-<?php
-if (!extension_loaded("openssl")) die("skip");
-?>
---FILE--
-<?php
-$p12 = file_get_contents(__DIR__.'/p12_with_extra_certs.p12');
-
-$result = openssl_pkcs12_read($p12, $cert_data, 'qwerty');
-var_dump($result);
-var_dump(openssl_error_string());
-?>
---EXPECTF--
-bool(true)
-bool(false)
diff --git a/ext/openssl/tests/bug71917.phpt b/ext/openssl/tests/bug71917.phpt
new file mode 100644
index 0000000000..d4415b3e32
--- /dev/null
+++ b/ext/openssl/tests/bug71917.phpt
@@ -0,0 +1,25 @@
+--TEST--
+Bug #71917: openssl_open() returns junk on envelope < 16 bytes
+--SKIPIF--
+<?php
+if (!extension_loaded("openssl")) die("skip openssl not loaded");
+?>
+--FILE--
+<?php
+function test($envkey) {
+ $publicKey = "file://" . dirname(__FILE__) . "/public.key";
+ $privateKey = "file://" . dirname(__FILE__) . "/private_rsa_1024.key";
+ openssl_public_encrypt($envkey, $envelope, $publicKey);
+ $sealed = openssl_encrypt('plaintext', 'rc4', $envkey, OPENSSL_RAW_DATA | OPENSSL_DONT_ZERO_PAD_KEY);
+ openssl_open($sealed, $output, $envelope, $privateKey, 'rc4');
+ var_dump($output === 'plaintext');
+}
+
+// works - key of 16 bytes
+test('1234567890123456i');
+// fails - key of 15 bytes
+test('123456789012345');
+?>
+--EXPECT--
+bool(true)
+bool(true)
diff --git a/ext/openssl/tests/bug72362.phpt b/ext/openssl/tests/bug72362.phpt
new file mode 100644
index 0000000000..40acdbed0c
--- /dev/null
+++ b/ext/openssl/tests/bug72362.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Bug #72362: OpenSSL Blowfish encryption is incorrect for short keys
+--SKIPIF--
+<?php
+if (!extension_loaded("openssl")) die("skip openssl not loaded");
+?>
+--FILE--
+<?php
+var_dump(bin2hex(openssl_encrypt("this is a test string","bf-ecb","12345678", OPENSSL_RAW_DATA | OPENSSL_DONT_ZERO_PAD_KEY)));
+var_dump(bin2hex(openssl_encrypt("this is a test string","bf-ecb","1234567812345678" , OPENSSL_RAW_DATA)));
+?>
+--EXPECT--
+string(48) "e3214d1b16e574828c8a3e222202dde81afd1ad2cb165ab3"
+string(48) "e3214d1b16e574828c8a3e222202dde81afd1ad2cb165ab3"
diff --git a/ext/openssl/tests/bug73478.phpt b/ext/openssl/tests/bug73478.phpt
new file mode 100644
index 0000000000..1dfc584164
--- /dev/null
+++ b/ext/openssl/tests/bug73478.phpt
@@ -0,0 +1,25 @@
+--TEST--
+Bug #73478: openssl_pkey_new() generates wrong pub/priv keys with Diffie Hellman
+--SKIPIF--
+<?php
+if (!extension_loaded("openssl")) die("skip openssl not loaded");
+?>
+--FILE--
+<?php
+$details = [
+ 'p' => base64_decode('3Pk6C4g5cuwOGZiaxaLOMQ4dN3F+jZVxu3Yjcxhm5h73Wi4niYsFf5iRwuJ6Y5w/KbYIFFgc07LKOYbSaDcFV31FwuflLcgcehcYduXOp0sUSL/frxiCjv0lGfFOReOCZjSvGUnltTXMgppIO4p2Ij5dSQolfwW9/xby+yLFg6s='),
+ 'g' => base64_decode('Ag=='),
+ 'priv_key' => base64_decode('jUdcV++P/m7oUodWiqKqKXZVenHRuj92Ig6Fmzs7QlqVdUc5mNBxmEWjug+ObffanPpOeab/LyXwjNMzevtBz3tW4oROau++9EIMJVVQr8fW9zdYBJcYieC5l4t8nRj5/Uu/Z0G2rWVLBleSi28mqqNEvnUs7uxYxrar69lwQYs=')
+];
+
+$opensslKeyResource = openssl_pkey_new(['dh' => $details]);
+$data = openssl_pkey_get_details($opensslKeyResource);
+
+printf("Private key:\n%s\n", base64_encode($data['dh']['priv_key']));
+printf("Public key:\n%s\n", base64_encode($data['dh']['pub_key']));
+?>
+--EXPECT--
+Private key:
+jUdcV++P/m7oUodWiqKqKXZVenHRuj92Ig6Fmzs7QlqVdUc5mNBxmEWjug+ObffanPpOeab/LyXwjNMzevtBz3tW4oROau++9EIMJVVQr8fW9zdYBJcYieC5l4t8nRj5/Uu/Z0G2rWVLBleSi28mqqNEvnUs7uxYxrar69lwQYs=
+Public key:
+0DmJUe9dr02pAtVoGyLHdC+rfBU3mDCelKGPXRDFHofx6mFfN2gcZCmp/ab4ezDXfpIBOatpVdbn2fTNUGo64DtKE2WGTsZCl90RgrGUv8XW/4WDPXeE7g5u7KWHBG/LCE5+XsilE5P5/GIyqr9gsiudTmk+H/hiYZl9Smar9k0=
diff --git a/ext/openssl/tests/bug74099.phpt b/ext/openssl/tests/bug74099.phpt
new file mode 100644
index 0000000000..c0e02ba0cc
--- /dev/null
+++ b/ext/openssl/tests/bug74099.phpt
@@ -0,0 +1,20 @@
+--TEST--
+Bug #74099 Memory leak with openssl_encrypt()
+--SKIPIF--
+<?php
+if (!extension_loaded("openssl")) die("skip");
+?>
+--FILE--
+<?php
+$aad = random_bytes(32);
+$iv = random_bytes(16);
+$key = random_bytes(32);
+
+$plaintext = '';
+$tag = null;
+
+$ciphertext = openssl_encrypt($plaintext, 'aes-256-gcm', $key, \OPENSSL_RAW_DATA, $iv, $tag, $aad);
+var_dump($ciphertext);
+?>
+--EXPECTF--
+string(0) ""
diff --git a/ext/openssl/tests/bug74720_0.phpt b/ext/openssl/tests/bug74720_0.phpt
new file mode 100644
index 0000000000..d84dc47d77
--- /dev/null
+++ b/ext/openssl/tests/bug74720_0.phpt
@@ -0,0 +1,93 @@
+--TEST--
+Bug #74720 pkcs7_en/decrypt does not work if \x1a is used in content, variant 0
+--SKIPIF--
+<?php
+if (!extension_loaded("openssl")) die("skip");
+?>
+--FILE--
+<?php
+
+$cert = "-----BEGIN CERTIFICATE-----
+MIIDXDCCAkSgAwIBAgIKq/f5U3FzthdKUzANBgkqhkiG9w0BAQUFADBcMRIwEAYD
+VQQDEwlzZXRhcGRmLWExCTAHBgNVBAoTADEJMAcGA1UECxMAMSMwIQYJKoZIhvcN
+AQkBFhRzdXBwb3J0QHNldGFzaWduLmNvbTELMAkGA1UEBhMCREUwHhcNMTUwOTA4
+MDkzNDExWhcNMjAwOTA4MDkzNDExWjBcMRIwEAYDVQQDEwlzZXRhcGRmLWExCTAH
+BgNVBAoTADEJMAcGA1UECxMAMSMwIQYJKoZIhvcNAQkBFhRzdXBwb3J0QHNldGFz
+aWduLmNvbTELMAkGA1UEBhMCREUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQCkmTLvUIYfqAKC1CHVgABlemrFIVRm4JGeB0jIGofyrm3yVwR4YcK0eUmt
+/0nbFfsFsU0/C9dXKZYD42t5YpLFsj666Z1EoU1CfSIW2bf0HaWVJ+oNT5twS3dv
+RTzAcnPM44GxO4y6GUW6un0/bT/MZbFKbb3NI1L0mwY7EoqUXR68XxuHqWETaslm
+Sbp1XvbLsJjgV9X3ihi7JC7A9kEzrKh+RBsXEXwlSv5JO2TUdwq9P4EbjqvgiaV1
+vFNAArioX5pIUIm9ahAm8d7jjW6DFfV798rTzaQ3GJs0yC3UD6xhmbTbdC/D9Pot
+8RGu89Fx6E+O5j4LwGH4kiYjgvYZAgMBAAGjIDAeMA8GCSqGSIb3LwEBCgQCBQAw
+CwYDVR0PBAQDAgOYMA0GCSqGSIb3DQEBBQUAA4IBAQBCA8iXz7zxAmCWNZ0faiLm
+aKw8u2PUq+EPKrGKc9Q70Ksw/e/EHvWrghFzmu5MOZRn/QIWq++sbbc8eOiaRDE8
+lWCW596FLW4habXKw0sjDNcyEBArPgDp17O6NgHqI2U1KL1P2Y40e0YH2BWxNS+f
+pmWZekjiC6jfId2JGsyPZUTX4USwthG4dFX0/BWYg+K7kXvQzMobes2NxW3Iqn8h
+FUNgraCzlQMSfE2gwAMSUXTJubUGorj4LFYSiLIIJvf6KlmuR5uOIi5lSjSeZf5E
+FLEHVhrz3o2icUeyb1K1BTAQRZ/H3GZ0QpgqSK5vmuV9C+rzezQMoy4/8UUnNPMt
+-----END CERTIFICATE-----
+";
+
+$pkey = "-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCkmTLvUIYfqAKC
+1CHVgABlemrFIVRm4JGeB0jIGofyrm3yVwR4YcK0eUmt/0nbFfsFsU0/C9dXKZYD
+42t5YpLFsj666Z1EoU1CfSIW2bf0HaWVJ+oNT5twS3dvRTzAcnPM44GxO4y6GUW6
+un0/bT/MZbFKbb3NI1L0mwY7EoqUXR68XxuHqWETaslmSbp1XvbLsJjgV9X3ihi7
+JC7A9kEzrKh+RBsXEXwlSv5JO2TUdwq9P4EbjqvgiaV1vFNAArioX5pIUIm9ahAm
+8d7jjW6DFfV798rTzaQ3GJs0yC3UD6xhmbTbdC/D9Pot8RGu89Fx6E+O5j4LwGH4
+kiYjgvYZAgMBAAECggEABO4JOGF4KOvQanB11HYNXPy4BLA5Pc0RU3M6pvKjen1m
+sdzRF5Vu/laJIkbchI0xm+Op8X7Wa+gFFBf8RLIS/QyYBCNh2Fe/74M9sYNDFxLJ
+vjBIOm6VVF1QRhMD7SwoY303adJjpkHCRMPX4z3PjLzJfPYROpsJnaWkf8GwCJ4+
+kufOhYcE8ekuLX7EzXFU74Uo0OnMEhMJgiAjlHhC21YLkgLoGq45sdktVgvD6sds
+7BcmH6oUCDCJ8wxUVM1+Ks1D3vKHfeAhCFHvj9M/lf5OWylhmQh4CQf+NMDNsAc6
+mXNIzUvZEgs/PJpd3SymHchf60m6faJtGv43GsdsgQKBgQDUx8t649O/vGRI26t4
+4XrzVC2w5271UlhFaRiH03BrV+pECVoH4bw+ZIUuGke2xe+LrNkhMNJSGcbB0WDa
+Jes+4Gf++AuSXRvMo+xHenfgUWQqYHJPkh5q6gknZ2YDfikFNLdqApXvp0m+FaVP
++F/2HMjQfTITQgkAiEn45s4agQKBgQDGCAZkIUcdyXQTplLE0Zx1/uiTBrQGyzsb
+cDgAQIHt19wIRjoGOFatAj6TQ5gUj9Remn12v6d5Wi02i7hlu8V6O1VXLGqqeQ27
+0MHBXzrFBVljOz96D7VP5Xx0DB1cGMbtg5ivjd91OUVrwC7fbXE9sfOI1FqlTb7p
+6xi9Wl+fmQKBgGdzR/vUfqPOvVcq7gBUaAmb2KcUrj65rU753MJUy2h1EuHHRi1k
+07fl+VZD0rALJf7bp6laajcebyLWYGdPXkNwqT8ua7naaOSiogLSiSvXhoKP56PG
+H+HNLWwp+lAia2Erky0IWstsow62yWvLDyTCM+QhqlHwnh3TJVvNI1GBAoGAUl+y
+MOJ6z5Ql2aqc0UwT1i1Tlxz5s73D93Tlho1Ovp3E5Bg6OK4kt9CwMNe0IhF2GGgQ
++l1cj6kIF6Fk8cR6r46QwDM0p3a1VMPQZNx0+NFxzkot7FsuY26lJyyG5fFUhiXw
+VE4ifoN1Mg3+MWg3657jG66hihNd77WgU9uM3TkCgYEAypuvVrfFrrtXnbEUcBHq
+mguKumn9miD9DPb6gi8ZaKOddGzw+qFPukiqi7rM3oRMg02evfK/VQC87Gmev663
+RV9sQOlB9gNlMOOw/0R3ABEWDoSRCcrLhb6Z5Y72WVnZvpTPO0cDw2i1hyaEM6d+
+2WR7c6FhRCLxG0DObEOfiO0=
+-----END PRIVATE KEY-----
+";
+
+$originalEnvelopeData = "any string with \x1a is cut at this point.";
+
+$tmpFileIn = tempnam(sys_get_temp_dir(), 'test');
+$tmpFileOut = tempnam(sys_get_temp_dir(), 'test');
+file_put_contents($tmpFileIn, $originalEnvelopeData);
+
+
+var_dump(filesize($tmpFileIn) === strlen($originalEnvelopeData));
+
+openssl_pkcs7_encrypt($tmpFileIn, $tmpFileOut, [$cert], array(), PKCS7_BINARY, OPENSSL_CIPHER_AES_128_CBC);
+
+$tmpFileOut2 = tempnam(sys_get_temp_dir(), 'test');
+openssl_pkcs7_decrypt($tmpFileOut, $tmpFileOut2, $cert, $pkey);
+
+$envelopeData = file_get_contents($tmpFileOut2);
+var_dump($originalEnvelopeData === $envelopeData); // need to be true
+var_dump(strlen($originalEnvelopeData), strlen($envelopeData), filesize($tmpFileOut2));
+
+unlink($tmpFileIn);
+unlink($tmpFileOut);
+unlink($tmpFileOut2);
+
+?>
+==DONE==
+--EXPECTF--
+bool(true)
+bool(true)
+int(39)
+int(39)
+int(39)
+==DONE==
+
diff --git a/ext/openssl/tests/bug74720_1.phpt b/ext/openssl/tests/bug74720_1.phpt
new file mode 100644
index 0000000000..9a8099a33c
--- /dev/null
+++ b/ext/openssl/tests/bug74720_1.phpt
@@ -0,0 +1,88 @@
+--TEST--
+Bug #74720 pkcs7_en/decrypt does not work if \x1a is used in content, variant 1
+--SKIPIF--
+<?php
+if (!extension_loaded("openssl")) die("skip");
+?>
+--FILE--
+<?php
+
+$cert = "-----BEGIN CERTIFICATE-----
+MIIDXDCCAkSgAwIBAgIKq/f5U3FzthdKUzANBgkqhkiG9w0BAQUFADBcMRIwEAYD
+VQQDEwlzZXRhcGRmLWExCTAHBgNVBAoTADEJMAcGA1UECxMAMSMwIQYJKoZIhvcN
+AQkBFhRzdXBwb3J0QHNldGFzaWduLmNvbTELMAkGA1UEBhMCREUwHhcNMTUwOTA4
+MDkzNDExWhcNMjAwOTA4MDkzNDExWjBcMRIwEAYDVQQDEwlzZXRhcGRmLWExCTAH
+BgNVBAoTADEJMAcGA1UECxMAMSMwIQYJKoZIhvcNAQkBFhRzdXBwb3J0QHNldGFz
+aWduLmNvbTELMAkGA1UEBhMCREUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQCkmTLvUIYfqAKC1CHVgABlemrFIVRm4JGeB0jIGofyrm3yVwR4YcK0eUmt
+/0nbFfsFsU0/C9dXKZYD42t5YpLFsj666Z1EoU1CfSIW2bf0HaWVJ+oNT5twS3dv
+RTzAcnPM44GxO4y6GUW6un0/bT/MZbFKbb3NI1L0mwY7EoqUXR68XxuHqWETaslm
+Sbp1XvbLsJjgV9X3ihi7JC7A9kEzrKh+RBsXEXwlSv5JO2TUdwq9P4EbjqvgiaV1
+vFNAArioX5pIUIm9ahAm8d7jjW6DFfV798rTzaQ3GJs0yC3UD6xhmbTbdC/D9Pot
+8RGu89Fx6E+O5j4LwGH4kiYjgvYZAgMBAAGjIDAeMA8GCSqGSIb3LwEBCgQCBQAw
+CwYDVR0PBAQDAgOYMA0GCSqGSIb3DQEBBQUAA4IBAQBCA8iXz7zxAmCWNZ0faiLm
+aKw8u2PUq+EPKrGKc9Q70Ksw/e/EHvWrghFzmu5MOZRn/QIWq++sbbc8eOiaRDE8
+lWCW596FLW4habXKw0sjDNcyEBArPgDp17O6NgHqI2U1KL1P2Y40e0YH2BWxNS+f
+pmWZekjiC6jfId2JGsyPZUTX4USwthG4dFX0/BWYg+K7kXvQzMobes2NxW3Iqn8h
+FUNgraCzlQMSfE2gwAMSUXTJubUGorj4LFYSiLIIJvf6KlmuR5uOIi5lSjSeZf5E
+FLEHVhrz3o2icUeyb1K1BTAQRZ/H3GZ0QpgqSK5vmuV9C+rzezQMoy4/8UUnNPMt
+-----END CERTIFICATE-----
+";
+
+$pkey = "-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCkmTLvUIYfqAKC
+1CHVgABlemrFIVRm4JGeB0jIGofyrm3yVwR4YcK0eUmt/0nbFfsFsU0/C9dXKZYD
+42t5YpLFsj666Z1EoU1CfSIW2bf0HaWVJ+oNT5twS3dvRTzAcnPM44GxO4y6GUW6
+un0/bT/MZbFKbb3NI1L0mwY7EoqUXR68XxuHqWETaslmSbp1XvbLsJjgV9X3ihi7
+JC7A9kEzrKh+RBsXEXwlSv5JO2TUdwq9P4EbjqvgiaV1vFNAArioX5pIUIm9ahAm
+8d7jjW6DFfV798rTzaQ3GJs0yC3UD6xhmbTbdC/D9Pot8RGu89Fx6E+O5j4LwGH4
+kiYjgvYZAgMBAAECggEABO4JOGF4KOvQanB11HYNXPy4BLA5Pc0RU3M6pvKjen1m
+sdzRF5Vu/laJIkbchI0xm+Op8X7Wa+gFFBf8RLIS/QyYBCNh2Fe/74M9sYNDFxLJ
+vjBIOm6VVF1QRhMD7SwoY303adJjpkHCRMPX4z3PjLzJfPYROpsJnaWkf8GwCJ4+
+kufOhYcE8ekuLX7EzXFU74Uo0OnMEhMJgiAjlHhC21YLkgLoGq45sdktVgvD6sds
+7BcmH6oUCDCJ8wxUVM1+Ks1D3vKHfeAhCFHvj9M/lf5OWylhmQh4CQf+NMDNsAc6
+mXNIzUvZEgs/PJpd3SymHchf60m6faJtGv43GsdsgQKBgQDUx8t649O/vGRI26t4
+4XrzVC2w5271UlhFaRiH03BrV+pECVoH4bw+ZIUuGke2xe+LrNkhMNJSGcbB0WDa
+Jes+4Gf++AuSXRvMo+xHenfgUWQqYHJPkh5q6gknZ2YDfikFNLdqApXvp0m+FaVP
++F/2HMjQfTITQgkAiEn45s4agQKBgQDGCAZkIUcdyXQTplLE0Zx1/uiTBrQGyzsb
+cDgAQIHt19wIRjoGOFatAj6TQ5gUj9Remn12v6d5Wi02i7hlu8V6O1VXLGqqeQ27
+0MHBXzrFBVljOz96D7VP5Xx0DB1cGMbtg5ivjd91OUVrwC7fbXE9sfOI1FqlTb7p
+6xi9Wl+fmQKBgGdzR/vUfqPOvVcq7gBUaAmb2KcUrj65rU753MJUy2h1EuHHRi1k
+07fl+VZD0rALJf7bp6laajcebyLWYGdPXkNwqT8ua7naaOSiogLSiSvXhoKP56PG
+H+HNLWwp+lAia2Erky0IWstsow62yWvLDyTCM+QhqlHwnh3TJVvNI1GBAoGAUl+y
+MOJ6z5Ql2aqc0UwT1i1Tlxz5s73D93Tlho1Ovp3E5Bg6OK4kt9CwMNe0IhF2GGgQ
++l1cj6kIF6Fk8cR6r46QwDM0p3a1VMPQZNx0+NFxzkot7FsuY26lJyyG5fFUhiXw
+VE4ifoN1Mg3+MWg3657jG66hihNd77WgU9uM3TkCgYEAypuvVrfFrrtXnbEUcBHq
+mguKumn9miD9DPb6gi8ZaKOddGzw+qFPukiqi7rM3oRMg02evfK/VQC87Gmev663
+RV9sQOlB9gNlMOOw/0R3ABEWDoSRCcrLhb6Z5Y72WVnZvpTPO0cDw2i1hyaEM6d+
+2WR7c6FhRCLxG0DObEOfiO0=
+-----END PRIVATE KEY-----
+";
+
+$tmpPath = tempnam(sys_get_temp_dir(), 'test');
+
+$content = "A simple \x1a test.";
+file_put_contents($tmpPath, $content);
+
+$outPath = tempnam(sys_get_temp_dir(), 'test');
+
+openssl_pkcs7_sign(
+ $tmpPath,
+ $outPath,
+ $cert,
+ $pkey,
+ array(),
+ PKCS7_BINARY | PKCS7_DETACHED
+);
+
+var_dump(strpos(file_get_contents($outPath), $content) !== false);
+
+unlink($tmpPath);
+unlink($outPath);
+
+?>
+==DONE==
+--EXPECTF--
+bool(true)
+==DONE==
+
diff --git a/ext/openssl/tests/bug74798.phpt b/ext/openssl/tests/bug74798.phpt
new file mode 100644
index 0000000000..5b41af3efc
--- /dev/null
+++ b/ext/openssl/tests/bug74798.phpt
@@ -0,0 +1,96 @@
+--TEST--
+Bug #74798 pkcs7_en/decrypt does not work if \x0a is used in content
+--SKIPIF--
+<?php
+if (!extension_loaded("openssl")) die("skip");
+?>
+--FILE--
+<?php
+
+$cert = "-----BEGIN CERTIFICATE-----
+MIIDXDCCAkSgAwIBAgIKq/f5U3FzthdKUzANBgkqhkiG9w0BAQUFADBcMRIwEAYD
+VQQDEwlzZXRhcGRmLWExCTAHBgNVBAoTADEJMAcGA1UECxMAMSMwIQYJKoZIhvcN
+AQkBFhRzdXBwb3J0QHNldGFzaWduLmNvbTELMAkGA1UEBhMCREUwHhcNMTUwOTA4
+MDkzNDExWhcNMjAwOTA4MDkzNDExWjBcMRIwEAYDVQQDEwlzZXRhcGRmLWExCTAH
+BgNVBAoTADEJMAcGA1UECxMAMSMwIQYJKoZIhvcNAQkBFhRzdXBwb3J0QHNldGFz
+aWduLmNvbTELMAkGA1UEBhMCREUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQCkmTLvUIYfqAKC1CHVgABlemrFIVRm4JGeB0jIGofyrm3yVwR4YcK0eUmt
+/0nbFfsFsU0/C9dXKZYD42t5YpLFsj666Z1EoU1CfSIW2bf0HaWVJ+oNT5twS3dv
+RTzAcnPM44GxO4y6GUW6un0/bT/MZbFKbb3NI1L0mwY7EoqUXR68XxuHqWETaslm
+Sbp1XvbLsJjgV9X3ihi7JC7A9kEzrKh+RBsXEXwlSv5JO2TUdwq9P4EbjqvgiaV1
+vFNAArioX5pIUIm9ahAm8d7jjW6DFfV798rTzaQ3GJs0yC3UD6xhmbTbdC/D9Pot
+8RGu89Fx6E+O5j4LwGH4kiYjgvYZAgMBAAGjIDAeMA8GCSqGSIb3LwEBCgQCBQAw
+CwYDVR0PBAQDAgOYMA0GCSqGSIb3DQEBBQUAA4IBAQBCA8iXz7zxAmCWNZ0faiLm
+aKw8u2PUq+EPKrGKc9Q70Ksw/e/EHvWrghFzmu5MOZRn/QIWq++sbbc8eOiaRDE8
+lWCW596FLW4habXKw0sjDNcyEBArPgDp17O6NgHqI2U1KL1P2Y40e0YH2BWxNS+f
+pmWZekjiC6jfId2JGsyPZUTX4USwthG4dFX0/BWYg+K7kXvQzMobes2NxW3Iqn8h
+FUNgraCzlQMSfE2gwAMSUXTJubUGorj4LFYSiLIIJvf6KlmuR5uOIi5lSjSeZf5E
+FLEHVhrz3o2icUeyb1K1BTAQRZ/H3GZ0QpgqSK5vmuV9C+rzezQMoy4/8UUnNPMt
+-----END CERTIFICATE-----
+";
+
+$pkey = "-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCkmTLvUIYfqAKC
+1CHVgABlemrFIVRm4JGeB0jIGofyrm3yVwR4YcK0eUmt/0nbFfsFsU0/C9dXKZYD
+42t5YpLFsj666Z1EoU1CfSIW2bf0HaWVJ+oNT5twS3dvRTzAcnPM44GxO4y6GUW6
+un0/bT/MZbFKbb3NI1L0mwY7EoqUXR68XxuHqWETaslmSbp1XvbLsJjgV9X3ihi7
+JC7A9kEzrKh+RBsXEXwlSv5JO2TUdwq9P4EbjqvgiaV1vFNAArioX5pIUIm9ahAm
+8d7jjW6DFfV798rTzaQ3GJs0yC3UD6xhmbTbdC/D9Pot8RGu89Fx6E+O5j4LwGH4
+kiYjgvYZAgMBAAECggEABO4JOGF4KOvQanB11HYNXPy4BLA5Pc0RU3M6pvKjen1m
+sdzRF5Vu/laJIkbchI0xm+Op8X7Wa+gFFBf8RLIS/QyYBCNh2Fe/74M9sYNDFxLJ
+vjBIOm6VVF1QRhMD7SwoY303adJjpkHCRMPX4z3PjLzJfPYROpsJnaWkf8GwCJ4+
+kufOhYcE8ekuLX7EzXFU74Uo0OnMEhMJgiAjlHhC21YLkgLoGq45sdktVgvD6sds
+7BcmH6oUCDCJ8wxUVM1+Ks1D3vKHfeAhCFHvj9M/lf5OWylhmQh4CQf+NMDNsAc6
+mXNIzUvZEgs/PJpd3SymHchf60m6faJtGv43GsdsgQKBgQDUx8t649O/vGRI26t4
+4XrzVC2w5271UlhFaRiH03BrV+pECVoH4bw+ZIUuGke2xe+LrNkhMNJSGcbB0WDa
+Jes+4Gf++AuSXRvMo+xHenfgUWQqYHJPkh5q6gknZ2YDfikFNLdqApXvp0m+FaVP
++F/2HMjQfTITQgkAiEn45s4agQKBgQDGCAZkIUcdyXQTplLE0Zx1/uiTBrQGyzsb
+cDgAQIHt19wIRjoGOFatAj6TQ5gUj9Remn12v6d5Wi02i7hlu8V6O1VXLGqqeQ27
+0MHBXzrFBVljOz96D7VP5Xx0DB1cGMbtg5ivjd91OUVrwC7fbXE9sfOI1FqlTb7p
+6xi9Wl+fmQKBgGdzR/vUfqPOvVcq7gBUaAmb2KcUrj65rU753MJUy2h1EuHHRi1k
+07fl+VZD0rALJf7bp6laajcebyLWYGdPXkNwqT8ua7naaOSiogLSiSvXhoKP56PG
+H+HNLWwp+lAia2Erky0IWstsow62yWvLDyTCM+QhqlHwnh3TJVvNI1GBAoGAUl+y
+MOJ6z5Ql2aqc0UwT1i1Tlxz5s73D93Tlho1Ovp3E5Bg6OK4kt9CwMNe0IhF2GGgQ
++l1cj6kIF6Fk8cR6r46QwDM0p3a1VMPQZNx0+NFxzkot7FsuY26lJyyG5fFUhiXw
+VE4ifoN1Mg3+MWg3657jG66hihNd77WgU9uM3TkCgYEAypuvVrfFrrtXnbEUcBHq
+mguKumn9miD9DPb6gi8ZaKOddGzw+qFPukiqi7rM3oRMg02evfK/VQC87Gmev663
+RV9sQOlB9gNlMOOw/0R3ABEWDoSRCcrLhb6Z5Y72WVnZvpTPO0cDw2i1hyaEM6d+
+2WR7c6FhRCLxG0DObEOfiO0=
+-----END PRIVATE KEY-----
+";
+
+$originalEnvelopeData = "String with \x0a will end in one byte more.";
+
+$tmpFileIn = tempnam(sys_get_temp_dir(), 'test');
+$tmpFileOut = tempnam(sys_get_temp_dir(), 'test');
+file_put_contents($tmpFileIn, $originalEnvelopeData);
+
+var_dump(filesize($tmpFileIn) === strlen($originalEnvelopeData));
+
+openssl_pkcs7_encrypt($tmpFileIn, $tmpFileOut, [$cert], array(), PKCS7_BINARY, OPENSSL_CIPHER_AES_128_CBC);
+
+$tmpFileOut2 = tempnam(sys_get_temp_dir(), 'test');
+openssl_pkcs7_decrypt($tmpFileOut, $tmpFileOut2, $cert, $pkey);
+
+$envelopeData = file_get_contents($tmpFileOut2);
+var_dump($originalEnvelopeData === $envelopeData); // need to be true
+var_dump(strlen($originalEnvelopeData), strlen($envelopeData), filesize($tmpFileOut2));
+
+var_dump(unpack('H*', $originalEnvelopeData)[1], unpack('H*', $envelopeData)[1]);
+
+/* Cleanup */
+unlink($tmpFileIn);
+unlink($tmpFileOut);
+unlink($tmpFileOut2);
+
+?>
+===DONE===
+--EXPECTF--
+bool(true)
+bool(true)
+int(40)
+int(40)
+int(40)
+string(80) "537472696e672077697468200a2077696c6c20656e6420696e206f6e652062797465206d6f72652e"
+string(80) "537472696e672077697468200a2077696c6c20656e6420696e206f6e652062797465206d6f72652e"
+===DONE===
diff --git a/ext/openssl/tests/cipher_tests.inc b/ext/openssl/tests/cipher_tests.inc
new file mode 100644
index 0000000000..b9e84af8f8
--- /dev/null
+++ b/ext/openssl/tests/cipher_tests.inc
@@ -0,0 +1,111 @@
+<?php
+$php_openssl_cipher_tests = array(
+ 'aes-256-ccm' => array(
+ array(
+ 'key' => '1bde3251d41a8b5ea013c195ae128b21' .
+ '8b3e0306376357077ef1c1c78548b92e',
+ 'iv' => '5b8e40746f6b98e00f1d13ff41',
+ 'aad' => 'c17a32514eb6103f3249e076d4c871dc' .
+ '97e04b286699e54491dc18f6d734d4c0',
+ 'tag' => '2024931d73bca480c24a24ece6b6c2bf',
+ 'pt' => '53bd72a97089e312422bf72e242377b3' .
+ 'c6ee3e2075389b999c4ef7f28bd2b80a',
+ 'ct' => '9a5fcccdb4cf04e7293d2775cc76a488' .
+ 'f042382d949b43b7d6bb2b9864786726',
+ ),
+ ),
+ 'aes-128-gcm' => array(
+ array(
+ 'key' => '00000000000000000000000000000000',
+ 'iv' => '000000000000000000000000',
+ 'tag' => '58e2fccefa7e3061367f1d57a4e7455a',
+ 'pt' => '',
+ 'ct' => '',
+ ),
+ array(
+ 'key' => '00000000000000000000000000000000',
+ 'iv' => '000000000000000000000000',
+ 'tag' => 'ab6e47d42cec13bdf53a67b21257bddf',
+ 'pt' => '00000000000000000000000000000000',
+ 'ct' => '0388dace60b6a392f328c2b971b2fe78',
+ ),
+ array(
+ 'key' => 'feffe9928665731c6d6a8f9467308308',
+ 'iv' => 'cafebabefacedbaddecaf888',
+ 'tag' => '4d5c2af327cd64a62cf35abd2ba6fab4',
+ 'pt' => 'd9313225f88406e5a55909c5aff5269a' .
+ '86a7a9531534f7da2e4c303d8a318a72' .
+ '1c3c0c95956809532fcf0e2449a6b525' .
+ 'b16aedf5aa0de657ba637b391aafd255',
+ 'ct' => '42831ec2217774244b7221b784d0d49c' .
+ 'e3aa212f2c02a4e035c17e2329aca12e' .
+ '21d514b25466931c7d8f6a5aac84aa05' .
+ '1ba30b396a0aac973d58e091473f5985',
+ ),
+ array(
+ 'key' => 'feffe9928665731c6d6a8f9467308308',
+ 'iv' => 'cafebabefacedbaddecaf888',
+ 'aad' => 'feedfacedeadbeeffeedfacedeadbeefabaddad2',
+ 'tag' => '5bc94fbc3221a5db94fae95ae7121a47',
+ 'pt' => 'd9313225f88406e5a55909c5aff5269a' .
+ '86a7a9531534f7da2e4c303d8a318a72' .
+ '1c3c0c95956809532fcf0e2449a6b525' .
+ 'b16aedf5aa0de657ba637b39',
+ 'ct' => '42831ec2217774244b7221b784d0d49c' .
+ 'e3aa212f2c02a4e035c17e2329aca12e' .
+ '21d514b25466931c7d8f6a5aac84aa05' .
+ '1ba30b396a0aac973d58e091',
+ ),
+ array(
+ 'key' => 'feffe9928665731c6d6a8f9467308308',
+ 'iv' => 'cafebabefacedbad',
+ 'aad' => 'feedfacedeadbeeffeedfacedeadbeefabaddad2',
+ 'tag' => '3612d2e79e3b0785561be14aaca2fccb',
+ 'pt' => 'd9313225f88406e5a55909c5aff5269a' .
+ '86a7a9531534f7da2e4c303d8a318a72' .
+ '1c3c0c95956809532fcf0e2449a6b525' .
+ 'b16aedf5aa0de657ba637b39',
+ 'ct' => '61353b4c2806934a777ff51fa22a4755' .
+ '699b2a714fcdc6f83766e5f97b6c7423' .
+ '73806900e49f24b22b097544d4896b42' .
+ '4989b5e1ebac0f07c23f4598'
+ ),
+ array(
+ 'key' => 'feffe9928665731c6d6a8f9467308308',
+ 'iv' => '9313225df88406e555909c5aff5269aa' .
+ '6a7a9538534f7da1e4c303d2a318a728' .
+ 'c3c0c95156809539fcf0e2429a6b5254' .
+ '16aedbf5a0de6a57a637b39b',
+ 'aad' => 'feedfacedeadbeeffeedfacedeadbeefabaddad2',
+ 'tag' => '619cc5aefffe0bfa462af43c1699d050',
+ 'pt' => 'd9313225f88406e5a55909c5aff5269a' .
+ '86a7a9531534f7da2e4c303d8a318a72' .
+ '1c3c0c95956809532fcf0e2449a6b525' .
+ 'b16aedf5aa0de657ba637b39',
+ 'ct' => '8ce24998625615b603a033aca13fb894' .
+ 'be9112a5c3a211a8ba262a3cca7e2ca7' .
+ '01e4a9a4fba43c90ccdcb281d48c7c6f' .
+ 'd62875d2aca417034c34aee5',
+ ),
+ )
+);
+
+function openssl_get_cipher_tests($method)
+{
+ global $php_openssl_cipher_tests;
+
+ $tests = array();
+
+ foreach ($php_openssl_cipher_tests[$method] as $instance) {
+ $test = array();
+ foreach ($instance as $field_name => $field_value) {
+ $test[$field_name] = pack("H*", $field_value);
+ }
+ if (!isset($test['aad'])) {
+ $test['aad'] = "";
+ }
+ $tests[] = $test;
+ }
+
+ return $tests;
+}
diff --git a/ext/openssl/tests/ecc.phpt b/ext/openssl/tests/ecc.phpt
new file mode 100644
index 0000000000..e4c1d20805
--- /dev/null
+++ b/ext/openssl/tests/ecc.phpt
@@ -0,0 +1,110 @@
+--TEST--
+openssl_*() with OPENSSL_KEYTYPE_EC
+--SKIPIF--
+<?php if (!extension_loaded("openssl") && !defined("OPENSSL_KEYTYPE_EC")) print "skip"; ?>
+--FILE--
+<?php
+$args = array(
+ "curve_name" => "secp384r1",
+ "private_key_type" => OPENSSL_KEYTYPE_EC,
+);
+echo "Testing openssl_pkey_new\n";
+$key1 = openssl_pkey_new($args);
+var_dump($key1);
+
+$argsFailed = array(
+ "curve_name" => "invalid_cuve_name",
+ "private_key_type" => OPENSSL_KEYTYPE_EC,
+);
+
+$keyFailed = openssl_pkey_new($argsFailed);
+var_dump($keyFailed);
+
+$d1 = openssl_pkey_get_details($key1);
+var_dump($d1["bits"]);
+var_dump(strlen($d1["key"]));
+var_dump($d1["ec"]["curve_name"]);
+var_dump($d1["type"] == OPENSSL_KEYTYPE_EC);
+
+$key2 = openssl_pkey_new($d1);
+var_dump($key2);
+
+$d2 = openssl_pkey_get_details($key2);
+// Compare array
+var_dump($d1 === $d2);
+
+$dn = array(
+ "countryName" => "BR",
+ "stateOrProvinceName" => "Rio Grande do Sul",
+ "localityName" => "Porto Alegre",
+ "commonName" => "Henrique do N. Angelo",
+ "emailAddress" => "hnangelo@php.net"
+);
+
+// openssl_csr_new creates a new public key pair if the key argument is null
+echo "Testing openssl_csr_new with key generation\n";
+$keyGenerate = null;
+var_dump($keyGenerate);
+$csr = openssl_csr_new($dn, $keyGenerate, $args);
+
+var_dump($keyGenerate);
+
+$args["digest_alg"] = "sha1";
+echo "Testing openssl_csr_new with existing ecc key\n";
+$csr = openssl_csr_new($dn, $key1, $args);
+var_dump($csr);
+
+$pubkey1 = openssl_pkey_get_details(openssl_csr_get_public_key($csr));
+var_dump(isset($pubkey1["ec"]["priv_key"]));
+unset($d1["ec"]["priv_key"]);
+var_dump(array_diff($d1["ec"], $pubkey1["ec"]));
+
+$x509 = openssl_csr_sign($csr, null, $key1, 365, $args);
+var_dump($x509);
+
+echo "Testing openssl_x509_check_private_key\n";
+var_dump(openssl_x509_check_private_key($x509, $key1));
+
+$key3 = openssl_pkey_new($args);
+var_dump(openssl_x509_check_private_key($x509, $key3));
+
+echo "Testing openssl_get_curve_names\n";
+$curve_names = openssl_get_curve_names();
+
+var_dump(is_array($curve_names));
+
+foreach ($curve_names as $curve_name) {
+ if ("secp384r1" === $curve_name) {
+ echo "Found secp384r1 in curve names\n";
+ }
+}
+?>
+--EXPECTF--
+Testing openssl_pkey_new
+resource(%d) of type (OpenSSL key)
+
+Warning: openssl_pkey_new(): Unknown elliptic curve (short) name invalid_cuve_name in %s on line %d
+bool(false)
+int(384)
+int(215)
+string(9) "secp384r1"
+bool(true)
+resource(%d) of type (OpenSSL key)
+bool(true)
+Testing openssl_csr_new with key generation
+NULL
+resource(%d) of type (OpenSSL key)
+Testing openssl_csr_new with existing ecc key
+resource(%d) of type (OpenSSL X.509 CSR)
+bool(false)
+array(1) {
+ ["d"]=>
+ string(%d) "%a"
+}
+resource(%d) of type (OpenSSL X.509)
+Testing openssl_x509_check_private_key
+bool(true)
+bool(false)
+Testing openssl_get_curve_names
+bool(true)
+Found secp384r1 in curve names
diff --git a/ext/openssl/tests/openssl_decrypt_basic.phpt b/ext/openssl/tests/openssl_decrypt_basic.phpt
index 1c29767cc5..37d17150fb 100644
--- a/ext/openssl/tests/openssl_decrypt_basic.phpt
+++ b/ext/openssl/tests/openssl_decrypt_basic.phpt
@@ -24,8 +24,13 @@ $padded_data = $data . str_repeat(' ', 16 - (strlen($data) % 16));
$encrypted = openssl_encrypt($padded_data, $method, $password, OPENSSL_RAW_DATA|OPENSSL_ZERO_PADDING, $iv);
$output = openssl_decrypt($encrypted, $method, $password, OPENSSL_RAW_DATA|OPENSSL_ZERO_PADDING, $iv);
var_dump(rtrim($output));
+// if we want to prefer variable length cipher setting
+$encrypted = openssl_encrypt($data, "bf-ecb", $password, OPENSSL_DONT_ZERO_PAD_KEY);
+$output = openssl_decrypt($encrypted, "bf-ecb", $password, OPENSSL_DONT_ZERO_PAD_KEY);
+var_dump($output);
?>
--EXPECT--
string(45) "openssl_encrypt() and openssl_decrypt() tests"
string(45) "openssl_encrypt() and openssl_decrypt() tests"
string(45) "openssl_encrypt() and openssl_decrypt() tests"
+string(45) "openssl_encrypt() and openssl_decrypt() tests"
diff --git a/ext/openssl/tests/openssl_decrypt_ccm.phpt b/ext/openssl/tests/openssl_decrypt_ccm.phpt
new file mode 100644
index 0000000000..beb3074938
--- /dev/null
+++ b/ext/openssl/tests/openssl_decrypt_ccm.phpt
@@ -0,0 +1,41 @@
+--TEST--
+openssl_decrypt() with CCM cipher algorithm tests
+--SKIPIF--
+<?php
+if (!extension_loaded("openssl"))
+ die("skip");
+if (!in_array('aes-256-ccm', openssl_get_cipher_methods()))
+ die("skip: aes-256-ccm not available");
+?>
+--FILE--
+<?php
+require_once __DIR__ . "/cipher_tests.inc";
+$method = 'aes-256-ccm';
+$tests = openssl_get_cipher_tests($method);
+
+foreach ($tests as $idx => $test) {
+ echo "TEST $idx\n";
+ $pt = openssl_decrypt($test['ct'], $method, $test['key'], OPENSSL_RAW_DATA,
+ $test['iv'], $test['tag'], $test['aad']);
+ var_dump($test['pt'] === $pt);
+}
+
+// no IV
+var_dump(openssl_decrypt($test['ct'], $method, $test['key'], OPENSSL_RAW_DATA,
+ NULL, $test['tag'], $test['aad']));
+// failed because no AAD
+var_dump(openssl_decrypt($test['ct'], $method, $test['key'], OPENSSL_RAW_DATA,
+ $test['iv'], $test['tag']));
+// failed because wrong tag
+var_dump(openssl_decrypt($test['ct'], $method, $test['key'], OPENSSL_RAW_DATA,
+ $test['iv'], str_repeat('x', 10), $test['aad']));
+
+?>
+--EXPECTF--
+TEST 0
+bool(true)
+
+Warning: openssl_decrypt(): Setting of IV length for AEAD mode failed in %s on line %d
+bool(false)
+bool(false)
+bool(false)
diff --git a/ext/openssl/tests/openssl_decrypt_error.phpt b/ext/openssl/tests/openssl_decrypt_error.phpt
index 40debbd04f..b34397a651 100644
--- a/ext/openssl/tests/openssl_decrypt_error.phpt
+++ b/ext/openssl/tests/openssl_decrypt_error.phpt
@@ -7,7 +7,7 @@ openssl_decrypt() error tests
$data = "openssl_decrypt() tests";
$method = "AES-128-CBC";
$password = "openssl";
-$wrong = "wrong";
+$wrong = base64_encode("wrong");
$iv = str_repeat("\0", openssl_cipher_iv_length($method));
$encrypted = openssl_encrypt($data, $method, $password);
@@ -22,6 +22,9 @@ var_dump(openssl_decrypt($wrong, $wrong, $wrong));
var_dump(openssl_decrypt(array(), $method, $password));
var_dump(openssl_decrypt($encrypted, array(), $password));
var_dump(openssl_decrypt($encrypted, $method, array()));
+
+// invalid using of an authentication tag
+var_dump(openssl_encrypt($data, $method, $password, 0, $iv, $wrong));
?>
--EXPECTF--
@@ -51,3 +54,6 @@ NULL
Warning: openssl_decrypt() expects parameter 3 to be string, array given in %s on line %d
NULL
+
+Warning: openssl_encrypt(): The authenticated tag cannot be provided for cipher that doesn not support AEAD in %s on line %d
+string(44) "yof6cPPH4mLee6TOc0YQSrh4dvywMqxGUyjp0lV6+aM="
diff --git a/ext/openssl/tests/openssl_decrypt_gcm.phpt b/ext/openssl/tests/openssl_decrypt_gcm.phpt
new file mode 100644
index 0000000000..11802e9d8e
--- /dev/null
+++ b/ext/openssl/tests/openssl_decrypt_gcm.phpt
@@ -0,0 +1,51 @@
+--TEST--
+openssl_decrypt() with GCM cipher algorithm tests
+--SKIPIF--
+<?php
+if (!extension_loaded("openssl"))
+ die("skip");
+if (!in_array('aes-128-gcm', openssl_get_cipher_methods()))
+ die("skip: aes-128-gcm not available");
+?>
+--FILE--
+<?php
+require_once __DIR__ . "/cipher_tests.inc";
+$method = 'aes-128-gcm';
+$tests = openssl_get_cipher_tests($method);
+
+foreach ($tests as $idx => $test) {
+ echo "TEST $idx\n";
+ $pt = openssl_decrypt($test['ct'], $method, $test['key'], OPENSSL_RAW_DATA,
+ $test['iv'], $test['tag'], $test['aad']);
+ var_dump($test['pt'] === $pt);
+}
+
+// no IV
+var_dump(openssl_decrypt($test['ct'], $method, $test['key'], OPENSSL_RAW_DATA,
+ NULL, $test['tag'], $test['aad']));
+// failed because no AAD
+var_dump(openssl_decrypt($test['ct'], $method, $test['key'], OPENSSL_RAW_DATA,
+ $test['iv'], $test['tag']));
+// failed because wrong tag
+var_dump(openssl_decrypt($test['ct'], $method, $test['key'], OPENSSL_RAW_DATA,
+ $test['iv'], str_repeat('x', 16), $test['aad']));
+
+?>
+--EXPECTF--
+TEST 0
+bool(true)
+TEST 1
+bool(true)
+TEST 2
+bool(true)
+TEST 3
+bool(true)
+TEST 4
+bool(true)
+TEST 5
+bool(true)
+
+Warning: openssl_decrypt(): Setting of IV length for AEAD mode failed in %s on line %d
+bool(false)
+bool(false)
+bool(false)
diff --git a/ext/openssl/tests/openssl_encrypt_ccm.phpt b/ext/openssl/tests/openssl_encrypt_ccm.phpt
new file mode 100644
index 0000000000..945e81dfd7
--- /dev/null
+++ b/ext/openssl/tests/openssl_encrypt_ccm.phpt
@@ -0,0 +1,39 @@
+--TEST--
+openssl_encrypt() with CCM cipher algorithm tests
+--SKIPIF--
+<?php
+if (!extension_loaded("openssl"))
+ die("skip");
+if (!in_array('aes-256-ccm', openssl_get_cipher_methods()))
+ die("skip: aes-256-ccm not available");
+?>
+--FILE--
+<?php
+require_once __DIR__ . "/cipher_tests.inc";
+$method = 'aes-256-ccm';
+$tests = openssl_get_cipher_tests($method);
+
+foreach ($tests as $idx => $test) {
+ echo "TEST $idx\n";
+ $ct = openssl_encrypt($test['pt'], $method, $test['key'], OPENSSL_RAW_DATA,
+ $test['iv'], $tag, $test['aad'], strlen($test['tag']));
+ var_dump($test['ct'] === $ct);
+ var_dump($test['tag'] === $tag);
+}
+
+// Empty IV error
+var_dump(openssl_encrypt('data', $method, 'password', 0, NULL, $tag, ''));
+
+// Test setting different IV length and unlimeted tag
+var_dump(openssl_encrypt('data', $method, 'password', 0, str_repeat('x', 10), $tag, '', 1024));
+var_dump(strlen($tag));
+?>
+--EXPECTF--
+TEST 0
+bool(true)
+bool(true)
+
+Warning: openssl_encrypt(): Setting of IV length for AEAD mode failed in %s on line %d
+bool(false)
+string(8) "p/lvgA=="
+int(1024)
diff --git a/ext/openssl/tests/openssl_encrypt_error.phpt b/ext/openssl/tests/openssl_encrypt_error.phpt
index 7376f48708..ea69ad9ee2 100644
--- a/ext/openssl/tests/openssl_encrypt_error.phpt
+++ b/ext/openssl/tests/openssl_encrypt_error.phpt
@@ -7,10 +7,12 @@ openssl_encrypt() error tests
$data = "openssl_encrypt() tests";
$method = "AES-128-CBC";
$password = "openssl";
+$iv = str_repeat("\0", openssl_cipher_iv_length($method));
$wrong = "wrong";
$object = new stdclass;
$arr = array(1);
+// wrong paramters tests
var_dump(openssl_encrypt($data, $wrong, $password));
var_dump(openssl_encrypt($object, $method, $password));
var_dump(openssl_encrypt($data, $object, $password));
@@ -18,6 +20,12 @@ var_dump(openssl_encrypt($data, $method, $object));
var_dump(openssl_encrypt($arr, $method, $object));
var_dump(openssl_encrypt($data, $arr, $object));
var_dump(openssl_encrypt($data, $method, $arr));
+
+// invalid using of an authentication tag
+var_dump(openssl_encrypt($data, $method, $password, 0, $iv, $wrong));
+
+// padding of the key is disabled
+var_dump(openssl_encrypt($data, $method, $password, OPENSSL_DONT_ZERO_PAD_KEY, $iv));
?>
--EXPECTF--
Warning: openssl_encrypt(): Unknown cipher algorithm in %s on line %d
@@ -41,3 +49,8 @@ NULL
Warning: openssl_encrypt() expects parameter 3 to be string, array given in %s on line %d
NULL
+Warning: openssl_encrypt(): The authenticated tag cannot be provided for cipher that doesn not support AEAD in %s on line %d
+string(44) "iPR4HulskuaP5Z6me5uImk6BqVyJG73+63tkPauVZYk="
+
+Warning: openssl_encrypt(): Key length cannot be set for the cipher method in %s on line %d
+bool(false)
diff --git a/ext/openssl/tests/openssl_encrypt_gcm.phpt b/ext/openssl/tests/openssl_encrypt_gcm.phpt
new file mode 100644
index 0000000000..60b48cd091
--- /dev/null
+++ b/ext/openssl/tests/openssl_encrypt_gcm.phpt
@@ -0,0 +1,60 @@
+--TEST--
+openssl_encrypt() with GCM cipher algorithm tests
+--SKIPIF--
+<?php
+if (!extension_loaded("openssl"))
+ die("skip");
+if (!in_array('aes-128-gcm', openssl_get_cipher_methods()))
+ die("skip: aes-128-gcm not available");
+?>
+--FILE--
+<?php
+require_once __DIR__ . "/cipher_tests.inc";
+$method = 'aes-128-gcm';
+$tests = openssl_get_cipher_tests($method);
+
+foreach ($tests as $idx => $test) {
+ echo "TEST $idx\n";
+ $ct = openssl_encrypt($test['pt'], $method, $test['key'], OPENSSL_RAW_DATA,
+ $test['iv'], $tag, $test['aad'], strlen($test['tag']));
+ var_dump($test['ct'] === $ct);
+ var_dump($test['tag'] === $tag);
+}
+
+// Empty IV error
+var_dump(openssl_encrypt('data', $method, 'password', 0, NULL, $tag, ''));
+
+// Failing to retrieve tag (max is 16 bytes)
+var_dump(openssl_encrypt('data', $method, 'password', 0, str_repeat('x', 32), $tag, '', 20));
+
+// Failing when no tag supplied
+var_dump(openssl_encrypt('data', $method, 'password', 0, str_repeat('x', 32)));
+?>
+--EXPECTF--
+TEST 0
+bool(true)
+bool(true)
+TEST 1
+bool(true)
+bool(true)
+TEST 2
+bool(true)
+bool(true)
+TEST 3
+bool(true)
+bool(true)
+TEST 4
+bool(true)
+bool(true)
+TEST 5
+bool(true)
+bool(true)
+
+Warning: openssl_encrypt(): Setting of IV length for AEAD mode failed in %s on line %d
+bool(false)
+
+Warning: openssl_encrypt(): Retrieving verification tag failed in %s on line %d
+bool(false)
+
+Warning: openssl_encrypt(): A tag should be provided when using AEAD mode in %s on line %d
+bool(false)
diff --git a/ext/openssl/tests/openssl_pkcs7_decrypt_basic.phpt b/ext/openssl/tests/openssl_pkcs7_decrypt_basic.phpt
index 5589abb039..27ca4d232c 100644
--- a/ext/openssl/tests/openssl_pkcs7_decrypt_basic.phpt
+++ b/ext/openssl/tests/openssl_pkcs7_decrypt_basic.phpt
@@ -6,10 +6,10 @@ openssl_pkcs7_decrypt() tests
<?php
$infile = dirname(__FILE__) . "/cert.crt";
$privkey = "file://" . dirname(__FILE__) . "/private_rsa_1024.key";
-$encrypted = tempnam("/tmp", "ssl");
+$encrypted = tempnam(sys_get_temp_dir(), "ssl");
if ($encrypted === false)
die("failed to get a temporary filename!");
-$outfile = tempnam("/tmp", "ssl");
+$outfile = tempnam(sys_get_temp_dir(), "ssl");
if ($outfile === false) {
unlink($outfile);
die("failed to get a temporary filename!");
diff --git a/ext/openssl/tests/openssl_pkcs7_encrypt_basic.phpt b/ext/openssl/tests/openssl_pkcs7_encrypt_basic.phpt
index 5f74f97b0c..09a7093b8d 100644
--- a/ext/openssl/tests/openssl_pkcs7_encrypt_basic.phpt
+++ b/ext/openssl/tests/openssl_pkcs7_encrypt_basic.phpt
@@ -5,10 +5,10 @@ openssl_pkcs7_encrypt() tests
--FILE--
<?php
$infile = dirname(__FILE__) . "/cert.crt";
-$outfile = tempnam("/tmp", "ssl");
+$outfile = tempnam(sys_get_temp_dir(), "ssl");
if ($outfile === false)
die("failed to get a temporary filename!");
-$outfile2 = tempnam("/tmp", "ssl");
+$outfile2 = tempnam(sys_get_temp_dir(), "ssl");
if ($outfile2 === false)
die("failed to get a temporary filename!");
diff --git a/ext/openssl/tests/openssl_pkcs7_sign_basic.phpt b/ext/openssl/tests/openssl_pkcs7_sign_basic.phpt
index ac8edf19a9..e06553f4b4 100644
--- a/ext/openssl/tests/openssl_pkcs7_sign_basic.phpt
+++ b/ext/openssl/tests/openssl_pkcs7_sign_basic.phpt
@@ -5,7 +5,7 @@ openssl_pkcs7_sign() tests
--FILE--
<?php
$infile = dirname(__FILE__) . "/cert.crt";
-$outfile = tempnam("/tmp", "ssl");
+$outfile = tempnam(sys_get_temp_dir(), "ssl");
if ($outfile === false) {
die("failed to get a temporary filename!");
}
diff --git a/ext/openssl/tests/openssl_pkey_export_basic.phpt b/ext/openssl/tests/openssl_pkey_export_basic.phpt
index d229d6b135..530158d7d9 100644
--- a/ext/openssl/tests/openssl_pkey_export_basic.phpt
+++ b/ext/openssl/tests/openssl_pkey_export_basic.phpt
@@ -2,8 +2,10 @@
openssl_pkey_export() with EC key
--SKIPIF--
<?php
-if (!extension_loaded("openssl")) die("skip");
-if (!defined('OPENSSL_KEYTYPE_EC')) die("skip no EC available");
+if (!extension_loaded("openssl"))
+ die("skip");
+if (!defined('OPENSSL_KEYTYPE_EC'))
+ die("skip no EC available");
?>
--FILE--
<?php
@@ -28,9 +30,9 @@ var_dump(OPENSSL_KEYTYPE_EC === $details['type']);
// Read public key
$pKey = openssl_pkey_get_public('file://' . dirname(__FILE__) . '/public_ec.key');
var_dump($pKey);
-// The details are the same for a public or private key
-var_dump($details === openssl_pkey_get_details($pKey));
-
+// The details are the same for a public or private key, expect the private key parameter 'd
+$detailsPKey = openssl_pkey_get_details($pKey);
+var_dump(array_diff_assoc($details['ec'], $detailsPKey['ec']));
// Export to file
$tempname = tempnam(sys_get_temp_dir(), 'openssl_ec');
@@ -40,7 +42,6 @@ var_dump(OPENSSL_KEYTYPE_EC === $details['type']);
// Clean the temporary file
@unlink($tempname);
-
?>
--EXPECTF--
resource(%d) of type (OpenSSL key)
@@ -49,6 +50,9 @@ bool(true)
bool(true)
bool(true)
resource(%d) of type (OpenSSL key)
-bool(true)
+array(1) {
+ ["d"]=>
+ string(32) "%a"
+}
bool(true)
bool(true)
diff --git a/ext/openssl/tests/openssl_pkey_get_details_basic.phpt b/ext/openssl/tests/openssl_pkey_get_details_basic.phpt
index 8e0cef46c0..3c239af2a2 100644
--- a/ext/openssl/tests/openssl_pkey_get_details_basic.phpt
+++ b/ext/openssl/tests/openssl_pkey_get_details_basic.phpt
@@ -3,11 +3,11 @@ openssl_pkey_get_details() with EC key
--SKIPIF--
<?php
if (!extension_loaded("openssl")) die("skip");
-if (!defined('OPENSSL_KEYTYPE_EC')) die("skip no EC available");
+if (!defined("OPENSSL_KEYTYPE_EC")) die("skip no EC available");
?>
--FILE--
<?php
-$key = openssl_pkey_get_private('file://' . dirname(__FILE__) . '/private_ec.key');
+$key = openssl_pkey_get_private("file://" . dirname(__FILE__) . "/private_ec.key");
print_r(openssl_pkey_get_details($key));
?>
@@ -22,6 +22,9 @@ Array
(
[curve_name] => prime256v1
[curve_oid] => 1.2.840.10045.3.1.7
+ [x] => %a
+ [y] => %a
+ [d] => %a
)
[type] => 3
diff --git a/ext/openssl/xp_ssl.c b/ext/openssl/xp_ssl.c
index 193eb531ee..c6391ca136 100644
--- a/ext/openssl/xp_ssl.c
+++ b/ext/openssl/xp_ssl.c
@@ -56,28 +56,19 @@
#include <sys/select.h>
#endif
-/* OpenSSL 1.0.2 removes SSLv2 support entirely*/
-#if OPENSSL_VERSION_NUMBER < 0x10002000L && !defined(OPENSSL_NO_SSL2)
-#define HAVE_SSL2 1
-#endif
-
#ifndef OPENSSL_NO_SSL3
#define HAVE_SSL3 1
#endif
-#if OPENSSL_VERSION_NUMBER >= 0x10001001L
#define HAVE_TLS11 1
#define HAVE_TLS12 1
-#endif
-#if !defined(OPENSSL_NO_ECDH) && OPENSSL_VERSION_NUMBER >= 0x0090800fL
+#ifndef OPENSSL_NO_ECDH
#define HAVE_ECDH 1
#endif
-#if !defined(OPENSSL_NO_TLSEXT)
-#if OPENSSL_VERSION_NUMBER >= 0x00908070L
+#ifndef OPENSSL_NO_TLSEXT
#define HAVE_TLS_SNI 1
-#endif
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
#define HAVE_TLS_ALPN 1
#endif
@@ -100,7 +91,7 @@
/* Used for peer verification in windows */
#define PHP_X509_NAME_ENTRY_TO_UTF8(ne, i, out) ASN1_STRING_to_UTF8(&out, X509_NAME_ENTRY_get_data(X509_NAME_get_entry(ne, i)))
-#ifndef OPENSSL_NO_RSA
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined (LIBRESSL_VERSION_NUMBER)
static RSA *tmp_rsa_cb(SSL *s, int is_export, int keylength);
#endif
@@ -413,7 +404,7 @@ static zend_bool matches_san_list(X509 *peer, const char *subject_name) /* {{{ *
if (san->type == GEN_DNS) {
ASN1_STRING_to_UTF8(&cert_name, san->d.dNSName);
- if (ASN1_STRING_length(san->d.dNSName) != strlen((const char*)cert_name)) {
+ if ((size_t)ASN1_STRING_length(san->d.dNSName) != strlen((const char*)cert_name)) {
OPENSSL_free(cert_name);
/* prevent null-byte poisoning*/
continue;
@@ -465,7 +456,7 @@ static zend_bool matches_common_name(X509 *peer, const char *subject_name) /* {{
if (cert_name_len == -1) {
php_error_docref(NULL, E_WARNING, "Unable to locate peer certificate CN");
- } else if (cert_name_len != strlen(buf)) {
+ } else if ((size_t)cert_name_len != strlen(buf)) {
php_error_docref(NULL, E_WARNING, "Peer certificate CN=`%.*s' is malformed", cert_name_len, buf);
} else if (matches_wildcard_name(subject_name, buf)) {
is_match = 1;
@@ -579,7 +570,7 @@ static int passwd_callback(char *buf, int num, int verify, void *data) /* {{{ */
GET_VER_OPT_STRING("passphrase", passphrase);
if (passphrase) {
- if (Z_STRLEN_P(val) < num - 1) {
+ if (Z_STRLEN_P(val) < (size_t)num - 1) {
memcpy(buf, Z_STRVAL_P(val), Z_STRLEN_P(val)+1);
return (int)Z_STRLEN_P(val);
}
@@ -588,12 +579,17 @@ static int passwd_callback(char *buf, int num, int verify, void *data) /* {{{ */
}
/* }}} */
-#if defined(PHP_WIN32) && OPENSSL_VERSION_NUMBER >= 0x00907000L
+#ifdef PHP_WIN32
#define RETURN_CERT_VERIFY_FAILURE(code) X509_STORE_CTX_set_error(x509_store_ctx, code); return 0;
static int win_cert_verify_callback(X509_STORE_CTX *x509_store_ctx, void *arg) /* {{{ */
{
PCCERT_CONTEXT cert_ctx = NULL;
PCCERT_CHAIN_CONTEXT cert_chain_ctx = NULL;
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+ X509 *cert = x509_store_ctx->cert;
+#else
+ X509 *cert = X509_STORE_CTX_get0_cert(x509_store_ctx);
+#endif
php_stream *stream;
php_openssl_netstream_data_t *sslsock;
@@ -608,7 +604,7 @@ static int win_cert_verify_callback(X509_STORE_CTX *x509_store_ctx, void *arg) /
unsigned char *der_buf = NULL;
int der_len;
- der_len = i2d_X509(x509_store_ctx->cert, &der_buf);
+ der_len = i2d_X509(cert, &der_buf);
if (der_len < 0) {
unsigned long err_code, e;
char err_buf[512];
@@ -685,7 +681,7 @@ static int win_cert_verify_callback(X509_STORE_CTX *x509_store_ctx, void *arg) /
int index, cert_name_utf8_len;
DWORD num_wchars;
- cert_name = X509_get_subject_name(x509_store_ctx->cert);
+ cert_name = X509_get_subject_name(cert);
index = X509_NAME_get_index_by_NID(cert_name, NID_commonName, -1);
if (index < 0) {
php_error_docref(NULL, E_WARNING, "Unable to locate certificate CN");
@@ -868,7 +864,7 @@ static int enable_peer_verification(SSL_CTX *ctx, php_stream *stream) /* {{{ */
}
}
} else {
-#if defined(PHP_WIN32) && OPENSSL_VERSION_NUMBER >= 0x00907000L
+#ifdef PHP_WIN32
SSL_CTX_set_cert_verify_callback(ctx, win_cert_verify_callback, (void *)stream);
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
#else
@@ -926,22 +922,6 @@ static int set_local_cert(SSL_CTX *ctx, php_stream *stream) /* {{{ */
}
}
-#if OPENSSL_VERSION_NUMBER < 0x10001001L
- do {
- /* Unnecessary as of OpenSSLv1.0.1 (will segfault if used with >= 10001001 ) */
- X509 *cert = NULL;
- EVP_PKEY *key = NULL;
- SSL *tmpssl = SSL_new(ctx);
- cert = SSL_get_certificate(tmpssl);
-
- if (cert) {
- key = X509_get_pubkey(cert);
- EVP_PKEY_copy_parameters(key, SSL_get_privatekey(tmpssl));
- EVP_PKEY_free(key);
- }
- SSL_free(tmpssl);
- } while (0);
-#endif
if (!SSL_CTX_check_private_key(ctx)) {
php_error_docref(NULL, E_WARNING, "Private key does not match certificate!");
}
@@ -955,13 +935,9 @@ static int set_local_cert(SSL_CTX *ctx, php_stream *stream) /* {{{ */
static const SSL_METHOD *php_select_crypto_method(zend_long method_value, int is_client) /* {{{ */
{
if (method_value == STREAM_CRYPTO_METHOD_SSLv2) {
-#ifdef HAVE_SSL2
- return is_client ? (SSL_METHOD *)SSLv2_client_method() : (SSL_METHOD *)SSLv2_server_method();
-#else
php_error_docref(NULL, E_WARNING,
- "SSLv2 unavailable in the OpenSSL library against which PHP is linked");
+ "SSLv2 unavailable in this PHP version");
return NULL;
-#endif
} else if (method_value == STREAM_CRYPTO_METHOD_SSLv3) {
#ifdef HAVE_SSL3
return is_client ? SSLv3_client_method() : SSLv3_server_method();
@@ -1015,10 +991,8 @@ static int php_get_crypto_method_ctx_flags(int method_flags) /* {{{ */
{
int ssl_ctx_options = SSL_OP_ALL;
-#ifdef HAVE_SSL2
- if (!(method_flags & STREAM_CRYPTO_METHOD_SSLv2)) {
- ssl_ctx_options |= SSL_OP_NO_SSLv2;
- }
+#ifdef SSL_OP_NO_SSLv2
+ ssl_ctx_options |= SSL_OP_NO_SSLv2;
#endif
#ifdef HAVE_SSL3
if (!(method_flags & STREAM_CRYPTO_METHOD_SSLv3)) {
@@ -1154,7 +1128,7 @@ static void init_server_reneg_limit(php_stream *stream, php_openssl_netstream_da
}
/* }}} */
-#ifndef OPENSSL_NO_RSA
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined (LIBRESSL_VERSION_NUMBER)
static RSA *tmp_rsa_cb(SSL *s, int is_export, int keylength)
{
BIGNUM *bn = NULL;
@@ -1178,7 +1152,6 @@ static RSA *tmp_rsa_cb(SSL *s, int is_export, int keylength)
}
#endif
-#ifndef OPENSSL_NO_DH
static int set_server_dh_param(php_stream * stream, SSL_CTX *ctx) /* {{{ */
{
DH *dh;
@@ -1197,7 +1170,7 @@ static int set_server_dh_param(php_stream * stream, SSL_CTX *ctx) /* {{{ */
}
convert_to_string_ex(zdhpath);
- bio = BIO_new_file(Z_STRVAL_P(zdhpath), "r");
+ bio = BIO_new_file(Z_STRVAL_P(zdhpath), PHP_OPENSSL_BIO_MODE_R(PKCS7_BINARY));
if (bio == NULL) {
php_error_docref(NULL, E_WARNING, "invalid dh_param");
@@ -1223,7 +1196,6 @@ static int set_server_dh_param(php_stream * stream, SSL_CTX *ctx) /* {{{ */
return SUCCESS;
}
/* }}} */
-#endif
#if defined(HAVE_ECDH) && (OPENSSL_VERSION_NUMBER < 0x10100000L || defined (LIBRESSL_VERSION_NUMBER))
static int set_server_ecdh_curve(php_stream *stream, SSL_CTX *ctx) /* {{{ */
@@ -1274,7 +1246,7 @@ static int set_server_specific_opts(php_stream *stream, SSL_CTX *ctx) /* {{{ */
}
#endif
-#ifndef OPENSSL_NO_RSA
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined (LIBRESSL_VERSION_NUMBER)
SSL_CTX_set_tmp_rsa_callback(ctx, tmp_rsa_cb);
#endif
/* We now use tmp_rsa_cb to generate a key of appropriate size whenever necessary */
@@ -1282,13 +1254,11 @@ static int set_server_specific_opts(php_stream *stream, SSL_CTX *ctx) /* {{{ */
php_error_docref(NULL, E_WARNING, "rsa_key_size context option has been removed");
}
-#ifndef OPENSSL_NO_DH
set_server_dh_param(stream, ctx);
zv = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "ssl", "single_dh_use");
if (zv != NULL && zend_is_true(zv)) {
ssl_ctx_options |= SSL_OP_SINGLE_DH_USE;
}
-#endif
zv = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "ssl", "honor_cipher_order");
if (zv != NULL && zend_is_true(zv)) {
@@ -1546,33 +1516,22 @@ int php_openssl_setup_crypto(php_stream *stream,
}
}
-#if OPENSSL_VERSION_NUMBER >= 0x10001001L
sslsock->ctx = SSL_CTX_new(method);
-#else
- /* Avoid const warning with old versions */
- sslsock->ctx = SSL_CTX_new((SSL_METHOD*)method);
-#endif
if (sslsock->ctx == NULL) {
php_error_docref(NULL, E_WARNING, "SSL context creation failure");
return FAILURE;
}
-#if OPENSSL_VERSION_NUMBER >= 0x0090806fL
if (GET_VER_OPT("no_ticket") && zend_is_true(val)) {
ssl_ctx_options |= SSL_OP_NO_TICKET;
}
-#endif
-#if OPENSSL_VERSION_NUMBER >= 0x0090605fL
ssl_ctx_options &= ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS;
-#endif
-#if OPENSSL_VERSION_NUMBER >= 0x10000000L
if (!GET_VER_OPT("disable_compression") || zend_is_true(val)) {
ssl_ctx_options |= SSL_OP_NO_COMPRESSION;
}
-#endif
if (GET_VER_OPT("verify_peer") && !zend_is_true(val)) {
disable_peer_verification(sslsock->ctx, stream);
@@ -1720,11 +1679,6 @@ static zend_array *capture_session_meta(SSL *ssl_handle) /* {{{ */
proto_str = "SSLv3";
break;
#endif
-#ifdef HAVE_SSL2
- case SSL2_VERSION:
- proto_str = "SSLv2";
- break;
-#endif
default: proto_str = "UNKNOWN";
}
@@ -2238,39 +2192,39 @@ static inline int php_openssl_tcp_sockop_accept(php_stream *stream, php_openssl_
php_stream_xport_param *xparam STREAMS_DC)
{
int clisock;
-
+ zend_bool nodelay = 0;
+ zval *tmpzval = NULL;
+
xparam->outputs.client = NULL;
+ if ((tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "tcp_nodelay")) != NULL &&
+ zend_is_true(tmpzval)) {
+ nodelay = 1;
+ }
+
clisock = php_network_accept_incoming(sock->s.socket,
- xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
- xparam->want_addr ? &xparam->outputs.addr : NULL,
- xparam->want_addr ? &xparam->outputs.addrlen : NULL,
- xparam->inputs.timeout,
- xparam->want_errortext ? &xparam->outputs.error_text : NULL,
- &xparam->outputs.error_code
- );
+ xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
+ xparam->want_addr ? &xparam->outputs.addr : NULL,
+ xparam->want_addr ? &xparam->outputs.addrlen : NULL,
+ xparam->inputs.timeout,
+ xparam->want_errortext ? &xparam->outputs.error_text : NULL,
+ &xparam->outputs.error_code,
+ nodelay);
if (clisock >= 0) {
- php_openssl_netstream_data_t *clisockdata;
-
- clisockdata = emalloc(sizeof(*clisockdata));
+ php_openssl_netstream_data_t *clisockdata = (php_openssl_netstream_data_t*) emalloc(sizeof(*clisockdata));
- if (clisockdata == NULL) {
- closesocket(clisock);
- /* technically a fatal error */
- } else {
- /* copy underlying tcp fields */
- memset(clisockdata, 0, sizeof(*clisockdata));
- memcpy(clisockdata, sock, sizeof(clisockdata->s));
+ /* copy underlying tcp fields */
+ memset(clisockdata, 0, sizeof(*clisockdata));
+ memcpy(clisockdata, sock, sizeof(clisockdata->s));
- clisockdata->s.socket = clisock;
+ clisockdata->s.socket = clisock;
- xparam->outputs.client = php_stream_alloc_rel(stream->ops, clisockdata, NULL, "r+");
- if (xparam->outputs.client) {
- xparam->outputs.client->ctx = stream->ctx;
- if (stream->ctx) {
- GC_REFCOUNT(stream->ctx)++;
- }
+ xparam->outputs.client = php_stream_alloc_rel(stream->ops, clisockdata, NULL, "r+");
+ if (xparam->outputs.client) {
+ xparam->outputs.client->ctx = stream->ctx;
+ if (stream->ctx) {
+ GC_REFCOUNT(stream->ctx)++;
}
}
@@ -2324,9 +2278,6 @@ static int php_openssl_sockop_set_option(php_stream *stream, int option, int val
#ifdef HAVE_SSL3
case SSL3_VERSION: proto_str = "SSLv3"; break;
#endif
-#ifdef HAVE_SSL2
- case SSL2_VERSION: proto_str = "SSLv2"; break;
-#endif
default: proto_str = "UNKNOWN";
}
@@ -2620,14 +2571,9 @@ php_stream *php_openssl_ssl_socket_factory(const char *proto, size_t protolen,
sslsock->enable_on_connect = 1;
sslsock->method = get_crypto_method(context, STREAM_CRYPTO_METHOD_ANY_CLIENT);
} else if (strncmp(proto, "sslv2", protolen) == 0) {
-#ifdef HAVE_SSL2
- sslsock->enable_on_connect = 1;
- sslsock->method = STREAM_CRYPTO_METHOD_SSLv2_CLIENT;
-#else
- php_error_docref(NULL, E_WARNING, "SSLv2 support is not compiled into the OpenSSL library against which PHP is linked");
+ php_error_docref(NULL, E_WARNING, "SSLv2 unavailable in this PHP version");
php_stream_close(stream);
return NULL;
-#endif
} else if (strncmp(proto, "sslv3", protolen) == 0) {
#ifdef HAVE_SSL3
sslsock->enable_on_connect = 1;
diff --git a/ext/pcntl/config.m4 b/ext/pcntl/config.m4
index 70e0aeb008..8e4dc41f33 100644
--- a/ext/pcntl/config.m4
+++ b/ext/pcntl/config.m4
@@ -10,5 +10,21 @@ if test "$PHP_PCNTL" != "no"; then
AC_CHECK_FUNCS(waitpid, [ AC_DEFINE(HAVE_WAITPID,1,[ ]) ], [ AC_MSG_ERROR(pcntl: waitpid() not supported by this platform) ])
AC_CHECK_FUNCS(sigaction, [ AC_DEFINE(HAVE_SIGACTION,1,[ ]) ], [ AC_MSG_ERROR(pcntl: sigaction() not supported by this platform) ])
AC_CHECK_FUNCS([getpriority setpriority wait3 wait4 sigprocmask sigwaitinfo sigtimedwait])
- PHP_NEW_EXTENSION(pcntl, pcntl.c php_signal.c, $ext_shared, cli)
+
+ AC_MSG_CHECKING([for siginfo_t])
+ AC_TRY_COMPILE([
+ #include <signal.h>
+ #ifdef HAVE_SIGINFO_H
+ #include <siginfo.h>
+ #endif
+ ],[
+ siginfo_t info;
+ ],[
+ AC_MSG_RESULT([yes])
+ PCNTL_CFLAGS="-DHAVE_STRUCT_SIGINFO_T"
+ ], [
+ AC_MSG_RESULT([no])
+ ])
+
+ PHP_NEW_EXTENSION(pcntl, pcntl.c php_signal.c, $ext_shared, cli, $PCNTL_CFLAGS)
fi
diff --git a/ext/pcntl/pcntl.c b/ext/pcntl/pcntl.c
index 561815f5cc..a1ac06982d 100644
--- a/ext/pcntl/pcntl.c
+++ b/ext/pcntl/pcntl.c
@@ -72,12 +72,18 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_pcntl_signal, 0, 0, 2)
ZEND_ARG_INFO(0, restart_syscalls)
ZEND_END_ARG_INFO()
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pcntl_signal_get_handler, 0, 0, 1)
+ ZEND_ARG_INFO(0, signo)
+ZEND_END_ARG_INFO()
+
ZEND_BEGIN_ARG_INFO_EX(arginfo_pcntl_sigprocmask, 0, 0, 2)
ZEND_ARG_INFO(0, how)
ZEND_ARG_INFO(0, set)
ZEND_ARG_INFO(1, oldset)
ZEND_END_ARG_INFO()
+#ifdef HAVE_STRUCT_SIGINFO_T
+# if HAVE_SIGWAITINFO && HAVE_SIGTIMEDWAIT
ZEND_BEGIN_ARG_INFO_EX(arginfo_pcntl_sigwaitinfo, 0, 0, 1)
ZEND_ARG_INFO(0, set)
ZEND_ARG_INFO(1, info)
@@ -89,6 +95,8 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_pcntl_sigtimedwait, 0, 0, 1)
ZEND_ARG_INFO(0, seconds)
ZEND_ARG_INFO(0, nanoseconds)
ZEND_END_ARG_INFO()
+# endif
+#endif
ZEND_BEGIN_ARG_INFO_EX(arginfo_pcntl_wifexited, 0, 0, 1)
ZEND_ARG_INFO(0, status)
@@ -148,6 +156,10 @@ ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_pcntl_strerror, 0, 0, 1)
ZEND_ARG_INFO(0, errno)
ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pcntl_async_signals, 0, 0, 1)
+ ZEND_ARG_INFO(0, on)
+ZEND_END_ARG_INFO()
/* }}} */
const zend_function_entry pcntl_functions[] = {
@@ -155,6 +167,7 @@ const zend_function_entry pcntl_functions[] = {
PHP_FE(pcntl_waitpid, arginfo_pcntl_waitpid)
PHP_FE(pcntl_wait, arginfo_pcntl_wait)
PHP_FE(pcntl_signal, arginfo_pcntl_signal)
+ PHP_FE(pcntl_signal_get_handler, arginfo_pcntl_signal_get_handler)
PHP_FE(pcntl_signal_dispatch, arginfo_pcntl_void)
PHP_FE(pcntl_wifexited, arginfo_pcntl_wifexited)
PHP_FE(pcntl_wifstopped, arginfo_pcntl_wifstopped)
@@ -176,13 +189,16 @@ const zend_function_entry pcntl_functions[] = {
#ifdef HAVE_SIGPROCMASK
PHP_FE(pcntl_sigprocmask, arginfo_pcntl_sigprocmask)
#endif
-#if HAVE_SIGWAITINFO && HAVE_SIGTIMEDWAIT
+#ifdef HAVE_STRUCT_SIGINFO_T
+# if HAVE_SIGWAITINFO && HAVE_SIGTIMEDWAIT
PHP_FE(pcntl_sigwaitinfo, arginfo_pcntl_sigwaitinfo)
PHP_FE(pcntl_sigtimedwait, arginfo_pcntl_sigtimedwait)
+# endif
#endif
#ifdef HAVE_WCONTINUED
PHP_FE(pcntl_wifcontinued, arginfo_pcntl_wifcontinued)
#endif
+ PHP_FE(pcntl_async_signals, arginfo_pcntl_async_signals)
PHP_FE_END
};
@@ -207,8 +223,16 @@ zend_module_entry pcntl_module_entry = {
ZEND_GET_MODULE(pcntl)
#endif
+static void (*orig_interrupt_function)(zend_execute_data *execute_data);
+
+#ifdef HAVE_STRUCT_SIGINFO_T
+static void pcntl_signal_handler(int, siginfo_t*, void*);
+static void pcntl_siginfo_to_zval(int, siginfo_t*, zval*);
+#else
static void pcntl_signal_handler(int);
+#endif
static void pcntl_signal_dispatch();
+static void pcntl_interrupt_function(zend_execute_data *execute_data);
void php_register_signal_constants(INIT_FUNC_ARGS)
{
@@ -506,6 +530,7 @@ PHP_RINIT_FUNCTION(pcntl)
{
zend_hash_init(&PCNTL_G(php_signal_table), 16, NULL, ZVAL_PTR_DTOR, 0);
PCNTL_G(head) = PCNTL_G(tail) = PCNTL_G(spares) = NULL;
+ PCNTL_G(async_signals) = 0;
return SUCCESS;
}
@@ -514,6 +539,8 @@ PHP_MINIT_FUNCTION(pcntl)
php_register_signal_constants(INIT_FUNC_ARGS_PASSTHRU);
php_pcntl_register_errno_constants(INIT_FUNC_ARGS_PASSTHRU);
php_add_tick_function(pcntl_signal_dispatch, NULL);
+ orig_interrupt_function = zend_interrupt_function;
+ zend_interrupt_function = pcntl_interrupt_function;
return SUCCESS;
}
@@ -981,12 +1008,12 @@ PHP_FUNCTION(pcntl_signal)
php_error_docref(NULL, E_WARNING, "Invalid value for handle argument specified");
RETURN_FALSE;
}
- if (php_signal(signo, (Sigfunc *) Z_LVAL_P(handle), (int) restart_syscalls) == SIG_ERR) {
+ if (php_signal(signo, (Sigfunc *) Z_LVAL_P(handle), (int) restart_syscalls) == (Sigfunc *)SIG_ERR) {
PCNTL_G(last_error) = errno;
php_error_docref(NULL, E_WARNING, "Error assigning signal");
RETURN_FALSE;
}
- zend_hash_index_del(&PCNTL_G(php_signal_table), signo);
+ zend_hash_index_update(&PCNTL_G(php_signal_table), signo, handle);
RETURN_TRUE;
}
@@ -1003,7 +1030,7 @@ PHP_FUNCTION(pcntl_signal)
if (Z_REFCOUNTED_P(handle)) Z_ADDREF_P(handle);
}
- if (php_signal4(signo, pcntl_signal_handler, (int) restart_syscalls, 1) == SIG_ERR) {
+ if (php_signal4(signo, pcntl_signal_handler, (int) restart_syscalls, 1) == (Sigfunc *)SIG_ERR) {
PCNTL_G(last_error) = errno;
php_error_docref(NULL, E_WARNING, "Error assigning signal");
RETURN_FALSE;
@@ -1012,6 +1039,29 @@ PHP_FUNCTION(pcntl_signal)
}
/* }}} */
+/* {{{ proto bool pcntl_signal_get_handler(int signo)
+ Gets signal handler */
+PHP_FUNCTION(pcntl_signal_get_handler)
+{
+ zval *prev_handle;
+ zend_long signo;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &signo) == FAILURE) {
+ return;
+ }
+
+ if (signo < 1 || signo > 32) {
+ php_error_docref(NULL, E_WARNING, "Invalid signal");
+ RETURN_FALSE;
+ }
+
+ if ((prev_handle = zend_hash_index_find(&PCNTL_G(php_signal_table), signo)) != NULL) {
+ RETURN_ZVAL(prev_handle, 1, 0);
+ } else {
+ RETURN_LONG((zend_long)SIG_DFL);
+ }
+}
+
/* {{{ proto bool pcntl_signal_dispatch()
Dispatch signals to signal handlers */
PHP_FUNCTION(pcntl_signal_dispatch)
@@ -1075,7 +1125,8 @@ PHP_FUNCTION(pcntl_sigprocmask)
/* }}} */
#endif
-#if HAVE_SIGWAITINFO && HAVE_SIGTIMEDWAIT
+#ifdef HAVE_STRUCT_SIGINFO_T
+# if HAVE_SIGWAITINFO && HAVE_SIGTIMEDWAIT
static void pcntl_sigwaitinfo(INTERNAL_FUNCTION_PARAMETERS, int timedwait) /* {{{ */
{
zval *user_set, *user_signo, *user_siginfo = NULL;
@@ -1129,7 +1180,30 @@ static void pcntl_sigwaitinfo(INTERNAL_FUNCTION_PARAMETERS, int timedwait) /* {{
if (!signo && siginfo.si_signo) {
signo = siginfo.si_signo;
}
+ pcntl_siginfo_to_zval(signo, &siginfo, user_siginfo);
+ RETURN_LONG(signo);
+}
+/* }}} */
+
+/* {{{ proto int pcnlt_sigwaitinfo(array set[, array &siginfo])
+ Synchronously wait for queued signals */
+PHP_FUNCTION(pcntl_sigwaitinfo)
+{
+ pcntl_sigwaitinfo(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
+}
+/* }}} */
+/* {{{ proto int pcntl_sigtimedwait(array set[, array &siginfo[, int seconds[, int nanoseconds]]])
+ Wait for queued signals */
+PHP_FUNCTION(pcntl_sigtimedwait)
+{
+ pcntl_sigwaitinfo(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
+}
+/* }}} */
+# endif
+
+static void pcntl_siginfo_to_zval(int signo, siginfo_t *siginfo, zval *user_siginfo) /* {{{ */
+{
if (signo > 0 && user_siginfo) {
if (Z_TYPE_P(user_siginfo) != IS_ARRAY) {
zval_dtor(user_siginfo);
@@ -1137,57 +1211,44 @@ static void pcntl_sigwaitinfo(INTERNAL_FUNCTION_PARAMETERS, int timedwait) /* {{
} else {
zend_hash_clean(Z_ARRVAL_P(user_siginfo));
}
- add_assoc_long_ex(user_siginfo, "signo", sizeof("signo")-1, siginfo.si_signo);
- add_assoc_long_ex(user_siginfo, "errno", sizeof("errno")-1, siginfo.si_errno);
- add_assoc_long_ex(user_siginfo, "code", sizeof("code")-1, siginfo.si_code);
+ add_assoc_long_ex(user_siginfo, "signo", sizeof("signo")-1, siginfo->si_signo);
+ add_assoc_long_ex(user_siginfo, "errno", sizeof("errno")-1, siginfo->si_errno);
+ add_assoc_long_ex(user_siginfo, "code", sizeof("code")-1, siginfo->si_code);
switch(signo) {
#ifdef SIGCHLD
case SIGCHLD:
- add_assoc_long_ex(user_siginfo, "status", sizeof("status")-1, siginfo.si_status);
+ add_assoc_long_ex(user_siginfo, "status", sizeof("status")-1, siginfo->si_status);
# ifdef si_utime
- add_assoc_double_ex(user_siginfo, "utime", sizeof("utime")-1, siginfo.si_utime);
+ add_assoc_double_ex(user_siginfo, "utime", sizeof("utime")-1, siginfo->si_utime);
# endif
# ifdef si_stime
- add_assoc_double_ex(user_siginfo, "stime", sizeof("stime")-1, siginfo.si_stime);
+ add_assoc_double_ex(user_siginfo, "stime", sizeof("stime")-1, siginfo->si_stime);
# endif
- add_assoc_long_ex(user_siginfo, "pid", sizeof("pid")-1, siginfo.si_pid);
- add_assoc_long_ex(user_siginfo, "uid", sizeof("uid")-1, siginfo.si_uid);
+ add_assoc_long_ex(user_siginfo, "pid", sizeof("pid")-1, siginfo->si_pid);
+ add_assoc_long_ex(user_siginfo, "uid", sizeof("uid")-1, siginfo->si_uid);
+ break;
+ case SIGUSR1:
+ case SIGUSR2:
+ add_assoc_long_ex(user_siginfo, "pid", sizeof("pid")-1, siginfo->si_pid);
+ add_assoc_long_ex(user_siginfo, "uid", sizeof("uid")-1, siginfo->si_uid);
break;
#endif
case SIGILL:
case SIGFPE:
case SIGSEGV:
case SIGBUS:
- add_assoc_double_ex(user_siginfo, "addr", sizeof("addr")-1, (zend_long)siginfo.si_addr);
+ add_assoc_double_ex(user_siginfo, "addr", sizeof("addr")-1, (zend_long)siginfo->si_addr);
break;
#ifdef SIGPOLL
case SIGPOLL:
- add_assoc_long_ex(user_siginfo, "band", sizeof("band")-1, siginfo.si_band);
+ add_assoc_long_ex(user_siginfo, "band", sizeof("band")-1, siginfo->si_band);
# ifdef si_fd
- add_assoc_long_ex(user_siginfo, "fd", sizeof("fd")-1, siginfo.si_fd);
+ add_assoc_long_ex(user_siginfo, "fd", sizeof("fd")-1, siginfo->si_fd);
# endif
break;
#endif
}
}
-
- RETURN_LONG(signo);
-}
-/* }}} */
-
-/* {{{ proto int pcnlt_sigwaitinfo(array set[, array &siginfo])
- Synchronously wait for queued signals */
-PHP_FUNCTION(pcntl_sigwaitinfo)
-{
- pcntl_sigwaitinfo(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
-}
-/* }}} */
-
-/* {{{ proto int pcntl_sigtimedwait(array set[, array &siginfo[, int seconds[, int nanoseconds]]])
- Wait for queued signals */
-PHP_FUNCTION(pcntl_sigtimedwait)
-{
- pcntl_sigwaitinfo(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
}
/* }}} */
#endif
@@ -1294,7 +1355,11 @@ PHP_FUNCTION(pcntl_strerror)
/* }}} */
/* Our custom signal handler that calls the appropriate php_function */
+#ifdef HAVE_STRUCT_SIGINFO_T
+static void pcntl_signal_handler(int signo, siginfo_t *siginfo, void *context)
+#else
static void pcntl_signal_handler(int signo)
+#endif
{
struct php_pcntl_pending_signal *psig;
@@ -1308,6 +1373,10 @@ static void pcntl_signal_handler(int signo)
psig->signo = signo;
psig->next = NULL;
+#ifdef HAVE_STRUCT_SIGINFO_T
+ psig->siginfo = *siginfo;
+#endif
+
/* the head check is important, as the tick handler cannot atomically clear both
* the head and tail */
if (PCNTL_G(head) && PCNTL_G(tail)) {
@@ -1317,11 +1386,14 @@ static void pcntl_signal_handler(int signo)
}
PCNTL_G(tail) = psig;
PCNTL_G(pending_signals) = 1;
+ if (PCNTL_G(async_signals)) {
+ EG(vm_interrupt) = 1;
+ }
}
void pcntl_signal_dispatch()
{
- zval param, *handle, retval;
+ zval params[2], *handle, retval;
struct php_pcntl_pending_signal *queue, *next;
sigset_t mask;
sigset_t old_mask;
@@ -1334,8 +1406,8 @@ void pcntl_signal_dispatch()
sigfillset(&mask);
sigprocmask(SIG_BLOCK, &mask, &old_mask);
- /* Bail if the queue is empty or if we are already playing the queue*/
- if (! PCNTL_G(head) || PCNTL_G(processing_signal_queue)) {
+ /* Bail if the queue is empty or if we are already playing the queue */
+ if (!PCNTL_G(head) || PCNTL_G(processing_signal_queue)) {
sigprocmask(SIG_SETMASK, &old_mask, NULL);
return;
}
@@ -1347,17 +1419,25 @@ void pcntl_signal_dispatch()
PCNTL_G(head) = NULL; /* simple stores are atomic */
/* Allocate */
-
while (queue) {
if ((handle = zend_hash_index_find(&PCNTL_G(php_signal_table), queue->signo)) != NULL) {
- ZVAL_NULL(&retval);
- ZVAL_LONG(&param, queue->signo);
-
- /* Call php signal handler - Note that we do not report errors, and we ignore the return value */
- /* FIXME: this is probably broken when multiple signals are handled in this while loop (retval) */
- call_user_function(EG(function_table), NULL, handle, &retval, 1, &param);
- zval_ptr_dtor(&param);
- zval_ptr_dtor(&retval);
+ if (Z_TYPE_P(handle) != IS_LONG) {
+ ZVAL_NULL(&retval);
+ ZVAL_LONG(&params[0], queue->signo);
+#ifdef HAVE_STRUCT_SIGINFO_T
+ array_init(&params[1]);
+ pcntl_siginfo_to_zval(queue->signo, &queue->siginfo, &params[1]);
+#else
+ ZVAL_NULL(&params[1]);
+#endif
+
+ /* Call php signal handler - Note that we do not report errors, and we ignore the return value */
+ /* FIXME: this is probably broken when multiple signals are handled in this while loop (retval) */
+ call_user_function(EG(function_table), NULL, handle, &retval, 2, params);
+ zval_ptr_dtor(&retval);
+ zval_ptr_dtor(&params[0]);
+ zval_ptr_dtor(&params[1]);
+ }
}
next = queue->next;
@@ -1375,7 +1455,30 @@ void pcntl_signal_dispatch()
sigprocmask(SIG_SETMASK, &old_mask, NULL);
}
+/* {{{ proto bool pcntl_async_signals([bool on[)
+ Enable/disable asynchronous signal handling and return the old setting. */
+PHP_FUNCTION(pcntl_async_signals)
+{
+ zend_bool on;
+ if (ZEND_NUM_ARGS() == 0) {
+ RETURN_BOOL(PCNTL_G(async_signals));
+ }
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &on) == FAILURE) {
+ return;
+ }
+ RETVAL_BOOL(PCNTL_G(async_signals));
+ PCNTL_G(async_signals) = on;
+}
+/* }}} */
+
+static void pcntl_interrupt_function(zend_execute_data *execute_data)
+{
+ pcntl_signal_dispatch();
+ if (orig_interrupt_function) {
+ orig_interrupt_function(execute_data);
+ }
+}
/*
* Local variables:
diff --git a/ext/pcntl/php_pcntl.h b/ext/pcntl/php_pcntl.h
index a2c56db308..1b13fb01c0 100644
--- a/ext/pcntl/php_pcntl.h
+++ b/ext/pcntl/php_pcntl.h
@@ -51,15 +51,18 @@ PHP_FUNCTION(pcntl_wexitstatus);
PHP_FUNCTION(pcntl_wtermsig);
PHP_FUNCTION(pcntl_wstopsig);
PHP_FUNCTION(pcntl_signal);
+PHP_FUNCTION(pcntl_signal_get_handler);
PHP_FUNCTION(pcntl_signal_dispatch);
PHP_FUNCTION(pcntl_get_last_error);
PHP_FUNCTION(pcntl_strerror);
#ifdef HAVE_SIGPROCMASK
PHP_FUNCTION(pcntl_sigprocmask);
#endif
-#if HAVE_SIGWAITINFO && HAVE_SIGTIMEDWAIT
+#ifdef HAVE_STRUCT_SIGINFO_T
+# if HAVE_SIGWAITINFO && HAVE_SIGTIMEDWAIT
PHP_FUNCTION(pcntl_sigwaitinfo);
PHP_FUNCTION(pcntl_sigtimedwait);
+# endif
#endif
PHP_FUNCTION(pcntl_exec);
#ifdef HAVE_GETPRIORITY
@@ -68,10 +71,14 @@ PHP_FUNCTION(pcntl_getpriority);
#ifdef HAVE_SETPRIORITY
PHP_FUNCTION(pcntl_setpriority);
#endif
+PHP_FUNCTION(pcntl_async_signals);
struct php_pcntl_pending_signal {
struct php_pcntl_pending_signal *next;
zend_long signo;
+#ifdef HAVE_STRUCT_SIGINFO_T
+ siginfo_t siginfo;
+#endif
};
ZEND_BEGIN_MODULE_GLOBALS(pcntl)
@@ -80,6 +87,7 @@ ZEND_BEGIN_MODULE_GLOBALS(pcntl)
struct php_pcntl_pending_signal *head, *tail, *spares;
int last_error;
volatile char pending_signals;
+ zend_bool async_signals;
ZEND_END_MODULE_GLOBALS(pcntl)
#ifdef ZTS
diff --git a/ext/pcntl/php_signal.c b/ext/pcntl/php_signal.c
index 8f1b350016..0197af1879 100644
--- a/ext/pcntl/php_signal.c
+++ b/ext/pcntl/php_signal.c
@@ -28,15 +28,21 @@
Sigfunc *php_signal4(int signo, Sigfunc *func, int restart, int mask_all)
{
struct sigaction act,oact;
-#ifdef ZEND_SIGNALS
-#endif
+
+#ifdef HAVE_STRUCT_SIGINFO_T
+ act.sa_sigaction = func;
+#else
act.sa_handler = func;
+#endif
if (mask_all) {
sigfillset(&act.sa_mask);
} else {
sigemptyset(&act.sa_mask);
}
act.sa_flags = 0;
+#ifdef HAVE_STRUCT_SIGINFO_T
+ act.sa_flags |= SA_SIGINFO;
+#endif
if (signo == SIGALRM || (! restart)) {
#ifdef SA_INTERRUPT
act.sa_flags |= SA_INTERRUPT; /* SunOS */
@@ -46,16 +52,15 @@ Sigfunc *php_signal4(int signo, Sigfunc *func, int restart, int mask_all)
act.sa_flags |= SA_RESTART; /* SVR4, 4.3+BSD */
#endif
}
-#ifdef ZEND_SIGNALS
- if (zend_sigaction(signo, &act, &oact) < 0)
-#else
- if (sigaction(signo, &act, &oact) < 0)
-#endif
- {
- return SIG_ERR;
+ if (zend_sigaction(signo, &act, &oact) < 0) {
+ return (Sigfunc*)SIG_ERR;
}
+#ifdef HAVE_STRUCT_SIGINFO_T
+ return oact.sa_sigaction;
+#else
return oact.sa_handler;
+#endif
}
Sigfunc *php_signal(int signo, Sigfunc *func, int restart)
diff --git a/ext/pcntl/php_signal.h b/ext/pcntl/php_signal.h
index b8c81d251c..dc9ef7a691 100644
--- a/ext/pcntl/php_signal.h
+++ b/ext/pcntl/php_signal.h
@@ -29,7 +29,11 @@
# define SIGRTMAX 64
#endif
+#ifdef HAVE_STRUCT_SIGINFO_T
+typedef void Sigfunc(int, siginfo_t*, void*);
+#else
typedef void Sigfunc(int);
+#endif
Sigfunc *php_signal(int signo, Sigfunc *func, int restart);
Sigfunc *php_signal4(int signo, Sigfunc *func, int restart, int mask_all);
diff --git a/ext/pcntl/tests/async_signals.phpt b/ext/pcntl/tests/async_signals.phpt
new file mode 100644
index 0000000000..b650606df3
--- /dev/null
+++ b/ext/pcntl/tests/async_signals.phpt
@@ -0,0 +1,25 @@
+--TEST--
+Asynchronous signal handling through VM interrupts
+--SKIPIF--
+<?php
+ if (!extension_loaded("pcntl")) print "skip";
+ elseif (!function_exists("pcntl_signal")) print "skip pcntl_signal() not available";
+ elseif (!function_exists("posix_kill")) print "skip posix_kill() not available";
+ elseif (!function_exists("posix_getpid")) print "skip posix_getpid() not available";
+?>
+--FILE--
+<?php
+pcntl_async_signals(1);
+
+pcntl_signal(SIGTERM, function ($signo) { echo "Signal handler called!\n"; });
+
+echo "Start!\n";
+posix_kill(posix_getpid(), SIGTERM);
+$i = 0; // dummy
+echo "Done!\n";
+
+?>
+--EXPECTF--
+Start!
+Signal handler called!
+Done!
diff --git a/ext/pcntl/tests/bug73783.phpt b/ext/pcntl/tests/bug73783.phpt
new file mode 100644
index 0000000000..beacdf6b8d
--- /dev/null
+++ b/ext/pcntl/tests/bug73783.phpt
@@ -0,0 +1,28 @@
+--TEST--
+Bug #73783: (SIG_IGN needs to be set to prevent syscals from returning early)
+--SKIPIF--
+<?php
+ if (!extension_loaded('pcntl')) die('skip pcntl extension not available');
+ elseif (!extension_loaded('posix')) die('skip posix extension not available');
+?>
+--FILE--
+<?php
+pcntl_signal(SIGCHLD, SIG_IGN);
+
+switch(pcntl_fork()) {
+ case 0:
+ exit;
+ break;
+}
+
+$before = microtime(true);
+sleep(1);
+
+if (microtime(true) - $before >= 0.8) {
+ echo "working\n";
+} else {
+ echo "failed\n";
+}
+?>
+--EXPECTF--
+working
diff --git a/ext/pcntl/tests/pcntl_signal.phpt b/ext/pcntl/tests/pcntl_signal.phpt
index 2db01305b5..a6441935c1 100644
--- a/ext/pcntl/tests/pcntl_signal.phpt
+++ b/ext/pcntl/tests/pcntl_signal.phpt
@@ -11,6 +11,12 @@ pcntl_signal(SIGTERM, function($signo){
posix_kill(posix_getpid(), SIGTERM);
pcntl_signal_dispatch();
+pcntl_signal(SIGUSR1, function($signo, $siginfo){
+ printf("got signal from %s\n", $siginfo['pid'] ?? 'nobody');
+});
+posix_kill(posix_getpid(), SIGUSR1);
+pcntl_signal_dispatch();
+
var_dump(pcntl_signal());
var_dump(pcntl_signal(SIGALRM, SIG_IGN));
var_dump(pcntl_signal(-1, -1));
@@ -24,6 +30,7 @@ echo "ok\n";
?>
--EXPECTF--
signal dispatched
+got signal from %r\d+|nobody%r
Warning: pcntl_signal() expects at least 2 parameters, 0 given in %s
NULL
diff --git a/ext/pcntl/tests/pcntl_signal_get_handler.phpt b/ext/pcntl/tests/pcntl_signal_get_handler.phpt
new file mode 100644
index 0000000000..48f911e5e3
--- /dev/null
+++ b/ext/pcntl/tests/pcntl_signal_get_handler.phpt
@@ -0,0 +1,30 @@
+--TEST--
+pcntl_signal_get_handler()
+--SKIPIF--
+<?php if (!extension_loaded("pcntl")) print "skip"; ?>
+<?php if (!extension_loaded("posix")) die("skip posix extension not available"); ?>
+--FILE--
+<?php
+var_dump(pcntl_signal_get_handler(SIGUSR1));
+
+function pcntl_test($signo) {}
+pcntl_signal(SIGUSR1, 'pcntl_test');
+var_dump(pcntl_signal_get_handler(SIGUSR1));
+
+pcntl_signal(SIGUSR1, SIG_DFL);
+var_dump(pcntl_signal_get_handler(SIGUSR1));
+
+pcntl_signal(SIGUSR1, SIG_IGN);
+var_dump(pcntl_signal_get_handler(SIGUSR1));
+
+posix_kill(posix_getpid(), SIGUSR1);
+pcntl_signal_dispatch();
+
+echo "ok\n";
+?>
+--EXPECTF--
+int(0)
+string(10) "pcntl_test"
+int(0)
+int(1)
+ok
diff --git a/ext/pcre/php_pcre.c b/ext/pcre/php_pcre.c
index 67c373f70a..2a28b9c3b3 100644
--- a/ext/pcre/php_pcre.c
+++ b/ext/pcre/php_pcre.c
@@ -1063,7 +1063,7 @@ static int preg_get_backref(char **str, int *backref)
}
if (in_brace) {
- if (*walk == 0 || *walk != '}')
+ if (*walk != '}')
return 0;
else
walk++;
@@ -1620,7 +1620,7 @@ static PHP_FUNCTION(preg_replace_callback)
if (!zend_is_callable(replace, 0, &callback_name)) {
php_error_docref(NULL, E_WARNING, "Requires argument 2, '%s', to be a valid callback", ZSTR_VAL(callback_name));
zend_string_release(callback_name);
- ZVAL_COPY(return_value, subject);
+ ZVAL_STR(return_value, zval_get_string(subject));
return;
}
zend_string_release(callback_name);
diff --git a/ext/pcre/tests/preg_replace_callback3.phpt b/ext/pcre/tests/preg_replace_callback3.phpt
index 6484c074fe..fc3218b9e7 100644
--- a/ext/pcre/tests/preg_replace_callback3.phpt
+++ b/ext/pcre/tests/preg_replace_callback3.phpt
@@ -28,13 +28,13 @@ Warning: preg_replace_callback() expects at least 3 parameters, 2 given in %s on
NULL
Warning: preg_replace_callback(): Requires argument 2, '2', to be a valid callback in %s on line %d
-int(3)
+string(1) "3"
Warning: preg_replace_callback(): Requires argument 2, '2', to be a valid callback in %s on line %d
-int(3)
+string(1) "3"
Warning: preg_replace_callback(): Requires argument 2, '2', to be a valid callback in %s on line %d
-int(3)
+string(1) "3"
Warning: preg_replace_callback() expects parameter 4 to be integer, string given in %s on line %d
NULL
diff --git a/ext/pcre/tests/preg_replace_error1.phpt b/ext/pcre/tests/preg_replace_error1.phpt
index ec573c77bf..04eef25e3d 100644
--- a/ext/pcre/tests/preg_replace_error1.phpt
+++ b/ext/pcre/tests/preg_replace_error1.phpt
@@ -55,5 +55,5 @@ string(1) "a"
Arg value is /[a-zA-Z]/
string(1) "1"
-Catchable fatal error: Object of class stdClass could not be converted to string in %spreg_replace_error1.php on line %d
+Recoverable fatal error: Object of class stdClass could not be converted to string in %spreg_replace_error1.php on line %d
diff --git a/ext/pcre/tests/preg_replace_error2.phpt b/ext/pcre/tests/preg_replace_error2.phpt
index bf5d3e072c..79f4c60d69 100644
--- a/ext/pcre/tests/preg_replace_error2.phpt
+++ b/ext/pcre/tests/preg_replace_error2.phpt
@@ -33,5 +33,5 @@ Arg value is: Array
Warning: preg_replace(): Parameter mismatch, pattern is a string while replacement is an array in %spreg_replace_error2.php on line %d
bool(false)
-Catchable fatal error: Object of class stdClass could not be converted to string in %spreg_replace_error2.php on line %d
+Recoverable fatal error: Object of class stdClass could not be converted to string in %spreg_replace_error2.php on line %d
diff --git a/ext/pdo/pdo.c b/ext/pdo/pdo.c
index 6ff2f3485e..b89c951755 100644
--- a/ext/pdo/pdo.c
+++ b/ext/pdo/pdo.c
@@ -32,8 +32,7 @@
#include "php_pdo_driver.h"
#include "php_pdo_int.h"
#include "zend_exceptions.h"
-
-static zend_class_entry *spl_ce_RuntimeException;
+#include "ext/spl/spl_exceptions.h"
zend_class_entry *pdo_dbh_ce, *pdo_dbstmt_ce, *pdo_row_ce;
@@ -79,20 +78,9 @@ PDO_API char *php_pdo_str_tolower_dup(const char *src, int len) /* {{{ */
PDO_API zend_class_entry *php_pdo_get_exception_base(int root) /* {{{ */
{
-#if defined(HAVE_SPL)
if (!root) {
- if (!spl_ce_RuntimeException) {
- zend_class_entry *pce;
-
- if ((pce = zend_hash_str_find_ptr(CG(class_table), "runtimeexception", sizeof("RuntimeException") - 1))) {
- spl_ce_RuntimeException = pce;
- return pce;
- }
- } else {
- return spl_ce_RuntimeException;
- }
+ return spl_ce_RuntimeException;
}
-#endif
return zend_ce_exception;
}
/* }}} */
@@ -128,14 +116,10 @@ const zend_function_entry pdo_functions[] = {
/* }}} */
/* {{{ pdo_functions[] */
-#if ZEND_MODULE_API_NO >= 20050922
static const zend_module_dep pdo_deps[] = {
-#ifdef HAVE_SPL
ZEND_MOD_REQUIRED("spl")
-#endif
ZEND_MOD_END
};
-#endif
/* }}} */
/* {{{ pdo_module_entry */
@@ -175,7 +159,7 @@ static PHP_GINIT_FUNCTION(pdo)
PDO_API int php_pdo_register_driver(pdo_driver_t *driver) /* {{{ */
{
if (driver->api_version != PDO_DRIVER_API) {
- zend_error(E_ERROR, "PDO: driver %s requires PDO API version %pd; this is PDO version %d",
+ zend_error(E_ERROR, "PDO: driver %s requires PDO API version " ZEND_ULONG_FMT "; this is PDO version %d",
driver->driver_name, driver->api_version, PDO_DRIVER_API);
return FAILURE;
}
@@ -206,7 +190,8 @@ pdo_driver_t *pdo_find_driver(const char *name, int namelen) /* {{{ */
PDO_API int php_pdo_parse_data_source(const char *data_source, zend_ulong data_source_len, struct pdo_data_src_parser *parsed, int nparams) /* {{{ */
{
- int i, j;
+ zend_ulong i;
+ int j;
int valstart = -1;
int semi = -1;
int optstart = 0;
@@ -351,8 +336,6 @@ PHP_MINIT_FUNCTION(pdo)
{
zend_class_entry ce;
- spl_ce_RuntimeException = NULL;
-
if (FAILURE == pdo_sqlstate_init_error_table()) {
return FAILURE;
}
diff --git a/ext/pdo/pdo_dbh.c b/ext/pdo/pdo_dbh.c
index 77b79765d4..792395bbf2 100644
--- a/ext/pdo/pdo_dbh.c
+++ b/ext/pdo/pdo_dbh.c
@@ -141,7 +141,7 @@ PDO_API void pdo_handle_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt) /* {{{ */
}
if (supp) {
- message = strpprintf(0, "SQLSTATE[%s]: %s: %ld %s", *pdo_err, msg, native_code, supp);
+ message = strpprintf(0, "SQLSTATE[%s]: %s: " ZEND_LONG_FMT " %s", *pdo_err, msg, native_code, supp);
} else {
message = strpprintf(0, "SQLSTATE[%s]: %s", *pdo_err, msg);
}
@@ -438,10 +438,8 @@ static void pdo_stmt_construct(zend_execute_data *execute_data, pdo_stmt_t *stmt
zval retval;
fci.size = sizeof(zend_fcall_info);
- fci.function_table = &dbstmt_ce->function_table;
ZVAL_UNDEF(&fci.function_name);
fci.object = Z_OBJ_P(object);
- fci.symbol_table = NULL;
fci.retval = &retval;
fci.param_count = 0;
fci.params = NULL;
@@ -451,7 +449,7 @@ static void pdo_stmt_construct(zend_execute_data *execute_data, pdo_stmt_t *stmt
fcc.initialized = 1;
fcc.function_handler = dbstmt_ce->constructor;
- fcc.calling_scope = EG(scope);
+ fcc.calling_scope = zend_get_executed_scope();
fcc.called_scope = Z_OBJCE_P(object);
fcc.object = Z_OBJ_P(object);
@@ -1250,7 +1248,7 @@ const zend_function_entry pdo_dbh_functions[] = /* {{{ */ {
PHP_ME(PDO, __wakeup, arginfo_pdo__void, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
PHP_ME(PDO, __sleep, arginfo_pdo__void, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
PHP_ME(PDO, getAvailableDrivers, arginfo_pdo__void, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
- {NULL, NULL, NULL}
+ PHP_FE_END
};
/* }}} */
@@ -1313,7 +1311,7 @@ int pdo_hash_methods(pdo_dbh_object_t *dbh_obj, int kind)
func.arg_info = (zend_internal_arg_info*)funcs->arg_info + 1;
func.num_args = funcs->num_args;
- if (info->required_num_args == -1) {
+ if (info->required_num_args == (uint32_t)-1) {
func.required_num_args = funcs->num_args;
} else {
func.required_num_args = info->required_num_args;
@@ -1434,9 +1432,7 @@ void pdo_dbh_init(void)
REGISTER_PDO_CLASS_CONST_LONG("FETCH_KEY_PAIR", (zend_long)PDO_FETCH_KEY_PAIR);
REGISTER_PDO_CLASS_CONST_LONG("FETCH_CLASSTYPE", (zend_long)PDO_FETCH_CLASSTYPE);
-#if PHP_VERSION_ID >= 50100
REGISTER_PDO_CLASS_CONST_LONG("FETCH_SERIALIZE",(zend_long)PDO_FETCH_SERIALIZE);
-#endif
REGISTER_PDO_CLASS_CONST_LONG("FETCH_PROPS_LATE", (zend_long)PDO_FETCH_PROPS_LATE);
REGISTER_PDO_CLASS_CONST_LONG("FETCH_NAMED", (zend_long)PDO_FETCH_NAMED);
diff --git a/ext/pdo/pdo_sql_parser.c b/ext/pdo/pdo_sql_parser.c
index 68d3a67f92..21847ccca1 100644
--- a/ext/pdo/pdo_sql_parser.c
+++ b/ext/pdo/pdo_sql_parser.c
@@ -400,7 +400,7 @@ PDO_API int pdo_parse_params(pdo_stmt_t *stmt, char *inquery, size_t inquery_len
Scanner s;
char *ptr, *newbuffer;
int t;
- int bindno = 0;
+ uint32_t bindno = 0;
int ret = 0;
size_t newbuffer_len;
HashTable *params;
diff --git a/ext/pdo/pdo_sql_parser.re b/ext/pdo/pdo_sql_parser.re
index ae1bad360d..8622e67003 100644
--- a/ext/pdo/pdo_sql_parser.re
+++ b/ext/pdo/pdo_sql_parser.re
@@ -86,7 +86,7 @@ PDO_API int pdo_parse_params(pdo_stmt_t *stmt, char *inquery, size_t inquery_len
Scanner s;
char *ptr, *newbuffer;
int t;
- int bindno = 0;
+ uint32_t bindno = 0;
int ret = 0;
size_t newbuffer_len;
HashTable *params;
diff --git a/ext/pdo/pdo_sqlstate.c b/ext/pdo/pdo_sqlstate.c
index 143eab3e9a..8c91335fdd 100644
--- a/ext/pdo/pdo_sqlstate.c
+++ b/ext/pdo/pdo_sqlstate.c
@@ -311,7 +311,7 @@ void pdo_sqlstate_fini_error_table(void)
int pdo_sqlstate_init_error_table(void)
{
- int i;
+ size_t i;
const struct pdo_sqlstate_info *info;
zend_hash_init(&err_hash, sizeof(err_initializer)/sizeof(err_initializer[0]), NULL, NULL, 1);
diff --git a/ext/pdo/pdo_stmt.c b/ext/pdo/pdo_stmt.c
index c2c65650c1..b953739a6a 100644
--- a/ext/pdo/pdo_stmt.c
+++ b/ext/pdo/pdo_stmt.c
@@ -113,12 +113,6 @@ ZEND_END_ARG_INFO()
RETURN_FALSE; \
} \
-static PHP_FUNCTION(dbrow_constructor) /* {{{ */
-{
- zend_throw_exception_ex(php_pdo_get_exception(), 0, "You may not create a PDORow manually");
-}
-/* }}} */
-
static inline int rewrite_name_to_position(pdo_stmt_t *stmt, struct pdo_bound_param_data *param) /* {{{ */
{
if (stmt->bound_param_map) {
@@ -275,7 +269,7 @@ static void param_dtor(zval *el) /* {{{ */
/* tell the driver that it is going away */
if (param->stmt->methods->param_hook) {
- param->stmt->methods->param_hook(param->stmt, param, PDO_PARAM_EVT_FREE);
+ param->stmt->methods->param_hook(param->stmt, param, PDO_PARAM_EVT_FREE);
}
if (param->name) {
@@ -551,7 +545,7 @@ static inline void fetch_value(pdo_stmt_t *stmt, zval *dest, int colno, int *typ
col = &stmt->columns[colno];
type = PDO_PARAM_TYPE(col->param_type);
- new_type = type_override ? PDO_PARAM_TYPE(*type_override) : type;
+ new_type = type_override ? (int)PDO_PARAM_TYPE(*type_override) : type;
value = NULL;
value_len = 0;
@@ -740,9 +734,7 @@ static int do_fetch_class_prepare(pdo_stmt_t *stmt) /* {{{ */
}
if (ce->constructor) {
- fci->function_table = &ce->function_table;
ZVAL_UNDEF(&fci->function_name);
- fci->symbol_table = NULL;
fci->retval = &stmt->fetch.cls.retval;
fci->param_count = 0;
fci->params = NULL;
@@ -752,7 +744,7 @@ static int do_fetch_class_prepare(pdo_stmt_t *stmt) /* {{{ */
fcc->initialized = 1;
fcc->function_handler = ce->constructor;
- fcc->calling_scope = EG(scope);
+ fcc->calling_scope = zend_get_executed_scope();
fcc->called_scope = ce;
return 1;
} else if (!Z_ISUNDEF(stmt->fetch.cls.ctor_args)) {
@@ -1539,10 +1531,11 @@ static PHP_METHOD(PDOStatement, fetchAll)
static int register_bound_param(INTERNAL_FUNCTION_PARAMETERS, pdo_stmt_t *stmt, int is_param) /* {{{ */
{
- struct pdo_bound_param_data param = {{{0}}};
+ struct pdo_bound_param_data param;
zend_long param_type = PDO_PARAM_STR;
zval *parameter, *driver_params = NULL;
+ memset(&param, 0, sizeof(param));
param.paramno = -1;
if (FAILURE == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(),
@@ -1582,11 +1575,12 @@ static int register_bound_param(INTERNAL_FUNCTION_PARAMETERS, pdo_stmt_t *stmt,
bind an input parameter to the value of a PHP variable. $paramno is the 1-based position of the placeholder in the SQL statement (but can be the parameter name for drivers that support named placeholders). It should be called prior to execute(). */
static PHP_METHOD(PDOStatement, bindValue)
{
- struct pdo_bound_param_data param = {{{0}}};
+ struct pdo_bound_param_data param;
zend_long param_type = PDO_PARAM_STR;
zval *parameter;
PHP_STMT_GET_OBJ;
+ memset(&param, 0, sizeof(param));
param.paramno = -1;
if (FAILURE == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(),
@@ -2126,7 +2120,7 @@ static PHP_METHOD(PDOStatement, debugDumpParams)
php_stream_printf(out, "Key: Name: [%zd] %.*s\n",
ZSTR_LEN(key), (int) ZSTR_LEN(key), ZSTR_VAL(key));
} else {
- php_stream_printf(out, "Key: Position #%pd:\n", num);
+ php_stream_printf(out, "Key: Position #" ZEND_ULONG_FMT ":\n", num);
}
php_stream_printf(out, "paramno=%pd\nname=[%zd] \"%.*s\"\nis_param=%d\nparam_type=%d\n",
@@ -2180,7 +2174,7 @@ const zend_function_entry pdo_dbstmt_functions[] = {
PHP_ME(PDOStatement, debugDumpParams, arginfo_pdostatement__void, ZEND_ACC_PUBLIC)
PHP_ME(PDOStatement, __wakeup, arginfo_pdostatement__void, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
PHP_ME(PDOStatement, __sleep, arginfo_pdostatement__void, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
- {NULL, NULL, NULL}
+ PHP_FE_END
};
/* {{{ overloaded handlers for PDOStatement class */
@@ -2452,6 +2446,7 @@ static zend_object_iterator_funcs pdo_stmt_iter_funcs = {
pdo_stmt_iter_get_data,
pdo_stmt_iter_get_key,
pdo_stmt_iter_move_forwards,
+ NULL,
NULL
};
@@ -2484,7 +2479,7 @@ zend_object_iterator *pdo_stmt_iter_get(zend_class_entry *ce, zval *object, int
/* {{{ overloaded handlers for PDORow class (used by PDO_FETCH_LAZY) */
const zend_function_entry pdo_row_functions[] = {
- {NULL, NULL, NULL}
+ PHP_FE_END
};
static zval *row_prop_read(zval *object, zval *member, int type, void **cache_slot, zval *rv)
@@ -2646,15 +2641,8 @@ static int row_call_method(zend_string *method, zend_object *object, INTERNAL_FU
static union _zend_function *row_get_ctor(zend_object *object)
{
- static zend_internal_function ctor = {0};
-
- ctor.type = ZEND_INTERNAL_FUNCTION;
- ctor.function_name = zend_string_init("__construct", sizeof("__construct") - 1, 0);
- ctor.scope = pdo_row_ce;
- ctor.handler = ZEND_FN(dbrow_constructor);
- ctor.fn_flags = ZEND_ACC_PUBLIC;
-
- return (union _zend_function*)&ctor;
+ zend_throw_exception_ex(php_pdo_get_exception(), 0, "You may not create a PDORow manually");
+ return NULL;
}
static zend_string *row_get_classname(const zend_object *object)
@@ -2690,6 +2678,11 @@ zend_object_handlers pdo_row_object_handlers = {
row_get_classname,
row_compare,
NULL, /* cast */
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
NULL
};
diff --git a/ext/pdo/tests/pdo_036.phpt b/ext/pdo/tests/pdo_036.phpt
index 61f0e49a1c..1bf369b33b 100644
--- a/ext/pdo/tests/pdo_036.phpt
+++ b/ext/pdo/tests/pdo_036.phpt
@@ -22,7 +22,6 @@ object(PDOStatement)#%d (1) {
Fatal error: Uncaught PDOException: You may not create a PDORow manually in %spdo_036.php:8
Stack trace:
-#0 [internal function]: PDORow->__construct()
-#1 %spdo_036.php(8): ReflectionClass->newInstance()
-#2 {main}
+#0 %spdo_036.php(8): ReflectionClass->newInstance()
+#1 {main}
thrown in %spdo_036.php on line 8
diff --git a/ext/pdo/tests/pdorow.phpt b/ext/pdo/tests/pdorow.phpt
index 7cd1198838..54850d3860 100644
--- a/ext/pdo/tests/pdorow.phpt
+++ b/ext/pdo/tests/pdorow.phpt
@@ -11,6 +11,5 @@ new PDORow;
--EXPECTF--
Fatal error: Uncaught PDOException: You may not create a PDORow manually in %spdorow.php:3
Stack trace:
-#0 %spdorow.php(3): PDORow->__construct()
-#1 {main}
+#0 {main}
thrown in %spdorow.php on line 3
diff --git a/ext/pdo_dblib/dblib_driver.c b/ext/pdo_dblib/dblib_driver.c
index c53ac89d26..5899d6f131 100644
--- a/ext/pdo_dblib/dblib_driver.c
+++ b/ext/pdo_dblib/dblib_driver.c
@@ -329,7 +329,7 @@ static int pdo_dblib_handle_factory(pdo_dbh_t *dbh, zval *driver_options)
,{"auto",0} /* Only works with FreeTDS. Other drivers will bork */
};
-
+
struct pdo_data_src_parser vars[] = {
{ "charset", NULL, 0 }
,{ "appname", "PHP " PDO_DBLIB_FLAVOUR, 0 }
@@ -341,7 +341,7 @@ static int pdo_dblib_handle_factory(pdo_dbh_t *dbh, zval *driver_options)
nvars = sizeof(vars)/sizeof(vars[0]);
nvers = sizeof(tdsver)/sizeof(tdsver[0]);
-
+
php_pdo_parse_data_source(dbh->data_source, dbh->data_source_len, vars, nvars);
H = pecalloc(1, sizeof(*H), dbh->is_persistent);
@@ -482,4 +482,3 @@ pdo_driver_t pdo_dblib_driver = {
#endif
pdo_dblib_handle_factory
};
-
diff --git a/ext/pdo_dblib/dblib_stmt.c b/ext/pdo_dblib/dblib_stmt.c
index 69d1a1af37..8595eabd1f 100644
--- a/ext/pdo_dblib/dblib_stmt.c
+++ b/ext/pdo_dblib/dblib_stmt.c
@@ -437,12 +437,6 @@ static int pdo_dblib_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr,
return 1;
}
-static int pdo_dblib_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param,
- enum pdo_param_event event_type)
-{
- return 1;
-}
-
static int pdo_dblib_stmt_get_column_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value)
{
pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
@@ -492,7 +486,7 @@ struct pdo_stmt_methods dblib_stmt_methods = {
pdo_dblib_stmt_fetch,
pdo_dblib_stmt_describe,
pdo_dblib_stmt_get_col,
- pdo_dblib_stmt_param_hook,
+ NULL, /* param hook */
NULL, /* set attr */
NULL, /* get attr */
pdo_dblib_stmt_get_column_meta, /* meta */
diff --git a/ext/pdo_dblib/pdo_dblib.c b/ext/pdo_dblib/pdo_dblib.c
index 7b5161c138..270a3c36d1 100644
--- a/ext/pdo_dblib/pdo_dblib.c
+++ b/ext/pdo_dblib/pdo_dblib.c
@@ -39,24 +39,18 @@ const zend_function_entry pdo_dblib_functions[] = {
PHP_FE_END
};
-#if ZEND_MODULE_API_NO >= 20050922
static const zend_module_dep pdo_dblib_deps[] = {
ZEND_MOD_REQUIRED("pdo")
ZEND_MOD_END
};
-#endif
#if PDO_DBLIB_IS_MSSQL
zend_module_entry pdo_mssql_module_entry = {
#else
zend_module_entry pdo_dblib_module_entry = {
#endif
-#if ZEND_MODULE_API_NO >= 20050922
STANDARD_MODULE_HEADER_EX, NULL,
pdo_dblib_deps,
-#else
- STANDARD_MODULE_HEADER,
-#endif
#if PDO_DBLIB_IS_MSSQL
"pdo_mssql",
#elif defined(PHP_WIN32)
@@ -239,4 +233,3 @@ PHP_MINFO_FUNCTION(pdo_dblib)
php_info_print_table_row(2, "Flavour", PDO_DBLIB_FLAVOUR);
php_info_print_table_end();
}
-
diff --git a/ext/pdo_dblib/php_pdo_dblib.h b/ext/pdo_dblib/php_pdo_dblib.h
index e2371465a5..ae7555d171 100644
--- a/ext/pdo_dblib/php_pdo_dblib.h
+++ b/ext/pdo_dblib/php_pdo_dblib.h
@@ -43,4 +43,3 @@ PHP_MINFO_FUNCTION(pdo_dblib);
PHP_RSHUTDOWN_FUNCTION(pdo_dblib);
#endif
-
diff --git a/ext/pdo_dblib/php_pdo_dblib_int.h b/ext/pdo_dblib/php_pdo_dblib_int.h
index 94ae3a0603..62cb289f6f 100644
--- a/ext/pdo_dblib/php_pdo_dblib_int.h
+++ b/ext/pdo_dblib/php_pdo_dblib_int.h
@@ -150,4 +150,3 @@ enum {
};
#endif
-
diff --git a/ext/pdo_firebird/config.m4 b/ext/pdo_firebird/config.m4
index f9188a09b7..e6362cd7a0 100644
--- a/ext/pdo_firebird/config.m4
+++ b/ext/pdo_firebird/config.m4
@@ -8,43 +8,56 @@ if test "$PHP_PDO_FIREBIRD" != "no"; then
AC_MSG_ERROR([PDO is not enabled! Add --enable-pdo to your configure line.])
fi
- if test "$PHP_PDO_FIREBIRD" = "yes"; then
- FIREBIRD_INCDIR=
- FIREBIRD_LIBDIR=
- FIREBIRD_LIBDIR_FLAG=
+ AC_PATH_PROG(FB_CONFIG, fb_config, no)
+
+ if test -x "$FB_CONFIG" && test "$PHP_PDO_FIREBIRD" = "yes"; then
+ AC_MSG_CHECKING(for libfbconfig)
+ FB_CFLAGS=`$FB_CONFIG --cflags`
+ FB_LIBDIR=`$FB_CONFIG --libs`
+ FB_VERSION=`$FB_CONFIG --version`
+ AC_MSG_RESULT(version $FB_VERSION)
+ PHP_EVAL_LIBLINE($FB_LIBDIR, PDO_FIREBIRD_SHARED_LIBADD)
+ PHP_EVAL_INCLINE($FB_CFLAGS)
+
else
- FIREBIRD_INCDIR=$PHP_PDO_FIREBIRD/include
- FIREBIRD_LIBDIR=$PHP_PDO_FIREBIRD/$PHP_LIBDIR
- FIREBIRD_LIBDIR_FLAG=-L$FIREBIRD_LIBDIR
- fi
+ if test "$PHP_PDO_FIREBIRD" = "yes"; then
+ FIREBIRD_INCDIR=
+ FIREBIRD_LIBDIR=
+ FIREBIRD_LIBDIR_FLAG=
+ else
+ FIREBIRD_INCDIR=$PHP_PDO_FIREBIRD/include
+ FIREBIRD_LIBDIR=$PHP_PDO_FIREBIRD/$PHP_LIBDIR
+ FIREBIRD_LIBDIR_FLAG=-L$FIREBIRD_LIBDIR
+ fi
- PHP_CHECK_LIBRARY(fbclient, isc_detach_database,
- [
- FIREBIRD_LIBNAME=fbclient
- ], [
- PHP_CHECK_LIBRARY(gds, isc_detach_database,
+ PHP_CHECK_LIBRARY(fbclient, isc_detach_database,
[
- FIREBIRD_LIBNAME=gds
+ FIREBIRD_LIBNAME=fbclient
], [
- PHP_CHECK_LIBRARY(ib_util, isc_detach_database,
+ PHP_CHECK_LIBRARY(gds, isc_detach_database,
[
- FIREBIRD_LIBNAME=ib_util
+ FIREBIRD_LIBNAME=gds
], [
- AC_MSG_ERROR([libfbclient, libgds or libib_util not found! Check config.log for more information.])
+ PHP_CHECK_LIBRARY(ib_util, isc_detach_database,
+ [
+ FIREBIRD_LIBNAME=ib_util
+ ], [
+ AC_MSG_ERROR([libfbclient, libgds or libib_util not found! Check config.log for more information.])
+ ], [
+ $FIREBIRD_LIBDIR_FLAG
+ ])
], [
$FIREBIRD_LIBDIR_FLAG
])
], [
$FIREBIRD_LIBDIR_FLAG
])
- ], [
- $FIREBIRD_LIBDIR_FLAG
- ])
+ PHP_ADD_LIBRARY_WITH_PATH($FIREBIRD_LIBNAME, $FIREBIRD_LIBDIR, PDO_FIREBIRD_SHARED_LIBADD)
+ PHP_ADD_INCLUDE($FIREBIRD_INCDIR)
+ fi
PHP_CHECK_PDO_INCLUDES
- PHP_ADD_LIBRARY_WITH_PATH($FIREBIRD_LIBNAME, $FIREBIRD_LIBDIR, PDO_FIREBIRD_SHARED_LIBADD)
- PHP_ADD_INCLUDE($FIREBIRD_INCDIR)
AC_DEFINE(HAVE_PDO_FIREBIRD,1,[ ])
PHP_NEW_EXTENSION(pdo_firebird, pdo_firebird.c firebird_driver.c firebird_statement.c, $ext_shared,,-I$pdo_cv_inc_path)
PHP_SUBST(PDO_FIREBIRD_SHARED_LIBADD)
diff --git a/ext/pdo_firebird/tests/testdb.inc b/ext/pdo_firebird/tests/testdb.inc
index f6951a7b13..cbf2023094 100644
--- a/ext/pdo_firebird/tests/testdb.inc
+++ b/ext/pdo_firebird/tests/testdb.inc
@@ -6,7 +6,7 @@ ini_set('ibase.default_user',$user);
ini_set('ibase.default_password',$password);
/* we need just the generated name, not the file itself */
-unlink($test_base = tempnam('/tmp',"php_ibase_test"));
+unlink($test_base = tempnam(sys_get_temp_dir(),"php_ibase_test"));
function init_db()
{
diff --git a/ext/pdo_mysql/mysql_driver.c b/ext/pdo_mysql/mysql_driver.c
index d5052479ef..deab508dc8 100644
--- a/ext/pdo_mysql/mysql_driver.c
+++ b/ext/pdo_mysql/mysql_driver.c
@@ -523,7 +523,10 @@ static struct pdo_dbh_methods mysql_methods = {
pdo_mysql_last_insert_id,
pdo_mysql_fetch_error_func,
pdo_mysql_get_attribute,
- pdo_mysql_check_liveness
+ pdo_mysql_check_liveness,
+ NULL,
+ NULL,
+ NULL
};
/* }}} */
@@ -537,7 +540,8 @@ static struct pdo_dbh_methods mysql_methods = {
static int pdo_mysql_handle_factory(pdo_dbh_t *dbh, zval *driver_options)
{
pdo_mysql_db_handle *H;
- int i, ret = 0;
+ size_t i;
+ int ret = 0;
char *host = NULL, *unix_socket = NULL;
unsigned int port = 3306;
char *dbname;
@@ -630,12 +634,7 @@ static int pdo_mysql_handle_factory(pdo_dbh_t *dbh, zval *driver_options)
}
#ifndef PDO_USE_MYSQLND
-#if PHP_API_VERSION < 20100412
- if ((PG(open_basedir) && PG(open_basedir)[0] != '\0') || PG(safe_mode))
-#else
- if (PG(open_basedir) && PG(open_basedir)[0] != '\0')
-#endif
- {
+ if (PG(open_basedir) && PG(open_basedir)[0] != '\0') {
local_infile = 0;
}
#endif
diff --git a/ext/pdo_mysql/pdo_mysql.c b/ext/pdo_mysql/pdo_mysql.c
index 99fb6d3b77..92279a56c8 100644
--- a/ext/pdo_mysql/pdo_mysql.c
+++ b/ext/pdo_mysql/pdo_mysql.c
@@ -232,7 +232,6 @@ const zend_function_entry pdo_mysql_functions[] = {
/* }}} */
/* {{{ pdo_mysql_deps[] */
-#if ZEND_MODULE_API_NO >= 20050922
static const zend_module_dep pdo_mysql_deps[] = {
ZEND_MOD_REQUIRED("pdo")
#ifdef PDO_USE_MYSQLND
@@ -240,7 +239,6 @@ static const zend_module_dep pdo_mysql_deps[] = {
#endif
ZEND_MOD_END
};
-#endif
/* }}} */
/* {{{ pdo_mysql_module_entry */
diff --git a/ext/pdo_mysql/php_pdo_mysql_int.h b/ext/pdo_mysql/php_pdo_mysql_int.h
index 89884120ab..fb437de348 100644
--- a/ext/pdo_mysql/php_pdo_mysql_int.h
+++ b/ext/pdo_mysql/php_pdo_mysql_int.h
@@ -126,9 +126,9 @@ typedef struct {
const MYSQL_FIELD *fields;
MYSQL_ROW current_data;
#if PDO_USE_MYSQLND
- zend_ulong *current_lengths;
+ const size_t *current_lengths;
#else
- zend_long *current_lengths;
+ zend_long *current_lengths;
#endif
pdo_mysql_error_info einfo;
#if PDO_USE_MYSQLND
@@ -144,7 +144,7 @@ typedef struct {
#endif
PDO_MYSQL_PARAM_BIND *bound_result;
my_bool *out_null;
- zend_ulong *out_length;
+ zend_ulong *out_length;
unsigned int params_given;
unsigned max_length:1;
} pdo_mysql_stmt;
diff --git a/ext/pdo_oci/pdo_oci.c b/ext/pdo_oci/pdo_oci.c
index 37e930eb1c..4e11ca828b 100644
--- a/ext/pdo_oci/pdo_oci.c
+++ b/ext/pdo_oci/pdo_oci.c
@@ -38,12 +38,10 @@ const zend_function_entry pdo_oci_functions[] = {
/* {{{ pdo_oci_module_entry */
-#if ZEND_MODULE_API_NO >= 20050922
static const zend_module_dep pdo_oci_deps[] = {
ZEND_MOD_REQUIRED("pdo")
ZEND_MOD_END
};
-#endif
zend_module_entry pdo_oci_module_entry = {
STANDARD_MODULE_HEADER_EX, NULL,
diff --git a/ext/pdo_oci/tests/pdo_oci_stream_2a.phpt b/ext/pdo_oci/tests/pdo_oci_stream_2a.phpt
index a7f22d5c41..72d2ed93b1 100644
--- a/ext/pdo_oci/tests/pdo_oci_stream_2a.phpt
+++ b/ext/pdo_oci/tests/pdo_oci_stream_2a.phpt
@@ -53,16 +53,16 @@ $a10 = str_repeat('j', 4095);
printf("Inserting 10000 Records ... ");
for($i=0; $i<1000; $i++) {
- do_insert($db, 1, $a1, $a10);
- do_insert($db, 1, $a2, $a9);
- do_insert($db, 1, $a3, $a8);
- do_insert($db, 1, $a4, $a7);
- do_insert($db, 1, $a5, $a6);
- do_insert($db, 1, $a6, $a5);
- do_insert($db, 1, $a7, $a4);
- do_insert($db, 1, $a8, $a3);
- do_insert($db, 1, $a9, $a2);
- do_insert($db, 1, $a10, $a1);
+ do_insert($db, $i * 10 + 1, $a1, $a10);
+ do_insert($db, $i * 10 + 2, $a2, $a9);
+ do_insert($db, $i * 10 + 3, $a3, $a8);
+ do_insert($db, $i * 10 + 4, $a4, $a7);
+ do_insert($db, $i * 10 + 5, $a5, $a6);
+ do_insert($db, $i * 10 + 6, $a6, $a5);
+ do_insert($db, $i * 10 + 7, $a7, $a4);
+ do_insert($db, $i * 10 + 8, $a8, $a3);
+ do_insert($db, $i * 10 + 9, $a9, $a2);
+ do_insert($db, $i * 10 + 10, $a10, $a1);
}
printf("Done\n");
diff --git a/ext/pdo_odbc/pdo_odbc.c b/ext/pdo_odbc/pdo_odbc.c
index 287dbbcfc4..8af2e5e7b3 100644
--- a/ext/pdo_odbc/pdo_odbc.c
+++ b/ext/pdo_odbc/pdo_odbc.c
@@ -37,12 +37,10 @@ const zend_function_entry pdo_odbc_functions[] = {
/* }}} */
/* {{{ pdo_odbc_deps[] */
-#if ZEND_MODULE_API_NO >= 20050922
static const zend_module_dep pdo_odbc_deps[] = {
ZEND_MOD_REQUIRED("pdo")
ZEND_MOD_END
};
-#endif
/* }}} */
/* {{{ pdo_odbc_module_entry */
@@ -123,7 +121,7 @@ PHP_MINIT_FUNCTION(pdo_odbc)
} else if (*pooling_val == '\0' || strcasecmp(pooling_val, "off") == 0) {
pdo_odbc_pool_on = SQL_CP_OFF;
} else {
- php_error_docref(NULL, E_ERROR, "Error in pdo_odbc.connection_pooling configuration. Value MUST be one of 'strict', 'relaxed' or 'off'");
+ php_error_docref(NULL, E_CORE_ERROR, "Error in pdo_odbc.connection_pooling configuration. Value MUST be one of 'strict', 'relaxed' or 'off'");
return FAILURE;
}
diff --git a/ext/pdo_odbc/php_pdo_odbc.h b/ext/pdo_odbc/php_pdo_odbc.h
index 6699e7f5b5..e2b3187e01 100644
--- a/ext/pdo_odbc/php_pdo_odbc.h
+++ b/ext/pdo_odbc/php_pdo_odbc.h
@@ -47,16 +47,6 @@ ZEND_BEGIN_MODULE_GLOBALS(pdo_odbc)
ZEND_END_MODULE_GLOBALS(pdo_odbc)
*/
-/* In every utility function you add that needs to use variables
- in php_pdo_odbc_globals, call TSRMLS_FETCH(); after declaring other
- variables used by that function, or better yet, pass in
- after the last function argument and declare your utility function
- with after the last declared argument. Always refer to
- the globals in your function as PDO_ODBC_G(variable). You are
- encouraged to rename these macros something shorter, see
- examples in any other php module directory.
-*/
-
#ifdef ZTS
#define PDO_ODBC_G(v) TSRMG(pdo_odbc_globals_id, zend_pdo_odbc_globals *, v)
#else
diff --git a/ext/pdo_pgsql/pdo_pgsql.c b/ext/pdo_pgsql/pdo_pgsql.c
index cffaf82eab..93712a6af6 100644
--- a/ext/pdo_pgsql/pdo_pgsql.c
+++ b/ext/pdo_pgsql/pdo_pgsql.c
@@ -41,18 +41,16 @@
/* {{{ pdo_pgsql_functions[] */
const zend_function_entry pdo_pgsql_functions[] = {
- {NULL, NULL, NULL}
+ PHP_FE_END
};
/* }}} */
/* {{{ pdo_sqlite_deps
*/
-#if ZEND_MODULE_API_NO >= 20050922
static const zend_module_dep pdo_pgsql_deps[] = {
ZEND_MOD_REQUIRED("pdo")
ZEND_MOD_END
};
-#endif
/* }}} */
/* {{{ pdo_pgsql_module_entry */
diff --git a/ext/pdo_pgsql/pgsql_driver.c b/ext/pdo_pgsql/pgsql_driver.c
index b04cadeda9..52a9b8f285 100644
--- a/ext/pdo_pgsql/pgsql_driver.c
+++ b/ext/pdo_pgsql/pgsql_driver.c
@@ -44,8 +44,6 @@
#include "php_pdo_pgsql_int.h"
#include "zend_exceptions.h"
-static int pgsql_handle_in_transaction(pdo_dbh_t *dbh);
-
static char * _pdo_pgsql_trim_message(const char *message, int persistent)
{
register int i = strlen(message)-1;
@@ -365,15 +363,8 @@ static char *pdo_pgsql_last_insert_id(pdo_dbh_t *dbh, const char *name, size_t *
char *id = NULL;
PGresult *res;
ExecStatusType status;
- zend_bool savepoint = 0;
if (name == NULL) {
- savepoint = pgsql_handle_in_transaction(dbh);
-
- if (savepoint) {
- /* The savepoint is overwritten every time. */
- (void)PQexec(H->server, "SAVEPOINT _php_lastid_savepoint");
- }
res = PQexec(H->server, "SELECT LASTVAL()");
} else {
const char *q[1];
@@ -387,16 +378,9 @@ static char *pdo_pgsql_last_insert_id(pdo_dbh_t *dbh, const char *name, size_t *
id = estrdup((char *)PQgetvalue(res, 0, 0));
*len = PQgetlength(res, 0, 0);
} else {
- if (savepoint) {
- (void)PQexec(H->server, "ROLLBACK TO SAVEPOINT _php_lastid_savepoint");
- }
pdo_pgsql_error(dbh, status, pdo_pgsql_sqlstate(res));
}
- if (savepoint) {
- (void)PQexec(H->server, "RELEASE SAVEPOINT _php_lastid_savepoint");
- }
-
if (res) {
PQclear(res);
}
@@ -828,7 +812,7 @@ static PHP_METHOD(PDO, pgsqlCopyToFile)
if (ret == -1) {
break; /* done */
} else if (ret > 0) {
- if (php_stream_write(stream, csv, ret) != ret) {
+ if (php_stream_write(stream, csv, ret) != (size_t)ret) {
pdo_pgsql_error_msg(dbh, PGRES_FATAL_ERROR, "Unable to write to file");
PQfreemem(csv);
php_stream_close(stream);
@@ -1233,13 +1217,13 @@ static int pdo_pgsql_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{
/* support both full connection string & connection string + login and/or password */
if (tmp_user && tmp_pass) {
- spprintf(&conn_str, 0, "%s user='%s' password='%s' connect_timeout=%pd", (char *) dbh->data_source, ZSTR_VAL(tmp_user), ZSTR_VAL(tmp_pass), connect_timeout);
+ spprintf(&conn_str, 0, "%s user='%s' password='%s' connect_timeout=" ZEND_LONG_FMT, (char *) dbh->data_source, ZSTR_VAL(tmp_user), ZSTR_VAL(tmp_pass), connect_timeout);
} else if (tmp_user) {
- spprintf(&conn_str, 0, "%s user='%s' connect_timeout=%pd", (char *) dbh->data_source, ZSTR_VAL(tmp_user), connect_timeout);
+ spprintf(&conn_str, 0, "%s user='%s' connect_timeout=" ZEND_LONG_FMT, (char *) dbh->data_source, ZSTR_VAL(tmp_user), connect_timeout);
} else if (tmp_pass) {
- spprintf(&conn_str, 0, "%s password='%s' connect_timeout=%pd", (char *) dbh->data_source, ZSTR_VAL(tmp_pass), connect_timeout);
+ spprintf(&conn_str, 0, "%s password='%s' connect_timeout=" ZEND_LONG_FMT, (char *) dbh->data_source, ZSTR_VAL(tmp_pass), connect_timeout);
} else {
- spprintf(&conn_str, 0, "%s connect_timeout=%pd", (char *) dbh->data_source, connect_timeout);
+ spprintf(&conn_str, 0, "%s connect_timeout=" ZEND_LONG_FMT, (char *) dbh->data_source, connect_timeout);
}
H->server = PQconnectdb(conn_str);
diff --git a/ext/pdo_pgsql/pgsql_statement.c b/ext/pdo_pgsql/pgsql_statement.c
index 00b11d8fa3..783dab2244 100644
--- a/ext/pdo_pgsql/pgsql_statement.c
+++ b/ext/pdo_pgsql/pgsql_statement.c
@@ -428,8 +428,8 @@ static int pgsql_stmt_fetch(pdo_stmt_t *stmt,
case PDO_FETCH_ORI_PRIOR: spprintf(&ori_str, 0, "BACKWARD"); break;
case PDO_FETCH_ORI_FIRST: spprintf(&ori_str, 0, "FIRST"); break;
case PDO_FETCH_ORI_LAST: spprintf(&ori_str, 0, "LAST"); break;
- case PDO_FETCH_ORI_ABS: spprintf(&ori_str, 0, "ABSOLUTE %pd", offset); break;
- case PDO_FETCH_ORI_REL: spprintf(&ori_str, 0, "RELATIVE %pd", offset); break;
+ case PDO_FETCH_ORI_ABS: spprintf(&ori_str, 0, "ABSOLUTE " ZEND_LONG_FMT, offset); break;
+ case PDO_FETCH_ORI_REL: spprintf(&ori_str, 0, "RELATIVE " ZEND_LONG_FMT, offset); break;
default:
return 0;
}
diff --git a/ext/pdo_sqlite/pdo_sqlite.c b/ext/pdo_sqlite/pdo_sqlite.c
index f0cc63e384..d0eb1823d6 100644
--- a/ext/pdo_sqlite/pdo_sqlite.c
+++ b/ext/pdo_sqlite/pdo_sqlite.c
@@ -39,12 +39,10 @@ const zend_function_entry pdo_sqlite_functions[] = {
/* {{{ pdo_sqlite_deps
*/
-#if ZEND_MODULE_API_NO >= 20050922
static const zend_module_dep pdo_sqlite_deps[] = {
ZEND_MOD_REQUIRED("pdo")
ZEND_MOD_END
};
-#endif
/* }}} */
/* {{{ pdo_sqlite_module_entry
@@ -71,6 +69,10 @@ ZEND_GET_MODULE(pdo_sqlite)
/* {{{ PHP_MINIT_FUNCTION */
PHP_MINIT_FUNCTION(pdo_sqlite)
{
+#ifdef SQLITE_DETERMINISTIC
+ REGISTER_PDO_CLASS_CONST_LONG("SQLITE_DETERMINISTIC", (zend_long)SQLITE_DETERMINISTIC);
+#endif
+
return php_pdo_register_driver(&pdo_sqlite_driver);
}
/* }}} */
diff --git a/ext/pdo_sqlite/php_pdo_sqlite.h b/ext/pdo_sqlite/php_pdo_sqlite.h
index 0079b7d6f1..c6db9c9aa1 100644
--- a/ext/pdo_sqlite/php_pdo_sqlite.h
+++ b/ext/pdo_sqlite/php_pdo_sqlite.h
@@ -48,16 +48,6 @@ ZEND_BEGIN_MODULE_GLOBALS(pdo_sqlite)
ZEND_END_MODULE_GLOBALS(pdo_sqlite)
*/
-/* In every utility function you add that needs to use variables
- in php_pdo_sqlite_globals, call TSRMLS_FETCH(); after declaring other
- variables used by that function, or better yet, pass in
- after the last function argument and declare your utility function
- with after the last declared argument. Always refer to
- the globals in your function as PDO_SQLITE_G(variable). You are
- encouraged to rename these macros something shorter, see
- examples in any other php module directory.
-*/
-
#ifdef ZTS
#define PDO_SQLITE_G(v) TSRMG(pdo_sqlite_globals_id, zend_pdo_sqlite_globals *, v)
#else
diff --git a/ext/pdo_sqlite/sqlite_driver.c b/ext/pdo_sqlite/sqlite_driver.c
index 481b62de97..71bac8580a 100644
--- a/ext/pdo_sqlite/sqlite_driver.c
+++ b/ext/pdo_sqlite/sqlite_driver.c
@@ -325,9 +325,7 @@ static int do_callback(struct pdo_sqlite_fci *fc, zval *cb,
fake_argc = argc + is_agg;
fc->fci.size = sizeof(fc->fci);
- fc->fci.function_table = EG(function_table);
ZVAL_COPY_VALUE(&fc->fci.function_name, cb);
- fc->fci.symbol_table = NULL;
fc->fci.object = NULL;
fc->fci.retval = &retval;
fc->fci.param_count = fake_argc;
@@ -476,9 +474,7 @@ static int php_sqlite3_collation_callback(void *context,
struct pdo_sqlite_collation *collation = (struct pdo_sqlite_collation*) context;
collation->fc.fci.size = sizeof(collation->fc.fci);
- collation->fc.fci.function_table = EG(function_table);
ZVAL_COPY_VALUE(&collation->fc.fci.function_name, &collation->callback);
- collation->fc.fci.symbol_table = NULL;
collation->fc.fci.object = NULL;
collation->fc.fci.retval = &retval;
@@ -509,7 +505,7 @@ static int php_sqlite3_collation_callback(void *context,
return ret;
}
-/* {{{ bool SQLite::sqliteCreateFunction(string name, mixed callback [, int argcount])
+/* {{{ bool SQLite::sqliteCreateFunction(string name, mixed callback [, int argcount, int flags])
Registers a UDF with the sqlite db handle */
static PHP_METHOD(SQLite, sqliteCreateFunction)
{
@@ -518,13 +514,14 @@ static PHP_METHOD(SQLite, sqliteCreateFunction)
char *func_name;
size_t func_name_len;
zend_long argc = -1;
+ zend_long flags = 0;
zend_string *cbname = NULL;
pdo_dbh_t *dbh;
pdo_sqlite_db_handle *H;
int ret;
- if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "sz|l",
- &func_name, &func_name_len, &callback, &argc)) {
+ if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "sz|ll",
+ &func_name, &func_name_len, &callback, &argc, &flags)) {
RETURN_FALSE;
}
@@ -542,7 +539,7 @@ static PHP_METHOD(SQLite, sqliteCreateFunction)
func = (struct pdo_sqlite_func*)ecalloc(1, sizeof(*func));
- ret = sqlite3_create_function(H->db, func_name, argc, SQLITE_UTF8,
+ ret = sqlite3_create_function(H->db, func_name, argc, flags | SQLITE_UTF8,
func, php_sqlite3_func_callback, NULL, NULL);
if (ret == SQLITE_OK) {
func->funcname = estrdup(func_name);
@@ -731,7 +728,8 @@ static struct pdo_dbh_methods sqlite_methods = {
pdo_sqlite_get_attribute,
NULL, /* check_liveness: not needed */
get_driver_methods,
- pdo_sqlite_request_shutdown
+ pdo_sqlite_request_shutdown,
+ NULL
};
static char *make_filename_safe(const char *filename)
diff --git a/ext/pdo_sqlite/tests/pdo_sqlite_createfunction_with_flags.phpt b/ext/pdo_sqlite/tests/pdo_sqlite_createfunction_with_flags.phpt
new file mode 100644
index 0000000000..70ccdd0cd3
--- /dev/null
+++ b/ext/pdo_sqlite/tests/pdo_sqlite_createfunction_with_flags.phpt
@@ -0,0 +1,41 @@
+--TEST--
+PDO_sqlite: Testing sqliteCreateFunction() with flags
+--SKIPIF--
+<?php
+if (!extension_loaded('pdo_sqlite')) print 'skip not loaded';
+if (!defined('PDO::SQLITE_DETERMINISTIC')) die('skip system sqlite is too old');
+?>
+--FILE--
+<?php
+
+$db = new pdo('sqlite::memory:');
+
+$db->query('CREATE TABLE IF NOT EXISTS foobar (id INT AUTO INCREMENT, name TEXT)');
+
+$db->query('INSERT INTO foobar VALUES (NULL, "PHP")');
+$db->query('INSERT INTO foobar VALUES (NULL, "PHP6")');
+
+
+$db->sqliteCreateFunction('testing', function($v) { return strtolower($v); }, 1, PDO::SQLITE_DETERMINISTIC);
+
+
+foreach ($db->query('SELECT testing(name) FROM foobar') as $row) {
+ var_dump($row);
+}
+
+$db->query('DROP TABLE foobar');
+
+?>
+--EXPECTF--
+array(2) {
+ ["testing(name)"]=>
+ string(3) "php"
+ [0]=>
+ string(3) "php"
+}
+array(2) {
+ ["testing(name)"]=>
+ string(4) "php6"
+ [0]=>
+ string(4) "php6"
+}
diff --git a/ext/pgsql/pgsql.c b/ext/pgsql/pgsql.c
index 34710b12bb..0fd2e79ecc 100644
--- a/ext/pgsql/pgsql.c
+++ b/ext/pgsql/pgsql.c
@@ -56,9 +56,13 @@
#define InvalidOid ((Oid) 0)
#endif
-#define PGSQL_ASSOC 1<<0
-#define PGSQL_NUM 1<<1
-#define PGSQL_BOTH (PGSQL_ASSOC|PGSQL_NUM)
+#define PGSQL_ASSOC 1<<0
+#define PGSQL_NUM 1<<1
+#define PGSQL_BOTH (PGSQL_ASSOC|PGSQL_NUM)
+
+#define PGSQL_NOTICE_LAST 1 /* Get the last notice */
+#define PGSQL_NOTICE_ALL 2 /* Get all notices */
+#define PGSQL_NOTICE_CLEAR 3 /* Remove notices */
#define PGSQL_STATUS_LONG 1
#define PGSQL_STATUS_STRING 2
@@ -208,6 +212,7 @@ ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_last_notice, 0, 0, 1)
ZEND_ARG_INFO(0, connection)
+ ZEND_ARG_INFO(0, option)
ZEND_END_ARG_INFO()
#ifdef HAVE_PQFTABLE
@@ -276,6 +281,7 @@ ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_fetch_all, 0, 0, 1)
ZEND_ARG_INFO(0, result)
+ ZEND_ARG_INFO(0, result_type)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_fetch_all_columns, 0, 0, 1)
@@ -582,6 +588,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_select, 0, 0, 3)
ZEND_ARG_INFO(0, table)
ZEND_ARG_INFO(0, ids)
ZEND_ARG_INFO(0, options)
+ ZEND_ARG_INFO(0, result_type)
ZEND_END_ARG_INFO()
/* }}} */
@@ -961,29 +968,24 @@ static void _close_pgsql_plink(zend_resource *rsrc)
*/
static void _php_pgsql_notice_handler(void *resource_id, const char *message)
{
- php_pgsql_notice *notice;
+ zval *notices;
+ zval tmp;
+ char *trimed_message;
+ size_t trimed_message_len;
if (! PGG(ignore_notices)) {
- notice = (php_pgsql_notice *)emalloc(sizeof(php_pgsql_notice));
- notice->message = _php_pgsql_trim_message(message, &notice->len);
+ notices = zend_hash_index_find(&PGG(notices), (zend_ulong)resource_id);
+ if (!notices) {
+ array_init(&tmp);
+ notices = &tmp;
+ zend_hash_index_update(&PGG(notices), (zend_ulong)resource_id, notices);
+ }
+ trimed_message = _php_pgsql_trim_message(message, &trimed_message_len);
if (PGG(log_notices)) {
- php_error_docref(NULL, E_NOTICE, "%s", notice->message);
+ php_error_docref(NULL, E_NOTICE, "%s", trimed_message);
}
- zend_hash_index_update_ptr(&PGG(notices), (zend_ulong)resource_id, notice);
- }
-}
-/* }}} */
-
-#define PHP_PGSQL_NOTICE_PTR_DTOR _php_pgsql_notice_ptr_dtor
-
-/* {{{ _php_pgsql_notice_dtor
- */
-static void _php_pgsql_notice_ptr_dtor(zval *el)
-{
- php_pgsql_notice *notice = (php_pgsql_notice *)Z_PTR_P(el);
- if (notice) {
- efree(notice->message);
- efree(notice);
+ add_next_index_stringl(notices, trimed_message, trimed_message_len);
+ efree(trimed_message);
}
}
/* }}} */
@@ -1096,7 +1098,7 @@ static PHP_GINIT_FUNCTION(pgsql)
#endif
memset(pgsql_globals, 0, sizeof(zend_pgsql_globals));
/* Initilize notice message hash at MINIT only */
- zend_hash_init_ex(&pgsql_globals->notices, 0, NULL, PHP_PGSQL_NOTICE_PTR_DTOR, 1, 0);
+ zend_hash_init_ex(&pgsql_globals->notices, 0, NULL, ZVAL_PTR_DTOR, 1, 0);
}
/* }}} */
@@ -1123,6 +1125,10 @@ PHP_MINIT_FUNCTION(pgsql)
REGISTER_LONG_CONSTANT("PGSQL_ASSOC", PGSQL_ASSOC, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("PGSQL_NUM", PGSQL_NUM, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("PGSQL_BOTH", PGSQL_BOTH, CONST_CS | CONST_PERSISTENT);
+ /* For pg_last_notice() */
+ REGISTER_LONG_CONSTANT("PGSQL_NOTICE_LAST", PGSQL_NOTICE_LAST, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PGSQL_NOTICE_ALL", PGSQL_NOTICE_ALL, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PGSQL_NOTICE_CLEAR", PGSQL_NOTICE_CLEAR, CONST_CS | CONST_PERSISTENT);
/* For pg_connection_status() */
REGISTER_LONG_CONSTANT("PGSQL_CONNECTION_BAD", CONNECTION_BAD, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("PGSQL_CONNECTION_OK", CONNECTION_OK, CONST_CS | CONST_PERSISTENT);
@@ -1337,12 +1343,12 @@ static void php_pgsql_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent)
if (PGG(max_links) != -1 && PGG(num_links) >= PGG(max_links)) {
php_error_docref(NULL, E_WARNING,
- "Cannot create new link. Too many open links (%pd)", PGG(num_links));
+ "Cannot create new link. Too many open links (" ZEND_LONG_FMT ")", PGG(num_links));
goto err;
}
if (PGG(max_persistent) != -1 && PGG(num_persistent) >= PGG(max_persistent)) {
php_error_docref(NULL, E_WARNING,
- "Cannot create new link. Too many open persistent links (%pd)", PGG(num_persistent));
+ "Cannot create new link. Too many open persistent links (" ZEND_LONG_FMT ")", PGG(num_persistent));
goto err;
}
@@ -1435,7 +1441,7 @@ static void php_pgsql_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent)
}
}
if (PGG(max_links) != -1 && PGG(num_links) >= PGG(max_links)) {
- php_error_docref(NULL, E_WARNING, "Cannot create new link. Too many open links (%pd)", PGG(num_links));
+ php_error_docref(NULL, E_WARNING, "Cannot create new link. Too many open links (" ZEND_LONG_FMT ")", PGG(num_links));
goto err;
}
@@ -2313,15 +2319,16 @@ PHP_FUNCTION(pg_affected_rows)
/* }}} */
#endif
-/* {{{ proto string pg_last_notice(resource connection)
+/* {{{ proto mixed pg_last_notice(resource connection [, long option])
Returns the last notice set by the backend */
PHP_FUNCTION(pg_last_notice)
{
zval *pgsql_link = NULL;
+ zval *notice, *notices;
PGconn *pg_link;
- php_pgsql_notice *notice;
+ zend_long option = PGSQL_NOTICE_LAST;
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &pgsql_link) == FAILURE) {
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|l", &pgsql_link, &option) == FAILURE) {
return;
}
@@ -2330,10 +2337,38 @@ PHP_FUNCTION(pg_last_notice)
RETURN_FALSE;
}
- if ((notice = zend_hash_index_find_ptr(&PGG(notices), (zend_ulong)Z_RES_HANDLE_P(pgsql_link))) == NULL) {
- RETURN_FALSE;
+ notices = zend_hash_index_find(&PGG(notices), (zend_ulong)Z_RES_HANDLE_P(pgsql_link));
+ switch (option) {
+ case PGSQL_NOTICE_LAST:
+ if (notices) {
+ zend_hash_internal_pointer_end(Z_ARRVAL_P(notices));
+ if ((notice = zend_hash_get_current_data(Z_ARRVAL_P(notices))) == NULL) {
+ RETURN_EMPTY_STRING();
+ }
+ RETURN_ZVAL(notice, 1, 0);
+ } else {
+ RETURN_EMPTY_STRING();
+ }
+ break;
+ case PGSQL_NOTICE_ALL:
+ if (notices) {
+ RETURN_ZVAL(notices, 1, 0);
+ } else {
+ array_init(return_value);
+ return;
+ }
+ break;
+ case PGSQL_NOTICE_CLEAR:
+ if (notices) {
+ zend_hash_clean(&PGG(notices));
+ }
+ RETURN_TRUE;
+ break;
+ default:
+ php_error_docref(NULL, E_WARNING,
+ "Invalid option specified (" ZEND_LONG_FMT ")", option);
}
- RETURN_STRINGL(notice->message, notice->len);
+ RETURN_FALSE;
}
/* }}} */
@@ -2646,7 +2681,7 @@ PHP_FUNCTION(pg_fetch_result)
}
} else {
if (row < 0 || row >= PQntuples(pgsql_result)) {
- php_error_docref(NULL, E_WARNING, "Unable to jump to row %pd on PostgreSQL result index %pd",
+ php_error_docref(NULL, E_WARNING, "Unable to jump to row " ZEND_LONG_FMT " on PostgreSQL result index " ZEND_LONG_FMT,
row, Z_LVAL_P(result));
RETURN_FALSE;
}
@@ -2737,7 +2772,7 @@ static void php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, zend_long result_
if (use_row) {
if (row < 0 || row >= PQntuples(pgsql_result)) {
- php_error_docref(NULL, E_WARNING, "Unable to jump to row %pd on PostgreSQL result index %pd",
+ php_error_docref(NULL, E_WARNING, "Unable to jump to row " ZEND_LONG_FMT " on PostgreSQL result index " ZEND_LONG_FMT,
row, Z_LVAL_P(result));
RETURN_FALSE;
}
@@ -2796,9 +2831,7 @@ static void php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, zend_long result_
if (ce->constructor) {
fci.size = sizeof(fci);
- fci.function_table = &ce->function_table;
ZVAL_UNDEF(&fci.function_name);
- fci.symbol_table = NULL;
fci.object = Z_OBJ_P(return_value);
fci.retval = &retval;
fci.params = NULL;
@@ -2820,12 +2853,12 @@ static void php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, zend_long result_
fcc.initialized = 1;
fcc.function_handler = ce->constructor;
- fcc.calling_scope = EG(scope);
+ fcc.calling_scope = zend_get_executed_scope();
fcc.called_scope = Z_OBJCE_P(return_value);
fcc.object = Z_OBJ_P(return_value);
if (zend_call_function(&fci, &fcc) == FAILURE) {
- zend_throw_exception_ex(zend_ce_exception, 0, "Could not execute %s::%s()", ce->name, ce->constructor->common.function_name);
+ zend_throw_exception_ex(zend_ce_exception, 0, "Could not execute %s::%s()", ZSTR_VAL(ce->name), ZSTR_VAL(ce->constructor->common.function_name));
} else {
zval_ptr_dtor(&retval);
}
@@ -2833,7 +2866,7 @@ static void php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, zend_long result_
efree(fci.params);
}
} else if (ctor_params) {
- zend_throw_exception_ex(zend_ce_exception, 0, "Class %s does not have a constructor hence you cannot use ctor_params", ce->name);
+ zend_throw_exception_ex(zend_ce_exception, 0, "Class %s does not have a constructor hence you cannot use ctor_params", ZSTR_VAL(ce->name));
}
}
}
@@ -2877,25 +2910,31 @@ PHP_FUNCTION(pg_fetch_object)
}
/* }}} */
-/* {{{ proto array pg_fetch_all(resource result)
+/* {{{ proto array pg_fetch_all(resource result [, int result_type])
Fetch all rows into array */
PHP_FUNCTION(pg_fetch_all)
{
zval *result;
+ long result_type = PGSQL_ASSOC;
PGresult *pgsql_result;
pgsql_result_handle *pg_result;
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &result) == FAILURE) {
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|l", &result, &result_type) == FAILURE) {
return;
}
+ if (!(result_type & PGSQL_BOTH)) {
+ php_error_docref(NULL, E_WARNING, "Invalid result type");
+ RETURN_FALSE;
+ }
+
if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
RETURN_FALSE;
}
pgsql_result = pg_result->result;
array_init(return_value);
- if (php_pgsql_result2array(pgsql_result, return_value) == FAILURE) {
+ if (php_pgsql_result2array(pgsql_result, return_value, result_type) == FAILURE) {
zval_dtor(return_value);
RETURN_FALSE;
}
@@ -2925,7 +2964,7 @@ PHP_FUNCTION(pg_fetch_all_columns)
num_fields = PQnfields(pgsql_result);
if (colno >= (zend_long)num_fields || colno < 0) {
- php_error_docref(NULL, E_WARNING, "Invalid column number '%pd'", colno);
+ php_error_docref(NULL, E_WARNING, "Invalid column number '" ZEND_LONG_FMT "'", colno);
RETURN_FALSE;
}
@@ -3009,7 +3048,7 @@ static void php_pgsql_data_info(INTERNAL_FUNCTION_PARAMETERS, int entry_type)
}
} else {
if (row < 0 || row >= PQntuples(pgsql_result)) {
- php_error_docref(NULL, E_WARNING, "Unable to jump to row %pd on PostgreSQL result index %pd",
+ php_error_docref(NULL, E_WARNING, "Unable to jump to row " ZEND_LONG_FMT " on PostgreSQL result index " ZEND_LONG_FMT,
row, Z_LVAL_P(result));
RETURN_FALSE;
}
@@ -3294,7 +3333,7 @@ PHP_FUNCTION(pg_lo_unlink)
}
else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
"rl", &pgsql_link, &oid_long) == SUCCESS) {
- if (oid_long <= InvalidOid) {
+ if (oid_long <= (zend_long)InvalidOid) {
php_error_docref(NULL, E_NOTICE, "Invalid OID specified");
RETURN_FALSE;
}
@@ -3314,7 +3353,7 @@ PHP_FUNCTION(pg_lo_unlink)
}
else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
"l", &oid_long) == SUCCESS) {
- if (oid_long <= InvalidOid) {
+ if (oid_long <= (zend_long)InvalidOid) {
php_error_docref(NULL, E_NOTICE, "Invalid OID is specified");
RETURN_FALSE;
}
@@ -3368,7 +3407,7 @@ PHP_FUNCTION(pg_lo_open)
}
else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
"rls", &pgsql_link, &oid_long, &mode_string, &mode_strlen) == SUCCESS) {
- if (oid_long <= InvalidOid) {
+ if (oid_long <= (zend_long)InvalidOid) {
php_error_docref(NULL, E_NOTICE, "Invalid OID specified");
RETURN_FALSE;
}
@@ -3388,7 +3427,7 @@ PHP_FUNCTION(pg_lo_open)
}
else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
"ls", &oid_long, &mode_string, &mode_strlen) == SUCCESS) {
- if (oid_long <= InvalidOid) {
+ if (oid_long <= (zend_long)InvalidOid) {
php_error_docref(NULL, E_NOTICE, "Invalid OID specified");
RETURN_FALSE;
}
@@ -3543,11 +3582,11 @@ PHP_FUNCTION(pg_lo_write)
if (argc > 2) {
if (z_len > (zend_long)str_len) {
- php_error_docref(NULL, E_WARNING, "Cannot write more than buffer size %d. Tried to write %pd", str_len, z_len);
+ php_error_docref(NULL, E_WARNING, "Cannot write more than buffer size %d. Tried to write " ZEND_LONG_FMT, str_len, z_len);
RETURN_FALSE;
}
if (z_len < 0) {
- php_error_docref(NULL, E_WARNING, "Buffer size must be larger than 0, but %pd was specified", z_len);
+ php_error_docref(NULL, E_WARNING, "Buffer size must be larger than 0, but " ZEND_LONG_FMT " was specified", z_len);
RETURN_FALSE;
}
len = z_len;
@@ -3560,7 +3599,7 @@ PHP_FUNCTION(pg_lo_write)
RETURN_FALSE;
}
- if ((nbytes = lo_write((PGconn *)pgsql->conn, pgsql->lofd, str, len)) == -1) {
+ if ((nbytes = lo_write((PGconn *)pgsql->conn, pgsql->lofd, str, len)) == (size_t)-1) {
RETURN_FALSE;
}
@@ -3699,7 +3738,7 @@ PHP_FUNCTION(pg_lo_export)
/* allow string to handle large OID value correctly */
if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
"rlp", &pgsql_link, &oid_long, &file_out, &name_len) == SUCCESS) {
- if (oid_long <= InvalidOid) {
+ if (oid_long <= (zend_long)InvalidOid) {
php_error_docref(NULL, E_NOTICE, "Invalid OID specified");
RETURN_FALSE;
}
@@ -3718,7 +3757,7 @@ PHP_FUNCTION(pg_lo_export)
}
else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
"lp", &oid_long, &file_out, &name_len) == SUCCESS) {
- if (oid_long <= InvalidOid) {
+ if (oid_long <= (zend_long)InvalidOid) {
php_error_docref(NULL, E_NOTICE, "Invalid OID specified");
RETURN_FALSE;
}
@@ -3750,7 +3789,7 @@ PHP_FUNCTION(pg_lo_export)
else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
"lpr", &oid_long, &file_out, &name_len, &pgsql_link) == SUCCESS) {
php_error_docref(NULL, E_NOTICE, "Old API is used");
- if (oid_long <= InvalidOid) {
+ if (oid_long <= (zend_long)InvalidOid) {
php_error_docref(NULL, E_NOTICE, "Invalid OID specified");
RETURN_FALSE;
}
@@ -6490,7 +6529,7 @@ PHP_FUNCTION(pg_convert)
}
/* }}} */
-static int do_exec(smart_str *querystr, int expect, PGconn *pg_link, zend_ulong opt) /* {{{ */
+static int do_exec(smart_str *querystr, ExecStatusType expect, PGconn *pg_link, zend_ulong opt) /* {{{ */
{
if (opt & PGSQL_DML_ASYNC) {
if (PQsendQuery(pg_link, ZSTR_VAL(querystr->s))) {
@@ -7015,7 +7054,7 @@ PHP_FUNCTION(pg_delete)
/* {{{ php_pgsql_result2array
*/
-PHP_PGSQL_API int php_pgsql_result2array(PGresult *pg_result, zval *ret_array)
+PHP_PGSQL_API int php_pgsql_result2array(PGresult *pg_result, zval *ret_array, long result_type)
{
zval row;
char *field_name;
@@ -7030,16 +7069,24 @@ PHP_PGSQL_API int php_pgsql_result2array(PGresult *pg_result, zval *ret_array)
for (pg_row = 0; pg_row < pg_numrows; pg_row++) {
array_init(&row);
for (i = 0, num_fields = PQnfields(pg_result); i < num_fields; i++) {
+ field_name = PQfname(pg_result, i);
if (PQgetisnull(pg_result, pg_row, i)) {
- field_name = PQfname(pg_result, i);
- add_assoc_null(&row, field_name);
+ if (result_type & PGSQL_ASSOC) {
+ add_assoc_null(&row, field_name);
+ }
+ if (result_type & PGSQL_NUM) {
+ add_next_index_null(&row);
+ }
} else {
char *element = PQgetvalue(pg_result, pg_row, i);
if (element) {
const size_t element_len = strlen(element);
-
- field_name = PQfname(pg_result, i);
- add_assoc_stringl(&row, field_name, element, element_len);
+ if (result_type & PGSQL_ASSOC) {
+ add_assoc_stringl(&row, field_name, element, element_len);
+ }
+ if (result_type & PGSQL_NUM) {
+ add_next_index_stringl(&row, element, element_len);
+ }
}
}
}
@@ -7051,7 +7098,7 @@ PHP_PGSQL_API int php_pgsql_result2array(PGresult *pg_result, zval *ret_array)
/* {{{ php_pgsql_select
*/
-PHP_PGSQL_API int php_pgsql_select(PGconn *pg_link, const char *table, zval *ids_array, zval *ret_array, zend_ulong opt, zend_string **sql)
+ PHP_PGSQL_API int php_pgsql_select(PGconn *pg_link, const char *table, zval *ids_array, zval *ret_array, zend_ulong opt, long result_type, zend_string **sql)
{
zval ids_converted;
smart_str querystr = {0};
@@ -7089,7 +7136,7 @@ PHP_PGSQL_API int php_pgsql_select(PGconn *pg_link, const char *table, zval *ids
pg_result = PQexec(pg_link, ZSTR_VAL(querystr.s));
if (PQresultStatus(pg_result) == PGRES_TUPLES_OK) {
- ret = php_pgsql_result2array(pg_result, ret_array);
+ ret = php_pgsql_result2array(pg_result, ret_array, result_type);
} else {
php_error_docref(NULL, E_NOTICE, "Failed to execute '%s'", ZSTR_VAL(querystr.s));
}
@@ -7107,7 +7154,7 @@ cleanup:
}
/* }}} */
-/* {{{ proto mixed pg_select(resource db, string table, array ids[, int options])
+/* {{{ proto mixed pg_select(resource db, string table, array ids[, int options [, int result_type])
Select records that has ids (id=>value) */
PHP_FUNCTION(pg_select)
{
@@ -7115,18 +7162,23 @@ PHP_FUNCTION(pg_select)
char *table;
size_t table_len;
zend_ulong option = PGSQL_DML_EXEC;
+ long result_type = PGSQL_ASSOC;
PGconn *pg_link;
zend_string *sql = NULL;
int argc = ZEND_NUM_ARGS();
if (zend_parse_parameters(argc, "rsa|l",
- &pgsql_link, &table, &table_len, &ids, &option) == FAILURE) {
+ &pgsql_link, &table, &table_len, &ids, &option, &result_type) == FAILURE) {
return;
}
if (option & ~(PGSQL_CONV_FORCE_NULL|PGSQL_DML_NO_CONV|PGSQL_DML_EXEC|PGSQL_DML_ASYNC|PGSQL_DML_STRING|PGSQL_DML_ESCAPE)) {
php_error_docref(NULL, E_WARNING, "Invalid option is specified");
RETURN_FALSE;
}
+ if (!(result_type & PGSQL_BOTH)) {
+ php_error_docref(NULL, E_WARNING, "Invalid result type");
+ RETURN_FALSE;
+ }
if ((pg_link = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
RETURN_FALSE;
@@ -7136,7 +7188,7 @@ PHP_FUNCTION(pg_select)
php_error_docref(NULL, E_NOTICE, "Detected unhandled result(s) in connection");
}
array_init(return_value);
- if (php_pgsql_select(pg_link, table, ids, return_value, option, &sql) == FAILURE) {
+ if (php_pgsql_select(pg_link, table, ids, return_value, option, result_type, &sql) == FAILURE) {
zval_ptr_dtor(return_value);
RETURN_FALSE;
}
diff --git a/ext/pgsql/php_pgsql.h b/ext/pgsql/php_pgsql.h
index 96cb387c84..ea656dbc86 100644
--- a/ext/pgsql/php_pgsql.h
+++ b/ext/pgsql/php_pgsql.h
@@ -211,15 +211,14 @@ PHP_FUNCTION(pg_select);
#define PGSQL_DML_STRING (1<<11) /* Return query string */
#define PGSQL_DML_ESCAPE (1<<12) /* No convert, but escape only */
-
/* exported functions */
PHP_PGSQL_API int php_pgsql_meta_data(PGconn *pg_link, const char *table_name, zval *meta, zend_bool extended);
PHP_PGSQL_API int php_pgsql_convert(PGconn *pg_link, const char *table_name, const zval *values, zval *result, zend_ulong opt);
PHP_PGSQL_API int php_pgsql_insert(PGconn *pg_link, const char *table, zval *values, zend_ulong opt, zend_string **sql);
PHP_PGSQL_API int php_pgsql_update(PGconn *pg_link, const char *table, zval *values, zval *ids, zend_ulong opt , zend_string **sql);
PHP_PGSQL_API int php_pgsql_delete(PGconn *pg_link, const char *table, zval *ids, zend_ulong opt, zend_string **sql);
-PHP_PGSQL_API int php_pgsql_select(PGconn *pg_link, const char *table, zval *ids, zval *ret_array, zend_ulong opt, zend_string **sql );
-PHP_PGSQL_API int php_pgsql_result2array(PGresult *pg_result, zval *ret_array);
+PHP_PGSQL_API int php_pgsql_select(PGconn *pg_link, const char *table, zval *ids, zval *ret_array, zend_ulong opt, long fetch_option, zend_string **sql );
+PHP_PGSQL_API int php_pgsql_result2array(PGresult *pg_result, zval *ret_array, long fetch_option);
/* internal functions */
static void php_pgsql_do_connect(INTERNAL_FUNCTION_PARAMETERS,int persistent);
diff --git a/ext/pgsql/tests/09notice.phpt b/ext/pgsql/tests/09notice.phpt
index db671016e3..64ab068e4a 100644
--- a/ext/pgsql/tests/09notice.phpt
+++ b/ext/pgsql/tests/09notice.phpt
@@ -9,35 +9,66 @@ _skip_lc_messages();
?>
--INI--
-pgsql.log_notice=1
-pgsql.ignore_notice=0
--FILE--
<?php
include 'config.inc';
include 'lcmess.inc';
+ini_set('pgsql.log_notice', TRUE);
+ini_set('pgsql.ignore_notice', FALSE);
+
$db = pg_connect($conn_str);
_set_lc_messages();
-$res = pg_query($db, 'SET client_min_messages TO NOTICE;');
+$res = pg_query($db, 'SET client_min_messages TO NOTICE;');
var_dump($res);
+// Get empty notice
+var_dump(pg_last_notice($db));
+var_dump(pg_last_notice($db, PGSQL_NOTICE_ALL));
+
+pg_query($db, "BEGIN;");
+pg_query($db, "BEGIN;");
pg_query($db, "BEGIN;");
pg_query($db, "BEGIN;");
-$msg = pg_last_notice($db);
-if ($msg === FALSE) {
- echo "Cannot find notice message in hash\n";
- var_dump($msg);
-}
-echo $msg."\n";
-echo "pg_last_notice() is Ok\n";
+// Get notices
+var_dump(pg_last_notice($db));
+var_dump(pg_last_notice($db, PGSQL_NOTICE_ALL));
+
+// Clear and get notices
+var_dump(pg_last_notice($db, PGSQL_NOTICE_CLEAR));
+var_dump(pg_last_notice($db, PGSQL_NOTICE_LAST));
+var_dump(pg_last_notice($db, PGSQL_NOTICE_ALL));
+// Invalid option
+var_dump(pg_last_notice($db, 99));
?>
--EXPECTF--
resource(%d) of type (pgsql result)
+string(0) ""
+array(0) {
+}
Notice: pg_query(): %s already a transaction in progress in %s on line %d
-%s already a transaction in progress
-pg_last_notice() is Ok
+
+Notice: pg_query(): %s already a transaction in progress in %s on line %d
+
+Notice: pg_query(): %s already a transaction in progress in %s on line %d
+string(52) "WARNING: there is already a transaction in progress"
+array(3) {
+ [0]=>
+ string(52) "WARNING: there is already a transaction in progress"
+ [1]=>
+ string(52) "WARNING: there is already a transaction in progress"
+ [2]=>
+ string(52) "WARNING: there is already a transaction in progress"
+}
+bool(true)
+string(0) ""
+array(0) {
+}
+
+Warning: pg_last_notice(): Invalid option specified (99) in %s%e09notice.php on line %d
+bool(false)
diff --git a/ext/pgsql/tests/bug72195.phpt b/ext/pgsql/tests/bug72195.phpt
index 2f33e1ab1c..34735d31f4 100644
--- a/ext/pgsql/tests/bug72195.phpt
+++ b/ext/pgsql/tests/bug72195.phpt
@@ -9,7 +9,7 @@ $var1 = $val;
printf("%x\n", count($val));
@pg_pconnect($var1, "2", "3", "4");
$var1 = "";
-tempnam('/tmp', 'ABCDEFGHI');
+tempnam(sys_get_temp_dir(), 'ABCDEFGHI');
printf("%x\n", count($val));
?>
--EXPECT--
diff --git a/ext/phar/dirstream.c b/ext/phar/dirstream.c
index d81f6939bc..4d00cf93a6 100644
--- a/ext/phar/dirstream.c
+++ b/ext/phar/dirstream.c
@@ -180,7 +180,7 @@ static int phar_compare_dir_name(const void *a, const void *b) /* {{{ */
static php_stream *phar_make_dirstream(char *dir, HashTable *manifest) /* {{{ */
{
HashTable *data;
- int dirlen = strlen(dir);
+ size_t dirlen = strlen(dir);
char *entry, *found, *save;
zend_string *str_key;
uint keylen;
@@ -199,7 +199,6 @@ static php_stream *phar_make_dirstream(char *dir, HashTable *manifest) /* {{{ */
zend_hash_internal_pointer_reset(manifest);
while (FAILURE != zend_hash_has_more_elements(manifest)) {
- keylen = 0;
if (HASH_KEY_NON_EXISTENT == zend_hash_get_current_key(manifest, &str_key, &unused)) {
break;
}
diff --git a/ext/phar/func_interceptors.c b/ext/phar/func_interceptors.c
index c4c7b6cba8..df50616278 100644
--- a/ext/phar/func_interceptors.c
+++ b/ext/phar/func_interceptors.c
@@ -200,7 +200,7 @@ phar_it:
}
if (offset > 0 && php_stream_seek(stream, offset, SEEK_SET) < 0) {
- php_error_docref(NULL, E_WARNING, "Failed to seek to position %pd in the stream", offset);
+ php_error_docref(NULL, E_WARNING, "Failed to seek to position " ZEND_LONG_FMT " in the stream", offset);
php_stream_close(stream);
RETURN_FALSE;
}
diff --git a/ext/phar/phar.c b/ext/phar/phar.c
index 9d91fd26ae..59c11f4e29 100644
--- a/ext/phar/phar.c
+++ b/ext/phar/phar.c
@@ -518,15 +518,15 @@ void phar_entry_remove(phar_entry_data *idata, char **error) /* {{{ */
memcpy(&var, buffer, sizeof(var)); \
buffer += 4
# define PHAR_GET_16(buffer, var) \
- var = *(php_uint16*)(buffer); \
+ var = *(uint16_t*)(buffer); \
buffer += 2
#endif
-#define PHAR_ZIP_16(var) ((php_uint16)((((php_uint16)var[0]) & 0xff) | \
- (((php_uint16)var[1]) & 0xff) << 8))
-#define PHAR_ZIP_32(var) ((php_uint32)((((php_uint32)var[0]) & 0xff) | \
- (((php_uint32)var[1]) & 0xff) << 8 | \
- (((php_uint32)var[2]) & 0xff) << 16 | \
- (((php_uint32)var[3]) & 0xff) << 24))
+#define PHAR_ZIP_16(var) ((uint16_t)((((uint16_t)var[0]) & 0xff) | \
+ (((uint16_t)var[1]) & 0xff) << 8))
+#define PHAR_ZIP_32(var) ((uint32_t)((((uint32_t)var[0]) & 0xff) | \
+ (((uint32_t)var[1]) & 0xff) << 8 | \
+ (((uint32_t)var[2]) & 0xff) << 16 | \
+ (((uint32_t)var[3]) & 0xff) << 24))
/**
* Open an already loaded phar
@@ -606,7 +606,7 @@ int phar_open_parsed_phar(char *fname, int fname_len, char *alias, int alias_len
*
* data is the serialized zval
*/
-int phar_parse_metadata(char **buffer, zval *metadata, php_uint32 zip_metadata_len) /* {{{ */
+int phar_parse_metadata(char **buffer, zval *metadata, uint32_t zip_metadata_len) /* {{{ */
{
php_unserialize_data_t var_hash;
@@ -651,14 +651,14 @@ int phar_parse_metadata(char **buffer, zval *metadata, php_uint32 zip_metadata_l
* This is used by phar_open_from_filename to process the manifest, but can be called
* directly.
*/
-static int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char *alias, int alias_len, zend_long halt_offset, phar_archive_data** pphar, php_uint32 compression, char **error) /* {{{ */
+static int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char *alias, int alias_len, zend_long halt_offset, phar_archive_data** pphar, uint32_t compression, char **error) /* {{{ */
{
char b32[4], *buffer, *endbuffer, *savebuf;
phar_archive_data *mydata = NULL;
phar_entry_info entry;
- php_uint32 manifest_len, manifest_count, manifest_flags, manifest_index, tmp_len, sig_flags;
- php_uint16 manifest_ver;
- php_uint32 len;
+ uint32_t manifest_len, manifest_count, manifest_flags, manifest_index, tmp_len, sig_flags;
+ uint16_t manifest_ver;
+ uint32_t len;
zend_long offset;
int sig_len, register_alias = 0, temp_alias = 0;
char *signature = NULL;
@@ -780,7 +780,7 @@ static int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char
switch(sig_flags) {
case PHAR_SIG_OPENSSL: {
- php_uint32 signature_len;
+ uint32_t signature_len;
char *sig;
zend_off_t whence;
@@ -1031,7 +1031,7 @@ static int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char
PHAR_GET_32(buffer, len);
}
}
- if(len > endbuffer - buffer) {
+ if(len > (size_t)(endbuffer - buffer)) {
MAPPHAR_FAIL("internal corruption of phar \"%s\" (trying to read past buffer end)");
}
if (phar_parse_metadata(&buffer, &mydata->metadata, len) == FAILURE) {
@@ -1072,7 +1072,7 @@ static int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char
entry.manifest_pos = manifest_index;
}
- if (entry.filename_len > endbuffer - buffer - 24) {
+ if (entry.filename_len > (size_t)(endbuffer - buffer - 24)) {
MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest entry)");
}
@@ -1114,7 +1114,7 @@ static int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char
} else {
entry.metadata_len = 0;
}
- if (len > endbuffer - buffer) {
+ if (len > (size_t)(endbuffer - buffer)) {
pefree(entry.filename, entry.is_persistent);
MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest entry)");
}
@@ -1327,11 +1327,6 @@ int phar_create_or_parse_filename(char *fname, int fname_len, char *alias, int a
if (!pphar) {
pphar = &mydata;
}
-#if PHP_API_VERSION < 20100412
- if (PG(safe_mode) && (!php_checkuid(fname, NULL, CHECKUID_ALLOW_ONLY_FILE))) {
- return FAILURE;
- }
-#endif
if (php_check_open_basedir(fname)) {
return FAILURE;
}
@@ -1491,11 +1486,6 @@ int phar_open_from_filename(char *fname, int fname_len, char *alias, int alias_l
} else if (error && *error) {
return FAILURE;
}
-#if PHP_API_VERSION < 20100412
- if (PG(safe_mode) && (!php_checkuid(fname, NULL, CHECKUID_ALLOW_ONLY_FILE))) {
- return FAILURE;
- }
-#endif
if (php_check_open_basedir(fname)) {
return FAILURE;
}
@@ -1576,7 +1566,7 @@ static int phar_open_from_fp(php_stream* fp, char *fname, int fname_len, char *a
const zend_long tokenlen = sizeof(token) - 1;
zend_long halt_offset;
size_t got;
- php_uint32 compression = PHAR_FILE_COMPRESSED_NONE;
+ uint32_t compression = PHAR_FILE_COMPRESSED_NONE;
if (error) {
*error = NULL;
@@ -2297,13 +2287,6 @@ int phar_open_executed_filename(char *alias, int alias_len, char **error) /* {{{
return FAILURE;
}
-
-#if PHP_API_VERSION < 20100412
- if (PG(safe_mode) && (!php_checkuid(fname, NULL, CHECKUID_ALLOW_ONLY_FILE))) {
- return FAILURE;
- }
-#endif
-
if (php_check_open_basedir(fname)) {
return FAILURE;
}
@@ -2338,9 +2321,9 @@ int phar_open_executed_filename(char *alias, int alias_len, char **error) /* {{{
/**
* Validate the CRC32 of a file opened from within the phar
*/
-int phar_postprocess_file(phar_entry_data *idata, php_uint32 crc32, char **error, int process_zip) /* {{{ */
+int phar_postprocess_file(phar_entry_data *idata, uint32_t crc32, char **error, int process_zip) /* {{{ */
{
- php_uint32 crc = ~0;
+ uint32_t crc = ~0;
int len = idata->internal_file->uncompressed_filesize;
php_stream *fp = idata->fp;
phar_entry_info *entry = idata->internal_file;
@@ -2505,8 +2488,8 @@ int phar_flush(phar_archive_data *phar, char *user_stub, zend_long len, int conv
zend_off_t manifest_ftell;
zend_long offset;
size_t wrote;
- php_uint32 manifest_len, mytime, loc, new_manifest_count;
- php_uint32 newcrc32;
+ uint32_t manifest_len, mytime, loc, new_manifest_count;
+ uint32_t newcrc32;
php_stream *file, *oldfile, *newfile, *stubfile;
php_stream_filter *filter;
php_serialize_data_t metadata_hash;
@@ -2838,7 +2821,7 @@ int phar_flush(phar_archive_data *phar, char *user_stub, zend_long len, int conv
php_stream_flush(entry->cfp);
php_stream_filter_remove(filter, 1);
php_stream_seek(entry->cfp, 0, SEEK_END);
- entry->compressed_filesize = (php_uint32) php_stream_tell(entry->cfp);
+ entry->compressed_filesize = (uint32_t) php_stream_tell(entry->cfp);
/* generate crc on compressed file */
php_stream_rewind(entry->cfp);
entry->old_flags = entry->flags;
@@ -3505,7 +3488,7 @@ void phar_request_initialize(void) /* {{{ */
PHP_RSHUTDOWN_FUNCTION(phar) /* {{{ */
{
- int i;
+ uint32_t i;
PHAR_G(request_ends) = 1;
@@ -3607,9 +3590,7 @@ static const zend_module_dep phar_deps[] = {
#if defined(HAVE_HASH) && !defined(COMPILE_DL_HASH)
ZEND_MOD_REQUIRED("hash")
#endif
-#if HAVE_SPL
ZEND_MOD_REQUIRED("spl")
-#endif
ZEND_MOD_END
};
diff --git a/ext/phar/phar_internal.h b/ext/phar/phar_internal.h
index c16c6b218e..939dee16be 100644
--- a/ext/phar/phar_internal.h
+++ b/ext/phar/phar_internal.h
@@ -55,13 +55,11 @@
#include "TSRM/tsrm_strtok_r.h"
#endif
#include "Zend/zend_virtual_cwd.h"
-#if HAVE_SPL
#include "ext/spl/spl_array.h"
#include "ext/spl/spl_directory.h"
#include "ext/spl/spl_engine.h"
#include "ext/spl/spl_exceptions.h"
#include "ext/spl/spl_iterators.h"
-#endif
#include "php_phar.h"
#ifdef PHAR_HASH_OK
#include "ext/hash/php_hash.h"
@@ -201,19 +199,10 @@ ZEND_EXTERN_MODULE_GLOBALS(phar)
ZEND_TSRMLS_CACHE_EXTERN()
#endif
-#ifndef php_uint16
-# if SIZEOF_SHORT == 2
-# define php_uint16 unsigned short
-# else
-# define php_uint16 uint16_t
-# endif
-#endif
#include "pharzip.h"
-#if HAVE_SPL
typedef union _phar_archive_object phar_archive_object;
typedef union _phar_entry_object phar_entry_object;
-#endif
/*
* used in phar_entry_info->fp_type to
@@ -233,17 +222,17 @@ enum phar_fp_type {
/* entry for one file in a phar file */
typedef struct _phar_entry_info {
/* first bytes are exactly as in file */
- php_uint32 uncompressed_filesize;
- php_uint32 timestamp;
- php_uint32 compressed_filesize;
- php_uint32 crc32;
- php_uint32 flags;
+ uint32_t uncompressed_filesize;
+ uint32_t timestamp;
+ uint32_t compressed_filesize;
+ uint32_t crc32;
+ uint32_t flags;
/* remainder */
/* when changing compression, save old flags in case fp is NULL */
- php_uint32 old_flags;
+ uint32_t old_flags;
zval metadata;
int metadata_len; /* only used for cached manifests */
- php_uint32 filename_len;
+ uint32_t filename_len;
char *filename;
enum phar_fp_type fp_type;
/* offset within original phar file of the file contents */
@@ -299,14 +288,14 @@ struct _phar_archive_data {
HashTable virtual_dirs;
/* hash of mounted directory paths */
HashTable mounted_dirs;
- php_uint32 flags;
- php_uint32 min_timestamp;
- php_uint32 max_timestamp;
+ uint32_t flags;
+ uint32_t min_timestamp;
+ uint32_t max_timestamp;
php_stream *fp;
/* decompressed file contents are stored here */
php_stream *ufp;
int refcount;
- php_uint32 sig_flags;
+ uint32_t sig_flags;
int sig_len;
char *signature;
zval metadata;
@@ -473,21 +462,17 @@ typedef struct _phar_entry_data {
phar_entry_info *internal_file;
} phar_entry_data;
-#if HAVE_SPL
/* archive php object */
union _phar_archive_object {
spl_filesystem_object spl;
phar_archive_data *archive;
};
-#endif
-#if HAVE_SPL
/* entry php object */
union _phar_entry_object {
spl_filesystem_object spl;
phar_entry_info *entry;
};
-#endif
#ifndef PHAR_MAIN
extern zend_string *(*phar_save_resolve_path)(const char *filename, int filename_len);
@@ -544,7 +529,7 @@ void phar_object_init(void);
void phar_destroy_phar_data(phar_archive_data *phar);
int phar_open_entry_file(phar_archive_data *phar, phar_entry_info *entry, char **error);
-int phar_postprocess_file(phar_entry_data *idata, php_uint32 crc32, char **error, int process_zip);
+int phar_postprocess_file(phar_entry_data *idata, uint32_t crc32, char **error, int process_zip);
int phar_open_from_filename(char *fname, int fname_len, char *alias, int alias_len, int options, phar_archive_data** pphar, char **error);
int phar_open_or_create_filename(char *fname, int fname_len, char *alias, int alias_len, int is_data, int options, phar_archive_data** pphar, char **error);
int phar_create_or_parse_filename(char *fname, int fname_len, char *alias, int alias_len, int is_data, int options, phar_archive_data** pphar, char **error);
@@ -552,7 +537,7 @@ int phar_open_executed_filename(char *alias, int alias_len, char **error);
int phar_free_alias(phar_archive_data *phar, char *alias, int alias_len);
int phar_get_archive(phar_archive_data **archive, char *fname, int fname_len, char *alias, int alias_len, char **error);
int phar_open_parsed_phar(char *fname, int fname_len, char *alias, int alias_len, int is_data, int options, phar_archive_data** pphar, char **error);
-int phar_verify_signature(php_stream *fp, size_t end_of_phar, php_uint32 sig_type, char *sig, int sig_len, char *fname, char **signature, int *signature_len, char **error);
+int phar_verify_signature(php_stream *fp, size_t end_of_phar, uint32_t sig_type, char *sig, int sig_len, char *fname, char **signature, int *signature_len, char **error);
int phar_create_signature(phar_archive_data *phar, php_stream *fp, char **signature, int *signature_length, char **error);
/* utility functions */
@@ -566,7 +551,7 @@ int phar_mount_entry(phar_archive_data *phar, char *filename, int filename_len,
zend_string *phar_find_in_include_path(char *file, int file_len, phar_archive_data **pphar);
char *phar_fix_filepath(char *path, int *new_len, int use_cwd);
phar_entry_info * phar_open_jit(phar_archive_data *phar, phar_entry_info *entry, char **error);
-int phar_parse_metadata(char **buffer, zval *metadata, php_uint32 zip_metadata_len);
+int phar_parse_metadata(char **buffer, zval *metadata, uint32_t zip_metadata_len);
void destroy_phar_manifest_entry(zval *zv);
int phar_seek_efp(phar_entry_info *entry, zend_off_t offset, int whence, zend_off_t position, int follow_links);
php_stream *phar_get_efp(phar_entry_info *entry, int follow_links);
@@ -580,7 +565,7 @@ int phar_copy_on_write(phar_archive_data **pphar);
/* tar functions in tar.c */
int phar_is_tar(char *buf, char *fname);
-int phar_parse_tarfile(php_stream* fp, char *fname, int fname_len, char *alias, int alias_len, phar_archive_data** pphar, int is_data, php_uint32 compression, char **error);
+int phar_parse_tarfile(php_stream* fp, char *fname, int fname_len, char *alias, int alias_len, phar_archive_data** pphar, int is_data, uint32_t compression, char **error);
int phar_open_or_create_tar(char *fname, int fname_len, char *alias, int alias_len, int is_data, int options, phar_archive_data** pphar, char **error);
int phar_tar_flush(phar_archive_data *phar, char *user_stub, zend_long len, int defaultstub, char **error);
diff --git a/ext/phar/phar_object.c b/ext/phar/phar_object.c
index 3afe8599b6..79cc9c1e31 100644
--- a/ext/phar/phar_object.c
+++ b/ext/phar/phar_object.c
@@ -25,16 +25,7 @@
static zend_class_entry *phar_ce_archive;
static zend_class_entry *phar_ce_data;
static zend_class_entry *phar_ce_PharException;
-
-#if HAVE_SPL
static zend_class_entry *phar_ce_entry;
-#endif
-
-#if PHP_VERSION_ID >= 50300
-# define PHAR_ARG_INFO
-#else
-# define PHAR_ARG_INFO static
-#endif
static int phar_file_type(HashTable *mimes, char *file, char **mime_type) /* {{{ */
{
@@ -694,6 +685,7 @@ PHP_METHOD(Phar, webPhar)
if (free_pathinfo) {
efree(path_info);
}
+ efree(pt);
return;
}
@@ -711,6 +703,7 @@ PHP_METHOD(Phar, webPhar)
if (free_pathinfo) {
efree(path_info);
}
+ efree(pt);
return;
}
@@ -720,6 +713,7 @@ PHP_METHOD(Phar, webPhar)
efree(path_info);
}
zend_throw_exception_ex(phar_ce_PharException, 0, "phar error: rewrite callback must return a string or false");
+ efree(pt);
return;
}
@@ -740,6 +734,7 @@ PHP_METHOD(Phar, webPhar)
if (free_pathinfo) {
efree(path_info);
}
+ efree(pt);
zend_bailout();
return;
@@ -747,6 +742,7 @@ PHP_METHOD(Phar, webPhar)
if (free_pathinfo) {
efree(path_info);
}
+ efree(pt);
zend_throw_exception_ex(phar_ce_PharException, 0, "phar error: rewrite callback must return a string or false");
return;
@@ -1105,7 +1101,6 @@ PHP_METHOD(Phar, isValidPharFilename)
}
/* }}} */
-#if HAVE_SPL
/**
* from spl_directory
*/
@@ -1138,7 +1133,6 @@ static spl_other_handler phar_spl_foreign_handler = {
phar_spl_foreign_dtor,
phar_spl_foreign_clone
};
-#endif /* HAVE_SPL */
/* {{{ proto void Phar::__construct(string fname [, int flags [, string alias]])
* Construct a Phar archive object
@@ -1151,9 +1145,6 @@ static spl_other_handler phar_spl_foreign_handler = {
*/
PHP_METHOD(Phar, __construct)
{
-#if !HAVE_SPL
- zend_throw_exception_ex(zend_ce_exception, 0, "Cannot instantiate Phar object without SPL extension");
-#else
char *fname, *alias = NULL, *error, *arch = NULL, *entry = NULL, *save_fname;
size_t fname_len, alias_len = 0;
int arch_len, entry_len, is_data;
@@ -1281,7 +1272,6 @@ PHP_METHOD(Phar, __construct)
phar_obj->spl.info_class = phar_ce_entry;
efree(fname);
-#endif /* HAVE_SPL */
}
/* }}} */
@@ -1370,7 +1360,7 @@ PHP_METHOD(Phar, unlinkArchive)
zname_len = (int)strlen(zname);
if (zname_len > 7 && !memcmp(zname, "phar://", 7) && SUCCESS == phar_split_fname(zname, zname_len, &arch, &arch_len, &entry, &entry_len, 2, 0)) {
- if (arch_len == fname_len && !memcmp(arch, fname, arch_len)) {
+ if ((size_t)arch_len == fname_len && !memcmp(arch, fname, arch_len)) {
zend_throw_exception_ex(phar_ce_PharException, 0, "phar archive \"%s\" cannot be unlinked from within itself", fname);
efree(arch);
efree(entry);
@@ -1403,8 +1393,6 @@ PHP_METHOD(Phar, unlinkArchive)
}
/* }}} */
-#if HAVE_SPL
-
#define PHAR_ARCHIVE_OBJECT() \
zval *zobj = getThis(); \
phar_archive_object *phar_obj = (phar_archive_object*)((char*)Z_OBJ_P(zobj) - Z_OBJ_P(zobj)->handlers->offset); \
@@ -1462,7 +1450,7 @@ static int phar_build(zend_object_iterator *iter, void *puser) /* {{{ */
if (!value) {
/* failure in get_current_data */
- zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "Iterator %v returned no value", ZSTR_VAL(ce->name));
+ zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "Iterator %s returned no value", ZSTR_VAL(ce->name));
return ZEND_HASH_APPLY_STOP;
}
@@ -1473,7 +1461,7 @@ static int phar_build(zend_object_iterator *iter, void *puser) /* {{{ */
php_stream_from_zval_no_verify(fp, value);
if (!fp) {
- zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Iterator %v returned an invalid stream handle", ZSTR_VAL(ce->name));
+ zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Iterator %s returned an invalid stream handle", ZSTR_VAL(ce->name));
return ZEND_HASH_APPLY_STOP;
}
@@ -1487,7 +1475,7 @@ static int phar_build(zend_object_iterator *iter, void *puser) /* {{{ */
if (Z_TYPE(key) != IS_STRING) {
zval_dtor(&key);
- zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "Iterator %v returned an invalid key (must return a string)", ZSTR_VAL(ce->name));
+ zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "Iterator %s returned an invalid key (must return a string)", ZSTR_VAL(ce->name));
return ZEND_HASH_APPLY_STOP;
}
@@ -1503,7 +1491,7 @@ static int phar_build(zend_object_iterator *iter, void *puser) /* {{{ */
save = str_key;
zval_dtor(&key);
} else {
- zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "Iterator %v returned an invalid key (must return a string)", ZSTR_VAL(ce->name));
+ zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "Iterator %s returned an invalid key (must return a string)", ZSTR_VAL(ce->name));
return ZEND_HASH_APPLY_STOP;
}
@@ -1517,7 +1505,7 @@ static int phar_build(zend_object_iterator *iter, void *puser) /* {{{ */
spl_filesystem_object *intern = (spl_filesystem_object*)((char*)Z_OBJ_P(value) - Z_OBJ_P(value)->handlers->offset);
if (!base_len) {
- zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Iterator %v returns an SplFileInfo object, so base directory must be specified", ZSTR_VAL(ce->name));
+ zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Iterator %s returns an SplFileInfo object, so base directory must be specified", ZSTR_VAL(ce->name));
return ZEND_HASH_APPLY_STOP;
}
@@ -1561,7 +1549,7 @@ static int phar_build(zend_object_iterator *iter, void *puser) /* {{{ */
}
/* fall-through */
default:
- zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "Iterator %v returned an invalid value (must return a string)", ZSTR_VAL(ce->name));
+ zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "Iterator %s returned an invalid value (must return a string)", ZSTR_VAL(ce->name));
return ZEND_HASH_APPLY_STOP;
}
@@ -1601,7 +1589,7 @@ phar_spl_fileinfo:
}
} else {
- zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "Iterator %v returned a path \"%s\" that is not in the base directory \"%s\"", ZSTR_VAL(ce->name), fname, base);
+ zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "Iterator %s returned a path \"%s\" that is not in the base directory \"%s\"", ZSTR_VAL(ce->name), fname, base);
if (save) {
efree(save);
@@ -1621,7 +1609,7 @@ phar_spl_fileinfo:
if (Z_TYPE(key) != IS_STRING) {
zval_dtor(&key);
- zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "Iterator %v returned an invalid key (must return a string)", ZSTR_VAL(ce->name));
+ zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "Iterator %s returned an invalid key (must return a string)", ZSTR_VAL(ce->name));
return ZEND_HASH_APPLY_STOP;
}
@@ -1637,28 +1625,13 @@ phar_spl_fileinfo:
save = str_key;
zval_dtor(&key);
} else {
- zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "Iterator %v returned an invalid key (must return a string)", ZSTR_VAL(ce->name));
+ zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "Iterator %s returned an invalid key (must return a string)", ZSTR_VAL(ce->name));
return ZEND_HASH_APPLY_STOP;
}
}
-#if PHP_API_VERSION < 20100412
- if (PG(safe_mode) && (!php_checkuid(fname, NULL, CHECKUID_ALLOW_ONLY_FILE))) {
- zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "Iterator %v returned a path \"%s\" that safe mode prevents opening", ZSTR_VAL(ce->name), fname);
-
- if (save) {
- efree(save);
- }
-
- if (temp) {
- efree(temp);
- }
-
- return ZEND_HASH_APPLY_STOP;
- }
-#endif
if (php_check_open_basedir(fname)) {
- zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "Iterator %v returned a path \"%s\" that open_basedir prevents opening", ZSTR_VAL(ce->name), fname);
+ zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "Iterator %s returned a path \"%s\" that open_basedir prevents opening", ZSTR_VAL(ce->name), fname);
if (save) {
efree(save);
@@ -1675,7 +1648,7 @@ phar_spl_fileinfo:
fp = php_stream_open_wrapper(fname, "rb", STREAM_MUST_SEEK|0, &opened);
if (!fp) {
- zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "Iterator %v returned a file that could not be opened \"%s\"", ZSTR_VAL(ce->name), fname);
+ zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "Iterator %s returned a file that could not be opened \"%s\"", ZSTR_VAL(ce->name), fname);
if (save) {
efree(save);
@@ -2263,7 +2236,7 @@ its_ok:
}
/* }}} */
-static zend_object *phar_convert_to_other(phar_archive_data *source, int convert, char *ext, php_uint32 flags) /* {{{ */
+static zend_object *phar_convert_to_other(phar_archive_data *source, int convert, char *ext, uint32_t flags) /* {{{ */
{
phar_archive_data *phar;
phar_entry_info *entry, newentry;
@@ -2387,7 +2360,7 @@ PHP_METHOD(Phar, convertToExecutable)
char *ext = NULL;
int is_data;
size_t ext_len = 0;
- php_uint32 flags;
+ uint32_t flags;
zend_object *ret;
/* a number that is not 0, 1 or 2 (Which is also Greg's birthday, so there) */
zend_long format = 9021976, method = 9021976;
@@ -2491,7 +2464,7 @@ PHP_METHOD(Phar, convertToData)
char *ext = NULL;
int is_data;
size_t ext_len = 0;
- php_uint32 flags;
+ uint32_t flags;
zend_object *ret;
/* a number that is not 0, 1 or 2 (Which is also Greg's birthday so there) */
zend_long format = 9021976, method = 9021976;
@@ -2757,7 +2730,7 @@ PHP_METHOD(Phar, setAlias)
if (ZEND_SIZE_T_INT_OVFL(alias_len)) {
RETURN_FALSE;
}
- if (alias_len == phar_obj->archive->alias_len && memcmp(phar_obj->archive->alias, alias, alias_len) == 0) {
+ if (alias_len == (size_t)phar_obj->archive->alias_len && memcmp(phar_obj->archive->alias, alias, alias_len) == 0) {
RETURN_TRUE;
}
if (alias_len && NULL != (fd_ptr = zend_hash_str_find_ptr(&(PHAR_G(phar_alias_map)), alias, alias_len))) {
@@ -3173,7 +3146,7 @@ PHP_METHOD(Phar, getModified)
static int phar_set_compression(zval *zv, void *argument) /* {{{ */
{
phar_entry_info *entry = (phar_entry_info *)Z_PTR_P(zv);
- php_uint32 compress = *(php_uint32 *)argument;
+ uint32_t compress = *(uint32_t *)argument;
if (entry->is_deleted) {
return ZEND_HASH_APPLY_KEEP;
@@ -3211,7 +3184,7 @@ static int phar_test_compression(zval *zv, void *argument) /* {{{ */
}
/* }}} */
-static void pharobj_set_compression(HashTable *manifest, php_uint32 compress) /* {{{ */
+static void pharobj_set_compression(HashTable *manifest, uint32_t compress) /* {{{ */
{
zend_hash_apply_with_argument(manifest, phar_set_compression, &compress);
}
@@ -3237,7 +3210,7 @@ PHP_METHOD(Phar, compress)
zend_long method;
char *ext = NULL;
size_t ext_len = 0;
- php_uint32 flags;
+ uint32_t flags;
zend_object *ret;
PHAR_ARCHIVE_OBJECT();
@@ -3346,7 +3319,7 @@ PHP_METHOD(Phar, decompress)
PHP_METHOD(Phar, compressFiles)
{
char *error;
- php_uint32 flags;
+ uint32_t flags;
zend_long method;
PHAR_ARCHIVE_OBJECT();
@@ -3641,7 +3614,7 @@ PHP_METHOD(Phar, offsetGet)
}
if (fname_len >= sizeof(".phar")-1 && !memcmp(fname, ".phar", sizeof(".phar")-1)) {
- zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Cannot directly get any files or directories in magic \".phar\" directory", phar_obj->archive->fname);
+ zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Cannot directly get any files or directories in magic \".phar\" directory");
return;
}
@@ -3667,8 +3640,8 @@ static void phar_add_file(phar_archive_data **pphar, char *filename, int filenam
phar_entry_data *data;
php_stream *contents_file;
- if (filename_len >= sizeof(".phar")-1 && !memcmp(filename, ".phar", sizeof(".phar")-1) && (filename[5] == '/' || filename[5] == '\\' || filename[5] == '\0')) {
- zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Cannot create any files in magic \".phar\" directory", (*pphar)->fname);
+ if (filename_len >= (int)sizeof(".phar")-1 && !memcmp(filename, ".phar", sizeof(".phar")-1) && (filename[5] == '/' || filename[5] == '\\' || filename[5] == '\0')) {
+ zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Cannot create any files in magic \".phar\" directory");
return;
}
@@ -3787,7 +3760,7 @@ PHP_METHOD(Phar, offsetSet)
}
if (fname_len >= sizeof(".phar")-1 && !memcmp(fname, ".phar", sizeof(".phar")-1)) {
- zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Cannot set any files or directories in magic \".phar\" directory", phar_obj->archive->fname);
+ zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Cannot set any files or directories in magic \".phar\" directory");
return;
}
@@ -3895,13 +3868,6 @@ PHP_METHOD(Phar, addFile)
RETURN_FALSE;
}
-#if PHP_API_VERSION < 20100412
- if (PG(safe_mode) && (!php_checkuid(fname, NULL, CHECKUID_ALLOW_ONLY_FILE))) {
- zend_throw_exception_ex(spl_ce_RuntimeException, 0, "phar error: unable to open file \"%s\" to add to phar archive, safe_mode restrictions prevent this", fname);
- return;
- }
-#endif
-
if (!strstr(fname, "://") && php_check_open_basedir(fname)) {
zend_throw_exception_ex(spl_ce_RuntimeException, 0, "phar error: unable to open file \"%s\" to add to phar archive, open_basedir restrictions prevent this", fname);
return;
@@ -4150,13 +4116,6 @@ PHP_METHOD(Phar, delMetadata)
}
}
/* }}} */
-#if PHP_API_VERSION < 20100412
-#define PHAR_OPENBASEDIR_CHECKPATH(filename) \
- (PG(safe_mode) && (!php_checkuid(filename, NULL, CHECKUID_CHECK_FILE_AND_DIR))) || php_check_open_basedir(filename)
-#else
-#define PHAR_OPENBASEDIR_CHECKPATH(filename) \
- php_check_open_basedir(filename)
-#endif
static int phar_extract_file(zend_bool overwrite, phar_entry_info *entry, char *dest, int dest_len, char **error) /* {{{ */
{
@@ -4235,7 +4194,7 @@ static int phar_extract_file(zend_bool overwrite, phar_entry_info *entry, char *
return FAILURE;
}
- if (PHAR_OPENBASEDIR_CHECKPATH(fullpath)) {
+ if (php_check_open_basedir(fullpath)) {
spprintf(error, 4096, "Cannot extract \"%s\" to \"%s\", openbasedir/safe mode restrictions in effect", entry->filename, fullpath);
efree(fullpath);
efree(new_state.cwd);
@@ -4291,11 +4250,7 @@ static int phar_extract_file(zend_bool overwrite, phar_entry_info *entry, char *
return SUCCESS;
}
-#if PHP_API_VERSION < 20100412
- fp = php_stream_open_wrapper(fullpath, "w+b", REPORT_ERRORS|ENFORCE_SAFE_MODE, NULL);
-#else
fp = php_stream_open_wrapper(fullpath, "w+b", REPORT_ERRORS, NULL);
-#endif
if (!fp) {
spprintf(error, 4096, "Cannot extract \"%s\", could not open for writing \"%s\"", entry->filename, fullpath);
@@ -5141,57 +5096,46 @@ PHP_METHOD(PharFileInfo, decompress)
}
/* }}} */
-#endif /* HAVE_SPL */
-
/* {{{ phar methods */
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar___construct, 0, 0, 1)
ZEND_ARG_INFO(0, filename)
ZEND_ARG_INFO(0, flags)
ZEND_ARG_INFO(0, alias)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_createDS, 0, 0, 0)
ZEND_ARG_INFO(0, index)
ZEND_ARG_INFO(0, webindex)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_cancompress, 0, 0, 0)
ZEND_ARG_INFO(0, method)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_isvalidpharfilename, 0, 0, 1)
ZEND_ARG_INFO(0, filename)
ZEND_ARG_INFO(0, executable)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_loadPhar, 0, 0, 1)
ZEND_ARG_INFO(0, filename)
ZEND_ARG_INFO(0, alias)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_mapPhar, 0, 0, 0)
ZEND_ARG_INFO(0, alias)
ZEND_ARG_INFO(0, offset)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_mount, 0, 0, 2)
ZEND_ARG_INFO(0, inphar)
ZEND_ARG_INFO(0, externalfile)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_mungServer, 0, 0, 1)
ZEND_ARG_INFO(0, munglist)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_webPhar, 0, 0, 0)
ZEND_ARG_INFO(0, alias)
ZEND_ARG_INFO(0, index)
@@ -5200,141 +5144,112 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_webPhar, 0, 0, 0)
ZEND_ARG_INFO(0, rewrites)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_running, 0, 0, 0)
ZEND_ARG_INFO(0, retphar)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_ua, 0, 0, 1)
ZEND_ARG_INFO(0, archive)
ZEND_END_ARG_INFO()
-#if HAVE_SPL
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_build, 0, 0, 1)
ZEND_ARG_INFO(0, iterator)
ZEND_ARG_INFO(0, base_directory)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_conv, 0, 0, 0)
ZEND_ARG_INFO(0, format)
ZEND_ARG_INFO(0, compression_type)
ZEND_ARG_INFO(0, file_ext)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_comps, 0, 0, 1)
ZEND_ARG_INFO(0, compression_type)
ZEND_ARG_INFO(0, file_ext)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_decomp, 0, 0, 0)
ZEND_ARG_INFO(0, file_ext)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_comp, 0, 0, 1)
ZEND_ARG_INFO(0, compression_type)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_compo, 0, 0, 0)
ZEND_ARG_INFO(0, compression_type)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_copy, 0, 0, 2)
ZEND_ARG_INFO(0, newfile)
ZEND_ARG_INFO(0, oldfile)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_delete, 0, 0, 1)
ZEND_ARG_INFO(0, entry)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_fromdir, 0, 0, 1)
ZEND_ARG_INFO(0, base_dir)
ZEND_ARG_INFO(0, regex)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_offsetExists, 0, 0, 1)
ZEND_ARG_INFO(0, entry)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_offsetSet, 0, 0, 2)
ZEND_ARG_INFO(0, entry)
ZEND_ARG_INFO(0, value)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_setAlias, 0, 0, 1)
ZEND_ARG_INFO(0, alias)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_setMetadata, 0, 0, 1)
ZEND_ARG_INFO(0, metadata)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_setSigAlgo, 0, 0, 1)
ZEND_ARG_INFO(0, algorithm)
ZEND_ARG_INFO(0, privatekey)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_setStub, 0, 0, 1)
ZEND_ARG_INFO(0, newstub)
ZEND_ARG_INFO(0, maxlen)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_emptydir, 0, 0, 0)
ZEND_ARG_INFO(0, dirname)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_extract, 0, 0, 1)
ZEND_ARG_INFO(0, pathto)
ZEND_ARG_INFO(0, files)
ZEND_ARG_INFO(0, overwrite)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_addfile, 0, 0, 1)
ZEND_ARG_INFO(0, filename)
ZEND_ARG_INFO(0, localname)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_fromstring, 0, 0, 1)
ZEND_ARG_INFO(0, localname)
ZEND_ARG_INFO(0, contents)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_isff, 0, 0, 1)
ZEND_ARG_INFO(0, fileformat)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO(arginfo_phar__void, 0)
ZEND_END_ARG_INFO()
-#endif /* HAVE_SPL */
-
zend_function_entry php_archive_methods[] = {
-#if !HAVE_SPL
- PHP_ME(Phar, __construct, arginfo_phar___construct, ZEND_ACC_PRIVATE)
-#else
PHP_ME(Phar, __construct, arginfo_phar___construct, ZEND_ACC_PUBLIC)
PHP_ME(Phar, __destruct, arginfo_phar__void, ZEND_ACC_PUBLIC)
PHP_ME(Phar, addEmptyDir, arginfo_phar_emptydir, ZEND_ACC_PUBLIC)
@@ -5376,7 +5291,6 @@ zend_function_entry php_archive_methods[] = {
PHP_ME(Phar, setStub, arginfo_phar_setStub, ZEND_ACC_PUBLIC)
PHP_ME(Phar, startBuffering, arginfo_phar__void, ZEND_ACC_PUBLIC)
PHP_ME(Phar, stopBuffering, arginfo_phar__void, ZEND_ACC_PUBLIC)
-#endif
/* static member functions */
PHP_ME(Phar, apiVersion, arginfo_phar__void, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL)
PHP_ME(Phar, canCompress, arginfo_phar_cancompress, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL)
@@ -5396,18 +5310,15 @@ zend_function_entry php_archive_methods[] = {
PHP_FE_END
};
-PHAR_ARG_INFO
+
ZEND_BEGIN_ARG_INFO_EX(arginfo_data___construct, 0, 0, 1)
- ZEND_ARG_INFO(0, filename)
- ZEND_ARG_INFO(0, flags)
- ZEND_ARG_INFO(0, alias)
- ZEND_ARG_INFO(0, fileformat)
+ ZEND_ARG_INFO(0, filename)
+ ZEND_ARG_INFO(0, flags)
+ ZEND_ARG_INFO(0, alias)
+ ZEND_ARG_INFO(0, fileformat)
ZEND_END_ARG_INFO()
zend_function_entry php_data_methods[] = {
-#if !HAVE_SPL
- PHP_ME(Phar, __construct, arginfo_data___construct, ZEND_ACC_PRIVATE)
-#else
PHP_ME(Phar, __construct, arginfo_data___construct, ZEND_ACC_PUBLIC)
PHP_ME(Phar, __destruct, arginfo_phar__void, ZEND_ACC_PUBLIC)
PHP_ME(Phar, addEmptyDir, arginfo_phar_emptydir, ZEND_ACC_PUBLIC)
@@ -5449,7 +5360,6 @@ zend_function_entry php_data_methods[] = {
PHP_ME(Phar, setStub, arginfo_phar_setStub, ZEND_ACC_PUBLIC)
PHP_ME(Phar, startBuffering, arginfo_phar__void, ZEND_ACC_PUBLIC)
PHP_ME(Phar, stopBuffering, arginfo_phar__void, ZEND_ACC_PUBLIC)
-#endif
/* static member functions */
PHP_ME(Phar, apiVersion, arginfo_phar__void, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL)
PHP_ME(Phar, canCompress, arginfo_phar_cancompress, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL)
@@ -5469,13 +5379,10 @@ zend_function_entry php_data_methods[] = {
PHP_FE_END
};
-#if HAVE_SPL
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_entry___construct, 0, 0, 1)
ZEND_ARG_INFO(0, filename)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_entry_chmod, 0, 0, 1)
ZEND_ARG_INFO(0, perms)
ZEND_END_ARG_INFO()
@@ -5498,7 +5405,6 @@ zend_function_entry php_entry_methods[] = {
PHP_ME(PharFileInfo, setMetadata, arginfo_phar_setMetadata, ZEND_ACC_PUBLIC)
PHP_FE_END
};
-#endif /* HAVE_SPL */
zend_function_entry phar_exception_methods[] = {
PHP_FE_END
@@ -5515,7 +5421,6 @@ void phar_object_init(void) /* {{{ */
INIT_CLASS_ENTRY(ce, "PharException", phar_exception_methods);
phar_ce_PharException = zend_register_internal_class_ex(&ce, zend_ce_exception);
-#if HAVE_SPL
INIT_CLASS_ENTRY(ce, "Phar", php_archive_methods);
phar_ce_archive = zend_register_internal_class_ex(&ce, spl_ce_RecursiveDirectoryIterator);
@@ -5528,15 +5433,6 @@ void phar_object_init(void) /* {{{ */
INIT_CLASS_ENTRY(ce, "PharFileInfo", php_entry_methods);
phar_ce_entry = zend_register_internal_class_ex(&ce, spl_ce_SplFileInfo);
-#else
- INIT_CLASS_ENTRY(ce, "Phar", php_archive_methods);
- phar_ce_archive = zend_register_internal_class(&ce);
- phar_ce_archive->ce_flags |= ZEND_ACC_FINAL;
-
- INIT_CLASS_ENTRY(ce, "PharData", php_archive_methods);
- phar_ce_data = zend_register_internal_class(&ce);
- phar_ce_data->ce_flags |= ZEND_ACC_FINAL;
-#endif
REGISTER_PHAR_CLASS_CONST_LONG(phar_ce_archive, "BZ2", PHAR_ENT_COMPRESSED_BZ2)
REGISTER_PHAR_CLASS_CONST_LONG(phar_ce_archive, "GZ", PHAR_ENT_COMPRESSED_GZ)
diff --git a/ext/phar/stream.c b/ext/phar/stream.c
index 69b7f43033..05f510f703 100644
--- a/ext/phar/stream.c
+++ b/ext/phar/stream.c
@@ -45,6 +45,7 @@ php_stream_wrapper_ops phar_stream_wops = {
phar_wrapper_rename, /* rename */
phar_wrapper_mkdir, /* create directory */
phar_wrapper_rmdir, /* remove directory */
+ NULL
};
php_stream_wrapper php_stream_phar_wrapper = {
diff --git a/ext/phar/stub.h b/ext/phar/stub.h
index 710dee66ce..28e32525b5 100644
--- a/ext/phar/stub.h
+++ b/ext/phar/stub.h
@@ -24,11 +24,10 @@ static inline zend_string* phar_get_stub(const char *index_php, const char *web,
static const char newstub1_0[] = "';\n\nif (in_array('phar', stream_get_wrappers()) && class_exists('Phar', 0)) {\nPhar::interceptFileFuncs();\nset_include_path('phar://' . __FILE__ . PATH_SEPARATOR . get_include_path());\nPhar::webPhar(null, $web);\ninclude 'phar://' . __FILE__ . '/' . Extract_Phar::START;\nreturn;\n}\n\nif (@(isset($_SERVER['REQUEST_URI']) && isset($_SERVER['REQUEST_METHOD']) && ($_SERVER['REQUEST_METHOD'] == 'GET' || $_SERVER['REQUEST_METHOD'] == 'POST'))) {\nExtract_Phar::go(true);\n$mimes = array(\n'phps' => 2,\n'c' => 'text/plain',\n'cc' => 'text/plain',\n'cpp' => 'text/plain',\n'c++' => 'text/plain',\n'dtd' => 'text/plain',\n'h' => 'text/plain',\n'log' => 'text/plain',\n'rng' => 'text/plain',\n'txt' => 'text/plain',\n'xsd' => 'text/plain',\n'php' => 1,\n'inc' => 1,\n'avi' => 'video/avi',\n'bmp' => 'image/bmp',\n'css' => 'text/css',\n'gif' => 'image/gif',\n'htm' => 'text/html',\n'html' => 'text/html',\n'htmls' => 'text/html',\n'ico' => 'image/x-ico',\n'jpe' => 'image/jpeg',\n'jpg' => 'image/jpeg',\n'jpeg' => 'image/jpeg',\n'js' => 'application/x-javascript',\n'midi' => 'audio/midi',\n'mid' => 'audio/midi',\n'mod' => 'audio/mod',\n'mov' => 'movie/quicktime',\n'mp3' => 'audio/mp3',\n'mpg' => 'video/mpeg',\n'mpeg' => 'video/mpeg',\n'pdf' => 'application/pdf',\n'png' => 'image/png',\n'swf' => 'application/shockwave-flash',\n'tif' => 'image/tiff',\n'tiff' => 'image/tiff',\n'wav' => 'audio/wav',\n'xbm' => 'image/xbm',\n'xml' => 'text/xml',\n);\n\nheader(\"Cache-Control: no-cache, must-revalidate\");\nheader(\"Pragma: no-cache\");\n\n$basename = basename(__FILE__);\nif (!strpos($_SERVER['REQUEST_URI'], $basename)) {\nchdir(Extract_Phar::$temp);\ninclude $web;\nreturn;\n}\n$pt = substr($_SERVER['REQUEST_URI'], strpos($_SERVER['REQUEST_URI'], $basename) + strlen($basename));\nif (!$pt || $pt == '/') {\n$pt = $web;\nheader('HTTP/1.1 301 Moved Permanently');\nheader('Location: ' . $_SERVER['REQUEST_URI'] . '/' . $pt);\nexit;\n}\n$a = realpath(Extract_Phar::$temp . DIRECTORY_SEPARATOR . $pt);\nif (!$a || strlen(dirname($a)) < strlen(";
static const char newstub1_1[] = "Extract_Phar::$temp)) {\nheader('HTTP/1.0 404 Not Found');\necho \"<html>\\n <head>\\n <title>File Not Found<title>\\n </head>\\n <body>\\n <h1>404 - File \", $pt, \" Not Found</h1>\\n </body>\\n</html>\";\nexit;\n}\n$b = pathinfo($a);\nif (!isset($b['extension'])) {\nheader('Content-Type: text/plain');\nheader('Content-Length: ' . filesize($a));\nreadfile($a);\nexit;\n}\nif (isset($mimes[$b['extension']])) {\nif ($mimes[$b['extension']] === 1) {\ninclude $a;\nexit;\n}\nif ($mimes[$b['extension']] === 2) {\nhighlight_file($a);\nexit;\n}\nheader('Content-Type: ' .$mimes[$b['extension']]);\nheader('Content-Length: ' . filesize($a));\nreadfile($a);\nexit;\n}\n}\n\nclass Extract_Phar\n{\nstatic $temp;\nstatic $origdir;\nconst GZ = 0x1000;\nconst BZ2 = 0x2000;\nconst MASK = 0x3000;\nconst START = '";
static const char newstub2[] = "';\nconst LEN = ";
- static const char newstub3_0[] = ";\n\nstatic function go($return = false)\n{\n$fp = fopen(__FILE__, 'rb');\nfseek($fp, self::LEN);\n$L = unpack('V', $a = (binary)fread($fp, 4));\n$m = (binary)'';\n\ndo {\n$read = 8192;\nif ($L[1] - strlen($m) < 8192) {\n$read = $L[1] - strlen($m);\n}\n$last = (binary)fread($fp, $read);\n$m .= $last;\n} while (strlen($last) && strlen($m) < $L[1]);\n\nif (strlen($m) < $L[1]) {\ndie('ERROR: manifest length read was \"' .\nstrlen($m) .'\" should be \"' .\n$L[1] . '\"');\n}\n\n$info = self::_unpack($m);\n$f = $info['c'];\n\nif ($f & self::GZ) {\nif (!function_exists('gzinflate')) {\ndie('Error: zlib extension is not enabled -' .\n' gzinflate() function needed for zlib-compressed .phars');\n}\n}\n\nif ($f & self::BZ2) {\nif (!function_exists('bzdecompress')) {\ndie('Error: bzip2 extension is not enabled -' .\n' bzdecompress() function needed for bz2-compressed .phars');\n}\n}\n\n$temp = self::tmpdir();\n\nif (!$temp || !is_writable($temp)) {\n$sessionpath = session_save_path();\nif (strpos ($sessionpath, \";\") !== false)\n$sessionpath = substr ($sessionpath, strpos ($sessionpath, \";\")+1);\nif (!file_exists($sessionpath) || !is_dir($sessionpath)) {\ndie('Could not locate temporary directory to extract phar');\n}\n$temp = $sessionpath;\n}\n\n$temp .= '/pharextract/'.basename(__FILE__, '.phar');\nself::$temp = $temp;\nself::$origdir = getcwd();\n@mkdir($temp, 0777, true);\n$temp = realpath($temp);\n\nif (!file_exists($temp . DIRECTORY_SEPARATOR . md5_file(__FILE__))) {\nself::_removeTmpFiles($temp, getcwd());\n@mkdir($temp, 0777, true);\n@file_put_contents($temp . '/' . md5_file(__FILE__), '');\n\nforeach ($info['m'] as $path => $file) {\n$a = !file_exists(dirname($temp . '/' . $path));\n@mkdir(dirname($temp . '/' . $path), 0777, true);\nclearstatcache();\n\nif ($path[strlen($path) - 1] == '/') {\n@mkdir($temp . '/' . $path, 0777);\n} else {\nfile_put_contents($temp . '/' . $path, self::extractFile($path, $file, $fp));\n@chmod($temp . '/' . $path, 0666);\n}\n}\n}\n\nchdir($temp);\n\nif (!$return) {\ninclude self::ST";
- static const char newstub3_1[] = "ART;\n}\n}\n\nstatic function tmpdir()\n{\nif (strpos(PHP_OS, 'WIN') !== false) {\nif ($var = getenv('TMP') ? getenv('TMP') : getenv('TEMP')) {\nreturn $var;\n}\nif (is_dir('/temp') || mkdir('/temp')) {\nreturn realpath('/temp');\n}\nreturn false;\n}\nif ($var = getenv('TMPDIR')) {\nreturn $var;\n}\nreturn realpath('/tmp');\n}\n\nstatic function _unpack($m)\n{\n$info = unpack('V', substr($m, 0, 4));\n $l = unpack('V', substr($m, 10, 4));\n$m = substr($m, 14 + $l[1]);\n$s = unpack('V', substr($m, 0, 4));\n$o = 0;\n$start = 4 + $s[1];\n$ret['c'] = 0;\n\nfor ($i = 0; $i < $info[1]; $i++) {\n $len = unpack('V', substr($m, $start, 4));\n$start += 4;\n $savepath = substr($m, $start, $len[1]);\n$start += $len[1];\n $ret['m'][$savepath] = array_values(unpack('Va/Vb/Vc/Vd/Ve/Vf', substr($m, $start, 24)));\n$ret['m'][$savepath][3] = sprintf('%u', $ret['m'][$savepath][3]\n& 0xffffffff);\n$ret['m'][$savepath][7] = $o;\n$o += $ret['m'][$savepath][2];\n$start += 24 + $ret['m'][$savepath][5];\n$ret['c'] |= $ret['m'][$savepath][4] & self::MASK;\n}\nreturn $ret;\n}\n\nstatic function extractFile($path, $entry, $fp)\n{\n$data = '';\n$c = $entry[2];\n\nwhile ($c) {\nif ($c < 8192) {\n$data .= @fread($fp, $c);\n$c = 0;\n} else {\n$c -= 8192;\n$data .= @fread($fp, 8192);\n}\n}\n\nif ($entry[4] & self::GZ) {\n$data = gzinflate($data);\n} elseif ($entry[4] & self::BZ2) {\n$data = bzdecompress($data);\n}\n\nif (strlen($data) != $entry[0]) {\ndie(\"Invalid internal .phar file (size error \" . strlen($data) . \" != \" .\n$stat[7] . \")\");\n}\n\nif ($entry[3] != sprintf(\"%u\", crc32((binary)$data) & 0xffffffff)) {\ndie(\"Invalid internal .phar file (checksum error)\");\n}\n\nreturn $data;\n}\n\nstatic function _removeTmpFiles($temp, $origdir)\n{\nchdir($temp);\n\nforeach (glob('*') as $f) {\nif (file_exists($f)) {\nis_dir($f) ? @rmdir($f) : @unlink($f);\nif (file_exists($f) && is_dir($f)) {\nself::_removeTmpFiles($f, getcwd());\n}\n}\n}\n\n@rmdir($temp);\nclearstatcache();\nchdir($origdir);\n}\n}\n\nExtract_Phar::go();\n__HALT_COMPIL";
- static const char newstub3_2[] = "ER(); ?>";
+ static const char newstub3_0[] = ";\n\nstatic function go($return = false)\n{\n$fp = fopen(__FILE__, 'rb');\nfseek($fp, self::LEN);\n$L = unpack('V', $a = fread($fp, 4));\n$m = '';\n\ndo {\n$read = 8192;\nif ($L[1] - strlen($m) < 8192) {\n$read = $L[1] - strlen($m);\n}\n$last = fread($fp, $read);\n$m .= $last;\n} while (strlen($last) && strlen($m) < $L[1]);\n\nif (strlen($m) < $L[1]) {\ndie('ERROR: manifest length read was \"' .\nstrlen($m) .'\" should be \"' .\n$L[1] . '\"');\n}\n\n$info = self::_unpack($m);\n$f = $info['c'];\n\nif ($f & self::GZ) {\nif (!function_exists('gzinflate')) {\ndie('Error: zlib extension is not enabled -' .\n' gzinflate() function needed for zlib-compressed .phars');\n}\n}\n\nif ($f & self::BZ2) {\nif (!function_exists('bzdecompress')) {\ndie('Error: bzip2 extension is not enabled -' .\n' bzdecompress() function needed for bz2-compressed .phars');\n}\n}\n\n$temp = self::tmpdir();\n\nif (!$temp || !is_writable($temp)) {\n$sessionpath = session_save_path();\nif (strpos ($sessionpath, \";\") !== false)\n$sessionpath = substr ($sessionpath, strpos ($sessionpath, \";\")+1);\nif (!file_exists($sessionpath) || !is_dir($sessionpath)) {\ndie('Could not locate temporary directory to extract phar');\n}\n$temp = $sessionpath;\n}\n\n$temp .= '/pharextract/'.basename(__FILE__, '.phar');\nself::$temp = $temp;\nself::$origdir = getcwd();\n@mkdir($temp, 0777, true);\n$temp = realpath($temp);\n\nif (!file_exists($temp . DIRECTORY_SEPARATOR . md5_file(__FILE__))) {\nself::_removeTmpFiles($temp, getcwd());\n@mkdir($temp, 0777, true);\n@file_put_contents($temp . '/' . md5_file(__FILE__), '');\n\nforeach ($info['m'] as $path => $file) {\n$a = !file_exists(dirname($temp . '/' . $path));\n@mkdir(dirname($temp . '/' . $path), 0777, true);\nclearstatcache();\n\nif ($path[strlen($path) - 1] == '/') {\n@mkdir($temp . '/' . $path, 0777);\n} else {\nfile_put_contents($temp . '/' . $path, self::extractFile($path, $file, $fp));\n@chmod($temp . '/' . $path, 0666);\n}\n}\n}\n\nchdir($temp);\n\nif (!$return) {\ninclude self::START;\n}\n}\n\nstatic fun";
+ static const char newstub3_1[] = "ction tmpdir()\n{\nif (strpos(PHP_OS, 'WIN') !== false) {\nif ($var = getenv('TMP') ? getenv('TMP') : getenv('TEMP')) {\nreturn $var;\n}\nif (is_dir('/temp') || mkdir('/temp')) {\nreturn realpath('/temp');\n}\nreturn false;\n}\nif ($var = getenv('TMPDIR')) {\nreturn $var;\n}\nreturn realpath('/tmp');\n}\n\nstatic function _unpack($m)\n{\n$info = unpack('V', substr($m, 0, 4));\n $l = unpack('V', substr($m, 10, 4));\n$m = substr($m, 14 + $l[1]);\n$s = unpack('V', substr($m, 0, 4));\n$o = 0;\n$start = 4 + $s[1];\n$ret['c'] = 0;\n\nfor ($i = 0; $i < $info[1]; $i++) {\n $len = unpack('V', substr($m, $start, 4));\n$start += 4;\n $savepath = substr($m, $start, $len[1]);\n$start += $len[1];\n $ret['m'][$savepath] = array_values(unpack('Va/Vb/Vc/Vd/Ve/Vf', substr($m, $start, 24)));\n$ret['m'][$savepath][3] = sprintf('%u', $ret['m'][$savepath][3]\n& 0xffffffff);\n$ret['m'][$savepath][7] = $o;\n$o += $ret['m'][$savepath][2];\n$start += 24 + $ret['m'][$savepath][5];\n$ret['c'] |= $ret['m'][$savepath][4] & self::MASK;\n}\nreturn $ret;\n}\n\nstatic function extractFile($path, $entry, $fp)\n{\n$data = '';\n$c = $entry[2];\n\nwhile ($c) {\nif ($c < 8192) {\n$data .= @fread($fp, $c);\n$c = 0;\n} else {\n$c -= 8192;\n$data .= @fread($fp, 8192);\n}\n}\n\nif ($entry[4] & self::GZ) {\n$data = gzinflate($data);\n} elseif ($entry[4] & self::BZ2) {\n$data = bzdecompress($data);\n}\n\nif (strlen($data) != $entry[0]) {\ndie(\"Invalid internal .phar file (size error \" . strlen($data) . \" != \" .\n$stat[7] . \")\");\n}\n\nif ($entry[3] != sprintf(\"%u\", crc32($data) & 0xffffffff)) {\ndie(\"Invalid internal .phar file (checksum error)\");\n}\n\nreturn $data;\n}\n\nstatic function _removeTmpFiles($temp, $origdir)\n{\nchdir($temp);\n\nforeach (glob('*') as $f) {\nif (file_exists($f)) {\nis_dir($f) ? @rmdir($f) : @unlink($f);\nif (file_exists($f) && is_dir($f)) {\nself::_removeTmpFiles($f, getcwd());\n}\n}\n}\n\n@rmdir($temp);\nclearstatcache();\nchdir($origdir);\n}\n}\n\nExtract_Phar::go();\n__HALT_COMPILER(); ?>";
- static const int newstub_len = 6665;
+ static const int newstub_len = 6633;
- return strpprintf(name_len + web_len + newstub_len, "%s%s%s%s%s%s%d%s%s%s", newstub0, web, newstub1_0, newstub1_1, index_php, newstub2, name_len + web_len + newstub_len, newstub3_0, newstub3_1, newstub3_2);
+ return strpprintf(name_len + web_len + newstub_len, "%s%s%s%s%s%s%d%s%s", newstub0, web, newstub1_0, newstub1_1, index_php, newstub2, name_len + web_len + newstub_len, newstub3_0, newstub3_1);
}
diff --git a/ext/phar/tar.c b/ext/phar/tar.c
index 37663ca917..198d27605b 100644
--- a/ext/phar/tar.c
+++ b/ext/phar/tar.c
@@ -19,9 +19,9 @@
#include "phar_internal.h"
-static php_uint32 phar_tar_number(char *buf, int len) /* {{{ */
+static uint32_t phar_tar_number(char *buf, int len) /* {{{ */
{
- php_uint32 num = 0;
+ uint32_t num = 0;
int i = 0;
while (i < len && buf[i] == ' ') {
@@ -62,7 +62,7 @@ static php_uint32 phar_tar_number(char *buf, int len) /* {{{ */
* (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 int phar_tar_octal(char *buf, php_uint32 val, int len) /* {{{ */
+static int phar_tar_octal(char *buf, uint32_t val, int len) /* {{{ */
{
char *p = buf;
int s = len;
@@ -84,9 +84,9 @@ static int phar_tar_octal(char *buf, php_uint32 val, int len) /* {{{ */
}
/* }}} */
-static php_uint32 phar_tar_checksum(char *buf, int len) /* {{{ */
+static uint32_t phar_tar_checksum(char *buf, int len) /* {{{ */
{
- php_uint32 sum = 0;
+ uint32_t sum = 0;
char *end = buf + len;
while (buf != end) {
@@ -100,8 +100,8 @@ static php_uint32 phar_tar_checksum(char *buf, int len) /* {{{ */
int phar_is_tar(char *buf, char *fname) /* {{{ */
{
tar_header *header = (tar_header *) buf;
- php_uint32 checksum = phar_tar_number(header->checksum, sizeof(header->checksum));
- php_uint32 ret;
+ uint32_t checksum = phar_tar_number(header->checksum, sizeof(header->checksum));
+ uint32_t ret;
char save[sizeof(header->checksum)], *bname;
/* assume that the first filename in a tar won't begin with <?php */
@@ -202,13 +202,13 @@ static size_t strnlen(const char *s, size_t maxlen) {
}
#endif
-int phar_parse_tarfile(php_stream* fp, char *fname, int fname_len, char *alias, int alias_len, phar_archive_data** pphar, int is_data, php_uint32 compression, char **error) /* {{{ */
+int phar_parse_tarfile(php_stream* fp, char *fname, int fname_len, char *alias, int alias_len, phar_archive_data** pphar, int is_data, uint32_t compression, char **error) /* {{{ */
{
char buf[512], *actual_alias = NULL, *p;
phar_entry_info entry = {0};
size_t pos = 0, read, totalsize;
tar_header *hdr;
- php_uint32 sum1, sum2, size, old;
+ uint32_t sum1, sum2, size, old;
phar_archive_data *myphar, *actual;
int last_was_longlink = 0;
int linkname_len;
@@ -299,7 +299,7 @@ bail:
| ((((unsigned char*)(buffer))[1]) << 8) \
| (((unsigned char*)(buffer))[0]))
#else
-# define PHAR_GET_32(buffer) (php_uint32) *(buffer)
+# define PHAR_GET_32(buffer) (uint32_t) *(buffer)
#endif
myphar->sig_flags = PHAR_GET_32(buf);
if (FAILURE == phar_verify_signature(fp, php_stream_tell(fp) - size - 512, myphar->sig_flags, buf + 8, size - 8, fname, &myphar->signature, &myphar->sig_len, error)) {
@@ -959,6 +959,8 @@ int phar_tar_flush(phar_archive_data *phar, char *user_stub, zend_long len, int
entry.tar_type = '0';
entry.phar = phar;
entry.fp_type = PHAR_MOD;
+ entry.fp = NULL;
+ entry.filename = NULL;
if (phar->is_persistent) {
if (error) {
@@ -977,6 +979,7 @@ int phar_tar_flush(phar_archive_data *phar, char *user_stub, zend_long len, int
entry.filename_len = sizeof(".phar/alias.txt")-1;
entry.fp = php_stream_fopen_tmpfile();
if (entry.fp == NULL) {
+ efree(entry.filename);
spprintf(error, 0, "phar error: unable to create temporary file");
return -1;
}
@@ -984,6 +987,8 @@ int phar_tar_flush(phar_archive_data *phar, char *user_stub, zend_long len, int
if (error) {
spprintf(error, 0, "unable to set alias in tar-based phar \"%s\"", phar->fname);
}
+ php_stream_close(entry.fp);
+ efree(entry.filename);
return EOF;
}
@@ -993,8 +998,12 @@ int phar_tar_flush(phar_archive_data *phar, char *user_stub, zend_long len, int
if (error) {
spprintf(error, 0, "unable to set alias in tar-based phar \"%s\"", phar->fname);
}
+ php_stream_close(entry.fp);
+ efree(entry.filename);
return EOF;
}
+ /* At this point the entry is saved into the manifest. The manifest destroy
+ routine will care about any resources to be freed. */
} else {
zend_hash_str_del(&phar->manifest, ".phar/alias.txt", sizeof(".phar/alias.txt")-1);
}
@@ -1230,12 +1239,12 @@ nostub:
}
#ifdef WORDS_BIGENDIAN
# define PHAR_SET_32(var, buffer) \
- *(php_uint32 *)(var) = (((((unsigned char*)&(buffer))[3]) << 24) \
+ *(uint32_t *)(var) = (((((unsigned char*)&(buffer))[3]) << 24) \
| ((((unsigned char*)&(buffer))[2]) << 16) \
| ((((unsigned char*)&(buffer))[1]) << 8) \
| (((unsigned char*)&(buffer))[0]))
#else
-# define PHAR_SET_32(var, buffer) *(php_uint32 *)(var) = (php_uint32) (buffer)
+# define PHAR_SET_32(var, buffer) *(uint32_t *)(var) = (uint32_t) (buffer)
#endif
PHAR_SET_32(sigbuf, phar->sig_flags);
PHAR_SET_32(sigbuf + 4, signature_length);
diff --git a/ext/phar/tests/bug74383.phpt b/ext/phar/tests/bug74383.phpt
index 366c4fcb9b..4257629ff5 100644
--- a/ext/phar/tests/bug74383.phpt
+++ b/ext/phar/tests/bug74383.phpt
@@ -17,4 +17,4 @@ echo (int) $rm->getParameters()[0]->isOptional();
--EXPECT--
1
0
-1 \ No newline at end of file
+1
diff --git a/ext/phar/tests/cache_list/copyonwrite11.phar.phpt b/ext/phar/tests/cache_list/copyonwrite11.phar.phpt
index 65388163dc..bf7367c8b6 100644
--- a/ext/phar/tests/cache_list/copyonwrite11.phar.phpt
+++ b/ext/phar/tests/cache_list/copyonwrite11.phar.phpt
@@ -18,5 +18,5 @@ echo strlen($p2->getStub()),"\n";
echo "ok\n";
__HALT_COMPILER(); ?>
"
-6685
+6653
ok \ No newline at end of file
diff --git a/ext/phar/tests/cache_list/files/frontcontroller17.phar b/ext/phar/tests/cache_list/files/frontcontroller17.phar
index b83d41fd5b..d9b8330e63 100644
--- a/ext/phar/tests/cache_list/files/frontcontroller17.phar
+++ b/ext/phar/tests/cache_list/files/frontcontroller17.phar
Binary files differ
diff --git a/ext/phar/tests/cache_list/files/frontcontroller17.phar.inc b/ext/phar/tests/cache_list/files/frontcontroller17.phar.inc
index 85b8729f31..715479552a 100644
--- a/ext/phar/tests/cache_list/files/frontcontroller17.phar.inc
+++ b/ext/phar/tests/cache_list/files/frontcontroller17.phar.inc
@@ -6,7 +6,7 @@ echo "hi";
';
$a->setStub('<?php
try {
-Phar::webPhar("test.phar", "/index.php", null, array(), "sort");
+Phar::webPhar("test.phar", "/index.php", null, array(), function() { throw new Exception; });
} catch (Exception $e) {
die($e->getMessage() . "\n");
}
diff --git a/ext/phar/tests/cache_list/files/nophar.phar b/ext/phar/tests/cache_list/files/nophar.phar
index 4eb3083c92..e6bf37a845 100644
--- a/ext/phar/tests/cache_list/files/nophar.phar
+++ b/ext/phar/tests/cache_list/files/nophar.phar
Binary files differ
diff --git a/ext/phar/tests/cache_list/files/openssl.phar b/ext/phar/tests/cache_list/files/openssl.phar
index f3864d7faa..1e6f5e2f7e 100644
--- a/ext/phar/tests/cache_list/files/openssl.phar
+++ b/ext/phar/tests/cache_list/files/openssl.phar
Binary files differ
diff --git a/ext/phar/tests/cache_list/frontcontroller32.phpt b/ext/phar/tests/cache_list/frontcontroller32.phpt
index 59116907a5..cb9abb8c19 100644
--- a/ext/phar/tests/cache_list/frontcontroller32.phpt
+++ b/ext/phar/tests/cache_list/frontcontroller32.phpt
@@ -13,4 +13,4 @@ Content-type: text/html; charset=UTF-8
--FILE_EXTERNAL--
files/frontcontroller17.phar
--EXPECTF--
-%ahar error: failed to call rewrite callback \ No newline at end of file
+%ahar error: rewrite callback must return a string or false
diff --git a/ext/phar/tests/files/frontcontroller17.phar b/ext/phar/tests/files/frontcontroller17.phar
index b83d41fd5b..4dab78a9ec 100644
--- a/ext/phar/tests/files/frontcontroller17.phar
+++ b/ext/phar/tests/files/frontcontroller17.phar
Binary files differ
diff --git a/ext/phar/tests/files/frontcontroller17.phar.inc b/ext/phar/tests/files/frontcontroller17.phar.inc
index 85b8729f31..715479552a 100644
--- a/ext/phar/tests/files/frontcontroller17.phar.inc
+++ b/ext/phar/tests/files/frontcontroller17.phar.inc
@@ -6,7 +6,7 @@ echo "hi";
';
$a->setStub('<?php
try {
-Phar::webPhar("test.phar", "/index.php", null, array(), "sort");
+Phar::webPhar("test.phar", "/index.php", null, array(), function() { throw new Exception; });
} catch (Exception $e) {
die($e->getMessage() . "\n");
}
diff --git a/ext/phar/tests/files/include_path2.phar b/ext/phar/tests/files/include_path2.phar
index bb0ba79c84..190b60e9a8 100644
--- a/ext/phar/tests/files/include_path2.phar
+++ b/ext/phar/tests/files/include_path2.phar
Binary files differ
diff --git a/ext/phar/tests/files/nophar.phar b/ext/phar/tests/files/nophar.phar
index 4eb3083c92..e9d3fef414 100644
--- a/ext/phar/tests/files/nophar.phar
+++ b/ext/phar/tests/files/nophar.phar
Binary files differ
diff --git a/ext/phar/tests/files/openssl.phar b/ext/phar/tests/files/openssl.phar
index f3864d7faa..1e6f5e2f7e 100644
--- a/ext/phar/tests/files/openssl.phar
+++ b/ext/phar/tests/files/openssl.phar
Binary files differ
diff --git a/ext/phar/tests/frontcontroller32.phpt b/ext/phar/tests/frontcontroller32.phpt
index 58f6fffa00..032d0f571d 100644
--- a/ext/phar/tests/frontcontroller32.phpt
+++ b/ext/phar/tests/frontcontroller32.phpt
@@ -12,4 +12,4 @@ Content-type: text/html; charset=UTF-8
--FILE_EXTERNAL--
files/frontcontroller17.phar
--EXPECTF--
-%ahar error: failed to call rewrite callback \ No newline at end of file
+%ahar error: rewrite callback must return a string or false
diff --git a/ext/phar/tests/open_for_write_existing_b.phpt b/ext/phar/tests/open_for_write_existing_b.phpt
index ef48906de1..448b3a3abc 100644
--- a/ext/phar/tests/open_for_write_existing_b.phpt
+++ b/ext/phar/tests/open_for_write_existing_b.phpt
@@ -21,7 +21,7 @@ $files['b/c.php'] = '<?php echo "This is b/c\n"; ?>';
include 'files/phar_test.inc';
function err_handler($errno, $errstr, $errfile, $errline) {
- echo "Catchable fatal error: $errstr in $errfile on line $errline\n";
+ echo "Recoverable fatal error: $errstr in $errfile on line $errline\n";
}
set_error_handler("err_handler", E_RECOVERABLE_ERROR);
diff --git a/ext/phar/tests/open_for_write_existing_b_5_2.phpt b/ext/phar/tests/open_for_write_existing_b_5_2.phpt
index 03edd54e5f..b72098c773 100644
--- a/ext/phar/tests/open_for_write_existing_b_5_2.phpt
+++ b/ext/phar/tests/open_for_write_existing_b_5_2.phpt
@@ -19,7 +19,7 @@ $files['b/c.php'] = '<?php echo "This is b/c\n"; ?>';
include 'files/phar_test.inc';
function err_handler($errno, $errstr, $errfile, $errline) {
- echo "Catchable fatal error: $errstr in $errfile on line $errline\n";
+ echo "Recoverable fatal error: $errstr in $errfile on line $errline\n";
}
set_error_handler("err_handler", E_RECOVERABLE_ERROR);
diff --git a/ext/phar/tests/open_for_write_newfile_b.phpt b/ext/phar/tests/open_for_write_newfile_b.phpt
index 45131c4892..2926140ff5 100644
--- a/ext/phar/tests/open_for_write_newfile_b.phpt
+++ b/ext/phar/tests/open_for_write_newfile_b.phpt
@@ -21,7 +21,7 @@ $files['b/c.php'] = '<?php echo "This is b/c\n"; ?>';
include 'files/phar_test.inc';
function err_handler($errno, $errstr, $errfile, $errline) {
- echo "Catchable fatal error: $errstr in $errfile on line $errline\n";
+ echo "Recoverable fatal error: $errstr in $errfile on line $errline\n";
}
set_error_handler("err_handler", E_RECOVERABLE_ERROR);
diff --git a/ext/phar/tests/open_for_write_newfile_b_5_2.phpt b/ext/phar/tests/open_for_write_newfile_b_5_2.phpt
index 7d43f1c8ee..8d84379fba 100644
--- a/ext/phar/tests/open_for_write_newfile_b_5_2.phpt
+++ b/ext/phar/tests/open_for_write_newfile_b_5_2.phpt
@@ -19,7 +19,7 @@ $files['b/c.php'] = '<?php echo "This is b/c\n"; ?>';
include 'files/phar_test.inc';
function err_handler($errno, $errstr, $errfile, $errline) {
- echo "Catchable fatal error: $errstr in $errfile on line $errline\n";
+ echo "Recoverable fatal error: $errstr in $errfile on line $errline\n";
}
set_error_handler("err_handler", E_RECOVERABLE_ERROR);
diff --git a/ext/phar/tests/phar_commitwrite.phpt b/ext/phar/tests/phar_commitwrite.phpt
index 36d473e5c2..63878355b0 100644
--- a/ext/phar/tests/phar_commitwrite.phpt
+++ b/ext/phar/tests/phar_commitwrite.phpt
@@ -29,7 +29,7 @@ unlink(dirname(__FILE__) . '/brandnewphar.phar');
__HALT_COMPILER();
?>
--EXPECT--
-int(6683)
+int(6651)
string(200) "<?php
function __autoload($class)
{
diff --git a/ext/phar/tests/phar_convert_repeated.phpt b/ext/phar/tests/phar_convert_repeated.phpt
index e4b1fe41d7..b2ef195ea7 100644
--- a/ext/phar/tests/phar_convert_repeated.phpt
+++ b/ext/phar/tests/phar_convert_repeated.phpt
@@ -123,7 +123,7 @@ NULL
bool(true)
bool(false)
bool(false)
-int(6683)
+int(6651)
NULL
================= convertToZip() =====================
bool(false)
diff --git a/ext/phar/tests/phar_create_in_cwd.phpt b/ext/phar/tests/phar_create_in_cwd.phpt
index 4b0e6594fb..83de7bed28 100644
--- a/ext/phar/tests/phar_create_in_cwd.phpt
+++ b/ext/phar/tests/phar_create_in_cwd.phpt
@@ -32,7 +32,7 @@ __HALT_COMPILER();
unlink(dirname(__FILE__) . '/brandnewphar.phar');
?>
--EXPECT--
-int(6683)
+int(6651)
string(200) "<?php
function __autoload($class)
{
diff --git a/ext/phar/tests/phar_createdefaultstub.phpt b/ext/phar/tests/phar_createdefaultstub.phpt
index abc9ad8e5b..a8648dc311 100644
--- a/ext/phar/tests/phar_createdefaultstub.phpt
+++ b/ext/phar/tests/phar_createdefaultstub.phpt
@@ -34,7 +34,7 @@ echo $e->getMessage() . "\n";
?>
===DONE===
--EXPECT--
-string(6683) "<?php
+string(6651) "<?php
$web = 'index.php';
@@ -144,21 +144,21 @@ const GZ = 0x1000;
const BZ2 = 0x2000;
const MASK = 0x3000;
const START = 'index.php';
-const LEN = 6685;
+const LEN = 6653;
static function go($return = false)
{
$fp = fopen(__FILE__, 'rb');
fseek($fp, self::LEN);
-$L = unpack('V', $a = (binary)fread($fp, 4));
-$m = (binary)'';
+$L = unpack('V', $a = fread($fp, 4));
+$m = '';
do {
$read = 8192;
if ($L[1] - strlen($m) < 8192) {
$read = $L[1] - strlen($m);
}
-$last = (binary)fread($fp, $read);
+$last = fread($fp, $read);
$m .= $last;
} while (strlen($last) && strlen($m) < $L[1]);
@@ -298,7 +298,7 @@ die("Invalid internal .phar file (size error " . strlen($data) . " != " .
$stat[7] . ")");
}
-if ($entry[3] != sprintf("%u", crc32((binary)$data) & 0xffffffff)) {
+if ($entry[3] != sprintf("%u", crc32($data) & 0xffffffff)) {
die("Invalid internal .phar file (checksum error)");
}
@@ -328,7 +328,7 @@ Extract_Phar::go();
__HALT_COMPILER(); ?>"
============================================================================
============================================================================
-string(6694) "<?php
+string(6662) "<?php
$web = 'index.php';
@@ -438,21 +438,21 @@ const GZ = 0x1000;
const BZ2 = 0x2000;
const MASK = 0x3000;
const START = 'my/custom/thingy.php';
-const LEN = 6696;
+const LEN = 6664;
static function go($return = false)
{
$fp = fopen(__FILE__, 'rb');
fseek($fp, self::LEN);
-$L = unpack('V', $a = (binary)fread($fp, 4));
-$m = (binary)'';
+$L = unpack('V', $a = fread($fp, 4));
+$m = '';
do {
$read = 8192;
if ($L[1] - strlen($m) < 8192) {
$read = $L[1] - strlen($m);
}
-$last = (binary)fread($fp, $read);
+$last = fread($fp, $read);
$m .= $last;
} while (strlen($last) && strlen($m) < $L[1]);
@@ -592,7 +592,7 @@ die("Invalid internal .phar file (size error " . strlen($data) . " != " .
$stat[7] . ")");
}
-if ($entry[3] != sprintf("%u", crc32((binary)$data) & 0xffffffff)) {
+if ($entry[3] != sprintf("%u", crc32($data) & 0xffffffff)) {
die("Invalid internal .phar file (checksum error)");
}
@@ -622,7 +622,7 @@ Extract_Phar::go();
__HALT_COMPILER(); ?>"
============================================================================
============================================================================
-int(7074)
+int(7042)
============================================================================
============================================================================
Illegal filename passed in for stub creation, was 401 characters long, and only 400 or less is allowed
@@ -630,7 +630,7 @@ Illegal filename passed in for stub creation, was 401 characters long, and only
============================================================================
============================================================================
============================================================================
-string(6696) "<?php
+string(6664) "<?php
$web = 'the/web.php';
@@ -740,21 +740,21 @@ const GZ = 0x1000;
const BZ2 = 0x2000;
const MASK = 0x3000;
const START = 'my/custom/thingy.php';
-const LEN = 6698;
+const LEN = 6666;
static function go($return = false)
{
$fp = fopen(__FILE__, 'rb');
fseek($fp, self::LEN);
-$L = unpack('V', $a = (binary)fread($fp, 4));
-$m = (binary)'';
+$L = unpack('V', $a = fread($fp, 4));
+$m = '';
do {
$read = 8192;
if ($L[1] - strlen($m) < 8192) {
$read = $L[1] - strlen($m);
}
-$last = (binary)fread($fp, $read);
+$last = fread($fp, $read);
$m .= $last;
} while (strlen($last) && strlen($m) < $L[1]);
@@ -894,7 +894,7 @@ die("Invalid internal .phar file (size error " . strlen($data) . " != " .
$stat[7] . ")");
}
-if ($entry[3] != sprintf("%u", crc32((binary)$data) & 0xffffffff)) {
+if ($entry[3] != sprintf("%u", crc32($data) & 0xffffffff)) {
die("Invalid internal .phar file (checksum error)");
}
@@ -924,6 +924,6 @@ Extract_Phar::go();
__HALT_COMPILER(); ?>"
============================================================================
============================================================================
-int(7074)
+int(7042)
Illegal web filename passed in for stub creation, was 401 characters long, and only 400 or less is allowed
===DONE===
diff --git a/ext/phar/tests/phar_offset_check.phpt b/ext/phar/tests/phar_offset_check.phpt
index fe12534915..a4a65e9e04 100644
--- a/ext/phar/tests/phar_offset_check.phpt
+++ b/ext/phar/tests/phar_offset_check.phpt
@@ -70,8 +70,8 @@ var_dump($phar->getAlias());
Entry .phar/stub.php does not exist
Entry .phar/alias.txt does not exist
Cannot set stub ".phar/stub.php" directly in phar "%sphar_offset_check.phar.php", use setStub
-int(6685)
-int(6685)
+int(6653)
+int(6653)
Cannot set alias ".phar/alias.txt" directly in phar "%sphar_offset_check.phar.php", use setAlias
string(5) "susan"
string(5) "susan"
diff --git a/ext/phar/tests/phar_setdefaultstub.phpt b/ext/phar/tests/phar_setdefaultstub.phpt
index 434e6471f5..a36c005eac 100644
--- a/ext/phar/tests/phar_setdefaultstub.phpt
+++ b/ext/phar/tests/phar_setdefaultstub.phpt
@@ -54,7 +54,7 @@ try {
unlink(dirname(__FILE__) . '/' . basename(__FILE__, '.clean.php') . '.phar');
?>
--EXPECT--
-string(6685) "<?php
+string(6653) "<?php
$web = 'index.php';
@@ -164,21 +164,21 @@ const GZ = 0x1000;
const BZ2 = 0x2000;
const MASK = 0x3000;
const START = 'index.php';
-const LEN = 6685;
+const LEN = 6653;
static function go($return = false)
{
$fp = fopen(__FILE__, 'rb');
fseek($fp, self::LEN);
-$L = unpack('V', $a = (binary)fread($fp, 4));
-$m = (binary)'';
+$L = unpack('V', $a = fread($fp, 4));
+$m = '';
do {
$read = 8192;
if ($L[1] - strlen($m) < 8192) {
$read = $L[1] - strlen($m);
}
-$last = (binary)fread($fp, $read);
+$last = fread($fp, $read);
$m .= $last;
} while (strlen($last) && strlen($m) < $L[1]);
@@ -318,7 +318,7 @@ die("Invalid internal .phar file (size error " . strlen($data) . " != " .
$stat[7] . ")");
}
-if ($entry[3] != sprintf("%u", crc32((binary)$data) & 0xffffffff)) {
+if ($entry[3] != sprintf("%u", crc32($data) & 0xffffffff)) {
die("Invalid internal .phar file (checksum error)");
}
@@ -349,7 +349,7 @@ __HALT_COMPILER(); ?>
"
============================================================================
============================================================================
-string(6696) "<?php
+string(6664) "<?php
$web = 'index.php';
@@ -459,21 +459,21 @@ const GZ = 0x1000;
const BZ2 = 0x2000;
const MASK = 0x3000;
const START = 'my/custom/thingy.php';
-const LEN = 6696;
+const LEN = 6664;
static function go($return = false)
{
$fp = fopen(__FILE__, 'rb');
fseek($fp, self::LEN);
-$L = unpack('V', $a = (binary)fread($fp, 4));
-$m = (binary)'';
+$L = unpack('V', $a = fread($fp, 4));
+$m = '';
do {
$read = 8192;
if ($L[1] - strlen($m) < 8192) {
$read = $L[1] - strlen($m);
}
-$last = (binary)fread($fp, $read);
+$last = fread($fp, $read);
$m .= $last;
} while (strlen($last) && strlen($m) < $L[1]);
@@ -613,7 +613,7 @@ die("Invalid internal .phar file (size error " . strlen($data) . " != " .
$stat[7] . ")");
}
-if ($entry[3] != sprintf("%u", crc32((binary)$data) & 0xffffffff)) {
+if ($entry[3] != sprintf("%u", crc32($data) & 0xffffffff)) {
die("Invalid internal .phar file (checksum error)");
}
@@ -644,7 +644,7 @@ __HALT_COMPILER(); ?>
"
============================================================================
============================================================================
-string(6698) "<?php
+string(6666) "<?php
$web = 'the/web.php';
@@ -754,21 +754,21 @@ const GZ = 0x1000;
const BZ2 = 0x2000;
const MASK = 0x3000;
const START = 'my/custom/thingy.php';
-const LEN = 6698;
+const LEN = 6666;
static function go($return = false)
{
$fp = fopen(__FILE__, 'rb');
fseek($fp, self::LEN);
-$L = unpack('V', $a = (binary)fread($fp, 4));
-$m = (binary)'';
+$L = unpack('V', $a = fread($fp, 4));
+$m = '';
do {
$read = 8192;
if ($L[1] - strlen($m) < 8192) {
$read = $L[1] - strlen($m);
}
-$last = (binary)fread($fp, $read);
+$last = fread($fp, $read);
$m .= $last;
} while (strlen($last) && strlen($m) < $L[1]);
@@ -908,7 +908,7 @@ die("Invalid internal .phar file (size error " . strlen($data) . " != " .
$stat[7] . ")");
}
-if ($entry[3] != sprintf("%u", crc32((binary)$data) & 0xffffffff)) {
+if ($entry[3] != sprintf("%u", crc32($data) & 0xffffffff)) {
die("Invalid internal .phar file (checksum error)");
}
@@ -939,6 +939,6 @@ __HALT_COMPILER(); ?>
"
============================================================================
============================================================================
-int(7076)
+int(7044)
Illegal filename passed in for stub creation, was 401 characters long, and only 400 or less is allowed
===DONE===
diff --git a/ext/phar/tests/tar/open_for_write_existing_b.phpt b/ext/phar/tests/tar/open_for_write_existing_b.phpt
index fa631e6181..3c7c67e941 100644
--- a/ext/phar/tests/tar/open_for_write_existing_b.phpt
+++ b/ext/phar/tests/tar/open_for_write_existing_b.phpt
@@ -31,7 +31,7 @@ $phar->stopBuffering();
ini_set('phar.readonly', 1);
function err_handler($errno, $errstr, $errfile, $errline) {
- echo "Catchable fatal error: $errstr in $errfile on line $errline\n";
+ echo "Recoverable fatal error: $errstr in $errfile on line $errline\n";
}
set_error_handler("err_handler", E_RECOVERABLE_ERROR);
diff --git a/ext/phar/tests/tar/open_for_write_existing_b_5_2.phpt b/ext/phar/tests/tar/open_for_write_existing_b_5_2.phpt
index a6fea062ad..19c2f4023a 100644
--- a/ext/phar/tests/tar/open_for_write_existing_b_5_2.phpt
+++ b/ext/phar/tests/tar/open_for_write_existing_b_5_2.phpt
@@ -29,7 +29,7 @@ $phar->stopBuffering();
ini_set('phar.readonly', 1);
function err_handler($errno, $errstr, $errfile, $errline) {
- echo "Catchable fatal error: $errstr in $errfile on line $errline\n";
+ echo "Recoverable fatal error: $errstr in $errfile on line $errline\n";
}
set_error_handler("err_handler", E_RECOVERABLE_ERROR);
diff --git a/ext/phar/tests/tar/open_for_write_newfile_b.phpt b/ext/phar/tests/tar/open_for_write_newfile_b.phpt
index 2ea557b8a0..83a510436a 100644
--- a/ext/phar/tests/tar/open_for_write_newfile_b.phpt
+++ b/ext/phar/tests/tar/open_for_write_newfile_b.phpt
@@ -31,7 +31,7 @@ $phar->stopBuffering();
ini_set('phar.readonly', 1);
function err_handler($errno, $errstr, $errfile, $errline) {
- echo "Catchable fatal error: $errstr in $errfile on line $errline\n";
+ echo "Recoverable fatal error: $errstr in $errfile on line $errline\n";
}
set_error_handler("err_handler", E_RECOVERABLE_ERROR);
diff --git a/ext/phar/tests/tar/open_for_write_newfile_b_5_2.phpt b/ext/phar/tests/tar/open_for_write_newfile_b_5_2.phpt
index 1bb02a0bec..7906bc9ddf 100644
--- a/ext/phar/tests/tar/open_for_write_newfile_b_5_2.phpt
+++ b/ext/phar/tests/tar/open_for_write_newfile_b_5_2.phpt
@@ -29,7 +29,7 @@ $phar->stopBuffering();
ini_set('phar.readonly', 1);
function err_handler($errno, $errstr, $errfile, $errline) {
- echo "Catchable fatal error: $errstr in $errfile on line $errline\n";
+ echo "Recoverable fatal error: $errstr in $errfile on line $errline\n";
}
set_error_handler("err_handler", E_RECOVERABLE_ERROR);
diff --git a/ext/phar/tests/tar/phar_convert_phar.phpt b/ext/phar/tests/tar/phar_convert_phar.phpt
index d754ac1df3..fce826a729 100644
--- a/ext/phar/tests/tar/phar_convert_phar.phpt
+++ b/ext/phar/tests/tar/phar_convert_phar.phpt
@@ -47,12 +47,12 @@ __HALT_COMPILER();
?>
--EXPECT--
bool(false)
-int(6683)
+int(6651)
bool(true)
string(60) "<?php // tar-based phar archive stub file
__HALT_COMPILER();"
bool(true)
-int(6683)
+int(6651)
bool(true)
-int(6683)
+int(6651)
===DONE===
diff --git a/ext/phar/tests/tar/phar_convert_phar2.phpt b/ext/phar/tests/tar/phar_convert_phar2.phpt
index 58901ca7fc..496948b14c 100644
--- a/ext/phar/tests/tar/phar_convert_phar2.phpt
+++ b/ext/phar/tests/tar/phar_convert_phar2.phpt
@@ -49,14 +49,14 @@ __HALT_COMPILER();
?>
--EXPECT--
bool(false)
-int(6683)
+int(6651)
bool(true)
string(60) "<?php // tar-based phar archive stub file
__HALT_COMPILER();"
bool(true)
int(4096)
-int(6683)
+int(6651)
bool(true)
bool(true)
-int(6683)
+int(6651)
===DONE===
diff --git a/ext/phar/tests/tar/phar_convert_phar3.phpt b/ext/phar/tests/tar/phar_convert_phar3.phpt
index 543c89b502..f4768194d1 100644
--- a/ext/phar/tests/tar/phar_convert_phar3.phpt
+++ b/ext/phar/tests/tar/phar_convert_phar3.phpt
@@ -49,14 +49,14 @@ __HALT_COMPILER();
?>
--EXPECT--
bool(false)
-int(6683)
+int(6651)
bool(true)
string(60) "<?php // tar-based phar archive stub file
__HALT_COMPILER();"
bool(true)
int(8192)
-int(6683)
+int(6651)
bool(true)
bool(true)
-int(6683)
+int(6651)
===DONE===
diff --git a/ext/phar/tests/tar/phar_convert_phar4.phpt b/ext/phar/tests/tar/phar_convert_phar4.phpt
index 9b095f11c2..544b96b0bd 100644
--- a/ext/phar/tests/tar/phar_convert_phar4.phpt
+++ b/ext/phar/tests/tar/phar_convert_phar4.phpt
@@ -14,7 +14,7 @@ $fname2 = dirname(__FILE__) . '/' . basename(__FILE__, '.php') . '2.phar';
$phar = new Phar($fname);
$phar['a.txt'] = 'some text';
-$phar->setMetadata(b'hi');
+$phar->setMetadata('hi');
$phar->stopBuffering();
var_dump($phar->isFileFormat(Phar::TAR));
var_dump(strlen($phar->getStub()));
@@ -54,7 +54,7 @@ __HALT_COMPILER();
?>
--EXPECT--
bool(false)
-int(6683)
+int(6651)
string(2) "hi"
bool(true)
string(60) "<?php // tar-based phar archive stub file
@@ -62,10 +62,10 @@ __HALT_COMPILER();"
string(2) "hi"
bool(true)
int(4096)
-int(6683)
+int(6651)
string(2) "hi"
bool(true)
bool(true)
-int(6683)
+int(6651)
string(2) "hi"
===DONE===
diff --git a/ext/phar/tests/zip/open_for_write_existing_b.phpt b/ext/phar/tests/zip/open_for_write_existing_b.phpt
index b997c68291..d1f963e9af 100644
--- a/ext/phar/tests/zip/open_for_write_existing_b.phpt
+++ b/ext/phar/tests/zip/open_for_write_existing_b.phpt
@@ -31,7 +31,7 @@ $phar->stopBuffering();
ini_set('phar.readonly', 1);
function err_handler($errno, $errstr, $errfile, $errline) {
- echo "Catchable fatal error: $errstr in $errfile on line $errline\n";
+ echo "Recoverable fatal error: $errstr in $errfile on line $errline\n";
}
set_error_handler("err_handler", E_RECOVERABLE_ERROR);
diff --git a/ext/phar/tests/zip/open_for_write_existing_b_5_2.phpt b/ext/phar/tests/zip/open_for_write_existing_b_5_2.phpt
index b88a496326..522a1deda6 100644
--- a/ext/phar/tests/zip/open_for_write_existing_b_5_2.phpt
+++ b/ext/phar/tests/zip/open_for_write_existing_b_5_2.phpt
@@ -29,7 +29,7 @@ $phar->stopBuffering();
ini_set('phar.readonly', 1);
function err_handler($errno, $errstr, $errfile, $errline) {
- echo "Catchable fatal error: $errstr in $errfile on line $errline\n";
+ echo "Recoverable fatal error: $errstr in $errfile on line $errline\n";
}
set_error_handler("err_handler", E_RECOVERABLE_ERROR);
diff --git a/ext/phar/tests/zip/open_for_write_newfile_b.phpt b/ext/phar/tests/zip/open_for_write_newfile_b.phpt
index 96fd2e426e..9f5328ff91 100644
--- a/ext/phar/tests/zip/open_for_write_newfile_b.phpt
+++ b/ext/phar/tests/zip/open_for_write_newfile_b.phpt
@@ -31,7 +31,7 @@ $phar->stopBuffering();
ini_set('phar.readonly', 1);
function err_handler($errno, $errstr, $errfile, $errline) {
- echo "Catchable fatal error: $errstr in $errfile on line $errline\n";
+ echo "Recoverable fatal error: $errstr in $errfile on line $errline\n";
}
set_error_handler("err_handler", E_RECOVERABLE_ERROR);
diff --git a/ext/phar/tests/zip/open_for_write_newfile_b_5_2.phpt b/ext/phar/tests/zip/open_for_write_newfile_b_5_2.phpt
index 3032427bcc..2575201bd1 100644
--- a/ext/phar/tests/zip/open_for_write_newfile_b_5_2.phpt
+++ b/ext/phar/tests/zip/open_for_write_newfile_b_5_2.phpt
@@ -29,7 +29,7 @@ $phar->stopBuffering();
ini_set('phar.readonly', 1);
function err_handler($errno, $errstr, $errfile, $errline) {
- echo "Catchable fatal error: $errstr in $errfile on line $errline\n";
+ echo "Recoverable fatal error: $errstr in $errfile on line $errline\n";
}
set_error_handler("err_handler", E_RECOVERABLE_ERROR);
diff --git a/ext/phar/tests/zip/phar_convert_phar.phpt b/ext/phar/tests/zip/phar_convert_phar.phpt
index cad6d9fc7f..815656dbf6 100644
--- a/ext/phar/tests/zip/phar_convert_phar.phpt
+++ b/ext/phar/tests/zip/phar_convert_phar.phpt
@@ -46,12 +46,12 @@ __HALT_COMPILER();
?>
--EXPECT--
bool(false)
-int(6683)
+int(6651)
bool(true)
string(60) "<?php // zip-based phar archive stub file
__HALT_COMPILER();"
bool(true)
-int(6683)
+int(6651)
bool(true)
-int(6683)
+int(6651)
===DONE===
diff --git a/ext/phar/util.c b/ext/phar/util.c
index 54ff13b5c0..64a659d54d 100644
--- a/ext/phar/util.c
+++ b/ext/phar/util.c
@@ -178,7 +178,7 @@ int phar_mount_entry(phar_archive_data *phar, char *filename, int filename_len,
return FAILURE;
}
- if (path_len >= sizeof(".phar")-1 && !memcmp(path, ".phar", sizeof(".phar")-1)) {
+ if (path_len >= (int)sizeof(".phar")-1 && !memcmp(path, ".phar", sizeof(".phar")-1)) {
/* no creating magic phar files by mounting them */
return FAILURE;
}
@@ -199,13 +199,6 @@ int phar_mount_entry(phar_archive_data *phar, char *filename, int filename_len,
entry.tmp = estrndup(filename, filename_len);
}
}
-#if PHP_API_VERSION < 20100412
- if (PG(safe_mode) && !is_phar && (!php_checkuid(entry.tmp, NULL, CHECKUID_CHECK_FILE_AND_DIR))) {
- efree(entry.tmp);
- efree(entry.filename);
- return FAILURE;
- }
-#endif
filename = entry.tmp;
/* only check openbasedir for files, not for phar streams */
@@ -1184,7 +1177,7 @@ char * phar_compress_filter(phar_entry_info * entry, int return_unknown) /* {{{
*/
char * phar_decompress_filter(phar_entry_info * entry, int return_unknown) /* {{{ */
{
- php_uint32 flags;
+ uint32_t flags;
if (entry->is_modified) {
flags = entry->old_flags;
@@ -1232,7 +1225,7 @@ phar_entry_info *phar_get_entry_info_dir(phar_archive_data *phar, char *path, in
*error = NULL;
}
- if (security && path_len >= sizeof(".phar")-1 && !memcmp(path, ".phar", sizeof(".phar")-1)) {
+ if (security && path_len >= (int)sizeof(".phar")-1 && !memcmp(path, ".phar", sizeof(".phar")-1)) {
if (error) {
spprintf(error, 4096, "phar error: cannot directly access magic \".phar\" directory or files within it");
}
@@ -1410,7 +1403,7 @@ static int phar_call_openssl_signverify(int is_sign, php_stream *fp, zend_off_t
ZVAL_EMPTY_STRING(&zp[0]);
}
- if (end != Z_STRLEN(zp[0])) {
+ if ((size_t)end != Z_STRLEN(zp[0])) {
zval_dtor(&zp[0]);
zval_dtor(&zp[1]);
zval_dtor(&zp[2]);
@@ -1480,7 +1473,7 @@ static int phar_call_openssl_signverify(int is_sign, php_stream *fp, zend_off_t
/* }}} */
#endif /* #ifndef PHAR_HAVE_OPENSSL */
-int phar_verify_signature(php_stream *fp, size_t end_of_phar, php_uint32 sig_type, char *sig, int sig_len, char *fname, char **signature, int *signature_len, char **error) /* {{{ */
+int phar_verify_signature(php_stream *fp, size_t end_of_phar, uint32_t sig_type, char *sig, int sig_len, char *fname, char **signature, int *signature_len, char **error) /* {{{ */
{
int read_size, len;
zend_off_t read_len;
@@ -1571,7 +1564,7 @@ int phar_verify_signature(php_stream *fp, size_t end_of_phar, php_uint32 sig_typ
EVP_VerifyInit(md_ctx, mdtype);
read_len = end_of_phar;
- if (read_len > sizeof(buf)) {
+ if ((size_t)read_len > sizeof(buf)) {
read_size = sizeof(buf);
} else {
read_size = (int)read_len;
@@ -1620,7 +1613,7 @@ int phar_verify_signature(php_stream *fp, size_t end_of_phar, php_uint32 sig_typ
PHP_SHA512Init(&context);
read_len = end_of_phar;
- if (read_len > sizeof(buf)) {
+ if ((size_t)read_len > sizeof(buf)) {
read_size = sizeof(buf);
} else {
read_size = (int)read_len;
@@ -1660,7 +1653,7 @@ int phar_verify_signature(php_stream *fp, size_t end_of_phar, php_uint32 sig_typ
PHP_SHA256Init(&context);
read_len = end_of_phar;
- if (read_len > sizeof(buf)) {
+ if ((size_t)read_len > sizeof(buf)) {
read_size = sizeof(buf);
} else {
read_size = (int)read_len;
@@ -1708,7 +1701,7 @@ int phar_verify_signature(php_stream *fp, size_t end_of_phar, php_uint32 sig_typ
PHP_SHA1Init(&context);
read_len = end_of_phar;
- if (read_len > sizeof(buf)) {
+ if ((size_t)read_len > sizeof(buf)) {
read_size = sizeof(buf);
} else {
read_size = (int)read_len;
@@ -1748,7 +1741,7 @@ int phar_verify_signature(php_stream *fp, size_t end_of_phar, php_uint32 sig_typ
PHP_MD5Init(&context);
read_len = end_of_phar;
- if (read_len > sizeof(buf)) {
+ if ((size_t)read_len > sizeof(buf)) {
read_size = sizeof(buf);
} else {
read_size = (int)read_len;
diff --git a/ext/phar/zip.c b/ext/phar/zip.c
index 1c05fbd80f..08417bc07b 100644
--- a/ext/phar/zip.c
+++ b/ext/phar/zip.c
@@ -18,28 +18,28 @@
#include "phar_internal.h"
-#define PHAR_GET_16(var) ((php_uint16)((((php_uint16)var[0]) & 0xff) | \
- (((php_uint16)var[1]) & 0xff) << 8))
-#define PHAR_GET_32(var) ((php_uint32)((((php_uint32)var[0]) & 0xff) | \
- (((php_uint32)var[1]) & 0xff) << 8 | \
- (((php_uint32)var[2]) & 0xff) << 16 | \
- (((php_uint32)var[3]) & 0xff) << 24))
-static inline void phar_write_32(char buffer[4], php_uint32 value)
+#define PHAR_GET_16(var) ((uint16_t)((((uint16_t)var[0]) & 0xff) | \
+ (((uint16_t)var[1]) & 0xff) << 8))
+#define PHAR_GET_32(var) ((uint32_t)((((uint32_t)var[0]) & 0xff) | \
+ (((uint32_t)var[1]) & 0xff) << 8 | \
+ (((uint32_t)var[2]) & 0xff) << 16 | \
+ (((uint32_t)var[3]) & 0xff) << 24))
+static inline void phar_write_32(char buffer[4], uint32_t value)
{
buffer[3] = (unsigned char) ((value & 0xff000000) >> 24);
buffer[2] = (unsigned char) ((value & 0xff0000) >> 16);
buffer[1] = (unsigned char) ((value & 0xff00) >> 8);
buffer[0] = (unsigned char) (value & 0xff);
}
-static inline void phar_write_16(char buffer[2], php_uint32 value)
+static inline void phar_write_16(char buffer[2], uint32_t value)
{
buffer[1] = (unsigned char) ((value & 0xff00) >> 8);
buffer[0] = (unsigned char) (value & 0xff);
}
-# define PHAR_SET_32(var, value) phar_write_32(var, (php_uint32) (value));
-# define PHAR_SET_16(var, value) phar_write_16(var, (php_uint16) (value));
+# define PHAR_SET_32(var, value) phar_write_32(var, (uint32_t) (value));
+# define PHAR_SET_16(var, value) phar_write_16(var, (uint16_t) (value));
-static int phar_zip_process_extra(php_stream *fp, phar_entry_info *entry, php_uint16 len) /* {{{ */
+static int phar_zip_process_extra(php_stream *fp, phar_entry_info *entry, uint16_t len) /* {{{ */
{
union {
phar_zip_extra_field_header header;
@@ -143,7 +143,7 @@ static time_t phar_zip_d2u_time(char *cdtime, char *cddate) /* {{{ */
static void phar_zip_u2d_time(time_t time, char *dtime, char *ddate) /* {{{ */
{
- php_uint16 ctime, cdate;
+ uint16_t ctime, cdate;
struct tm *tm, tmbuf;
tm = php_localtime_r(&time, &tmbuf);
@@ -167,8 +167,8 @@ int phar_parse_zipfile(php_stream *fp, char *fname, int fname_len, char *alias,
{
phar_zip_dir_end locator;
char buf[sizeof(locator) + 65536];
- zend_long size;
- php_uint16 i;
+ size_t size;
+ uint16_t i;
phar_archive_data *mydata = NULL;
phar_entry_info entry = {0};
char *p = buf, *ext, *actual_alias = NULL;
@@ -395,10 +395,9 @@ foundit:
if (entry.filename_len == sizeof(".phar/signature.bin")-1 && !strncmp(entry.filename, ".phar/signature.bin", sizeof(".phar/signature.bin")-1)) {
size_t read;
php_stream *sigfile;
- zend_off_t now;
char *sig;
- now = php_stream_tell(fp);
+ php_stream_tell(fp);
pefree(entry.filename, entry.is_persistent);
sigfile = php_stream_fopen_tmpfile();
if (!sigfile) {
@@ -791,7 +790,7 @@ static int phar_zip_changed_apply_int(phar_entry_info *entry, void *arg) /* {{{
phar_zip_unix3 perms;
phar_zip_central_dir_file central;
struct _phar_zip_pass *p;
- php_uint32 newcrc32;
+ uint32_t newcrc32;
zend_off_t offset;
int not_really_modified = 0;
p = (struct _phar_zip_pass*) arg;
@@ -822,7 +821,7 @@ static int phar_zip_changed_apply_int(phar_entry_info *entry, void *arg) /* {{{
PHAR_SET_16(perms.size, sizeof(perms) - 4);
PHAR_SET_16(perms.perms, entry->flags & PHAR_ENT_PERM_MASK);
{
- php_uint32 crc = (php_uint32) ~0;
+ uint32_t crc = (uint32_t) ~0;
CRC32(crc, perms.perms[0]);
CRC32(crc, perms.perms[1]);
PHAR_SET_32(perms.crc32, ~crc);
@@ -848,7 +847,7 @@ static int phar_zip_changed_apply_int(phar_entry_info *entry, void *arg) /* {{{
/* do extra field for perms later */
if (entry->is_modified) {
- php_uint32 loc;
+ uint32_t loc;
php_stream_filter *filter;
php_stream *efp;
@@ -936,7 +935,7 @@ static int phar_zip_changed_apply_int(phar_entry_info *entry, void *arg) /* {{{
php_stream_flush(entry->cfp);
php_stream_filter_remove(filter, 1);
php_stream_seek(entry->cfp, 0, SEEK_END);
- entry->compressed_filesize = (php_uint32) php_stream_tell(entry->cfp);
+ entry->compressed_filesize = (uint32_t) php_stream_tell(entry->cfp);
PHAR_SET_32(central.compsize, entry->compressed_filesize);
PHAR_SET_32(local.compsize, entry->compressed_filesize);
/* generate crc on compressed file */
@@ -1110,14 +1109,14 @@ static int phar_zip_applysignature(phar_archive_data *phar, struct _phar_zip_pas
char *signature, sigbuf[8];
phar_entry_info entry = {0};
php_stream *newfile;
- zend_off_t tell, st;
+ zend_off_t tell;
newfile = php_stream_fopen_tmpfile();
if (newfile == NULL) {
spprintf(pass->error, 0, "phar error: unable to create temporary file for the signature file");
return FAILURE;
}
- st = tell = php_stream_tell(pass->filefp);
+ tell = php_stream_tell(pass->filefp);
/* copy the local files, central directory, and the zip comment to generate the hash */
php_stream_seek(pass->filefp, 0, SEEK_SET);
php_stream_copy_to_stream_ex(pass->filefp, newfile, tell, NULL);
@@ -1193,7 +1192,7 @@ int phar_zip_flush(phar_archive_data *phar, char *user_stub, zend_long len, int
char *temperr = NULL;
struct _phar_zip_pass pass;
phar_zip_dir_end eocd;
- php_uint32 cdir_size, cdir_offset;
+ uint32_t cdir_size, cdir_offset;
pass.error = &temperr;
entry.flags = PHAR_ENT_PERM_DEF_FILE;
diff --git a/ext/posix/tests/posix_getgrgid_variation.phpt b/ext/posix/tests/posix_getgrgid_variation.phpt
index 9b9bc25aab..7abd32b97f 100644
--- a/ext/posix/tests/posix_getgrgid_variation.phpt
+++ b/ext/posix/tests/posix_getgrgid_variation.phpt
@@ -185,4 +185,4 @@ valid output
Arg value
valid output
-Catchable fatal error: Object of class stdClass could not be converted to string in %s on line %d
+Recoverable fatal error: Object of class stdClass could not be converted to string in %s on line %d
diff --git a/ext/posix/tests/posix_getpgid_variation.phpt b/ext/posix/tests/posix_getpgid_variation.phpt
index 49ed890e95..478164c210 100644
--- a/ext/posix/tests/posix_getpgid_variation.phpt
+++ b/ext/posix/tests/posix_getpgid_variation.phpt
@@ -185,4 +185,4 @@ valid output
Arg value
valid output
-Catchable fatal error: Object of class stdClass could not be converted to string in %s on line %d
+Recoverable fatal error: Object of class stdClass could not be converted to string in %s on line %d
diff --git a/ext/posix/tests/posix_getpwuid_variation.phpt b/ext/posix/tests/posix_getpwuid_variation.phpt
index 8a4b83ba76..fa000f5b8a 100644
--- a/ext/posix/tests/posix_getpwuid_variation.phpt
+++ b/ext/posix/tests/posix_getpwuid_variation.phpt
@@ -185,4 +185,4 @@ valid output
Arg value
valid output
-Catchable fatal error: Object of class stdClass could not be converted to string in %s on line %d
+Recoverable fatal error: Object of class stdClass could not be converted to string in %s on line %d
diff --git a/ext/posix/tests/posix_kill_variation1.phpt b/ext/posix/tests/posix_kill_variation1.phpt
index 99bb8e0151..d26f11df03 100644
--- a/ext/posix/tests/posix_kill_variation1.phpt
+++ b/ext/posix/tests/posix_kill_variation1.phpt
@@ -179,4 +179,4 @@ bool(false)
Arg value
bool(false)
-Catchable fatal error: Object of class stdClass could not be converted to string in %s on line %d
+Recoverable fatal error: Object of class stdClass could not be converted to string in %s on line %d
diff --git a/ext/posix/tests/posix_kill_variation2.phpt b/ext/posix/tests/posix_kill_variation2.phpt
index 5eeaa1fc5a..399151a7ed 100644
--- a/ext/posix/tests/posix_kill_variation2.phpt
+++ b/ext/posix/tests/posix_kill_variation2.phpt
@@ -179,4 +179,4 @@ bool(false)
Arg value
bool(false)
-Catchable fatal error: Object of class stdClass could not be converted to string in %s on line %d
+Recoverable fatal error: Object of class stdClass could not be converted to string in %s on line %d
diff --git a/ext/posix/tests/posix_strerror_variation1.phpt b/ext/posix/tests/posix_strerror_variation1.phpt
index ed50492125..40fff34be8 100644
--- a/ext/posix/tests/posix_strerror_variation1.phpt
+++ b/ext/posix/tests/posix_strerror_variation1.phpt
@@ -178,4 +178,4 @@ string
Arg value
string
-Catchable fatal error: Object of class stdClass could not be converted to string in %s on line %d
+Recoverable fatal error: Object of class stdClass could not be converted to string in %s on line %d
diff --git a/ext/pspell/pspell.c b/ext/pspell/pspell.c
index db2298117b..9639ecd514 100644
--- a/ext/pspell/pspell.c
+++ b/ext/pspell/pspell.c
@@ -230,7 +230,7 @@ static void php_pspell_close_config(zend_resource *rsrc)
#define PSPELL_FETCH_CONFIG do { \
zval *res = zend_hash_index_find(&EG(regular_list), conf); \
if (res == NULL || Z_RES_P(res)->type != le_pspell_config) { \
- php_error_docref(NULL, E_WARNING, "%ld is not a PSPELL config index", conf); \
+ php_error_docref(NULL, E_WARNING, ZEND_LONG_FMT " is not a PSPELL config index", conf); \
RETURN_FALSE; \
} \
config = (PspellConfig *)Z_RES_P(res)->ptr; \
@@ -239,7 +239,7 @@ static void php_pspell_close_config(zend_resource *rsrc)
#define PSPELL_FETCH_MANAGER do { \
zval *res = zend_hash_index_find(&EG(regular_list), scin); \
if (res == NULL || Z_RES_P(res)->type != le_pspell) { \
- php_error_docref(NULL, E_WARNING, "%ld is not a PSPELL result index", scin); \
+ php_error_docref(NULL, E_WARNING, ZEND_LONG_FMT " is not a PSPELL result index", scin); \
RETURN_FALSE; \
} \
manager = (PspellManager *)Z_RES_P(res)->ptr; \
@@ -808,7 +808,7 @@ static PHP_FUNCTION(pspell_config_ignore)
PSPELL_FETCH_CONFIG;
- snprintf(ignore_str, sizeof(ignore_str), "%ld", ignore);
+ snprintf(ignore_str, sizeof(ignore_str), ZEND_LONG_FMT, ignore);
pspell_config_replace(config, "ignore", ignore_str);
RETURN_TRUE;
diff --git a/ext/readline/config.w32 b/ext/readline/config.w32
new file mode 100644
index 0000000000..482c99cc04
--- /dev/null
+++ b/ext/readline/config.w32
@@ -0,0 +1,16 @@
+// $Id$
+// vim:ft=javascript
+
+ARG_WITH("readline", "Readline support", "yes");
+
+if (PHP_READLINE != "no") {
+ if (CHECK_LIB("edit_a.lib;edit.lib", "readline", PHP_READLINE) &&
+ CHECK_HEADER_ADD_INCLUDE("editline/readline.h", "CFLAGS_READLINE")) {
+ EXTENSION("readline", "readline.c readline_cli.c");
+ ADD_FLAG("CFLAGS_READLINE", "/D HAVE_LIBEDIT");
+ ADD_FLAG("CFLAGS_READLINE", "/D HAVE_RL_COMPLETION_MATCHES");
+ } else {
+ WARNING("readline not enabled; libraries and headers not found");
+ }
+}
+
diff --git a/ext/readline/php_readline.h b/ext/readline/php_readline.h
index 9eb5981e24..784a515604 100644
--- a/ext/readline/php_readline.h
+++ b/ext/readline/php_readline.h
@@ -22,9 +22,11 @@
#define PHP_READLINE_H
#if HAVE_LIBREADLINE || HAVE_LIBEDIT
+#ifndef PHP_WIN32
#ifdef ZTS
#warning Readline module will *NEVER* be thread-safe
#endif
+#endif
extern zend_module_entry readline_module_entry;
#define phpext_readline_ptr &readline_module_entry
diff --git a/ext/readline/readline.c b/ext/readline/readline.c
index c771b071b2..53a4c9e9ab 100644
--- a/ext/readline/readline.c
+++ b/ext/readline/readline.c
@@ -122,9 +122,11 @@ ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(arginfo_readline_redisplay, 0)
ZEND_END_ARG_INFO()
+#if HAVE_RL_ON_NEW_LINE
ZEND_BEGIN_ARG_INFO(arginfo_readline_on_new_line, 0)
ZEND_END_ARG_INFO()
#endif
+#endif
/* }}} */
/* {{{ module stuff */
@@ -251,7 +253,9 @@ PHP_FUNCTION(readline_info)
array_init(return_value);
add_assoc_string(return_value,"line_buffer",SAFE_STRING(rl_line_buffer));
add_assoc_long(return_value,"point",rl_point);
+#ifndef PHP_WIN32
add_assoc_long(return_value,"end",rl_end);
+#endif
#ifdef HAVE_LIBREADLINE
add_assoc_long(return_value,"mark",rl_mark);
add_assoc_long(return_value,"done",rl_done);
@@ -262,7 +266,9 @@ PHP_FUNCTION(readline_info)
#if HAVE_ERASE_EMPTY_LINE
add_assoc_long(return_value,"erase_empty_line",rl_erase_empty_line);
#endif
+#ifndef PHP_WIN32
add_assoc_string(return_value,"library_version",(char *)SAFE_STRING(rl_library_version));
+#endif
add_assoc_string(return_value,"readline_name",(char *)SAFE_STRING(rl_readline_name));
add_assoc_long(return_value,"attempted_completion_over",rl_attempted_completion_over);
} else {
@@ -276,8 +282,10 @@ PHP_FUNCTION(readline_info)
RETVAL_STRING(SAFE_STRING(oldstr));
} else if (!strcasecmp(what, "point")) {
RETVAL_LONG(rl_point);
+#ifndef PHP_WIN32
} else if (!strcasecmp(what, "end")) {
RETVAL_LONG(rl_end);
+#endif
#ifdef HAVE_LIBREADLINE
} else if (!strcasecmp(what, "mark")) {
RETVAL_LONG(rl_mark);
@@ -309,8 +317,10 @@ PHP_FUNCTION(readline_info)
}
RETVAL_LONG(oldval);
#endif
+#ifndef PHP_WIN32
} else if (!strcasecmp(what,"library_version")) {
RETVAL_STRING((char *)SAFE_STRING(rl_library_version));
+#endif
} else if (!strcasecmp(what, "readline_name")) {
oldstr = (char*)rl_readline_name;
if (value) {
diff --git a/ext/readline/readline_cli.c b/ext/readline/readline_cli.c
index 822559e193..de37a85d4d 100644
--- a/ext/readline/readline_cli.c
+++ b/ext/readline/readline_cli.c
@@ -67,7 +67,7 @@
#include "sapi/cli/cli.h"
#include "readline_cli.h"
-#ifdef COMPILE_DL_READLINE
+#if defined(COMPILE_DL_READLINE) && !defined(PHP_WIN32)
#include <dlfcn.h>
#endif
@@ -592,8 +592,9 @@ static int readline_shell_run(void) /* {{{ */
if (PG(auto_prepend_file) && PG(auto_prepend_file)[0]) {
zend_file_handle *prepend_file_p;
- zend_file_handle prepend_file = {{0}};
+ zend_file_handle prepend_file;
+ memset(&prepend_file, 0, sizeof(prepend_file));
prepend_file.filename = PG(auto_prepend_file);
prepend_file.opened_path = NULL;
prepend_file.free_filename = 0;
@@ -603,9 +604,15 @@ static int readline_shell_run(void) /* {{{ */
zend_execute_scripts(ZEND_REQUIRE, NULL, 1, prepend_file_p);
}
+#ifndef PHP_WIN32
history_file = tilde_expand("~/.php_history");
+#else
+ spprintf(&history_file, MAX_PATH, "%s/.php_history", getenv("USERPROFILE"));
+#endif
rl_attempted_completion_function = cli_code_completion;
+#ifndef PHP_WIN32
rl_special_prefixes = "$";
+#endif
read_history(history_file);
EG(exit_status) = 0;
@@ -691,13 +698,33 @@ static int readline_shell_run(void) /* {{{ */
php_last_char = '\0';
}
+#ifdef PHP_WIN32
+ efree(history_file);
+#else
free(history_file);
+#endif
efree(code);
zend_string_release(prompt);
return EG(exit_status);
}
/* }}} */
+#ifdef PHP_WIN32
+typedef cli_shell_callbacks_t *(__cdecl *get_cli_shell_callbacks)(void);
+#define GET_SHELL_CB(cb) \
+ do { \
+ get_cli_shell_callbacks get_callbacks; \
+ HMODULE hMod = GetModuleHandle("php.exe"); \
+ (cb) = NULL; \
+ if (strlen(sapi_module.name) >= 3 && 0 == strncmp("cli", sapi_module.name, 3)) { \
+ get_callbacks = (get_cli_shell_callbacks)GetProcAddress(hMod, "php_cli_get_shell_callbacks"); \
+ if (get_callbacks) { \
+ (cb) = get_callbacks(); \
+ } \
+ } \
+ } while(0)
+
+#else
/*
#ifdef COMPILE_DL_READLINE
This dlsym() is always used as even the CGI SAPI is linked against "CLI"-only
@@ -716,6 +743,7 @@ this extension sharedto offer compatibility.
/*#else
#define GET_SHELL_CB(cb) (cb) = php_cli_get_shell_callbacks()
#endif*/
+#endif
PHP_MINIT_FUNCTION(cli_readline)
{
@@ -760,7 +788,11 @@ PHP_MINFO_FUNCTION(cli_readline)
{
php_info_print_table_start();
php_info_print_table_header(2, "Readline Support", "enabled");
+#ifdef PHP_WIN32
+ php_info_print_table_row(2, "Readline library", "WinEditLine");
+#else
php_info_print_table_row(2, "Readline library", (rl_library_version ? rl_library_version : "Unknown"));
+#endif
php_info_print_table_end();
DISPLAY_INI_ENTRIES();
diff --git a/ext/readline/tests/bug72538.phpt b/ext/readline/tests/bug72538.phpt
index 98b9b2651e..7de37f2e6e 100644
--- a/ext/readline/tests/bug72538.phpt
+++ b/ext/readline/tests/bug72538.phpt
@@ -3,6 +3,7 @@ Bug #72538 (readline_redisplay crashes php)
--SKIPIF--
<?php if (!extension_loaded("readline")) die("skip");
if (READLINE_LIB != "libedit") die("skip libedit only");
+if (!function_exists("readline_redisplay")) die("skip readline_redisplay not available");
?>
--FILE--
<?php
diff --git a/ext/readline/tests/libedit_info_001-win32.phpt b/ext/readline/tests/libedit_info_001-win32.phpt
new file mode 100644
index 0000000000..5d43921c60
--- /dev/null
+++ b/ext/readline/tests/libedit_info_001-win32.phpt
@@ -0,0 +1,42 @@
+--TEST--
+readline_info(): Basic test
+--SKIPIF--
+<?php if (!extension_loaded("readline")) die("skip");
+if (READLINE_LIB != "libedit") die("skip libedit only");
+if(substr(PHP_OS, 0, 3) != 'WIN' ) {
+ die('skip windows only test');
+}
+?>
+--FILE--
+<?php
+
+var_dump(readline_info());
+var_dump(readline_info(1));
+var_dump(readline_info(1,1));
+var_dump(readline_info('line_buffer'));
+var_dump(readline_info('readline_name'));
+var_dump(readline_info('readline_name', 1));
+var_dump(readline_info('readline_name'));
+var_dump(readline_info('attempted_completion_over',1));
+var_dump(readline_info('attempted_completion_over'));
+
+?>
+--EXPECTF--
+array(4) {
+ ["line_buffer"]=>
+ string(0) ""
+ ["point"]=>
+ int(0)
+ ["readline_name"]=>
+ string(0) ""
+ ["attempted_completion_over"]=>
+ int(0)
+}
+NULL
+NULL
+string(0) ""
+string(0) ""
+string(0) ""
+string(1) "1"
+int(0)
+int(1)
diff --git a/ext/readline/tests/libedit_info_001.phpt b/ext/readline/tests/libedit_info_001.phpt
index 1d79f4ad0c..33dc144add 100644
--- a/ext/readline/tests/libedit_info_001.phpt
+++ b/ext/readline/tests/libedit_info_001.phpt
@@ -3,6 +3,9 @@ readline_info(): Basic test
--SKIPIF--
<?php if (!extension_loaded("readline")) die("skip");
if (READLINE_LIB != "libedit") die("skip libedit only");
+if(substr(PHP_OS, 0, 3) == 'WIN' ) {
+ die('skip not for windows');
+}
?>
--FILE--
<?php
diff --git a/ext/readline/tests/libedit_write_history_001-win32.phpt b/ext/readline/tests/libedit_write_history_001-win32.phpt
new file mode 100644
index 0000000000..28af4cbfdd
--- /dev/null
+++ b/ext/readline/tests/libedit_write_history_001-win32.phpt
@@ -0,0 +1,29 @@
+--TEST--
+readline_write_history(): Basic test
+--SKIPIF--
+<?php if (!extension_loaded("readline") || !function_exists('readline_add_history')) die("skip");
+if (READLINE_LIB != "libedit") die("skip libedit only");
+if(substr(PHP_OS, 0, 3) != 'WIN' ) {
+ die('skip windows only test');
+}
+?>
+--FILE--
+<?php
+
+$name = tempnam(sys_get_temp_dir(), 'readline.tmp');
+
+readline_add_history('foo');
+readline_add_history('');
+readline_add_history(1);
+readline_add_history(NULL);
+readline_write_history($name);
+
+var_dump(file_get_contents($name));
+
+unlink($name);
+
+?>
+--EXPECT--
+string(6) "foo
+1
+"
diff --git a/ext/readline/tests/libedit_write_history_001.phpt b/ext/readline/tests/libedit_write_history_001.phpt
index e9b6dbee8d..14c3282e6d 100644
--- a/ext/readline/tests/libedit_write_history_001.phpt
+++ b/ext/readline/tests/libedit_write_history_001.phpt
@@ -3,6 +3,9 @@ readline_write_history(): Basic test
--SKIPIF--
<?php if (!extension_loaded("readline") || !function_exists('readline_add_history')) die("skip");
if (READLINE_LIB != "libedit") die("skip libedit only");
+if(substr(PHP_OS, 0, 3) == 'WIN' ) {
+ die('skip not for windows');
+}
?>
--FILE--
<?php
diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c
index e5303663dc..fa936d2e15 100644
--- a/ext/reflection/php_reflection.c
+++ b/ext/reflection/php_reflection.c
@@ -60,10 +60,12 @@ PHPAPI zend_class_entry *reflection_function_ptr;
PHPAPI zend_class_entry *reflection_generator_ptr;
PHPAPI zend_class_entry *reflection_parameter_ptr;
PHPAPI zend_class_entry *reflection_type_ptr;
+PHPAPI zend_class_entry *reflection_named_type_ptr;
PHPAPI zend_class_entry *reflection_class_ptr;
PHPAPI zend_class_entry *reflection_object_ptr;
PHPAPI zend_class_entry *reflection_method_ptr;
PHPAPI zend_class_entry *reflection_property_ptr;
+PHPAPI zend_class_entry *reflection_class_constant_ptr;
PHPAPI zend_class_entry *reflection_extension_ptr;
PHPAPI zend_class_entry *reflection_zend_extension_ptr;
@@ -87,7 +89,7 @@ ZEND_DECLARE_MODULE_GLOBALS(reflection)
/* Method macros */
#define METHOD_NOTSTATIC(ce) \
- if (!Z_OBJ(EX(This)) || !instanceof_function(Z_OBJCE(EX(This)), ce)) { \
+ if ((Z_TYPE(EX(This)) != IS_OBJECT) || !instanceof_function(Z_OBJCE(EX(This)), ce)) { \
php_error_docref(NULL, E_ERROR, "%s() cannot be called statically", get_active_function_name()); \
return; \
} \
@@ -104,9 +106,10 @@ ZEND_DECLARE_MODULE_GLOBALS(reflection)
#define GET_REFLECTION_OBJECT() \
intern = Z_REFLECTION_P(getThis()); \
- if (intern->ptr == NULL) { \
+ if (intern->ptr == NULL) { \
RETURN_ON_EXCEPTION \
- php_error_docref(NULL, E_ERROR, "Internal error: Failed to retrieve the reflection object"); \
+ zend_throw_error(NULL, "Internal error: Failed to retrieve the reflection object"); \
+ return; \
} \
#define GET_REFLECTION_OBJECT_PTR(target) \
@@ -215,7 +218,8 @@ typedef enum {
REF_TYPE_PARAMETER,
REF_TYPE_TYPE,
REF_TYPE_PROPERTY,
- REF_TYPE_DYNAMIC_PROPERTY
+ REF_TYPE_DYNAMIC_PROPERTY,
+ REF_TYPE_CLASS_CONSTANT
} reflection_type_t;
/* Struct for reflection objects */
@@ -342,7 +346,7 @@ static void reflection_free_objects_storage(zend_object *object) /* {{{ */
efree(intern->ptr);
break;
case REF_TYPE_GENERATOR:
- break;
+ case REF_TYPE_CLASS_CONSTANT:
case REF_TYPE_OTHER:
break;
}
@@ -386,6 +390,7 @@ static zval *reflection_instantiate(zend_class_entry *pce, zval *object) /* {{{
static void _const_string(string *str, char *name, zval *value, char *indent);
static void _function_string(string *str, zend_function *fptr, zend_class_entry *scope, char* indent);
static void _property_string(string *str, zend_property_info *prop, char *prop_name, char* indent);
+static void _class_const_string(string *str, char *name, zend_class_constant *c, char* indent);
static void _class_string(string *str, zend_class_entry *ce, zval *obj, char *indent);
static void _extension_string(string *str, zend_module_entry *module, char *indent);
static void _zend_extension_string(string *str, zend_extension *extension, char *indent);
@@ -468,13 +473,13 @@ static void _class_string(string *str, zend_class_entry *ce, zval *obj, char *in
string_printf(str, "%s - Constants [%d] {\n", indent, count);
if (count > 0) {
zend_string *key;
- zval *value;
+ zend_class_constant *c;
- ZEND_HASH_FOREACH_STR_KEY_VAL(&ce->constants_table, key, value) {
- if (UNEXPECTED(zval_update_constant_ex(value, 1, NULL) == FAILURE)) {
+ ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->constants_table, key, c) {
+ _class_const_string(str, ZSTR_VAL(key), c, ZSTR_VAL(sub_indent.buf));
+ if (UNEXPECTED(EG(exception))) {
return;
}
- _const_string(str, ZSTR_VAL(key), value, indent);
} ZEND_HASH_FOREACH_END();
}
string_printf(str, "%s }\n", indent);
@@ -652,6 +657,29 @@ static void _const_string(string *str, char *name, zval *value, char *indent)
}
/* }}} */
+/* {{{ _class_const_string */
+static void _class_const_string(string *str, char *name, zend_class_constant *c, char *indent)
+{
+ char *visibility = zend_visibility_string(Z_ACCESS_FLAGS(c->value));
+ char *type;
+
+ zval_update_constant_ex(&c->value, c->ce);
+ type = zend_zval_type_name(&c->value);
+
+ if (Z_TYPE(c->value) == IS_ARRAY) {
+ string_printf(str, "%sConstant [ %s %s %s ] { Array }\n",
+ indent, visibility, type, name);
+ } else {
+ zend_string *value_str = zval_get_string(&c->value);
+
+ string_printf(str, "%sConstant [ %s %s %s ] { %s }\n",
+ indent, visibility, type, name, ZSTR_VAL(value_str));
+
+ zend_string_release(value_str);
+ }
+}
+/* }}} */
+
/* {{{ _get_recv_opcode */
static zend_op* _get_recv_op(zend_op_array *op_array, uint32_t offset)
{
@@ -661,7 +689,7 @@ static zend_op* _get_recv_op(zend_op_array *op_array, uint32_t offset)
++offset;
while (op < end) {
if ((op->opcode == ZEND_RECV || op->opcode == ZEND_RECV_INIT
- || op->opcode == ZEND_RECV_VARIADIC) && op->op1.num == (zend_long)offset)
+ || op->opcode == ZEND_RECV_VARIADIC) && op->op1.num == offset)
{
return op;
}
@@ -714,18 +742,13 @@ static void _parameter_string(string *str, zend_function *fptr, struct _zend_arg
zend_op *precv = _get_recv_op((zend_op_array*)fptr, offset);
if (precv && precv->opcode == ZEND_RECV_INIT && precv->op2_type != IS_UNUSED) {
zval zv;
- zend_class_entry *old_scope;
string_write(str, " = ", sizeof(" = ")-1);
ZVAL_DUP(&zv, RT_CONSTANT(&fptr->op_array, precv->op2));
- old_scope = EG(scope);
- EG(scope) = fptr->common.scope;
- if (UNEXPECTED(zval_update_constant_ex(&zv, 1, NULL) == FAILURE)) {
- EG(scope) = old_scope;
+ if (UNEXPECTED(zval_update_constant_ex(&zv, fptr->common.scope) == FAILURE)) {
zval_ptr_dtor(&zv);
return;
}
- EG(scope) = old_scope;
if (Z_TYPE(zv) == IS_TRUE) {
string_write(str, "true", sizeof("true")-1);
} else if (Z_TYPE(zv) == IS_FALSE) {
@@ -1279,7 +1302,7 @@ static void reflection_type_factory(zend_function *fptr, zval *closure_object, s
reflection_object *intern;
type_reference *reference;
- reflection_instantiate(reflection_type_ptr, object);
+ reflection_instantiate(reflection_named_type_ptr, object);
intern = Z_REFLECTION_P(object);
reference = (type_reference*) emalloc(sizeof(type_reference));
reference->arg_info = arg_info;
@@ -1385,11 +1408,31 @@ static void reflection_property_factory(zend_class_entry *ce, zend_property_info
}
/* }}} */
+/* {{{ reflection_class_constant_factory */
+static void reflection_class_constant_factory(zend_class_entry *ce, zend_string *name_str, zend_class_constant *constant, zval *object)
+{
+ reflection_object *intern;
+ zval name;
+ zval classname;
+
+ ZVAL_STR_COPY(&name, name_str);
+ ZVAL_STR_COPY(&classname, ce->name);
+
+ reflection_instantiate(reflection_class_constant_ptr, object);
+ intern = Z_REFLECTION_P(object);
+ intern->ptr = constant;
+ intern->ref_type = REF_TYPE_CLASS_CONSTANT;
+ intern->ce = constant->ce;
+ intern->ignore_visibility = 0;
+ reflection_update_property(object, "name", &name);
+ reflection_update_property(object, "class", &classname);
+}
+/* }}} */
+
/* {{{ _reflection_export */
static void _reflection_export(INTERNAL_FUNCTION_PARAMETERS, zend_class_entry *ce_ptr, int ctor_argc)
{
zval reflector;
- zval output, *output_ptr = &output;
zval *argument_ptr, *argument2_ptr;
zval retval, params[2];
int result;
@@ -1419,9 +1462,7 @@ static void _reflection_export(INTERNAL_FUNCTION_PARAMETERS, zend_class_entry *c
/* Call __construct() */
fci.size = sizeof(fci);
- fci.function_table = NULL;
ZVAL_UNDEF(&fci.function_name);
- fci.symbol_table = NULL;
fci.object = Z_OBJ(reflector);
fci.retval = &retval;
fci.param_count = ctor_argc;
@@ -1448,12 +1489,10 @@ static void _reflection_export(INTERNAL_FUNCTION_PARAMETERS, zend_class_entry *c
}
/* Call static reflection::export */
- ZVAL_BOOL(&output, return_output);
ZVAL_COPY_VALUE(&params[0], &reflector);
- ZVAL_COPY_VALUE(&params[1], output_ptr);
+ ZVAL_BOOL(&params[1], return_output);
ZVAL_STRINGL(&fci.function_name, "reflection::export", sizeof("reflection::export") - 1);
- fci.function_table = &reflection_ptr->function_table;
fci.object = NULL;
fci.retval = &retval;
fci.param_count = 2;
@@ -1492,7 +1531,8 @@ static parameter_reference *_reflection_param_get_default_param(INTERNAL_FUNCTIO
if (EG(exception) && EG(exception)->ce == reflection_exception_ptr) {
return NULL;
}
- php_error_docref(NULL, E_ERROR, "Internal error: Failed to retrieve the reflection object");
+ zend_throw_error(NULL, "Internal error: Failed to retrieve the reflection object");
+ return NULL;
}
param = intern->ptr;
@@ -1919,7 +1959,7 @@ ZEND_METHOD(reflection_function, getStaticVariables)
fptr->op_array.static_variables = zend_array_dup(fptr->op_array.static_variables);
}
ZEND_HASH_FOREACH_VAL(fptr->op_array.static_variables, val) {
- if (UNEXPECTED(zval_update_constant_ex(val, 1, fptr->common.scope) != SUCCESS)) {
+ if (UNEXPECTED(zval_update_constant_ex(val, fptr->common.scope) != SUCCESS)) {
return;
}
} ZEND_HASH_FOREACH_END();
@@ -1948,9 +1988,7 @@ ZEND_METHOD(reflection_function, invoke)
}
fci.size = sizeof(fci);
- fci.function_table = NULL;
ZVAL_UNDEF(&fci.function_name);
- fci.symbol_table = NULL;
fci.object = NULL;
fci.retval = &retval;
fci.param_count = num_args;
@@ -1959,7 +1997,7 @@ ZEND_METHOD(reflection_function, invoke)
fcc.initialized = 1;
fcc.function_handler = fptr;
- fcc.calling_scope = EG(scope);
+ fcc.calling_scope = zend_get_executed_scope();
fcc.called_scope = NULL;
fcc.object = NULL;
@@ -1972,6 +2010,9 @@ ZEND_METHOD(reflection_function, invoke)
}
if (Z_TYPE(retval) != IS_UNDEF) {
+ if (Z_ISREF(retval)) {
+ zend_unwrap_reference(&retval);
+ }
ZVAL_COPY_VALUE(return_value, &retval);
}
}
@@ -2008,9 +2049,7 @@ ZEND_METHOD(reflection_function, invokeArgs)
} ZEND_HASH_FOREACH_END();
fci.size = sizeof(fci);
- fci.function_table = NULL;
ZVAL_UNDEF(&fci.function_name);
- fci.symbol_table = NULL;
fci.object = NULL;
fci.retval = &retval;
fci.param_count = argc;
@@ -2019,7 +2058,7 @@ ZEND_METHOD(reflection_function, invokeArgs)
fcc.initialized = 1;
fcc.function_handler = fptr;
- fcc.calling_scope = EG(scope);
+ fcc.calling_scope = zend_get_executed_scope();
fcc.called_scope = NULL;
fcc.object = NULL;
@@ -2037,6 +2076,9 @@ ZEND_METHOD(reflection_function, invokeArgs)
}
if (Z_TYPE(retval) != IS_UNDEF) {
+ if (Z_ISREF(retval)) {
+ zend_unwrap_reference(&retval);
+ }
ZVAL_COPY_VALUE(return_value, &retval);
}
}
@@ -2304,7 +2346,7 @@ ZEND_METHOD(reflection_generator, getThis)
REFLECTION_CHECK_VALID_GENERATOR(ex)
- if (Z_OBJ(ex->This)) {
+ if (Z_TYPE(ex->This) == IS_OBJECT) {
ZVAL_COPY(return_value, &ex->This);
} else {
ZVAL_NULL(return_value);
@@ -2880,15 +2922,9 @@ ZEND_METHOD(reflection_parameter, getDefaultValue)
return;
}
- ZVAL_COPY_VALUE(return_value, RT_CONSTANT(&param->fptr->op_array, precv->op2));
+ ZVAL_DUP(return_value, RT_CONSTANT(&param->fptr->op_array, precv->op2));
if (Z_CONSTANT_P(return_value)) {
- zend_class_entry *old_scope = EG(scope);
-
- EG(scope) = param->fptr->common.scope;
- zval_update_constant_ex(return_value, 0, NULL);
- EG(scope) = old_scope;
- } else {
- zval_copy_ctor(return_value);
+ zval_update_constant_ex(return_value, param->fptr->common.scope);
}
}
/* }}} */
@@ -2989,6 +3025,28 @@ ZEND_METHOD(reflection_type, isBuiltin)
}
/* }}} */
+/* {{{ reflection_type_name */
+static zend_string *reflection_type_name(type_reference *param) {
+ switch (param->arg_info->type_hint) {
+ case IS_ARRAY: return zend_string_init("array", sizeof("array") - 1, 0);
+ case IS_CALLABLE: return zend_string_init("callable", sizeof("callable") - 1, 0);
+ case IS_OBJECT:
+ if (param->fptr->type == ZEND_INTERNAL_FUNCTION &&
+ !(param->fptr->common.fn_flags & ZEND_ACC_USER_ARG_INFO)) {
+ return zend_string_init(((zend_internal_arg_info*)param->arg_info)->class_name, strlen(((zend_internal_arg_info*)param->arg_info)->class_name), 0);
+ }
+ return zend_string_copy(param->arg_info->class_name);
+ case IS_STRING: return zend_string_init("string", sizeof("string") - 1, 0);
+ case _IS_BOOL: return zend_string_init("bool", sizeof("bool") - 1, 0);
+ case IS_LONG: return zend_string_init("int", sizeof("int") - 1, 0);
+ case IS_DOUBLE: return zend_string_init("float", sizeof("float") - 1, 0);
+ case IS_VOID: return zend_string_init("void", sizeof("void") - 1, 0);
+ case IS_ITERABLE: return zend_string_init("iterable", sizeof("iterable") - 1, 0);
+ EMPTY_SWITCH_DEFAULT_CASE()
+ }
+}
+/* }}} */
+
/* {{{ proto public string ReflectionType::__toString()
Return the text of the type hint */
ZEND_METHOD(reflection_type, __toString)
@@ -3000,22 +3058,24 @@ ZEND_METHOD(reflection_type, __toString)
return;
}
GET_REFLECTION_OBJECT_PTR(param);
+
+ RETURN_STR(reflection_type_name(param));
+}
+/* }}} */
- switch (param->arg_info->type_hint) {
- case IS_ARRAY: RETURN_STRINGL("array", sizeof("array") - 1);
- case IS_CALLABLE: RETURN_STRINGL("callable", sizeof("callable") - 1);
- case IS_OBJECT:
- if (param->fptr->type == ZEND_INTERNAL_FUNCTION &&
- !(param->fptr->common.fn_flags & ZEND_ACC_USER_ARG_INFO)) {
- RETURN_STRING(((zend_internal_arg_info*)param->arg_info)->class_name);
- }
- RETURN_STR_COPY(param->arg_info->class_name);
- case IS_STRING: RETURN_STRINGL("string", sizeof("string") - 1);
- case _IS_BOOL: RETURN_STRINGL("bool", sizeof("bool") - 1);
- case IS_LONG: RETURN_STRINGL("int", sizeof("int") - 1);
- case IS_DOUBLE: RETURN_STRINGL("float", sizeof("float") - 1);
- EMPTY_SWITCH_DEFAULT_CASE()
+/* {{{ proto public string ReflectionNamedType::getName()
+ Return the text of the type hint */
+ZEND_METHOD(reflection_named_type, getName)
+{
+ reflection_object *intern;
+ type_reference *param;
+
+ if (zend_parse_parameters_none() == FAILURE) {
+ return;
}
+ GET_REFLECTION_OBJECT_PTR(param);
+
+ RETURN_STR(reflection_type_name(param));
}
/* }}} */
@@ -3174,19 +3234,18 @@ ZEND_METHOD(reflection_method, getClosure)
}
/* }}} */
-/* {{{ proto public mixed ReflectionMethod::invoke(mixed object, mixed* args)
- Invokes the method. */
-ZEND_METHOD(reflection_method, invoke)
+/* {{{ reflection_method_invoke */
+static void reflection_method_invoke(INTERNAL_FUNCTION_PARAMETERS, int variadic)
{
zval retval;
- zval *params = NULL;
- zend_object *object;
+ zval *params = NULL, *val, *object;
reflection_object *intern;
zend_function *mptr;
- int result, num_args = 0;
+ int i, argc = 0, result;
zend_fcall_info fci;
zend_fcall_info_cache fcc;
zend_class_entry *obj_ce;
+ zval *param_array;
METHOD_NOTSTATIC(reflection_method_ptr);
@@ -3210,115 +3269,25 @@ ZEND_METHOD(reflection_method, invoke)
return;
}
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &params, &num_args) == FAILURE) {
- return;
- }
-
- /* In case this is a static method, we should'nt pass an object_ptr
- * (which is used as calling context aka $this). We can thus ignore the
- * first parameter.
- *
- * Else, we verify that the given object is an instance of the class.
- */
- if (mptr->common.fn_flags & ZEND_ACC_STATIC) {
- object = NULL;
- obj_ce = mptr->common.scope;
- } else {
- if (Z_TYPE(params[0]) != IS_OBJECT) {
- _DO_THROW("Non-object passed to Invoke()");
- /* Returns from this function */
+ if (variadic) {
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "o!*", &object, &params, &argc) == FAILURE) {
+ return;
}
-
- obj_ce = Z_OBJCE(params[0]);
-
- if (!instanceof_function(obj_ce, mptr->common.scope)) {
- _DO_THROW("Given object is not an instance of the class this method was declared in");
- /* Returns from this function */
+ } else {
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "o!a", &object, &param_array) == FAILURE) {
+ return;
}
- object = Z_OBJ(params[0]);
- }
-
- fci.size = sizeof(fci);
- fci.function_table = NULL;
- ZVAL_UNDEF(&fci.function_name);
- fci.symbol_table = NULL;
- fci.object = object;
- fci.retval = &retval;
- fci.param_count = num_args - 1;
- fci.params = params + 1;
- fci.no_separation = 1;
-
- fcc.initialized = 1;
- fcc.function_handler = mptr;
- fcc.calling_scope = obj_ce;
- fcc.called_scope = intern->ce;
- fcc.object = object;
-
- result = zend_call_function(&fci, &fcc);
+ argc = zend_hash_num_elements(Z_ARRVAL_P(param_array));
- if (result == FAILURE) {
- zend_throw_exception_ex(reflection_exception_ptr, 0,
- "Invocation of method %s::%s() failed", ZSTR_VAL(mptr->common.scope->name), ZSTR_VAL(mptr->common.function_name));
- return;
- }
-
- if (Z_TYPE(retval) != IS_UNDEF) {
- ZVAL_COPY_VALUE(return_value, &retval);
- }
-}
-/* }}} */
-
-/* {{{ proto public mixed ReflectionMethod::invokeArgs(mixed object, array args)
- Invokes the function and pass its arguments as array. */
-ZEND_METHOD(reflection_method, invokeArgs)
-{
- zval retval;
- zval *params, *val, *object;
- reflection_object *intern;
- zend_function *mptr;
- int i, argc;
- int result;
- zend_fcall_info fci;
- zend_fcall_info_cache fcc;
- zend_class_entry *obj_ce;
- zval *param_array;
-
- METHOD_NOTSTATIC(reflection_method_ptr);
-
- GET_REFLECTION_OBJECT_PTR(mptr);
-
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "o!a", &object, &param_array) == FAILURE) {
- return;
- }
-
- if ((!(mptr->common.fn_flags & ZEND_ACC_PUBLIC)
- || (mptr->common.fn_flags & ZEND_ACC_ABSTRACT))
- && intern->ignore_visibility == 0)
- {
- if (mptr->common.fn_flags & ZEND_ACC_ABSTRACT) {
- zend_throw_exception_ex(reflection_exception_ptr, 0,
- "Trying to invoke abstract method %s::%s()",
- ZSTR_VAL(mptr->common.scope->name), ZSTR_VAL(mptr->common.function_name));
- } else {
- zend_throw_exception_ex(reflection_exception_ptr, 0,
- "Trying to invoke %s method %s::%s() from scope %s",
- mptr->common.fn_flags & ZEND_ACC_PROTECTED ? "protected" : "private",
- ZSTR_VAL(mptr->common.scope->name), ZSTR_VAL(mptr->common.function_name),
- ZSTR_VAL(Z_OBJCE_P(getThis())->name));
- }
- return;
+ params = safe_emalloc(sizeof(zval), argc, 0);
+ argc = 0;
+ ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(param_array), val) {
+ ZVAL_COPY(&params[argc], val);
+ argc++;
+ } ZEND_HASH_FOREACH_END();
}
- argc = zend_hash_num_elements(Z_ARRVAL_P(param_array));
-
- params = safe_emalloc(sizeof(zval), argc, 0);
- argc = 0;
- ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(param_array), val) {
- ZVAL_COPY(&params[argc], val);
- argc++;
- } ZEND_HASH_FOREACH_END();
-
/* In case this is a static method, we should'nt pass an object_ptr
* (which is used as calling context aka $this). We can thus ignore the
* first parameter.
@@ -3330,7 +3299,6 @@ ZEND_METHOD(reflection_method, invokeArgs)
obj_ce = mptr->common.scope;
} else {
if (!object) {
- efree(params);
zend_throw_exception_ex(reflection_exception_ptr, 0,
"Trying to invoke non static method %s::%s() without an object",
ZSTR_VAL(mptr->common.scope->name), ZSTR_VAL(mptr->common.function_name));
@@ -3340,16 +3308,16 @@ ZEND_METHOD(reflection_method, invokeArgs)
obj_ce = Z_OBJCE_P(object);
if (!instanceof_function(obj_ce, mptr->common.scope)) {
- efree(params);
+ if (!variadic) {
+ efree(params);
+ }
_DO_THROW("Given object is not an instance of the class this method was declared in");
/* Returns from this function */
}
}
fci.size = sizeof(fci);
- fci.function_table = NULL;
ZVAL_UNDEF(&fci.function_name);
- fci.symbol_table = NULL;
fci.object = object ? Z_OBJ_P(object) : NULL;
fci.retval = &retval;
fci.param_count = argc;
@@ -3360,21 +3328,25 @@ ZEND_METHOD(reflection_method, invokeArgs)
fcc.function_handler = mptr;
fcc.calling_scope = obj_ce;
fcc.called_scope = intern->ce;
- fcc.object = (object) ? Z_OBJ_P(object) : NULL;
+ fcc.object = object ? Z_OBJ_P(object) : NULL;
- /*
- * Copy the zend_function when calling via handler (e.g. Closure::__invoke())
- */
- if ((mptr->internal_function.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) {
- fcc.function_handler = _copy_function(mptr);
+ if (!variadic) {
+ /*
+ * Copy the zend_function when calling via handler (e.g. Closure::__invoke())
+ */
+ if ((mptr->internal_function.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) {
+ fcc.function_handler = _copy_function(mptr);
+ }
}
result = zend_call_function(&fci, &fcc);
- for (i = 0; i < argc; i++) {
- zval_ptr_dtor(&params[i]);
+ if (!variadic) {
+ for (i = 0; i < argc; i++) {
+ zval_ptr_dtor(&params[i]);
+ }
+ efree(params);
}
- efree(params);
if (result == FAILURE) {
zend_throw_exception_ex(reflection_exception_ptr, 0,
@@ -3383,11 +3355,30 @@ ZEND_METHOD(reflection_method, invokeArgs)
}
if (Z_TYPE(retval) != IS_UNDEF) {
+ if (Z_ISREF(retval)) {
+ zend_unwrap_reference(&retval);
+ }
ZVAL_COPY_VALUE(return_value, &retval);
}
}
/* }}} */
+/* {{{ proto public mixed ReflectionMethod::invoke(mixed object, mixed* args)
+ Invokes the method. */
+ZEND_METHOD(reflection_method, invoke)
+{
+ reflection_method_invoke(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
+}
+/* }}} */
+
+/* {{{ proto public mixed ReflectionMethod::invokeArgs(mixed object, array args)
+ Invokes the function and pass its arguments as array. */
+ZEND_METHOD(reflection_method, invokeArgs)
+{
+ reflection_method_invoke(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
+}
+/* }}} */
+
/* {{{ proto public bool ReflectionMethod::isFinal()
Returns whether this method is final */
ZEND_METHOD(reflection_method, isFinal)
@@ -3676,6 +3667,197 @@ ZEND_METHOD(reflection_method, setAccessible)
}
/* }}} */
+/* {{{ proto public void ReflectionClassConstant::__construct(mixed class, string name)
+ Constructor. Throws an Exception in case the given class constant does not exist */
+ZEND_METHOD(reflection_class_constant, __construct)
+{
+ zval *classname, *object, name, cname;
+ zend_string *constname;
+ reflection_object *intern;
+ zend_class_entry *ce;
+ zend_class_constant *constant = NULL;
+
+ if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "zS", &classname, &constname) == FAILURE) {
+ return;
+ }
+
+ object = getThis();
+ intern = Z_REFLECTION_P(object);
+
+ /* Find the class entry */
+ switch (Z_TYPE_P(classname)) {
+ case IS_STRING:
+ if ((ce = zend_lookup_class(Z_STR_P(classname))) == NULL) {
+ zend_throw_exception_ex(reflection_exception_ptr, 0,
+ "Class %s does not exist", Z_STRVAL_P(classname));
+ return;
+ }
+ break;
+
+ case IS_OBJECT:
+ ce = Z_OBJCE_P(classname);
+ break;
+
+ default:
+ _DO_THROW("The parameter class is expected to be either a string or an object");
+ /* returns out of this function */
+ }
+
+ if ((constant = zend_hash_find_ptr(&ce->constants_table, constname)) == NULL) {
+ zend_throw_exception_ex(reflection_exception_ptr, 0, "Class Constant %s::%s does not exist", ZSTR_VAL(ce->name), ZSTR_VAL(constname));
+ return;
+ }
+
+ ZVAL_STR_COPY(&name, constname);
+ ZVAL_STR_COPY(&cname, ce->name);
+
+ intern->ptr = constant;
+ intern->ref_type = REF_TYPE_CLASS_CONSTANT;
+ intern->ce = constant->ce;
+ intern->ignore_visibility = 0;
+ reflection_update_property(object, "name", &name);
+ reflection_update_property(object, "class", &cname);
+}
+/* }}} */
+
+/* {{{ proto public string ReflectionClassConstant::__toString()
+ Returns a string representation */
+ZEND_METHOD(reflection_class_constant, __toString)
+{
+ reflection_object *intern;
+ zend_class_constant *ref;
+ string str;
+ zval name;
+
+ if (zend_parse_parameters_none() == FAILURE) {
+ return;
+ }
+ GET_REFLECTION_OBJECT_PTR(ref);
+ string_init(&str);
+ _default_get_entry(getThis(), "name", sizeof("name")-1, &name);
+ _class_const_string(&str, Z_STRVAL(name), ref, "");
+ zval_ptr_dtor(&name);
+ RETURN_NEW_STR(str.buf);
+}
+/* }}} */
+
+/* {{{ proto public string ReflectionClassConstant::getName()
+ Returns the constant' name */
+ZEND_METHOD(reflection_class_constant, getName)
+{
+ if (zend_parse_parameters_none() == FAILURE) {
+ return;
+ }
+ _default_get_entry(getThis(), "name", sizeof("name")-1, return_value);
+}
+/* }}} */
+
+static void _class_constant_check_flag(INTERNAL_FUNCTION_PARAMETERS, int mask) /* {{{ */
+{
+ reflection_object *intern;
+ zend_class_constant *ref;
+
+ if (zend_parse_parameters_none() == FAILURE) {
+ return;
+ }
+ GET_REFLECTION_OBJECT_PTR(ref);
+ RETURN_BOOL(Z_ACCESS_FLAGS(ref->value) & mask);
+}
+/* }}} */
+
+/* {{{ proto public bool ReflectionClassConstant::isPublic()
+ Returns whether this constant is public */
+ZEND_METHOD(reflection_class_constant, isPublic)
+{
+ _class_constant_check_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_ACC_PUBLIC | ZEND_ACC_IMPLICIT_PUBLIC);
+}
+/* }}} */
+
+/* {{{ proto public bool ReflectionClassConstant::isPrivate()
+ Returns whether this constant is private */
+ZEND_METHOD(reflection_class_constant, isPrivate)
+{
+ _class_constant_check_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_ACC_PRIVATE);
+}
+/* }}} */
+
+/* {{{ proto public bool ReflectionClassConstant::isProtected()
+ Returns whether this constant is protected */
+ZEND_METHOD(reflection_class_constant, isProtected)
+{
+ _class_constant_check_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_ACC_PROTECTED);
+}
+/* }}} */
+
+/* {{{ proto public int ReflectionClassConstant::getModifiers()
+ Returns a bitfield of the access modifiers for this constant */
+ZEND_METHOD(reflection_class_constant, getModifiers)
+{
+ reflection_object *intern;
+ zend_class_constant *ref;
+
+ if (zend_parse_parameters_none() == FAILURE) {
+ return;
+ }
+ GET_REFLECTION_OBJECT_PTR(ref);
+
+ RETURN_LONG(Z_ACCESS_FLAGS(ref->value));
+}
+/* }}} */
+
+/* {{{ proto public mixed ReflectionClassConstant::getValue()
+ Returns this constant's value */
+ZEND_METHOD(reflection_class_constant, getValue)
+{
+ reflection_object *intern;
+ zend_class_constant *ref;
+
+ if (zend_parse_parameters_none() == FAILURE) {
+ return;
+ }
+ GET_REFLECTION_OBJECT_PTR(ref);
+
+ ZVAL_DUP(return_value, &ref->value);
+ if (Z_CONSTANT_P(return_value)) {
+ zval_update_constant_ex(return_value, ref->ce);
+ }
+}
+/* }}} */
+
+/* {{{ proto public ReflectionClass ReflectionClassConstant::getDeclaringClass()
+ Get the declaring class */
+ZEND_METHOD(reflection_class_constant, getDeclaringClass)
+{
+ reflection_object *intern;
+ zend_class_constant *ref;
+
+ if (zend_parse_parameters_none() == FAILURE) {
+ return;
+ }
+ GET_REFLECTION_OBJECT_PTR(ref);
+
+ zend_reflection_class_factory(ref->ce, return_value);
+}
+/* }}} */
+
+/* {{{ proto public string ReflectionClassConstant::getDocComment()
+ Returns the doc comment for this constant */
+ZEND_METHOD(reflection_class_constant, getDocComment)
+{
+ reflection_object *intern;
+ zend_class_constant *ref;
+
+ if (zend_parse_parameters_none() == FAILURE) {
+ return;
+ }
+ GET_REFLECTION_OBJECT_PTR(ref);
+ if (ref->doc_comment) {
+ RETURN_STR_COPY(ref->doc_comment);
+ }
+ RETURN_FALSE;
+}
+/* }}} */
+
/* {{{ proto public static mixed ReflectionClass::export(mixed argument [, bool return]) throws ReflectionException
Exports a reflection object. Returns the output if TRUE is specified for return, printing it otherwise. */
ZEND_METHOD(reflection_class, export)
@@ -3773,7 +3955,7 @@ static void add_class_vars(zend_class_entry *ce, int statics, zval *return_value
/* this is necessary to make it able to work with default array
* properties, returned to user */
if (Z_CONSTANT(prop_copy)) {
- if (UNEXPECTED(zval_update_constant_ex(&prop_copy, 1, NULL) != SUCCESS)) {
+ if (UNEXPECTED(zval_update_constant_ex(&prop_copy, NULL) != SUCCESS)) {
return;
}
}
@@ -4423,6 +4605,8 @@ ZEND_METHOD(reflection_class, getConstants)
{
reflection_object *intern;
zend_class_entry *ce;
+ zend_string *key;
+ zend_class_constant *c;
zval *val;
if (zend_parse_parameters_none() == FAILURE) {
@@ -4430,13 +4614,36 @@ ZEND_METHOD(reflection_class, getConstants)
}
GET_REFLECTION_OBJECT_PTR(ce);
array_init(return_value);
- ZEND_HASH_FOREACH_VAL(&ce->constants_table, val) {
- ZVAL_DEREF(val);
- if (UNEXPECTED(zval_update_constant_ex(val, 1, ce) != SUCCESS)) {
+ ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->constants_table, key, c) {
+ if (UNEXPECTED(zval_update_constant_ex(&c->value, ce) != SUCCESS)) {
+ zend_array_destroy(Z_ARRVAL_P(return_value));
return;
}
+ val = zend_hash_add_new(Z_ARRVAL_P(return_value), key, &c->value);
+ Z_TRY_ADDREF_P(val);
+ } ZEND_HASH_FOREACH_END();
+}
+/* }}} */
+
+/* {{{ proto public array ReflectionClass::getReflectionConstants()
+ Returns an associative array containing this class' constants as ReflectionClassConstant objects */
+ZEND_METHOD(reflection_class, getReflectionConstants)
+{
+ reflection_object *intern;
+ zend_class_entry *ce;
+ zend_string *name;
+ zend_class_constant *constant;
+
+ if (zend_parse_parameters_none() == FAILURE) {
+ return;
+ }
+ GET_REFLECTION_OBJECT_PTR(ce);
+ array_init(return_value);
+ ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->constants_table, name, constant) {
+ zval class_const;
+ reflection_class_constant_factory(ce, name, constant, &class_const);
+ zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &class_const);
} ZEND_HASH_FOREACH_END();
- zend_hash_copy(Z_ARRVAL_P(return_value), &ce->constants_table, zval_add_ref_unref);
}
/* }}} */
@@ -4446,7 +4653,7 @@ ZEND_METHOD(reflection_class, getConstant)
{
reflection_object *intern;
zend_class_entry *ce;
- zval *value;
+ zend_class_constant *c;
zend_string *name;
METHOD_NOTSTATIC(reflection_class_ptr);
@@ -4455,16 +4662,36 @@ ZEND_METHOD(reflection_class, getConstant)
}
GET_REFLECTION_OBJECT_PTR(ce);
- ZEND_HASH_FOREACH_VAL(&ce->constants_table, value) {
- ZVAL_DEREF(value);
- if (UNEXPECTED(zval_update_constant_ex(value, 1, ce) != SUCCESS)) {
+ ZEND_HASH_FOREACH_PTR(&ce->constants_table, c) {
+ if (UNEXPECTED(zval_update_constant_ex(&c->value, ce) != SUCCESS)) {
return;
}
} ZEND_HASH_FOREACH_END();
- if ((value = zend_hash_find(&ce->constants_table, name)) == NULL) {
+ if ((c = zend_hash_find_ptr(&ce->constants_table, name)) == NULL) {
RETURN_FALSE;
}
- ZVAL_DUP(return_value, value);
+ ZVAL_DUP(return_value, &c->value);
+}
+/* }}} */
+
+/* {{{ proto public mixed ReflectionClass::getReflectionConstant(string name)
+ Returns the class' constant as ReflectionClassConstant objects */
+ZEND_METHOD(reflection_class, getReflectionConstant)
+{
+ reflection_object *intern;
+ zend_class_entry *ce;
+ zend_class_constant *constant;
+ zend_string *name;
+
+ GET_REFLECTION_OBJECT_PTR(ce);
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &name) == FAILURE) {
+ return;
+ }
+
+ if ((constant = zend_hash_find_ptr(&ce->constants_table, name)) == NULL) {
+ RETURN_FALSE;
+ }
+ reflection_class_constant_factory(ce, name, constant, return_value);
}
/* }}} */
@@ -4586,7 +4813,7 @@ ZEND_METHOD(reflection_class, getModifiers)
}
GET_REFLECTION_OBJECT_PTR(ce);
- RETURN_LONG(ce->ce_flags & ~(ZEND_ACC_CONSTANTS_UPDATED|ZEND_ACC_USE_GUARDS));
+ RETURN_LONG(ce->ce_flags & ~(ZEND_ACC_CONSTANTS_UPDATED|ZEND_ACC_USE_GUARDS|ZEND_ACC_INHERITED));
}
/* }}} */
@@ -4623,10 +4850,10 @@ ZEND_METHOD(reflection_class, newInstance)
return;
}
- old_scope = EG(scope);
- EG(scope) = ce;
+ old_scope = EG(fake_scope);
+ EG(fake_scope) = ce;
constructor = Z_OBJ_HT_P(return_value)->get_constructor(Z_OBJ_P(return_value));
- EG(scope) = old_scope;
+ EG(fake_scope) = old_scope;
/* Run the constructor if there is one */
if (constructor) {
@@ -4651,9 +4878,7 @@ ZEND_METHOD(reflection_class, newInstance)
}
fci.size = sizeof(fci);
- fci.function_table = EG(function_table);
ZVAL_UNDEF(&fci.function_name);
- fci.symbol_table = NULL;
fci.object = Z_OBJ_P(return_value);
fci.retval = &retval;
fci.param_count = num_args;
@@ -4662,7 +4887,7 @@ ZEND_METHOD(reflection_class, newInstance)
fcc.initialized = 1;
fcc.function_handler = constructor;
- fcc.calling_scope = EG(scope);
+ fcc.calling_scope = zend_get_executed_scope();;
fcc.called_scope = Z_OBJCE_P(return_value);
fcc.object = Z_OBJ_P(return_value);
@@ -4728,10 +4953,10 @@ ZEND_METHOD(reflection_class, newInstanceArgs)
return;
}
- old_scope = EG(scope);
- EG(scope) = ce;
+ old_scope = EG(fake_scope);
+ EG(fake_scope) = ce;
constructor = Z_OBJ_HT_P(return_value)->get_constructor(Z_OBJ_P(return_value));
- EG(scope) = old_scope;
+ EG(fake_scope) = old_scope;
/* Run the constructor if there is one */
if (constructor) {
@@ -4755,9 +4980,7 @@ ZEND_METHOD(reflection_class, newInstanceArgs)
}
fci.size = sizeof(fci);
- fci.function_table = EG(function_table);
ZVAL_UNDEF(&fci.function_name);
- fci.symbol_table = NULL;
fci.object = Z_OBJ_P(return_value);
fci.retval = &retval;
fci.param_count = argc;
@@ -4766,7 +4989,7 @@ ZEND_METHOD(reflection_class, newInstanceArgs)
fcc.initialized = 1;
fcc.function_handler = constructor;
- fcc.calling_scope = EG(scope);
+ fcc.calling_scope = zend_get_executed_scope();
fcc.called_scope = Z_OBJCE_P(return_value);
fcc.object = Z_OBJ_P(return_value);
@@ -4961,9 +5184,9 @@ ZEND_METHOD(reflection_class, isSubclassOf)
case IS_OBJECT:
if (instanceof_function(Z_OBJCE_P(class_name), reflection_class_ptr)) {
argument = Z_REFLECTION_P(class_name);
- if (argument == NULL || argument->ptr == NULL) {
- php_error_docref(NULL, E_ERROR, "Internal error: Failed to retrieve the argument's reflection object");
- /* Bails out */
+ if (argument->ptr == NULL) {
+ zend_throw_error(NULL, "Internal error: Failed to retrieve the argument's reflection object");
+ return;
}
class_ce = argument->ptr;
break;
@@ -5005,9 +5228,9 @@ ZEND_METHOD(reflection_class, implementsInterface)
case IS_OBJECT:
if (instanceof_function(Z_OBJCE_P(interface), reflection_class_ptr)) {
argument = Z_REFLECTION_P(interface);
- if (argument == NULL || argument->ptr == NULL) {
- php_error_docref(NULL, E_ERROR, "Internal error: Failed to retrieve the argument's reflection object");
- /* Bails out */
+ if (argument->ptr == NULL) {
+ zend_throw_error(NULL, "Internal error: Failed to retrieve the argument's reflection object");
+ return;
}
interface_ce = argument->ptr;
break;
@@ -5182,6 +5405,14 @@ ZEND_METHOD(reflection_property, export)
}
/* }}} */
+/* {{{ proto public static mixed ReflectionClassConstant::export(mixed class, string name [, bool return]) throws ReflectionException
+ Exports a reflection object. Returns the output if TRUE is specified for return, printing it otherwise. */
+ZEND_METHOD(reflection_class_constant, export)
+{
+ _reflection_export(INTERNAL_FUNCTION_PARAM_PASSTHRU, reflection_class_constant_ptr, 2);
+}
+/* }}} */
+
/* {{{ proto public void ReflectionProperty::__construct(mixed class, string name)
Constructor. Throws an Exception in case the given property does not exist */
ZEND_METHOD(reflection_property, __construct)
@@ -5399,10 +5630,12 @@ ZEND_METHOD(reflection_property, getValue)
return;
}
if (Z_TYPE(CE_STATIC_MEMBERS(intern->ce)[ref->prop.offset]) == IS_UNDEF) {
- php_error_docref(NULL, E_ERROR, "Internal error: Could not find the property %s::%s", ZSTR_VAL(intern->ce->name), ZSTR_VAL(ref->prop.name));
- /* Bails out */
+ zend_throw_error(NULL, "Internal error: Could not find the property %s::%s", ZSTR_VAL(intern->ce->name), ZSTR_VAL(ref->prop.name));
+ return;
}
- ZVAL_DUP(return_value, &CE_STATIC_MEMBERS(intern->ce)[ref->prop.offset]);
+ member_p = &CE_STATIC_MEMBERS(intern->ce)[ref->prop.offset];
+ ZVAL_DEREF(member_p);
+ ZVAL_COPY(return_value, member_p);
} else {
const char *class_name, *prop_name;
size_t prop_name_len;
@@ -5412,11 +5645,20 @@ ZEND_METHOD(reflection_property, getValue)
return;
}
+ if (!instanceof_function(Z_OBJCE_P(object), ref->ce)) {
+ _DO_THROW("Given object is not an instance of the class this property was declared in");
+ /* Returns from this function */
+ }
+
zend_unmangle_property_name_ex(ref->prop.name, &class_name, &prop_name, &prop_name_len);
member_p = zend_read_property(ref->ce, object, prop_name, prop_name_len, 0, &rv);
if (member_p != &rv) {
+ ZVAL_DEREF(member_p);
ZVAL_COPY(return_value, member_p);
} else {
+ if (Z_ISREF_P(member_p)) {
+ zend_unwrap_reference(member_p);
+ }
ZVAL_COPY_VALUE(return_value, member_p);
}
}
@@ -5455,8 +5697,8 @@ ZEND_METHOD(reflection_property, setValue)
}
if (Z_TYPE(CE_STATIC_MEMBERS(intern->ce)[ref->prop.offset]) == IS_UNDEF) {
- php_error_docref(NULL, E_ERROR, "Internal error: Could not find the property %s::%s", ZSTR_VAL(intern->ce->name), ZSTR_VAL(ref->prop.name));
- /* Bails out */
+ zend_throw_error(NULL, "Internal error: Could not find the property %s::%s", ZSTR_VAL(intern->ce->name), ZSTR_VAL(ref->prop.name));
+ return;
}
variable_ptr = &CE_STATIC_MEMBERS(intern->ce)[ref->prop.offset];
if (variable_ptr != value) {
@@ -5585,7 +5827,6 @@ ZEND_METHOD(reflection_extension, __construct)
object = getThis();
intern = Z_REFLECTION_P(object);
-
lcname = do_alloca(name_len + 1, use_heap);
zend_str_tolower_copy(lcname, name_str, name_len);
if ((module = zend_hash_str_find_ptr(&module_registry, lcname, name_len)) == NULL) {
@@ -6337,7 +6578,9 @@ static const zend_function_entry reflection_class_functions[] = {
ZEND_ME(reflection_class, getProperties, arginfo_reflection_class_getProperties, 0)
ZEND_ME(reflection_class, hasConstant, arginfo_reflection_class_hasConstant, 0)
ZEND_ME(reflection_class, getConstants, arginfo_reflection__void, 0)
+ ZEND_ME(reflection_class, getReflectionConstants, arginfo_reflection__void, 0)
ZEND_ME(reflection_class, getConstant, arginfo_reflection_class_getConstant, 0)
+ ZEND_ME(reflection_class, getReflectionConstant, arginfo_reflection_class_getConstant, 0)
ZEND_ME(reflection_class, getInterfaces, arginfo_reflection__void, 0)
ZEND_ME(reflection_class, getInterfaceNames, arginfo_reflection__void, 0)
ZEND_ME(reflection_class, isInterface, arginfo_reflection__void, 0)
@@ -6429,6 +6672,33 @@ static const zend_function_entry reflection_property_functions[] = {
PHP_FE_END
};
+ZEND_BEGIN_ARG_INFO_EX(arginfo_reflection_class_constant_export, 0, 0, 2)
+ ZEND_ARG_INFO(0, class)
+ ZEND_ARG_INFO(0, name)
+ ZEND_ARG_INFO(0, return)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_reflection_class_constant___construct, 0, 0, 2)
+ ZEND_ARG_INFO(0, class)
+ ZEND_ARG_INFO(0, name)
+ZEND_END_ARG_INFO()
+
+static const zend_function_entry reflection_class_constant_functions[] = {
+ ZEND_ME(reflection, __clone, arginfo_reflection__void, ZEND_ACC_PRIVATE|ZEND_ACC_FINAL)
+ ZEND_ME(reflection_class_constant, export, arginfo_reflection_class_constant_export, ZEND_ACC_STATIC|ZEND_ACC_PUBLIC)
+ ZEND_ME(reflection_class_constant, __construct, arginfo_reflection_class_constant___construct, 0)
+ ZEND_ME(reflection_class_constant, __toString, arginfo_reflection__void, 0)
+ ZEND_ME(reflection_class_constant, getName, arginfo_reflection__void, 0)
+ ZEND_ME(reflection_class_constant, getValue, arginfo_reflection__void, 0)
+ ZEND_ME(reflection_class_constant, isPublic, arginfo_reflection__void, 0)
+ ZEND_ME(reflection_class_constant, isPrivate, arginfo_reflection__void, 0)
+ ZEND_ME(reflection_class_constant, isProtected, arginfo_reflection__void, 0)
+ ZEND_ME(reflection_class_constant, getModifiers, arginfo_reflection__void, 0)
+ ZEND_ME(reflection_class_constant, getDeclaringClass, arginfo_reflection__void, 0)
+ ZEND_ME(reflection_class_constant, getDocComment, arginfo_reflection__void, 0)
+ PHP_FE_END
+};
+
ZEND_BEGIN_ARG_INFO_EX(arginfo_reflection_parameter_export, 0, 0, 2)
ZEND_ARG_INFO(0, function)
ZEND_ARG_INFO(0, parameter)
@@ -6470,10 +6740,18 @@ static const zend_function_entry reflection_type_functions[] = {
ZEND_ME(reflection, __clone, arginfo_reflection__void, ZEND_ACC_PRIVATE|ZEND_ACC_FINAL)
ZEND_ME(reflection_type, allowsNull, arginfo_reflection__void, 0)
ZEND_ME(reflection_type, isBuiltin, arginfo_reflection__void, 0)
+ /* ReflectionType::__toString() is deprecated, but we currently do not mark it as such
+ * due to bad interaction with the PHPUnit error handler and exceptions in __toString().
+ * See PR2137. */
ZEND_ME(reflection_type, __toString, arginfo_reflection__void, 0)
PHP_FE_END
};
+static const zend_function_entry reflection_named_type_functions[] = {
+ ZEND_ME(reflection_named_type, getName, arginfo_reflection__void, 0)
+ PHP_FE_END
+};
+
ZEND_BEGIN_ARG_INFO_EX(arginfo_reflection_extension_export, 0, 0, 1)
ZEND_ARG_INFO(0, name)
ZEND_ARG_INFO(0, return)
@@ -6591,6 +6869,10 @@ PHP_MINIT_FUNCTION(reflection) /* {{{ */
INIT_CLASS_ENTRY(_reflection_entry, "ReflectionType", reflection_type_functions);
_reflection_entry.create_object = reflection_objects_new;
reflection_type_ptr = zend_register_internal_class(&_reflection_entry);
+
+ INIT_CLASS_ENTRY(_reflection_entry, "ReflectionNamedType", reflection_named_type_functions);
+ _reflection_entry.create_object = reflection_objects_new;
+ reflection_named_type_ptr = zend_register_internal_class_ex(&_reflection_entry, reflection_type_ptr);
INIT_CLASS_ENTRY(_reflection_entry, "ReflectionMethod", reflection_method_functions);
_reflection_entry.create_object = reflection_objects_new;
@@ -6626,6 +6908,13 @@ PHP_MINIT_FUNCTION(reflection) /* {{{ */
zend_declare_property_string(reflection_property_ptr, "name", sizeof("name")-1, "", ZEND_ACC_PUBLIC);
zend_declare_property_string(reflection_property_ptr, "class", sizeof("class")-1, "", ZEND_ACC_PUBLIC);
+ INIT_CLASS_ENTRY(_reflection_entry, "ReflectionClassConstant", reflection_class_constant_functions);
+ _reflection_entry.create_object = reflection_objects_new;
+ reflection_class_constant_ptr = zend_register_internal_class(&_reflection_entry);
+ zend_class_implements(reflection_class_constant_ptr, 1, reflector_ptr);
+ zend_declare_property_string(reflection_class_constant_ptr, "name", sizeof("name")-1, "", ZEND_ACC_PUBLIC);
+ zend_declare_property_string(reflection_class_constant_ptr, "class", sizeof("class")-1, "", ZEND_ACC_PUBLIC);
+
REGISTER_REFLECTION_CLASS_CONST_LONG(property, "IS_STATIC", ZEND_ACC_STATIC);
REGISTER_REFLECTION_CLASS_CONST_LONG(property, "IS_PUBLIC", ZEND_ACC_PUBLIC);
REGISTER_REFLECTION_CLASS_CONST_LONG(property, "IS_PROTECTED", ZEND_ACC_PROTECTED);
diff --git a/ext/reflection/php_reflection.h b/ext/reflection/php_reflection.h
index b9ef494017..71de1c01c6 100644
--- a/ext/reflection/php_reflection.h
+++ b/ext/reflection/php_reflection.h
@@ -38,6 +38,7 @@ extern PHPAPI zend_class_entry *reflection_function_abstract_ptr;
extern PHPAPI zend_class_entry *reflection_function_ptr;
extern PHPAPI zend_class_entry *reflection_parameter_ptr;
extern PHPAPI zend_class_entry *reflection_type_ptr;
+extern PHPAPI zend_class_entry *reflection_named_type_ptr;
extern PHPAPI zend_class_entry *reflection_class_ptr;
extern PHPAPI zend_class_entry *reflection_object_ptr;
extern PHPAPI zend_class_entry *reflection_method_ptr;
diff --git a/ext/reflection/tests/004.phpt b/ext/reflection/tests/004.phpt
index 41632aa3ba..36ae406b43 100644
--- a/ext/reflection/tests/004.phpt
+++ b/ext/reflection/tests/004.phpt
@@ -38,6 +38,6 @@ try {
echo "===DONE===\n";?>
--EXPECTF--
Deprecated: Methods with the same name as their class will not be constructors in a future version of PHP; a has a deprecated constructor in %s on line %d
-Non-object passed to Invoke()
+Trying to invoke non static method a::a() without an object
Given object is not an instance of the class this method was declared in
===DONE===
diff --git a/ext/reflection/tests/007.phpt b/ext/reflection/tests/007.phpt
index 004158ccff..d9204171b5 100644
--- a/ext/reflection/tests/007.phpt
+++ b/ext/reflection/tests/007.phpt
@@ -25,6 +25,10 @@ function test($class)
{
var_dump($e->getMessage());
}
+ catch (Throwable $e)
+ {
+ echo "Exception: " . $e->getMessage() . "\n";
+ }
echo "====>newInstance(25)\n";
try
@@ -129,15 +133,7 @@ object(WithCtor)#%d (0) {
====>WithCtorWithArgs
====>newInstance()
-
-Warning: Missing argument 1 for WithCtorWithArgs::__construct() in %s007.php on line %d
-
-Notice: Undefined variable: arg in %s007.php on line %d
-WithCtorWithArgs::__construct()
-array(0) {
-}
-object(WithCtorWithArgs)#%d (0) {
-}
+Exception: Too few arguments to function WithCtorWithArgs::__construct(), 0 passed and exactly 1 expected
====>newInstance(25)
WithCtorWithArgs::__construct(25)
array(1) {
diff --git a/ext/reflection/tests/017.phpt b/ext/reflection/tests/017.phpt
index d40c4d83f9..1d9275d21b 100644
--- a/ext/reflection/tests/017.phpt
+++ b/ext/reflection/tests/017.phpt
@@ -10,12 +10,12 @@ class Foo {
$class = new ReflectionClass("Foo");
echo $class;
?>
---EXPECTF--
+--EXPECTF--
Class [ <user> class Foo ] {
@@ %s017.php 2-4
- Constants [1] {
- Constant [ string test ] { ok }
+ Constant [ public string test ] { ok }
}
- Static properties [0] {
diff --git a/ext/reflection/tests/ReflectionClassConstant_basic1.phpt b/ext/reflection/tests/ReflectionClassConstant_basic1.phpt
new file mode 100644
index 0000000000..fd8118650f
--- /dev/null
+++ b/ext/reflection/tests/ReflectionClassConstant_basic1.phpt
@@ -0,0 +1,194 @@
+--TEST--
+Test usage of ReflectionClassConstant methods __toString(), export(), getName(), getValue(), isPublic(), isPrivate(), isProtected(), getModifiers(), getDeclaringClass() and getDocComment().
+--FILE--
+<?php
+
+function reflectClassConstant($base, $constant) {
+ $constInfo = new ReflectionClassConstant($base, $constant);
+ echo "**********************************\n";
+ $class = is_object($base) ? get_class($base) : $base;
+ echo "Reflecting on class constant $class::$constant\n\n";
+ echo "__toString():\n";
+ var_dump($constInfo->__toString());
+ echo "export():\n";
+ var_dump(ReflectionClassConstant::export($base, $constant, true));
+ echo "export():\n";
+ var_dump(ReflectionClassConstant::export($base, $constant, false));
+ echo "getName():\n";
+ var_dump($constInfo->getName());
+ echo "getValue():\n";
+ var_dump($constInfo->getValue());
+ echo "isPublic():\n";
+ var_dump($constInfo->isPublic());
+ echo "isPrivate():\n";
+ var_dump($constInfo->isPrivate());
+ echo "isProtected():\n";
+ var_dump($constInfo->isProtected());
+ echo "getModifiers():\n";
+ var_dump($constInfo->getModifiers());
+ echo "getDeclaringClass():\n";
+ var_dump($constInfo->getDeclaringClass());
+ echo "getDocComment():\n";
+ var_dump($constInfo->getDocComment());
+ echo "\n**********************************\n";
+}
+
+class TestClass {
+ public const /** My Doc comment */ PUB = true;
+ /** Another doc comment */
+ protected const PROT = 4;
+ private const PRIV = "keepOut";
+}
+$instance = new TestClass();
+
+reflectClassConstant("TestClass", "PUB");
+reflectClassConstant("TestClass", "PROT");
+reflectClassConstant("TestClass", "PRIV");
+reflectClassConstant($instance, "PRIV");
+reflectClassConstant($instance, "BAD_CONST");
+
+?>
+--EXPECTF--
+**********************************
+Reflecting on class constant TestClass::PUB
+
+__toString():
+string(38) "Constant [ public boolean PUB ] { 1 }
+"
+export():
+string(38) "Constant [ public boolean PUB ] { 1 }
+"
+export():
+Constant [ public boolean PUB ] { 1 }
+
+NULL
+getName():
+string(3) "PUB"
+getValue():
+bool(true)
+isPublic():
+bool(true)
+isPrivate():
+bool(false)
+isProtected():
+bool(false)
+getModifiers():
+int(256)
+getDeclaringClass():
+object(ReflectionClass)#3 (1) {
+ ["name"]=>
+ string(9) "TestClass"
+}
+getDocComment():
+string(21) "/** My Doc comment */"
+
+**********************************
+**********************************
+Reflecting on class constant TestClass::PROT
+
+__toString():
+string(42) "Constant [ protected integer PROT ] { 4 }
+"
+export():
+string(42) "Constant [ protected integer PROT ] { 4 }
+"
+export():
+Constant [ protected integer PROT ] { 4 }
+
+NULL
+getName():
+string(4) "PROT"
+getValue():
+int(4)
+isPublic():
+bool(false)
+isPrivate():
+bool(false)
+isProtected():
+bool(true)
+getModifiers():
+int(512)
+getDeclaringClass():
+object(ReflectionClass)#3 (1) {
+ ["name"]=>
+ string(9) "TestClass"
+}
+getDocComment():
+string(26) "/** Another doc comment */"
+
+**********************************
+**********************************
+Reflecting on class constant TestClass::PRIV
+
+__toString():
+string(45) "Constant [ private string PRIV ] { keepOut }
+"
+export():
+string(45) "Constant [ private string PRIV ] { keepOut }
+"
+export():
+Constant [ private string PRIV ] { keepOut }
+
+NULL
+getName():
+string(4) "PRIV"
+getValue():
+string(7) "keepOut"
+isPublic():
+bool(false)
+isPrivate():
+bool(true)
+isProtected():
+bool(false)
+getModifiers():
+int(1024)
+getDeclaringClass():
+object(ReflectionClass)#3 (1) {
+ ["name"]=>
+ string(9) "TestClass"
+}
+getDocComment():
+bool(false)
+
+**********************************
+**********************************
+Reflecting on class constant TestClass::PRIV
+
+__toString():
+string(45) "Constant [ private string PRIV ] { keepOut }
+"
+export():
+string(45) "Constant [ private string PRIV ] { keepOut }
+"
+export():
+Constant [ private string PRIV ] { keepOut }
+
+NULL
+getName():
+string(4) "PRIV"
+getValue():
+string(7) "keepOut"
+isPublic():
+bool(false)
+isPrivate():
+bool(true)
+isProtected():
+bool(false)
+getModifiers():
+int(1024)
+getDeclaringClass():
+object(ReflectionClass)#3 (1) {
+ ["name"]=>
+ string(9) "TestClass"
+}
+getDocComment():
+bool(false)
+
+**********************************
+
+Fatal error: Uncaught ReflectionException: Class Constant TestClass::BAD_CONST does not exist in %s:%d
+Stack trace:
+#0 %s(%d): ReflectionClassConstant->__construct(Object(TestClass), 'BAD_CONST')
+#1 %s(%d): reflectClassConstant(Object(TestClass), 'BAD_CONST')
+#2 {main}
+ thrown in %s on line %d
diff --git a/ext/reflection/tests/ReflectionClassConstant_getValue.phpt b/ext/reflection/tests/ReflectionClassConstant_getValue.phpt
new file mode 100644
index 0000000000..e447d15357
--- /dev/null
+++ b/ext/reflection/tests/ReflectionClassConstant_getValue.phpt
@@ -0,0 +1,47 @@
+--TEST--
+Test variations of getting constant values
+--FILE--
+<?php
+
+/* Use separate classes to make sure that in-place constant updates don't interfere */
+class A {
+ const X = self::Y * 2;
+ const Y = 1;
+}
+class B {
+ const X = self::Y * 2;
+ const Y = 1;
+}
+class C {
+ const X = self::Y * 2;
+ const Y = 1;
+}
+
+var_dump((new ReflectionClassConstant('A', 'X'))->getValue());
+echo new ReflectionClassConstant('B', 'X');
+echo new ReflectionClass('C');
+
+?>
+--EXPECTF--
+int(2)
+Constant [ public integer X ] { 2 }
+Class [ <user> class C ] {
+ @@ %s 12-15
+
+ - Constants [2] {
+ Constant [ public integer X ] { 2 }
+ Constant [ public integer Y ] { 1 }
+ }
+
+ - Static properties [0] {
+ }
+
+ - Static methods [0] {
+ }
+
+ - Properties [0] {
+ }
+
+ - Methods [0] {
+ }
+}
diff --git a/ext/reflection/tests/ReflectionClass_export_array_bug72222.phpt b/ext/reflection/tests/ReflectionClass_export_array_bug72222.phpt
index e64dc97109..9ccc285433 100644
--- a/ext/reflection/tests/ReflectionClass_export_array_bug72222.phpt
+++ b/ext/reflection/tests/ReflectionClass_export_array_bug72222.phpt
@@ -13,8 +13,8 @@ Class [ <user> class A ] {
@@ %s 2-5
- Constants [2] {
- Constant [ integer A ] { 8 }
- Constant [ array B ] { Array }
+ Constant [ public integer A ] { 8 }
+ Constant [ public array B ] { Array }
}
- Static properties [0] {
diff --git a/ext/reflection/tests/ReflectionClass_isArray.phpt b/ext/reflection/tests/ReflectionClass_isArray.phpt
new file mode 100644
index 0000000000..3eec0dac54
--- /dev/null
+++ b/ext/reflection/tests/ReflectionClass_isArray.phpt
@@ -0,0 +1,24 @@
+--TEST--
+public bool ReflectionParameter::isArray ( void );
+--CREDITS--
+marcosptf - <marcosptf@yahoo.com.br> - @phpsp - sao paulo - br
+--FILE--
+<?php
+function testReflectionIsArray($a = null, $b = 0, array $c, $d=true, array $e, $f=1.5, $g="", array $h, $i="#F989898") {}
+
+$reflection = new ReflectionFunction('testReflectionIsArray');
+
+foreach ($reflection->getParameters() as $parameter) {
+ var_dump($parameter->isArray());
+}
+?>
+--EXPECT--
+bool(false)
+bool(false)
+bool(true)
+bool(false)
+bool(true)
+bool(false)
+bool(false)
+bool(true)
+bool(false)
diff --git a/ext/reflection/tests/ReflectionClass_newInstanceArgs_001.phpt b/ext/reflection/tests/ReflectionClass_newInstanceArgs_001.phpt
index d3a426de4c..3ad654dd84 100644
--- a/ext/reflection/tests/ReflectionClass_newInstanceArgs_001.phpt
+++ b/ext/reflection/tests/ReflectionClass_newInstanceArgs_001.phpt
@@ -38,13 +38,27 @@ $rcC = new ReflectionClass('C');
$rcD = new ReflectionClass('D');
$rcE = new ReflectionClass('E');
-$a1 = $rcA->newInstanceArgs();
-$a2 = $rcA->newInstanceArgs(array('x'));
-var_dump($a1, $a2);
+try {
+ var_dump($rcA->newInstanceArgs());
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
+try {
+ var_dump($rcA->newInstanceArgs(array('x')));
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
-$b1 = $rcB->newInstanceArgs();
-$b2 = $rcB->newInstanceArgs(array('x', 123));
-var_dump($b1, $b2);
+try {
+ var_dump($rcB->newInstanceArgs());
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
+try {
+ var_dump($rcB->newInstanceArgs(array('x', 123)));
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
try {
$rcC->newInstanceArgs();
@@ -73,25 +87,15 @@ try {
--EXPECTF--
Deprecated: Methods with the same name as their class will not be constructors in a future version of PHP; A has a deprecated constructor in %s on line %d
In constructor of class A
-In constructor of class A
object(A)#%d (0) {
}
+In constructor of class A
object(A)#%d (0) {
}
-
-Warning: Missing argument 1 for B::__construct() in %s on line 9
-
-Warning: Missing argument 2 for B::__construct() in %s on line 9
-
-Notice: Undefined variable: a in %s on line 10
-
-Notice: Undefined variable: b in %s on line 10
-In constructor of class B with args ,
+Exception: Too few arguments to function B::__construct(), 0 passed and exactly 2 expected
In constructor of class B with args x, 123
object(B)#%d (0) {
}
-object(B)#%d (0) {
-}
Access to non-public constructor of class C
Access to non-public constructor of class D
object(E)#%d (0) {
diff --git a/ext/reflection/tests/ReflectionClass_newInstance_001.phpt b/ext/reflection/tests/ReflectionClass_newInstance_001.phpt
index afa278a9a1..e29cc8734f 100644
--- a/ext/reflection/tests/ReflectionClass_newInstance_001.phpt
+++ b/ext/reflection/tests/ReflectionClass_newInstance_001.phpt
@@ -42,9 +42,16 @@ $a1 = $rcA->newInstance();
$a2 = $rcA->newInstance('x');
var_dump($a1, $a2);
-$b1 = $rcB->newInstance();
-$b2 = $rcB->newInstance('x', 123);
-var_dump($b1, $b2);
+try {
+ var_dump($rcB->newInstance());
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
+try {
+ var_dump($rcB->newInstance('x', 123));
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
try {
$rcC->newInstance();
@@ -78,20 +85,10 @@ object(A)#%d (0) {
}
object(A)#%d (0) {
}
-
-Warning: Missing argument 1 for B::__construct() in %s on line 9
-
-Warning: Missing argument 2 for B::__construct() in %s on line 9
-
-Notice: Undefined variable: a in %s on line 10
-
-Notice: Undefined variable: b in %s on line 10
-In constructor of class B with args ,
+Exception: Too few arguments to function B::__construct(), 0 passed and exactly 2 expected
In constructor of class B with args x, 123
object(B)#%d (0) {
}
-object(B)#%d (0) {
-}
Access to non-public constructor of class C
Access to non-public constructor of class D
object(E)#%d (0) {
diff --git a/ext/reflection/tests/ReflectionClass_toString_001.phpt b/ext/reflection/tests/ReflectionClass_toString_001.phpt
index b9a9b0d559..29d58420e3 100644
--- a/ext/reflection/tests/ReflectionClass_toString_001.phpt
+++ b/ext/reflection/tests/ReflectionClass_toString_001.phpt
@@ -12,9 +12,9 @@ echo $rc;
Class [ <internal:Reflection> class ReflectionClass implements Reflector ] {
- Constants [3] {
- Constant [ integer IS_IMPLICIT_ABSTRACT ] { 16 }
- Constant [ integer IS_EXPLICIT_ABSTRACT ] { 32 }
- Constant [ integer IS_FINAL ] { 4 }
+ Constant [ public integer IS_IMPLICIT_ABSTRACT ] { 16 }
+ Constant [ public integer IS_EXPLICIT_ABSTRACT ] { 32 }
+ Constant [ public integer IS_FINAL ] { 4 }
}
- Static properties [0] {
@@ -34,7 +34,7 @@ Class [ <internal:Reflection> class ReflectionClass implements Reflector ] {
Property [ <default> public $name ]
}
- - Methods [50] {
+ - Methods [52] {
Method [ <internal:Reflection> final private method __clone ] {
- Parameters [0] {
@@ -175,6 +175,12 @@ Class [ <internal:Reflection> class ReflectionClass implements Reflector ] {
}
}
+ Method [ <internal:Reflection> public method getReflectionConstants ] {
+
+ - Parameters [0] {
+ }
+ }
+
Method [ <internal:Reflection> public method getConstant ] {
- Parameters [1] {
@@ -182,6 +188,13 @@ Class [ <internal:Reflection> class ReflectionClass implements Reflector ] {
}
}
+ Method [ <internal:Reflection> public method getReflectionConstant ] {
+
+ - Parameters [1] {
+ Parameter #0 [ <required> $name ]
+ }
+ }
+
Method [ <internal:Reflection> public method getInterfaces ] {
- Parameters [0] {
diff --git a/ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt b/ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt
index 4eda22a3f9..9b2122d1b9 100644
--- a/ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt
+++ b/ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt
@@ -9,7 +9,7 @@ var_dump($ext->getClasses());
?>
==DONE==
--EXPECT--
-array(14) {
+array(16) {
["ReflectionException"]=>
object(ReflectionClass)#2 (1) {
["name"]=>
@@ -50,33 +50,43 @@ array(14) {
["name"]=>
string(14) "ReflectionType"
}
- ["ReflectionMethod"]=>
+ ["ReflectionNamedType"]=>
object(ReflectionClass)#10 (1) {
["name"]=>
+ string(19) "ReflectionNamedType"
+ }
+ ["ReflectionMethod"]=>
+ object(ReflectionClass)#11 (1) {
+ ["name"]=>
string(16) "ReflectionMethod"
}
["ReflectionClass"]=>
- object(ReflectionClass)#11 (1) {
+ object(ReflectionClass)#12 (1) {
["name"]=>
string(15) "ReflectionClass"
}
["ReflectionObject"]=>
- object(ReflectionClass)#12 (1) {
+ object(ReflectionClass)#13 (1) {
["name"]=>
string(16) "ReflectionObject"
}
["ReflectionProperty"]=>
- object(ReflectionClass)#13 (1) {
+ object(ReflectionClass)#14 (1) {
["name"]=>
string(18) "ReflectionProperty"
}
+ ["ReflectionClassConstant"]=>
+ object(ReflectionClass)#15 (1) {
+ ["name"]=>
+ string(23) "ReflectionClassConstant"
+ }
["ReflectionExtension"]=>
- object(ReflectionClass)#14 (1) {
+ object(ReflectionClass)#16 (1) {
["name"]=>
string(19) "ReflectionExtension"
}
["ReflectionZendExtension"]=>
- object(ReflectionClass)#15 (1) {
+ object(ReflectionClass)#17 (1) {
["name"]=>
string(23) "ReflectionZendExtension"
}
diff --git a/ext/reflection/tests/ReflectionMethod_invokeArgs_error1.phpt b/ext/reflection/tests/ReflectionMethod_invokeArgs_error1.phpt
index ac97e3ed2a..eec5a3e618 100644
--- a/ext/reflection/tests/ReflectionMethod_invokeArgs_error1.phpt
+++ b/ext/reflection/tests/ReflectionMethod_invokeArgs_error1.phpt
@@ -25,12 +25,9 @@ var_dump($methodWithArgs->invokeArgs($testClassInstance, array()));
--EXPECTF--
Method with args:
-Warning: Missing argument 1 for TestClass::methodWithArgs() in %s on line %d
-
-Warning: Missing argument 2 for TestClass::methodWithArgs() in %s on line %d
-
-Notice: Undefined variable: a in %s on line %d
-
-Notice: Undefined variable: b in %s on line %d
-Called methodWithArgs(, )
-NULL
+Fatal error: Uncaught ArgumentCountError: Too few arguments to function TestClass::methodWithArgs(), 0 passed and exactly 2 expected in %sReflectionMethod_invokeArgs_error1.php:5
+Stack trace:
+#0 [internal function]: TestClass->methodWithArgs()
+#1 %sReflectionMethod_invokeArgs_error1.php(19): ReflectionMethod->invokeArgs(Object(TestClass), Array)
+#2 {main}
+ thrown in %sReflectionMethod_invokeArgs_error1.php on line 5
diff --git a/ext/reflection/tests/ReflectionMethod_invokeArgs_error3.phpt b/ext/reflection/tests/ReflectionMethod_invokeArgs_error3.phpt
index 513cc1845f..1222467a6b 100644
--- a/ext/reflection/tests/ReflectionMethod_invokeArgs_error3.phpt
+++ b/ext/reflection/tests/ReflectionMethod_invokeArgs_error3.phpt
@@ -14,7 +14,11 @@ class TestClass {
public static function staticMethod() {
echo "Called staticMethod()\n";
- var_dump($this);
+ try {
+ var_dump($this);
+ } catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+ }
}
private static function privateMethod() {
@@ -103,9 +107,7 @@ NULL
Warning: ReflectionMethod::invokeArgs() expects parameter 1 to be object, boolean given in %s on line %d
NULL
Called staticMethod()
-
-Notice: Undefined variable: this in %s on line %d
-NULL
+Exception: Using $this when not in object context
NULL
Private method:
@@ -113,5 +115,4 @@ string(86) "Trying to invoke private method TestClass::privateMethod() from scop
Abstract method:
string(53) "Trying to invoke abstract method AbstractClass::foo()"
-
-Warning: ReflectionMethod::invokeArgs() expects exactly 2 parameters, 1 given in %s on line %d
+string(53) "Trying to invoke abstract method AbstractClass::foo()"
diff --git a/ext/reflection/tests/ReflectionMethod_invoke_basic.phpt b/ext/reflection/tests/ReflectionMethod_invoke_basic.phpt
index cbf358c062..7bfe245f82 100644
--- a/ext/reflection/tests/ReflectionMethod_invoke_basic.phpt
+++ b/ext/reflection/tests/ReflectionMethod_invoke_basic.phpt
@@ -22,7 +22,11 @@ class TestClass {
public static function staticMethod() {
echo "Called staticMethod()\n";
- var_dump($this);
+ try {
+ var_dump($this);
+ } catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+ }
}
private static function privateMethod() {
@@ -93,15 +97,11 @@ Static method:
Warning: ReflectionMethod::invoke() expects at least 1 parameter, 0 given in %s on line %d
NULL
-Called staticMethod()
-Notice: Undefined variable: this in %s on line %d
-NULL
+Warning: ReflectionMethod::invoke() expects parameter 1 to be object, boolean given in %s on line %d
NULL
Called staticMethod()
-
-Notice: Undefined variable: this in %s on line %d
-NULL
+Exception: Using $this when not in object context
NULL
Method that throws an exception:
diff --git a/ext/reflection/tests/ReflectionMethod_invoke_error1.phpt b/ext/reflection/tests/ReflectionMethod_invoke_error1.phpt
index 758f1acd13..ef5621e7e5 100644
--- a/ext/reflection/tests/ReflectionMethod_invoke_error1.phpt
+++ b/ext/reflection/tests/ReflectionMethod_invoke_error1.phpt
@@ -59,7 +59,9 @@ try {
?>
--EXPECTF--
invoke() on a non-object:
-string(29) "Non-object passed to Invoke()"
+
+Warning: ReflectionMethod::invoke() expects parameter 1 to be object, boolean given in %s%eReflectionMethod_invoke_error1.php on line %d
+NULL
invoke() on a non-instance:
string(72) "Given object is not an instance of the class this method was declared in"
diff --git a/ext/reflection/tests/ReflectionMethod_invoke_error2.phpt b/ext/reflection/tests/ReflectionMethod_invoke_error2.phpt
index a070c8f583..5dba208beb 100644
--- a/ext/reflection/tests/ReflectionMethod_invoke_error2.phpt
+++ b/ext/reflection/tests/ReflectionMethod_invoke_error2.phpt
@@ -21,12 +21,9 @@ var_dump($methodWithArgs->invoke($testClassInstance));
--EXPECTF--
Method with args:
-Warning: Missing argument 1 for TestClass::methodWithArgs() in %s on line %d
-
-Warning: Missing argument 2 for TestClass::methodWithArgs() in %s on line %d
-
-Notice: Undefined variable: a in %s on line %d
-
-Notice: Undefined variable: b in %s on line %d
-Called methodWithArgs(, )
-NULL
+Fatal error: Uncaught ArgumentCountError: Too few arguments to function TestClass::methodWithArgs(), 0 passed and exactly 2 expected in %sReflectionMethod_invoke_error2.php:5
+Stack trace:
+#0 [internal function]: TestClass->methodWithArgs()
+#1 %sReflectionMethod_invoke_error2.php(15): ReflectionMethod->invoke(Object(TestClass))
+#2 {main}
+ thrown in %sReflectionMethod_invoke_error2.php on line 5
diff --git a/ext/reflection/tests/ReflectionNamedType.phpt b/ext/reflection/tests/ReflectionNamedType.phpt
new file mode 100644
index 0000000000..a40d4066ec
--- /dev/null
+++ b/ext/reflection/tests/ReflectionNamedType.phpt
@@ -0,0 +1,41 @@
+--TEST--
+ReflectionNamedType::getName() and ReflectionNamedType::__toString()
+--FILE--
+<?php
+
+function testInternalTypes(?Traversable $traversable): ?string {
+ return 'test';
+}
+
+function testUserDefinedTypes(?Test $traversable): ?Test {
+ return new Test;
+}
+
+$function = new ReflectionFunction('testInternalTypes');
+$type = $function->getParameters()[0]->getType();
+$return = $function->getReturnType();
+
+var_dump($type->getName());
+var_dump((string) $type);
+var_dump($return->getName());
+var_dump((string) $return);
+
+$function = new ReflectionFunction('testUserDefinedTypes');
+$type = $function->getParameters()[0]->getType();
+$return = $function->getReturnType();
+
+var_dump($type->getName());
+var_dump((string) $type);
+var_dump($return->getName());
+var_dump((string) $return);
+
+?>
+--EXPECT--
+string(11) "Traversable"
+string(11) "Traversable"
+string(6) "string"
+string(6) "string"
+string(4) "Test"
+string(4) "Test"
+string(4) "Test"
+string(4) "Test"
diff --git a/ext/reflection/tests/ReflectionProperty_getValue_error.phpt b/ext/reflection/tests/ReflectionProperty_getValue_error.phpt
index 62009d8bb9..c9bf0b0f25 100644
--- a/ext/reflection/tests/ReflectionProperty_getValue_error.phpt
+++ b/ext/reflection/tests/ReflectionProperty_getValue_error.phpt
@@ -15,7 +15,7 @@ class AnotherClass {
}
$instance = new TestClass();
-$instanceWithNoProperties = new AnotherClass();
+$invalidInstance = new AnotherClass();
$propInfo = new ReflectionProperty('TestClass', 'pub2');
echo "Too few args:\n";
@@ -45,9 +45,9 @@ catch(Exception $exc) {
echo $exc->getMessage();
}
-echo "\n\nInstance without property:\n";
+echo "\n\nInvalid instance:\n";
$propInfo = new ReflectionProperty('TestClass', 'pub2');
-var_dump($propInfo->getValue($instanceWithNoProperties));
+var_dump($propInfo->getValue($invalidInstance));
?>
--EXPECTF--
@@ -77,7 +77,10 @@ string(15) "static property"
Protected property:
Cannot access non-public member TestClass::prot
-Instance without property:
+Invalid instance:
-Notice: Undefined property: AnotherClass::$pub2 in %s on line %d
-NULL
+Fatal error: Uncaught ReflectionException: Given object is not an instance of the class this property was declared in in %s:47
+Stack trace:
+#0 %s(47): ReflectionProperty->getValue(Object(AnotherClass))
+#1 {main}
+ thrown in %s on line 47
diff --git a/ext/reflection/tests/ReflectionType_001.phpt b/ext/reflection/tests/ReflectionType_001.phpt
index f764cf1519..d949e18107 100644
--- a/ext/reflection/tests/ReflectionType_001.phpt
+++ b/ext/reflection/tests/ReflectionType_001.phpt
@@ -28,7 +28,7 @@ foreach ([
if ($ra) {
var_dump($ra->allowsNull());
var_dump($ra->isBuiltin());
- var_dump((string)$ra);
+ var_dump($ra->getName());
}
}
}
@@ -48,7 +48,7 @@ foreach ([
if ($ra) {
var_dump($ra->allowsNull());
var_dump($ra->isBuiltin());
- var_dump((string)$ra);
+ var_dump($ra->getName());
}
}
}
@@ -70,9 +70,11 @@ foreach ([
if ($ra) {
var_dump($ra->allowsNull());
var_dump($ra->isBuiltin());
- var_dump((string)$ra);
+ var_dump($ra->getName());
}
}
+
+?>
--EXPECT--
*** functions
** Function 0 - Parameter 0
diff --git a/ext/reflection/tests/ReflectionType_002.phpt b/ext/reflection/tests/ReflectionType_002.phpt
index 8313862ec5..501dfc8d89 100644
--- a/ext/reflection/tests/ReflectionType_002.phpt
+++ b/ext/reflection/tests/ReflectionType_002.phpt
@@ -9,7 +9,7 @@ $rp = $rm->getParameters()[0];
$rt = $rp->getType();
$rrt = $rm->getReturnType();
unset($rm, $rp);
-var_dump((string) $rt, (string) $rrt);
+var_dump($rt->getName(), $rrt->getName());
--EXPECT--
string(4) "Test"
diff --git a/ext/reflection/tests/ReflectionType_possible_types.phpt b/ext/reflection/tests/ReflectionType_possible_types.phpt
new file mode 100644
index 0000000000..3b486a60fd
--- /dev/null
+++ b/ext/reflection/tests/ReflectionType_possible_types.phpt
@@ -0,0 +1,33 @@
+--TEST--
+ReflectionType possible types
+--FILE--
+<?php
+
+$functions = [
+ function(): void {},
+ function(): int {},
+ function(): float {},
+ function(): string {},
+ function(): bool {},
+ function(): array {},
+ function(): callable {},
+ function(): iterable {},
+ function(): StdClass {}
+];
+
+foreach ($functions as $function) {
+ $reflectionFunc = new ReflectionFunction($function);
+ $returnType = $reflectionFunc->getReturnType();
+ var_dump($returnType->getName());
+}
+?>
+--EXPECTF--
+string(4) "void"
+string(3) "int"
+string(5) "float"
+string(6) "string"
+string(4) "bool"
+string(5) "array"
+string(8) "callable"
+string(8) "iterable"
+string(8) "StdClass"
diff --git a/ext/reflection/tests/bug29986.phpt b/ext/reflection/tests/bug29986.phpt
index 4c4d629f39..f5aa62a00b 100644
--- a/ext/reflection/tests/bug29986.phpt
+++ b/ext/reflection/tests/bug29986.phpt
@@ -20,11 +20,11 @@ Class [ <user> class just_constants ] {
@@ %s %d-%d
- Constants [5] {
- Constant [ boolean BOOLEAN_CONSTANT ] { 1 }
- Constant [ null NULL_CONSTANT ] { }
- Constant [ string STRING_CONSTANT ] { This is a string }
- Constant [ integer INTEGER_CONSTANT ] { 1000 }
- Constant [ float FLOAT_CONSTANT ] { 3.14159265 }
+ Constant [ public boolean BOOLEAN_CONSTANT ] { 1 }
+ Constant [ public null NULL_CONSTANT ] { }
+ Constant [ public string STRING_CONSTANT ] { This is a string }
+ Constant [ public integer INTEGER_CONSTANT ] { 1000 }
+ Constant [ public float FLOAT_CONSTANT ] { 3.14159265 }
}
- Static properties [0] {
diff --git a/ext/reflection/tests/bug38217.phpt b/ext/reflection/tests/bug38217.phpt
index cf007d9547..988f1c8953 100644
--- a/ext/reflection/tests/bug38217.phpt
+++ b/ext/reflection/tests/bug38217.phpt
@@ -18,7 +18,11 @@ class Object1 {
}
$class= new ReflectionClass('Object1');
-var_dump($class->newInstanceArgs());
+try {
+ var_dump($class->newInstanceArgs());
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
var_dump($class->newInstanceArgs(array('test')));
@@ -27,13 +31,7 @@ echo "Done\n";
--EXPECTF--
object(Object)#%d (0) {
}
-
-Warning: Missing argument 1 for Object1::__construct() in %s on line %d
-
-Notice: Undefined variable: var in %s on line %d
-NULL
-object(Object1)#%d (0) {
-}
+Exception: Too few arguments to function Object1::__construct(), 0 passed and exactly 1 expected
string(4) "test"
object(Object1)#%d (0) {
}
diff --git a/ext/reflection/tests/bug42976.phpt b/ext/reflection/tests/bug42976.phpt
index 2e4ade2847..21aff8d4cc 100644
--- a/ext/reflection/tests/bug42976.phpt
+++ b/ext/reflection/tests/bug42976.phpt
@@ -27,12 +27,8 @@ echo "Done\n";
string(9) "x.changed"
Warning: Parameter 1 to C::__construct() expected to be a reference, value given in %sbug42976.php on line 15
-
-Warning: ReflectionClass::newInstance(): Invocation of C's constructor failed in %sbug42976.php on line 15
string(10) "x.original"
Warning: Parameter 1 to C::__construct() expected to be a reference, value given in %sbug42976.php on line 18
-
-Warning: ReflectionClass::newInstanceArgs(): Invocation of C's constructor failed in %sbug42976.php on line 18
string(10) "x.original"
Done
diff --git a/ext/reflection/tests/bug45765.phpt b/ext/reflection/tests/bug45765.phpt
index b0c1be2c4c..7963a03eea 100644
--- a/ext/reflection/tests/bug45765.phpt
+++ b/ext/reflection/tests/bug45765.phpt
@@ -31,7 +31,7 @@ Object of class [ <user> class foo extends foo2 ] {
@@ %s 7-21
- Constants [1] {
- Constant [ string BAR ] { foo's bar }
+ Constant [ public string BAR ] { foo's bar }
}
- Static properties [0] {
diff --git a/ext/reflection/tests/bug69802.phpt b/ext/reflection/tests/bug69802.phpt
index e71ac1aa71..0a58d0b2ac 100644
--- a/ext/reflection/tests/bug69802.phpt
+++ b/ext/reflection/tests/bug69802.phpt
@@ -7,7 +7,7 @@ $r = new ReflectionMethod($f, '__invoke');
var_dump($r->getParameters()[0]->getName());
var_dump($r->getParameters()[0]->getClass());
echo $r->getParameters()[0], "\n";
-echo $r->getReturnType(),"\n";
+echo $r->getReturnType()->getName(), "\n";
echo $r,"\n";
?>
--EXPECT--
diff --git a/ext/reflection/tests/bug72661.phpt b/ext/reflection/tests/bug72661.phpt
new file mode 100644
index 0000000000..40d14922b8
--- /dev/null
+++ b/ext/reflection/tests/bug72661.phpt
@@ -0,0 +1,10 @@
+--TEST--
+Bug #72661 (ReflectionType::__toString crashes with iterable)
+--FILE--
+<?php
+function test(iterable $arg) { }
+
+var_dump((string)(new ReflectionParameter("test", 0))->getType());
+?>
+--EXPECT--
+string(8) "iterable"
diff --git a/ext/reflection/tests/bug72846.phpt b/ext/reflection/tests/bug72846.phpt
deleted file mode 100644
index ab8b6cac79..0000000000
--- a/ext/reflection/tests/bug72846.phpt
+++ /dev/null
@@ -1,48 +0,0 @@
---TEST--
-Bug #72846 (getConstant for a array constant with constant values returns NULL/NFC/UKNOWN)
---FILE--
-<?php
-
-namespace Some {
-
- abstract class A
- {
- const ONE = '1';
- const TWO = '2';
-
- const CONST_NUMBERS = [
- self::ONE,
- self::TWO,
- ];
-
- const NUMBERS = [
- '1',
- '2',
- ];
- }
-
- class B extends A
- {
- }
-
- $ref = new \ReflectionClass('Some\B');
-
- var_dump($ref->getConstant('ONE'));
- var_dump($ref->getConstant('CONST_NUMBERS'));
- var_dump($ref->getConstant('NUMBERS'));
-}
-?>
---EXPECT--
-string(1) "1"
-array(2) {
- [0]=>
- string(1) "1"
- [1]=>
- string(1) "2"
-}
-array(2) {
- [0]=>
- string(1) "1"
- [1]=>
- string(1) "2"
-}
diff --git a/ext/reflection/tests/request38992.phpt b/ext/reflection/tests/request38992.phpt
new file mode 100644
index 0000000000..8c0052fd85
--- /dev/null
+++ b/ext/reflection/tests/request38992.phpt
@@ -0,0 +1,22 @@
+--TEST--
+Request #38992 (invoke() and invokeArgs() static method calls should match)
+--FILE--
+<?php
+class MyClass
+{
+ public static function doSomething()
+ {
+ echo "Did it!\n";
+ }
+}
+
+$r = new ReflectionMethod('MyClass', 'doSomething');
+$r->invoke('WTF?');
+$r->invokeArgs('WTF?', array());
+?>
+===DONE===
+--EXPECTF--
+Warning: ReflectionMethod::invoke() expects parameter 1 to be object, string given in %s%erequest38992.php on line %d
+
+Warning: ReflectionMethod::invokeArgs() expects parameter 1 to be object, string given in %s%erequest38992.php on line %d
+===DONE===
diff --git a/ext/session/mod_files.c b/ext/session/mod_files.c
index 5917acec62..38cc80236e 100644
--- a/ext/session/mod_files.c
+++ b/ext/session/mod_files.c
@@ -230,7 +230,7 @@ static void ps_files_open(ps_files *data, const char *key)
static int ps_files_write(ps_files *data, zend_string *key, zend_string *val)
{
- zend_long n = 0;
+ size_t n = 0;
/* PS(id) may be changed by calling session_regenerate_id().
Re-initialization should be tried here. ps_files_open() checks
@@ -270,7 +270,7 @@ static int ps_files_write(ps_files *data, zend_string *key, zend_string *val)
#endif
if (n != ZSTR_LEN(val)) {
- if (n == -1) {
+ if (n == (size_t)-1) {
php_error_docref(NULL, E_WARNING, "write failed: %s (%d)", strerror(errno), errno);
} else {
php_error_docref(NULL, E_WARNING, "write wrote less bytes than requested");
@@ -652,9 +652,11 @@ PS_GC_FUNC(files)
if (data->dirdepth == 0) {
*nrdels = ps_files_cleanup_dir(data->basedir, maxlifetime);
+ } else {
+ *nrdels = -1; // Cannot process multiple depth save dir
}
- return SUCCESS;
+ return *nrdels;
}
diff --git a/ext/session/mod_mm.c b/ext/session/mod_mm.c
index 74554cff4e..2b54b3fad9 100644
--- a/ext/session/mod_mm.c
+++ b/ext/session/mod_mm.c
@@ -29,6 +29,7 @@
#include <sys/types.h>
#include <fcntl.h>
+#include "php_stdint.h"
#include "php_session.h"
#include "mod_mm.h"
#include "SAPI.h"
@@ -39,14 +40,11 @@
#define PS_MM_FILE "session_mm_"
-/* For php_uint32 */
-#include "ext/standard/basic_functions.h"
-
/* This list holds all data associated with one session. */
typedef struct ps_sd {
struct ps_sd *next;
- php_uint32 hv; /* hash value of key */
+ uint32_t hv; /* hash value of key */
time_t ctime; /* time of last change */
void *data;
size_t datalen; /* amount of valid data */
@@ -57,8 +55,8 @@ typedef struct ps_sd {
typedef struct {
MM *mm;
ps_sd **hash;
- php_uint32 hash_max;
- php_uint32 hash_cnt;
+ uint32_t hash_max;
+ uint32_t hash_cnt;
pid_t owner;
} ps_mm;
@@ -70,9 +68,9 @@ static ps_mm *ps_mm_instance = NULL;
# define ps_mm_debug(a)
#endif
-static inline php_uint32 ps_sd_hash(const char *data, int len)
+static inline uint32_t ps_sd_hash(const char *data, int len)
{
- php_uint32 h;
+ uint32_t h;
const char *e = data + len;
for (h = 2166136261U; data < e; ) {
@@ -85,7 +83,7 @@ static inline php_uint32 ps_sd_hash(const char *data, int len)
static void hash_split(ps_mm *data)
{
- php_uint32 nmax;
+ uint32_t nmax;
ps_sd **nhash;
ps_sd **ohash, **ehash;
ps_sd *ps, *next;
@@ -114,7 +112,7 @@ static void hash_split(ps_mm *data)
static ps_sd *ps_sd_new(ps_mm *data, const char *key)
{
- php_uint32 hv, slot;
+ uint32_t hv, slot;
ps_sd *sd;
int keylen;
@@ -155,7 +153,7 @@ static ps_sd *ps_sd_new(ps_mm *data, const char *key)
static void ps_sd_destroy(ps_mm *data, ps_sd *sd)
{
- php_uint32 slot;
+ uint32_t slot;
slot = ps_sd_hash(sd->key, strlen(sd->key)) & data->hash_max;
@@ -180,7 +178,7 @@ static void ps_sd_destroy(ps_mm *data, ps_sd *sd)
static ps_sd *ps_sd_lookup(ps_mm *data, const char *key, int rw)
{
- php_uint32 hv, slot;
+ uint32_t hv, slot;
ps_sd *ret, *prev;
hv = ps_sd_hash(key, strlen(key));
@@ -470,7 +468,7 @@ PS_GC_FUNC(mm)
mm_unlock(data->mm);
- return SUCCESS;
+ return nrdels;
}
PS_CREATE_SID_FUNC(mm)
diff --git a/ext/session/mod_user.c b/ext/session/mod_user.c
index 056e43567c..2424049b19 100644
--- a/ext/session/mod_user.c
+++ b/ext/session/mod_user.c
@@ -30,12 +30,20 @@ ps_module ps_mod_user = {
static void ps_call_handler(zval *func, int argc, zval *argv, zval *retval)
{
int i;
+ if (PS(in_save_handler)) {
+ PS(in_save_handler) = 0;
+ ZVAL_UNDEF(retval);
+ php_error_docref(NULL, E_WARNING, "Cannot call session save handler in a recursive manner");
+ return;
+ }
+ PS(in_save_handler) = 1;
if (call_user_function(EG(function_table), NULL, func, retval, argc, argv) == FAILURE) {
zval_ptr_dtor(retval);
ZVAL_UNDEF(retval);
} else if (Z_ISUNDEF_P(retval)) {
ZVAL_NULL(retval);
}
+ PS(in_save_handler) = 0;
for (i = 0; i < argc; i++) {
zval_ptr_dtor(&argv[i]);
}
@@ -85,7 +93,16 @@ PS_OPEN_FUNC(user)
ZVAL_STRING(&args[0], (char*)save_path);
ZVAL_STRING(&args[1], (char*)session_name);
- ps_call_handler(&PSF(open), 2, args, &retval);
+ zend_try {
+ ps_call_handler(&PSF(open), 2, args, &retval);
+ } zend_catch {
+ PS(session_status) = php_session_none;
+ if (!Z_ISUNDEF(retval)) {
+ zval_ptr_dtor(&retval);
+ }
+ zend_bailout();
+ } zend_end_try();
+
PS(mod_user_implemented) = 1;
FINISH;
@@ -167,13 +184,22 @@ PS_DESTROY_FUNC(user)
PS_GC_FUNC(user)
{
zval args[1];
- STDVARS;
+ zval retval;
ZVAL_LONG(&args[0], maxlifetime);
ps_call_handler(&PSF(gc), 1, args, &retval);
- FINISH;
+ if (Z_TYPE(retval) == IS_LONG) {
+ convert_to_long(&retval);
+ return Z_LVAL(retval);
+ }
+ /* This is for older API compatibility */
+ if (Z_TYPE(retval) == IS_TRUE) {
+ return 1;
+ }
+ /* Anything else is some kind of error */
+ return -1; // Error
}
PS_CREATE_SID_FUNC(user)
@@ -191,12 +217,12 @@ PS_CREATE_SID_FUNC(user)
}
zval_ptr_dtor(&retval);
} else {
- php_error_docref(NULL, E_ERROR, "No session id returned by function");
+ zend_throw_error(NULL, "No session id returned by function");
return NULL;
}
if (!id) {
- php_error_docref(NULL, E_ERROR, "Session id must be a string");
+ zend_throw_error(NULL, "Session id must be a string");
return NULL;
}
diff --git a/ext/session/mod_user_class.c b/ext/session/mod_user_class.c
index 70a7a35fcc..a7c1676eb3 100644
--- a/ext/session/mod_user_class.c
+++ b/ext/session/mod_user_class.c
@@ -22,6 +22,10 @@
#include "php_session.h"
#define PS_SANITY_CHECK \
+ if (PS(session_status) != php_session_active) { \
+ php_error_docref(NULL, E_WARNING, "Session is not active"); \
+ RETURN_FALSE; \
+ } \
if (PS(default_mod) == NULL) { \
php_error_docref(NULL, E_CORE_ERROR, "Cannot call default session handler"); \
RETURN_FALSE; \
@@ -40,6 +44,7 @@ PHP_METHOD(SessionHandler, open)
{
char *save_path = NULL, *session_name = NULL;
size_t save_path_len, session_name_len;
+ int ret;
PS_SANITY_CHECK;
@@ -48,7 +53,15 @@ PHP_METHOD(SessionHandler, open)
}
PS(mod_user_is_open) = 1;
- RETVAL_BOOL(SUCCESS == PS(default_mod)->s_open(&PS(mod_data), save_path, session_name));
+
+ zend_try {
+ ret = PS(default_mod)->s_open(&PS(mod_data), save_path, session_name);
+ } zend_catch {
+ PS(session_status) = php_session_none;
+ zend_bailout();
+ } zend_end_try();
+
+ RETVAL_BOOL(SUCCESS == ret);
}
/* }}} */
@@ -56,6 +69,8 @@ PHP_METHOD(SessionHandler, open)
Wraps the old close handler */
PHP_METHOD(SessionHandler, close)
{
+ int ret;
+
PS_SANITY_CHECK_IS_OPEN;
// don't return on failure, since not closing the default handler
@@ -63,7 +78,15 @@ PHP_METHOD(SessionHandler, close)
zend_parse_parameters_none();
PS(mod_user_is_open) = 0;
- RETVAL_BOOL(SUCCESS == PS(default_mod)->s_close(&PS(mod_data)));
+
+ zend_try {
+ ret = PS(default_mod)->s_close(&PS(mod_data));
+ } zend_catch {
+ PS(session_status) = php_session_none;
+ zend_bailout();
+ } zend_end_try();
+
+ RETVAL_BOOL(SUCCESS == ret);
}
/* }}} */
@@ -125,7 +148,7 @@ PHP_METHOD(SessionHandler, destroy)
PHP_METHOD(SessionHandler, gc)
{
zend_long maxlifetime;
- int nrdels;
+ zend_long nrdels = -1;
PS_SANITY_CHECK_IS_OPEN;
@@ -133,7 +156,10 @@ PHP_METHOD(SessionHandler, gc)
return;
}
- RETURN_BOOL(SUCCESS == PS(default_mod)->s_gc(&PS(mod_data), maxlifetime, &nrdels));
+ if (PS(default_mod)->s_gc(&PS(mod_data), maxlifetime, &nrdels) == FAILURE) {
+ RETURN_FALSE;
+ }
+ RETURN_LONG(nrdels);
}
/* }}} */
diff --git a/ext/session/php_session.h b/ext/session/php_session.h
index 6d3ca7db4b..1b1393af47 100644
--- a/ext/session/php_session.h
+++ b/ext/session/php_session.h
@@ -39,7 +39,7 @@
#define PS_READ_ARGS void **mod_data, zend_string *key, zend_string **val, zend_long maxlifetime
#define PS_WRITE_ARGS void **mod_data, zend_string *key, zend_string *val, zend_long maxlifetime
#define PS_DESTROY_ARGS void **mod_data, zend_string *key
-#define PS_GC_ARGS void **mod_data, zend_long maxlifetime, int *nrdels
+#define PS_GC_ARGS void **mod_data, zend_long maxlifetime, zend_long *nrdels
#define PS_CREATE_SID_ARGS void **mod_data
#define PS_VALIDATE_SID_ARGS void **mod_data, zend_string *key
#define PS_UPDATE_TIMESTAMP_ARGS void **mod_data, zend_string *key, zend_string *val, zend_long maxlifetime
@@ -51,7 +51,7 @@ typedef struct ps_module_struct {
int (*s_read)(PS_READ_ARGS);
int (*s_write)(PS_WRITE_ARGS);
int (*s_destroy)(PS_DESTROY_ARGS);
- int (*s_gc)(PS_GC_ARGS);
+ zend_long (*s_gc)(PS_GC_ARGS);
zend_string *(*s_create_sid)(PS_CREATE_SID_ARGS);
int (*s_validate_sid)(PS_VALIDATE_SID_ARGS);
int (*s_update_timestamp)(PS_UPDATE_TIMESTAMP_ARGS);
@@ -65,7 +65,7 @@ typedef struct ps_module_struct {
#define PS_READ_FUNC(x) int ps_read_##x(PS_READ_ARGS)
#define PS_WRITE_FUNC(x) int ps_write_##x(PS_WRITE_ARGS)
#define PS_DESTROY_FUNC(x) int ps_delete_##x(PS_DESTROY_ARGS)
-#define PS_GC_FUNC(x) int ps_gc_##x(PS_GC_ARGS)
+#define PS_GC_FUNC(x) zend_long ps_gc_##x(PS_GC_ARGS)
#define PS_CREATE_SID_FUNC(x) zend_string *ps_create_sid_##x(PS_CREATE_SID_ARGS)
#define PS_VALIDATE_SID_FUNC(x) int ps_validate_sid_##x(PS_VALIDATE_SID_ARGS)
#define PS_UPDATE_TIMESTAMP_FUNC(x) int ps_update_timestamp_##x(PS_UPDATE_TIMESTAMP_ARGS)
@@ -151,9 +151,7 @@ typedef struct _php_ps_globals {
char *session_name;
zend_string *id;
char *extern_referer_chk;
- char *entropy_file;
char *cache_limiter;
- zend_long entropy_length;
zend_long cookie_lifetime;
char *cookie_path;
char *cookie_domain;
@@ -191,11 +189,8 @@ typedef struct _php_ps_globals {
zend_bool use_only_cookies;
zend_bool use_trans_sid; /* contains the INI value of whether to use trans-sid */
- zend_long hash_func;
-#if defined(HAVE_HASH_EXT) && !defined(COMPILE_DL_HASH)
- php_hash_ops *hash_ops;
-#endif
- zend_long hash_bits_per_character;
+ zend_long sid_length;
+ zend_long sid_bits_per_character;
int send_cookie;
int define_sid;
@@ -209,6 +204,7 @@ typedef struct _php_ps_globals {
zend_bool use_strict_mode; /* whether or not PHP accepts unknown session ids */
zend_bool lazy_write; /* omit session write when it is possible */
+ zend_bool in_save_handler; /* state that if session is in save handler or not */
zend_string *session_vars; /* serialized original session data */
} php_ps_globals;
@@ -299,11 +295,11 @@ PHPAPI void php_session_reset_id(void);
HashTable *_ht = Z_ARRVAL_P(Z_REFVAL(PS(http_session_vars))); \
ZEND_HASH_FOREACH_KEY(_ht, num_key, key) { \
if (key == NULL) { \
- php_error_docref(NULL, E_NOTICE, \
- "Skipping numeric key %pd", num_key); \
+ php_error_docref(NULL, E_NOTICE, \
+ "Skipping numeric key " ZEND_LONG_FMT, num_key);\
continue; \
} \
- if ((struc = php_get_session_var(key))) { \
+ if ((struc = php_get_session_var(key))) { \
code; \
} \
} ZEND_HASH_FOREACH_END(); \
diff --git a/ext/session/session.c b/ext/session/session.c
index daea59c4ff..fa392dd3bc 100644
--- a/ext/session/session.c
+++ b/ext/session/session.c
@@ -40,13 +40,11 @@
#include "rfc1867.h"
#include "php_variables.h"
#include "php_session.h"
-#include "ext/standard/md5.h"
-#include "ext/standard/sha1.h"
+#include "ext/standard/php_random.h"
#include "ext/standard/php_var.h"
#include "ext/date/php_date.h"
#include "ext/standard/php_lcg.h"
#include "ext/standard/url_scanner_ex.h"
-#include "ext/standard/php_rand.h" /* for RAND_MAX */
#include "ext/standard/info.h"
#include "zend_smart_str.h"
#include "ext/standard/url.h"
@@ -81,6 +79,8 @@ zend_class_entry *php_session_update_timestamp_class_entry;
/* SessionUpdateTimestampInterface */
zend_class_entry *php_session_update_timestamp_iface_entry;
+#define PS_MAX_SID_LENGTH 256
+
/* ***********
* Helpers *
*********** */
@@ -97,6 +97,7 @@ zend_class_entry *php_session_update_timestamp_iface_entry;
#define APPLY_TRANS_SID (PS(use_trans_sid) && !PS(use_only_cookies))
static void php_session_send_cookie(void);
+static void php_session_abort(void);
/* Initialized in MINIT, readonly otherwise. */
static int my_module_number = 0;
@@ -105,8 +106,10 @@ static int my_module_number = 0;
static inline void php_rinit_session_globals(void) /* {{{ */
{
/* Do NOT init PS(mod_user_names) here! */
+ /* TODO: These could be moved to MINIT and removed. These should be initialized by php_rshutdown_session_globals() always when execution is finished. */
PS(id) = NULL;
PS(session_status) = php_session_none;
+ PS(in_save_handler) = 0;
PS(mod_data) = NULL;
PS(mod_user_is_open) = 0;
PS(define_sid) = 1;
@@ -133,10 +136,15 @@ static inline void php_rshutdown_session_globals(void) /* {{{ */
zend_string_release(PS(id));
PS(id) = NULL;
}
+
if (PS(session_vars)) {
zend_string_release(PS(session_vars));
PS(session_vars) = NULL;
}
+
+ /* User save handlers may end up directly here by misuse, bugs in user script, etc. */
+ /* Set session status to prevent error while restoring save handler INI value. */
+ PS(session_status) = php_session_none;
}
/* }}} */
@@ -253,17 +261,12 @@ static int php_session_decode(zend_string *data) /* {{{ */
static char hexconvtab[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,-";
-enum {
- PS_HASH_FUNC_MD5,
- PS_HASH_FUNC_SHA1,
- PS_HASH_FUNC_OTHER
-};
-
/* returns a pointer to the byte after the last valid character in out */
-static char *bin_to_readable(char *in, size_t inlen, char *out, char nbits) /* {{{ */
+static size_t bin_to_readable(unsigned char *in, size_t inlen, char *out, char nbits) /* {{{ */
{
unsigned char *p, *q;
unsigned short w;
+ size_t len = inlen;
int mask;
int have;
@@ -274,7 +277,7 @@ static char *bin_to_readable(char *in, size_t inlen, char *out, char nbits) /* {
have = 0;
mask = (1 << nbits) - 1;
- while (1) {
+ while (inlen--) {
if (have < nbits) {
if (p < q) {
w |= *p++ << have;
@@ -294,151 +297,24 @@ static char *bin_to_readable(char *in, size_t inlen, char *out, char nbits) /* {
}
*out = '\0';
- return out;
+ return len;
}
/* }}} */
+#define PS_EXTRA_RAND_BYTES 60
+
PHPAPI zend_string *php_session_create_id(PS_CREATE_SID_ARGS) /* {{{ */
{
- PHP_MD5_CTX md5_context;
- PHP_SHA1_CTX sha1_context;
-#if defined(HAVE_HASH_EXT) && !defined(COMPILE_DL_HASH)
- void *hash_context = NULL;
-#endif
- unsigned char *digest;
- size_t digest_len;
- char *buf;
- struct timeval tv;
- zval *array;
- zval *token;
+ unsigned char rbuf[PS_MAX_SID_LENGTH + PS_EXTRA_RAND_BYTES];
zend_string *outid;
- char *remote_addr = NULL;
-
- gettimeofday(&tv, NULL);
- if ((array = zend_hash_str_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER") - 1)) &&
- Z_TYPE_P(array) == IS_ARRAY &&
- (token = zend_hash_str_find(Z_ARRVAL_P(array), "REMOTE_ADDR", sizeof("REMOTE_ADDR") - 1)) &&
- Z_TYPE_P(token) == IS_STRING
- ) {
- remote_addr = Z_STRVAL_P(token);
+ /* Read additional PS_EXTRA_RAND_BYTES just in case CSPRNG is not safe enough */
+ if (php_random_bytes_throw(rbuf, PS(sid_length) + PS_EXTRA_RAND_BYTES) == FAILURE) {
+ return NULL;
}
- /* maximum 15+19+19+10 bytes */
- spprintf(&buf, 0, "%.15s%ld" ZEND_LONG_FMT "%0.8F", remote_addr ? remote_addr : "", tv.tv_sec, (zend_long)tv.tv_usec, php_combined_lcg() * 10);
-
- switch (PS(hash_func)) {
- case PS_HASH_FUNC_MD5:
- PHP_MD5Init(&md5_context);
- PHP_MD5Update(&md5_context, (unsigned char *) buf, strlen(buf));
- digest_len = 16;
- break;
- case PS_HASH_FUNC_SHA1:
- PHP_SHA1Init(&sha1_context);
- PHP_SHA1Update(&sha1_context, (unsigned char *) buf, strlen(buf));
- digest_len = 20;
- break;
-#if defined(HAVE_HASH_EXT) && !defined(COMPILE_DL_HASH)
- case PS_HASH_FUNC_OTHER:
- if (!PS(hash_ops)) {
- efree(buf);
- php_error_docref(NULL, E_ERROR, "Invalid session hash function");
- return NULL;
- }
-
- hash_context = emalloc(PS(hash_ops)->context_size);
- PS(hash_ops)->hash_init(hash_context);
- PS(hash_ops)->hash_update(hash_context, (unsigned char *) buf, strlen(buf));
- digest_len = PS(hash_ops)->digest_size;
- break;
-#endif /* HAVE_HASH_EXT */
- default:
- efree(buf);
- php_error_docref(NULL, E_ERROR, "Invalid session hash function");
- return NULL;
- }
- efree(buf);
-
- if (PS(entropy_length) > 0) {
-#ifdef PHP_WIN32
- unsigned char rbuf[2048];
- size_t toread = PS(entropy_length);
-
- if (php_win32_get_random_bytes(rbuf, MIN(toread, sizeof(rbuf))) == SUCCESS){
-
- switch (PS(hash_func)) {
- case PS_HASH_FUNC_MD5:
- PHP_MD5Update(&md5_context, rbuf, toread);
- break;
- case PS_HASH_FUNC_SHA1:
- PHP_SHA1Update(&sha1_context, rbuf, toread);
- break;
-# if defined(HAVE_HASH_EXT) && !defined(COMPILE_DL_HASH)
- case PS_HASH_FUNC_OTHER:
- PS(hash_ops)->hash_update(hash_context, rbuf, toread);
- break;
-# endif /* HAVE_HASH_EXT */
- }
- }
-#else
- int fd;
-
- fd = VCWD_OPEN(PS(entropy_file), O_RDONLY);
- if (fd >= 0) {
- unsigned char rbuf[2048];
- int n;
- int to_read = PS(entropy_length);
-
- while (to_read > 0) {
- n = read(fd, rbuf, MIN(to_read, sizeof(rbuf)));
- if (n <= 0) break;
-
- switch (PS(hash_func)) {
- case PS_HASH_FUNC_MD5:
- PHP_MD5Update(&md5_context, rbuf, n);
- break;
- case PS_HASH_FUNC_SHA1:
- PHP_SHA1Update(&sha1_context, rbuf, n);
- break;
-#if defined(HAVE_HASH_EXT) && !defined(COMPILE_DL_HASH)
- case PS_HASH_FUNC_OTHER:
- PS(hash_ops)->hash_update(hash_context, rbuf, n);
- break;
-#endif /* HAVE_HASH_EXT */
- }
- to_read -= n;
- }
- close(fd);
- }
-#endif
- }
-
- digest = emalloc(digest_len + 1);
- switch (PS(hash_func)) {
- case PS_HASH_FUNC_MD5:
- PHP_MD5Final(digest, &md5_context);
- break;
- case PS_HASH_FUNC_SHA1:
- PHP_SHA1Final(digest, &sha1_context);
- break;
-#if defined(HAVE_HASH_EXT) && !defined(COMPILE_DL_HASH)
- case PS_HASH_FUNC_OTHER:
- PS(hash_ops)->hash_final(digest, hash_context);
- efree(hash_context);
- break;
-#endif /* HAVE_HASH_EXT */
- }
-
- if (PS(hash_bits_per_character) < 4
- || PS(hash_bits_per_character) > 6) {
- PS(hash_bits_per_character) = 4;
-
- php_error_docref(NULL, E_WARNING, "The ini setting hash_bits_per_character is out of range (should be 4, 5, or 6) - using 4 for now");
- }
-
- outid = zend_string_alloc((digest_len + 2) * ((8.0f / PS(hash_bits_per_character) + 0.5)), 0);
- ZSTR_LEN(outid) = (size_t)(bin_to_readable((char *)digest, digest_len, ZSTR_VAL(outid), (char)PS(hash_bits_per_character)) - (char *)&ZSTR_VAL(outid));
- efree(digest);
+ outid = zend_string_alloc(PS(sid_length), 0);
+ ZSTR_LEN(outid) = bin_to_readable(rbuf, PS(sid_length), ZSTR_VAL(outid), (char)PS(sid_bits_per_character));
return outid;
}
@@ -470,7 +346,7 @@ PHPAPI int php_session_valid_key(const char *key) /* {{{ */
/* Somewhat arbitrary length limit here, but should be way more than
anyone needs and avoids file-level warnings later on if we exceed MAX_PATH */
- if (len == 0 || len > 128) {
+ if (len == 0 || len > PS_MAX_SID_LENGTH) {
ret = FAILURE;
}
@@ -479,31 +355,33 @@ PHPAPI int php_session_valid_key(const char *key) /* {{{ */
/* }}} */
-static void php_session_gc(void) /* {{{ */
+static zend_long php_session_gc(zend_bool immediate) /* {{{ */
{
int nrand;
+ zend_long num = -1;
/* GC must be done before reading session data. */
- if ((PS(mod_data) || PS(mod_user_implemented)) && PS(gc_probability) > 0) {
- int nrdels = -1;
-
- nrand = (int) ((float) PS(gc_divisor) * php_combined_lcg());
- if (nrand < PS(gc_probability)) {
- PS(mod)->s_gc(&PS(mod_data), PS(gc_maxlifetime), &nrdels);
-#ifdef SESSION_DEBUG
- if (nrdels != -1) {
- php_error_docref(NULL, E_NOTICE, "purged %d expired session objects", nrdels);
- }
-#endif
+ if ((PS(mod_data) || PS(mod_user_implemented))) {
+ if (immediate) {
+ PS(mod)->s_gc(&PS(mod_data), PS(gc_maxlifetime), &num);
+ return num;
+ }
+ nrand = (zend_long) ((float) PS(gc_divisor) * php_combined_lcg());
+ if (PS(gc_probability) > 0 && nrand < PS(gc_probability)) {
+ PS(mod)->s_gc(&PS(mod_data), PS(gc_maxlifetime), &num);
}
}
+ return num;
} /* }}} */
static void php_session_initialize(void) /* {{{ */
{
zend_string *val = NULL;
+ PS(session_status) = php_session_active;
+
if (!PS(mod)) {
+ PS(session_status) = php_session_disabled;
php_error_docref(NULL, E_ERROR, "No storage module chosen - failed to initialize session");
return;
}
@@ -512,15 +390,20 @@ static void php_session_initialize(void) /* {{{ */
if (PS(mod)->s_open(&PS(mod_data), PS(save_path), PS(session_name)) == FAILURE
/* || PS(mod_data) == NULL */ /* FIXME: open must set valid PS(mod_data) with success */
) {
+ php_session_abort();
php_error_docref(NULL, E_ERROR, "Failed to initialize storage module: %s (path: %s)", PS(mod)->s_name, PS(save_path));
return;
}
/* If there is no ID, use session module to create one */
- if (!PS(id)) {
+ if (!PS(id) || !ZSTR_VAL(PS(id))[0]) {
+ if (PS(id)) {
+ zend_string_release(PS(id));
+ }
PS(id) = PS(mod)->s_create_sid(&PS(mod_data));
if (!PS(id)) {
- php_error_docref(NULL, E_ERROR, "Failed to create session ID: %s (path: %s)", PS(mod)->s_name, PS(save_path));
+ php_session_abort();
+ zend_throw_error(NULL, "Failed to create session ID: %s (path: %s)", PS(mod)->s_name, PS(save_path));
return;
}
if (PS(use_cookies)) {
@@ -541,20 +424,18 @@ static void php_session_initialize(void) /* {{{ */
}
php_session_reset_id();
- PS(session_status) = php_session_active;
/* Read data */
php_session_track_init();
if (PS(mod)->s_read(&PS(mod_data), PS(id), &val, PS(gc_maxlifetime)) == FAILURE) {
- /* Some broken save handler implementation returns FAILURE for non-existent session ID */
- /* It's better to raise error for this, but disabled error for better compatibility */
- /*
- php_error_docref(NULL, E_NOTICE, "Failed to read session data: %s (path: %s)", PS(mod)->s_name, PS(save_path));
- */
+ php_session_abort();
+ /* FYI: Some broken save handlers return FAILURE for non-existent session ID, this is incorrect */
+ php_error_docref(NULL, E_WARNING, "Failed to read session data: %s (path: %s)", PS(mod)->s_name, PS(save_path));
+ return;
}
/* GC must be done after read */
- php_session_gc();
+ php_session_gc(0);
if (PS(session_vars)) {
zend_string_release(PS(session_vars));
@@ -598,11 +479,16 @@ static void php_session_save_current_state(int write) /* {{{ */
}
if ((ret == FAILURE) && !EG(exception)) {
- php_error_docref(NULL, E_WARNING, "Failed to write session data (%s). Please "
- "verify that the current setting of session.save_path "
- "is correct (%s)",
- PS(mod)->s_name,
- PS(save_path));
+ if (!PS(mod_user_implemented)) {
+ php_error_docref(NULL, E_WARNING, "Failed to write session data (%s). Please "
+ "verify that the current setting of session.save_path "
+ "is correct (%s)",
+ PS(mod)->s_name,
+ PS(save_path));
+ } else {
+ php_error_docref(NULL, E_WARNING, "Failed to write session data using user "
+ "defined save handler. (session.save_path: %s)", PS(save_path));
+ }
}
}
}
@@ -760,55 +646,43 @@ static PHP_INI_MH(OnUpdateName) /* {{{ */
}
/* }}} */
-static PHP_INI_MH(OnUpdateHashFunc) /* {{{ */
+static PHP_INI_MH(OnUpdateSidLength) /* {{{ */
{
zend_long val;
char *endptr = NULL;
-#if defined(HAVE_HASH_EXT) && !defined(COMPILE_DL_HASH)
- PS(hash_ops) = NULL;
-#endif
-
val = ZEND_STRTOL(ZSTR_VAL(new_value), &endptr, 10);
- if (endptr && (*endptr == '\0')) {
+ if (endptr && (*endptr == '\0')
+ && val >= 22 && val <= PS_MAX_SID_LENGTH) {
/* Numeric value */
- PS(hash_func) = val ? 1 : 0;
-
- return SUCCESS;
- }
-
- if (ZSTR_LEN(new_value) == (sizeof("md5") - 1) &&
- strncasecmp(ZSTR_VAL(new_value), "md5", sizeof("md5") - 1) == 0) {
- PS(hash_func) = PS_HASH_FUNC_MD5;
-
+ PS(sid_length) = val;
return SUCCESS;
}
- if (ZSTR_LEN(new_value) == (sizeof("sha1") - 1) &&
- strncasecmp(ZSTR_VAL(new_value), "sha1", sizeof("sha1") - 1) == 0) {
- PS(hash_func) = PS_HASH_FUNC_SHA1;
-
- return SUCCESS;
- }
+ php_error_docref(NULL, E_WARNING, "session.configuration 'session.sid_length' must be between 22 and 256.");
+ return FAILURE;
+}
+/* }}} */
-#if defined(HAVE_HASH_EXT) && !defined(COMPILE_DL_HASH) /* {{{ */
+static PHP_INI_MH(OnUpdateSidBits) /* {{{ */
{
- php_hash_ops *ops = (php_hash_ops*)php_hash_fetch_ops(ZSTR_VAL(new_value), ZSTR_LEN(new_value));
-
- if (ops) {
- PS(hash_func) = PS_HASH_FUNC_OTHER;
- PS(hash_ops) = ops;
+ zend_long val;
+ char *endptr = NULL;
+ val = ZEND_STRTOL(ZSTR_VAL(new_value), &endptr, 10);
+ if (endptr && (*endptr == '\0')
+ && val >= 4 && val <=6) {
+ /* Numeric value */
+ PS(sid_bits_per_character) = val;
return SUCCESS;
}
-}
-#endif /* HAVE_HASH_EXT }}} */
- php_error_docref(NULL, E_WARNING, "session.configuration 'session.hash_function' must be existing hash function. %s does not exist.", ZSTR_VAL(new_value));
+ php_error_docref(NULL, E_WARNING, "session.configuration 'session.sid_bits' must be between 4 and 6.");
return FAILURE;
}
/* }}} */
+
static PHP_INI_MH(OnUpdateRfc1867Freq) /* {{{ */
{
int tmp;
@@ -849,21 +723,11 @@ PHP_INI_BEGIN()
STD_PHP_INI_BOOLEAN("session.use_only_cookies", "1", PHP_INI_ALL, OnUpdateBool, use_only_cookies, php_ps_globals, ps_globals)
STD_PHP_INI_BOOLEAN("session.use_strict_mode", "0", PHP_INI_ALL, OnUpdateBool, use_strict_mode, php_ps_globals, ps_globals)
STD_PHP_INI_ENTRY("session.referer_check", "", PHP_INI_ALL, OnUpdateString, extern_referer_chk, php_ps_globals, ps_globals)
-#if HAVE_DEV_URANDOM
- STD_PHP_INI_ENTRY("session.entropy_file", "/dev/urandom", PHP_INI_ALL, OnUpdateString, entropy_file, php_ps_globals, ps_globals)
- STD_PHP_INI_ENTRY("session.entropy_length", "32", PHP_INI_ALL, OnUpdateLong, entropy_length, php_ps_globals, ps_globals)
-#elif HAVE_DEV_ARANDOM
- STD_PHP_INI_ENTRY("session.entropy_file", "/dev/arandom", PHP_INI_ALL, OnUpdateString, entropy_file, php_ps_globals, ps_globals)
- STD_PHP_INI_ENTRY("session.entropy_length", "32", PHP_INI_ALL, OnUpdateLong, entropy_length, php_ps_globals, ps_globals)
-#else
- STD_PHP_INI_ENTRY("session.entropy_file", "", PHP_INI_ALL, OnUpdateString, entropy_file, php_ps_globals, ps_globals)
- STD_PHP_INI_ENTRY("session.entropy_length", "0", PHP_INI_ALL, OnUpdateLong, entropy_length, php_ps_globals, ps_globals)
-#endif
STD_PHP_INI_ENTRY("session.cache_limiter", "nocache", PHP_INI_ALL, OnUpdateString, cache_limiter, php_ps_globals, ps_globals)
STD_PHP_INI_ENTRY("session.cache_expire", "180", PHP_INI_ALL, OnUpdateLong, cache_expire, php_ps_globals, ps_globals)
PHP_INI_ENTRY("session.use_trans_sid", "0", PHP_INI_ALL, OnUpdateTransSid)
- PHP_INI_ENTRY("session.hash_function", "0", PHP_INI_ALL, OnUpdateHashFunc)
- STD_PHP_INI_ENTRY("session.hash_bits_per_character", "4", PHP_INI_ALL, OnUpdateLong, hash_bits_per_character, php_ps_globals, ps_globals)
+ PHP_INI_ENTRY("session.sid_length", "32", PHP_INI_ALL, OnUpdateSidLength)
+ PHP_INI_ENTRY("session.sid_bits_per_character", "4", PHP_INI_ALL, OnUpdateSidBits)
STD_PHP_INI_BOOLEAN("session.lazy_write", "1", PHP_INI_ALL, OnUpdateBool, lazy_write, php_ps_globals, ps_globals)
/* Upload progress */
@@ -971,13 +835,10 @@ PS_SERIALIZER_DECODE_FUNC(php_binary) /* {{{ */
int namelen;
zend_string *name;
php_unserialize_data_t var_hash;
- int skip = 0;
PHP_VAR_UNSERIALIZE_INIT(var_hash);
for (p = val; p < endptr; ) {
- zval *tmp;
- skip = 0;
namelen = ((unsigned char)(*p)) & (~PS_BIN_UNDEF);
if (namelen < 0 || namelen > PS_BIN_MAX || (p + namelen) >= endptr) {
@@ -991,21 +852,12 @@ PS_SERIALIZER_DECODE_FUNC(php_binary) /* {{{ */
p += namelen + 1;
- if ((tmp = zend_hash_find(&EG(symbol_table), name))) {
- if ((Z_TYPE_P(tmp) == IS_ARRAY &&
- Z_ARRVAL_P(tmp) == &EG(symbol_table)) || tmp == &PS(http_session_vars)) {
- skip = 1;
- }
- }
-
if (has_value) {
zval *current, rv;
current = var_tmp_var(&var_hash);
if (php_var_unserialize(current, (const unsigned char **) &p, (const unsigned char *) endptr, &var_hash)) {
ZVAL_PTR(&rv, current);
- if (!skip) {
- php_set_session_var(name, &rv, &var_hash);
- }
+ php_set_session_var(name, &rv, &var_hash);
} else {
zend_string_release(name);
php_session_normalize_vars();
@@ -1067,16 +919,13 @@ PS_SERIALIZER_DECODE_FUNC(php) /* {{{ */
zend_string *name;
int has_value, retval = SUCCESS;
php_unserialize_data_t var_hash;
- int skip = 0;
PHP_VAR_UNSERIALIZE_INIT(var_hash);
p = val;
while (p < endptr) {
- zval *tmp;
q = p;
- skip = 0;
while (*q != PS_DELIMITER) {
if (++q >= endptr) goto break_outer_loop;
}
@@ -1091,30 +940,19 @@ PS_SERIALIZER_DECODE_FUNC(php) /* {{{ */
name = zend_string_init(p, namelen, 0);
q++;
- if ((tmp = zend_hash_find(&EG(symbol_table), name))) {
- if ((Z_TYPE_P(tmp) == IS_ARRAY &&
- Z_ARRVAL_P(tmp) == &EG(symbol_table)) || tmp == &PS(http_session_vars)) {
- skip = 1;
- }
- }
-
if (has_value) {
zval *current, rv;
current = var_tmp_var(&var_hash);
if (php_var_unserialize(current, (const unsigned char **)&q, (const unsigned char *)endptr, &var_hash)) {
ZVAL_PTR(&rv, current);
- if (!skip) {
- php_set_session_var(name, &rv, &var_hash);
- }
+ php_set_session_var(name, &rv, &var_hash);
} else {
zend_string_release(name);
retval = FAILURE;
goto break_outer_loop;
}
} else {
- if(!skip) {
- PS_ADD_VARL(name);
- }
+ PS_ADD_VARL(name);
}
zend_string_release(name);
@@ -1140,7 +978,7 @@ static ps_serializer ps_serializers[MAX_SERIALIZERS + 1] = {
PHPAPI int php_session_register_serializer(const char *name, zend_string *(*encode)(PS_SERIALIZER_ENCODE_ARGS), int (*decode)(PS_SERIALIZER_DECODE_ARGS)) /* {{{ */
{
- int ret = -1;
+ int ret = FAILURE;
int i;
for (i = 0; i < MAX_SERIALIZERS; i++) {
@@ -1149,7 +987,7 @@ PHPAPI int php_session_register_serializer(const char *name, zend_string *(*enco
ps_serializers[i].encode = encode;
ps_serializers[i].decode = decode;
ps_serializers[i + 1].name = NULL;
- ret = 0;
+ ret = SUCCESS;
break;
}
}
@@ -1171,13 +1009,13 @@ static ps_module *ps_modules[MAX_MODULES + 1] = {
PHPAPI int php_session_register_module(ps_module *ptr) /* {{{ */
{
- int ret = -1;
+ int ret = FAILURE;
int i;
for (i = 0; i < MAX_MODULES; i++) {
if (!ps_modules[i]) {
ps_modules[i] = ptr;
- ret = 0;
+ ret = SUCCESS;
break;
}
}
@@ -1326,11 +1164,13 @@ static int php_session_cache_limiter(void) /* {{{ */
php_session_cache_limiter_t *lim;
if (PS(cache_limiter)[0] == '\0') return 0;
+ if (PS(session_status) != php_session_active) return -1;
if (SG(headers_sent)) {
const char *output_start_filename = php_output_get_start_filename();
int output_start_lineno = php_output_get_start_lineno();
+ php_session_abort();
if (output_start_filename) {
php_error_docref(NULL, E_WARNING, "Cannot send session cache limiter - headers already sent (output started at %s:%d)", output_start_filename, output_start_lineno);
} else {
@@ -1521,6 +1361,7 @@ PHPAPI void php_session_reset_id(void) /* {{{ */
{
int module_number = PS(module_number);
zval *sid, *data, *ppid;
+ zend_bool apply_trans_sid;
if (!PS(id)) {
php_error_docref(NULL, E_WARNING, "Cannot set session ID - session ID is not initialized");
@@ -1561,20 +1402,26 @@ PHPAPI void php_session_reset_id(void) /* {{{ */
}
/* Apply trans sid if sid cookie is not set */
- if (APPLY_TRANS_SID
- && (data = zend_hash_str_find(&EG(symbol_table), "_COOKIE", sizeof("_COOKIE") - 1))) {
- ZVAL_DEREF(data);
- if (Z_TYPE_P(data) == IS_ARRAY && (ppid = zend_hash_str_find(Z_ARRVAL_P(data), PS(session_name), strlen(PS(session_name))))) {
- ZVAL_DEREF(ppid);
- } else {
- /* FIXME: Resetting vars are required when
- session is stop/start/regenerated. However,
- php_url_scanner_reset_vars() resets all vars
- including other URL rewrites set by elsewhere. */
- /* php_url_scanner_reset_vars(); */
- php_url_scanner_add_var(PS(session_name), strlen(PS(session_name)), ZSTR_VAL(PS(id)), ZSTR_LEN(PS(id)), 1);
+ apply_trans_sid = 0;
+ if (APPLY_TRANS_SID) {
+ apply_trans_sid = 1;
+ if (PS(use_cookies) &&
+ (data = zend_hash_str_find(&EG(symbol_table), "_COOKIE", sizeof("_COOKIE") - 1))) {
+ ZVAL_DEREF(data);
+ if (Z_TYPE_P(data) == IS_ARRAY &&
+ (ppid = zend_hash_str_find(Z_ARRVAL_P(data), PS(session_name), strlen(PS(session_name))))) {
+ ZVAL_DEREF(ppid);
+ apply_trans_sid = 0;
+ }
}
}
+ if (apply_trans_sid) {
+ zend_string *sname;
+ sname = zend_string_init(PS(session_name), strlen(PS(session_name)), 0);
+ php_url_scanner_reset_session_var(sname, 1); /* This may fail when session name has changed */
+ zend_string_release(sname);
+ php_url_scanner_add_session_var(PS(session_name), strlen(PS(session_name)), ZSTR_VAL(PS(id)), ZSTR_LEN(PS(id)), 1);
+ }
}
/* }}} */
@@ -1696,8 +1543,8 @@ PHPAPI void php_session_start(void) /* {{{ */
static void php_session_flush(int write) /* {{{ */
{
if (PS(session_status) == php_session_active) {
- PS(session_status) = php_session_none;
php_session_save_current_state(write);
+ PS(session_status) = php_session_none;
}
}
/* }}} */
@@ -1705,10 +1552,10 @@ static void php_session_flush(int write) /* {{{ */
static void php_session_abort(void) /* {{{ */
{
if (PS(session_status) == php_session_active) {
- PS(session_status) = php_session_none;
if (PS(mod_data) || PS(mod_user_implemented)) {
PS(mod)->s_close(&PS(mod_data));
}
+ PS(session_status) = php_session_none;
}
}
/* }}} */
@@ -2083,13 +1930,13 @@ static PHP_FUNCTION(session_regenerate_id)
return;
}
- if (SG(headers_sent) && PS(use_cookies)) {
- php_error_docref(NULL, E_WARNING, "Cannot regenerate session id - headers already sent");
+ if (PS(session_status) != php_session_active) {
+ php_error_docref(NULL, E_WARNING, "Cannot regenerate session id - session is not active");
RETURN_FALSE;
}
- if (PS(session_status) != php_session_active) {
- php_error_docref(NULL, E_WARNING, "Cannot regenerate session id - session is not active");
+ if (SG(headers_sent) && PS(use_cookies)) {
+ php_error_docref(NULL, E_WARNING, "Cannot regenerate session id - headers already sent");
RETURN_FALSE;
}
@@ -2125,15 +1972,18 @@ static PHP_FUNCTION(session_regenerate_id)
PS(session_vars) = NULL;
}
zend_string_release(PS(id));
- PS(id) = PS(mod)->s_create_sid(&PS(mod_data));
- if (!PS(id)) {
+ PS(id) = NULL;
+
+ if (PS(mod)->s_open(&PS(mod_data), PS(save_path), PS(session_name)) == FAILURE) {
PS(session_status) = php_session_none;
- php_error_docref(NULL, E_RECOVERABLE_ERROR, "Failed to create new session ID: %s (path: %s)", PS(mod)->s_name, PS(save_path));
+ zend_throw_error(NULL, "Failed to open session: %s (path: %s)", PS(mod)->s_name, PS(save_path));
RETURN_FALSE;
}
- if (PS(mod)->s_open(&PS(mod_data), PS(save_path), PS(session_name)) == FAILURE) {
+
+ PS(id) = PS(mod)->s_create_sid(&PS(mod_data));
+ if (!PS(id)) {
PS(session_status) = php_session_none;
- php_error_docref(NULL, E_RECOVERABLE_ERROR, "Failed to create(open) session ID: %s (path: %s)", PS(mod)->s_name, PS(save_path));
+ zend_throw_error(NULL, "Failed to create new session ID: %s (path: %s)", PS(mod)->s_name, PS(save_path));
RETURN_FALSE;
}
if (PS(use_strict_mode) && PS(mod)->s_validate_sid &&
@@ -2141,15 +1991,17 @@ static PHP_FUNCTION(session_regenerate_id)
zend_string_release(PS(id));
PS(id) = PS(mod)->s_create_sid(&PS(mod_data));
if (!PS(id)) {
+ PS(mod)->s_close(&PS(mod_data));
PS(session_status) = php_session_none;
- php_error_docref(NULL, E_RECOVERABLE_ERROR, "Failed to create session ID by collision: %s (path: %s)", PS(mod)->s_name, PS(save_path));
+ zend_throw_error(NULL, "Failed to create session ID by collision: %s (path: %s)", PS(mod)->s_name, PS(save_path));
RETURN_FALSE;
}
}
/* Read is required to make new session data at this point. */
if (PS(mod)->s_read(&PS(mod_data), PS(id), &data, PS(gc_maxlifetime)) == FAILURE) {
+ PS(mod)->s_close(&PS(mod_data));
PS(session_status) = php_session_none;
- php_error_docref(NULL, E_RECOVERABLE_ERROR, "Failed to create(read) session ID: %s (path: %s)", PS(mod)->s_name, PS(save_path));
+ zend_throw_error(NULL, "Failed to create(read) session ID: %s (path: %s)", PS(mod)->s_name, PS(save_path));
RETURN_FALSE;
}
if (data) {
@@ -2167,7 +2019,6 @@ static PHP_FUNCTION(session_regenerate_id)
/* {{{ proto void session_create_id([string prefix])
Generate new session ID. Intended for user save handlers. */
-#if 0
/* This is not used yet */
static PHP_FUNCTION(session_create_id)
{
@@ -2188,8 +2039,21 @@ static PHP_FUNCTION(session_create_id)
}
}
- if (PS(session_status) == php_session_active) {
- new_id = PS(mod)->s_create_sid(&PS(mod_data));
+ if (!PS(in_save_handler) && PS(session_status) == php_session_active) {
+ int limit = 3;
+ while (limit--) {
+ new_id = PS(mod)->s_create_sid(&PS(mod_data));
+ if (!PS(mod)->s_validate_sid) {
+ break;
+ } else {
+ /* Detect collision and retry */
+ if (PS(mod)->s_validate_sid(&PS(mod_data), new_id) == FAILURE) {
+ zend_string_release(new_id);
+ continue;
+ }
+ break;
+ }
+ }
} else {
new_id = php_session_create_id(NULL);
}
@@ -2204,9 +2068,7 @@ static PHP_FUNCTION(session_create_id)
}
smart_str_0(&id);
RETVAL_NEW_STR(id.s);
- smart_str_free(&id);
}
-#endif
/* }}} */
/* {{{ proto string session_cache_limiter([string new_cache_limiter])
@@ -2319,11 +2181,6 @@ static PHP_FUNCTION(session_start)
RETURN_FALSE;
}
- if (PS(id) && !(ZSTR_LEN(PS(id)))) {
- php_error_docref(NULL, E_WARNING, "Cannot start session with empty session ID");
- RETURN_FALSE;
- }
-
/* set options */
if (options) {
ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(options), num_idx, str_idx, value) {
@@ -2396,6 +2253,31 @@ static PHP_FUNCTION(session_unset)
}
/* }}} */
+/* {{{ proto int session_gc(void)
+ Perform GC and return number of deleted sessions */
+static PHP_FUNCTION(session_gc)
+{
+ zend_long num;
+
+ if (zend_parse_parameters_none() == FAILURE) {
+ return;
+ }
+
+ if (PS(session_status) != php_session_active) {
+ php_error_docref(NULL, E_WARNING, "Session is not active");
+ RETURN_FALSE;
+ }
+
+ num = php_session_gc(1);
+ if (num < 0) {
+ RETURN_FALSE;
+ }
+
+ RETURN_LONG(num);
+}
+/* }}} */
+
+
/* {{{ proto void session_write_close(void)
Write session data and end session */
static PHP_FUNCTION(session_write_close)
@@ -2483,6 +2365,10 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_session_id, 0, 0, 0)
ZEND_ARG_INFO(0, id)
ZEND_END_ARG_INFO()
+ZEND_BEGIN_ARG_INFO_EX(arginfo_session_create_id, 0, 0, 0)
+ ZEND_ARG_INFO(0, prefix)
+ZEND_END_ARG_INFO()
+
ZEND_BEGIN_ARG_INFO_EX(arginfo_session_regenerate_id, 0, 0, 0)
ZEND_ARG_INFO(0, delete_old_session)
ZEND_END_ARG_INFO()
@@ -2571,12 +2457,14 @@ static const zend_function_entry session_functions[] = {
PHP_FE(session_module_name, arginfo_session_module_name)
PHP_FE(session_save_path, arginfo_session_save_path)
PHP_FE(session_id, arginfo_session_id)
+ PHP_FE(session_create_id, arginfo_session_create_id)
PHP_FE(session_regenerate_id, arginfo_session_regenerate_id)
PHP_FE(session_decode, arginfo_session_decode)
PHP_FE(session_encode, arginfo_session_void)
PHP_FE(session_start, arginfo_session_start)
PHP_FE(session_destroy, arginfo_session_void)
PHP_FE(session_unset, arginfo_session_void)
+ PHP_FE(session_gc, arginfo_session_void)
PHP_FE(session_set_save_handler, arginfo_session_set_save_handler)
PHP_FE(session_cache_limiter, arginfo_session_cache_limiter)
PHP_FE(session_cache_expire, arginfo_session_cache_expire)
@@ -2601,7 +2489,7 @@ static const zend_function_entry php_session_iface_functions[] = {
PHP_ABSTRACT_ME(SessionHandlerInterface, write, arginfo_session_class_write)
PHP_ABSTRACT_ME(SessionHandlerInterface, destroy, arginfo_session_class_destroy)
PHP_ABSTRACT_ME(SessionHandlerInterface, gc, arginfo_session_class_gc)
- { NULL, NULL, NULL }
+ PHP_FE_END
};
/* }}} */
@@ -2609,7 +2497,7 @@ static const zend_function_entry php_session_iface_functions[] = {
*/
static const zend_function_entry php_session_id_iface_functions[] = {
PHP_ABSTRACT_ME(SessionIdInterface, create_sid, arginfo_session_class_create_sid)
- { NULL, NULL, NULL }
+ PHP_FE_END
};
/* }}} */
@@ -2618,7 +2506,7 @@ static const zend_function_entry php_session_id_iface_functions[] = {
static const zend_function_entry php_session_update_timestamp_iface_functions[] = {
PHP_ABSTRACT_ME(SessionUpdateTimestampHandlerInterface, validateId, arginfo_session_class_validateId)
PHP_ABSTRACT_ME(SessionUpdateTimestampHandlerInterface, updateTimestamp, arginfo_session_class_updateTimestamp)
- { NULL, NULL, NULL }
+ PHP_FE_END
};
/* }}} */
@@ -2632,7 +2520,7 @@ static const zend_function_entry php_session_class_functions[] = {
PHP_ME(SessionHandler, destroy, arginfo_session_class_destroy, ZEND_ACC_PUBLIC)
PHP_ME(SessionHandler, gc, arginfo_session_class_gc, ZEND_ACC_PUBLIC)
PHP_ME(SessionHandler, create_sid, arginfo_session_class_create_sid, ZEND_ACC_PUBLIC)
- { NULL, NULL, NULL }
+ PHP_FE_END
};
/* }}} */
@@ -2685,9 +2573,11 @@ static PHP_RSHUTDOWN_FUNCTION(session) /* {{{ */
{
int i;
- zend_try {
- php_session_flush(1);
- } zend_end_try();
+ if (PS(session_status) == php_session_active) {
+ zend_try {
+ php_session_flush(1);
+ } zend_end_try();
+ }
php_rshutdown_session_globals();
/* this should NOT be done in php_rshutdown_session_globals() */
diff --git a/ext/session/tests/016.phpt b/ext/session/tests/016.phpt
index fc2187e127..5e20ca348b 100644
--- a/ext/session/tests/016.phpt
+++ b/ext/session/tests/016.phpt
@@ -24,7 +24,6 @@ print "I live\n";
--EXPECTF--
Warning: session_start(): Failed to create session data file path. Too short session ID, invalid save_path or path lentgth exceeds MAXPATHLEN(%d) in %s on line 4
-Warning: session_write_close(): Failed to create session data file path. Too short session ID, invalid save_path or path lentgth exceeds MAXPATHLEN(%d) in %s on line 6
-
-Warning: session_write_close(): Failed to write session data (files). Please verify that the current setting of session.save_path is correct (123;:/really%scompletely:::/invalid;;,23123;213) in %s on line 6
+Warning: session_start(): Failed to read session data: files (path: 123;:/really%scompletely:::/invalid;;,23123;213) in %s on line 4
I live
+
diff --git a/ext/session/tests/021.phpt b/ext/session/tests/021.phpt
index e199972899..4a97d7d32a 100644
--- a/ext/session/tests/021.phpt
+++ b/ext/session/tests/021.phpt
@@ -16,11 +16,15 @@ session.save_handler=files
<?php
error_reporting(E_ALL);
+ini_set('session.trans_sid_hosts', 'php.net');
+$_SERVER['HTTP_HOST'] = 'php.net';
session_id("abtest");
session_start();
?>
-<form>
+<form action="//bad.net/do.php">
+<fieldset>
+<form action="//php.net/do.php">
<fieldset>
<?php
@@ -29,7 +33,7 @@ ob_flush();
ini_set("url_rewriter.tags", "a=href,area=href,frame=src,input=src,form=");
?>
-<form>
+<form action="../do.php">
<fieldset>
<?php
@@ -38,7 +42,7 @@ ob_flush();
ini_set("url_rewriter.tags", "a=href,area=href,frame=src,input=src,form=fakeentry");
?>
-<form>
+<form action="/do.php">
<fieldset>
<?php
@@ -47,18 +51,20 @@ ob_flush();
ini_set("url_rewriter.tags", "a=href,fieldset=,area=href,frame=src,input=src");
?>
-<form>
+<form action="/foo/do.php">
<fieldset>
<?php
session_destroy();
?>
--EXPECT--
-<form><input type="hidden" name="PHPSESSID" value="abtest" />
-<fieldset><input type="hidden" name="PHPSESSID" value="abtest" />
-<form><input type="hidden" name="PHPSESSID" value="abtest" />
+<form action="//bad.net/do.php">
+<fieldset>
+<form action="//php.net/do.php"><input type="hidden" name="PHPSESSID" value="abtest" />
+<fieldset>
+<form action="../do.php"><input type="hidden" name="PHPSESSID" value="abtest" />
+<fieldset>
+<form action="/do.php"><input type="hidden" name="PHPSESSID" value="abtest" />
<fieldset>
-<form><input type="hidden" name="PHPSESSID" value="abtest" />
+<form action="/foo/do.php"><input type="hidden" name="PHPSESSID" value="abtest" />
<fieldset>
-<form>
-<fieldset><input type="hidden" name="PHPSESSID" value="abtest" />
diff --git a/ext/session/tests/bug26862.phpt b/ext/session/tests/bug26862.phpt
index 7990f74359..9b15305b77 100644
--- a/ext/session/tests/bug26862.phpt
+++ b/ext/session/tests/bug26862.phpt
@@ -6,6 +6,8 @@ Bug #26862 (ob_flush() before output_reset_rewrite_vars() results in data loss)
html_errors=0
session.use_trans_sid=0
session.save_handler=files
+session.trans_sid_tags="a=href,area=href,frame=src,form="
+url_rewriter.tags="a=href,area=href,frame=src,form="
--FILE--
<?php
session_start();
diff --git a/ext/session/tests/bug50308.phpt b/ext/session/tests/bug50308.phpt
index 110277ce3c..fe0b0e89fa 100644
--- a/ext/session/tests/bug50308.phpt
+++ b/ext/session/tests/bug50308.phpt
@@ -16,15 +16,21 @@ session.use_only_cookies=0
<a href="foo"/>
<a href="foo" />
<a href=foo/>
+<a href="/">
<a href=/>
<a href=?foo=bar/>
<a href="?foo=bar"/>
+<a href=./>
+<a href="./">
--EXPECTF--
<a href="?PHPSESSID=%s"/>
<a href="?PHPSESSID=%s" />
<a href="foo?PHPSESSID=%s"/>
<a href="foo?PHPSESSID=%s" />
<a href=foo/?PHPSESSID=%s>
+<a href="/?PHPSESSID=%s">
<a href=/?PHPSESSID=%s>
<a href=?foo=bar/&PHPSESSID=%s>
<a href="?foo=bar&PHPSESSID=%s"/>
+<a href=./?PHPSESSID=%s>
+<a href="./?PHPSESSID=%s">
diff --git a/ext/session/tests/bug55688.phpt b/ext/session/tests/bug55688.phpt
index 8db48384af..b073dc3c5c 100644
--- a/ext/session/tests/bug55688.phpt
+++ b/ext/session/tests/bug55688.phpt
@@ -12,4 +12,4 @@ $x = new SessionHandler;
$x->gc(1);
?>
--EXPECTF--
-Warning: SessionHandler::gc(): Parent session handler is not open in %s on line %d
+Warning: SessionHandler::gc(): Session is not active in %s on line %d
diff --git a/ext/session/tests/bug60634.phpt b/ext/session/tests/bug60634.phpt
index 5f170c45b5..56b235af55 100644
--- a/ext/session/tests/bug60634.phpt
+++ b/ext/session/tests/bug60634.phpt
@@ -40,8 +40,20 @@ session_start();
session_write_close();
echo "um, hi\n";
+/*
+ * write() calls die(). This results in calling session_flush() which calls
+ * write() in request shutdown. The code is inside save handler still and
+ * calls save close handler.
+ *
+ * Because session_write_close() fails by die(), write() is called twice.
+ * close() is still called at request shutdown since session is active.
+ */
+
?>
--EXPECTF--
write: goodbye cruel world
-close: goodbye cruel world
+Warning: Unknown: Cannot call session save handler in a recursive manner in Unknown on line 0
+
+Warning: Unknown: Failed to write session data using user defined save handler. (session.save_path: ) in Unknown on line 0
+close: goodbye cruel world
diff --git a/ext/session/tests/bug60634_error_1.phpt b/ext/session/tests/bug60634_error_1.phpt
index 38060c2c6e..5ad8fcb02a 100644
--- a/ext/session/tests/bug60634_error_1.phpt
+++ b/ext/session/tests/bug60634_error_1.phpt
@@ -42,6 +42,11 @@ session_start();
session_write_close();
echo "um, hi\n";
+/*
+FIXME: Something wrong. It should try to close after error, otherwise session
+may keep "open" state.
+*/
+
?>
--EXPECTF--
write: goodbye cruel world
@@ -52,3 +57,4 @@ Stack trace:
#1 %s(%d): session_write_close()
#2 {main}
thrown in %s on line %d
+
diff --git a/ext/session/tests/bug60634_error_3.phpt b/ext/session/tests/bug60634_error_3.phpt
index 74ae249892..f97da00dce 100644
--- a/ext/session/tests/bug60634_error_3.phpt
+++ b/ext/session/tests/bug60634_error_3.phpt
@@ -49,4 +49,5 @@ Stack trace:
#0 [internal function]: write(%s, '')
#1 {main}
thrown in %s on line %d
-close: goodbye cruel world
+
+Warning: Unknown: Cannot call session save handler in a recursive manner in Unknown on line 0
diff --git a/ext/session/tests/bug60634_error_4.phpt b/ext/session/tests/bug60634_error_4.phpt
index b74f987c2b..ca8672e4f4 100644
--- a/ext/session/tests/bug60634_error_4.phpt
+++ b/ext/session/tests/bug60634_error_4.phpt
@@ -49,5 +49,5 @@ Stack trace:
#0 [internal function]: write('%s', '')
#1 {main}
thrown in %s on line %d
-close: goodbye cruel world
+Warning: Unknown: Cannot call session save handler in a recursive manner in Unknown on line 0
diff --git a/ext/session/tests/bug61728.phpt b/ext/session/tests/bug61728.phpt
index 3f8dbeb58a..2780d7b7e2 100644
--- a/ext/session/tests/bug61728.phpt
+++ b/ext/session/tests/bug61728.phpt
@@ -8,32 +8,34 @@ function output_html($ext) {
return strlen($ext);
}
-function open ($save_path, $session_name) {
+function open ($save_path, $session_name) {
return true;
-}
+}
-function close() {
+function close() {
return true;
-}
+}
-function read ($id) {
-}
+function read ($id) {
+ return '';
+}
-function write ($id, $sess_data) {
+function write ($id, $sess_data) {
ob_start("output_html");
echo "laruence";
ob_end_flush();
return true;
-}
+}
-function destroy ($id) {
-}
+function destroy ($id) {
+ return true;
+}
-function gc ($maxlifetime) {
- return true;
-}
+function gc ($maxlifetime) {
+ return true;
+}
-session_set_save_handler ("open", "close", "read", "write", "destroy", "gc");
+session_set_save_handler ("open", "close", "read", "write", "destroy", "gc");
session_start();
--EXPECTF--
8
diff --git a/ext/session/tests/bug67972.phpt b/ext/session/tests/bug67972.phpt
index 63ed3a95b8..92c3044ac5 100644
--- a/ext/session/tests/bug67972.phpt
+++ b/ext/session/tests/bug67972.phpt
@@ -7,4 +7,5 @@ Bug #67972: SessionHandler Invalid memory read create_sid()
(new SessionHandler)->create_sid();
--EXPECTF--
-Fatal error: SessionHandler::create_sid(): Cannot call default session handler in %s on line %d
+Warning: SessionHandler::create_sid(): Session is not active in %s on line %d
+
diff --git a/ext/session/tests/bug68063.phpt b/ext/session/tests/bug68063.phpt
index d3da470d06..d21a877631 100644
--- a/ext/session/tests/bug68063.phpt
+++ b/ext/session/tests/bug68063.phpt
@@ -3,18 +3,22 @@ Bug #68063 (Empty session IDs do still start sessions)
--SKIPIF--
<?php include('skipif.inc'); ?>
--INI--
+session.use_strict_mode=0
+session.sid_length=40
+session.sid_bits_per_character=4
--FILE--
<?php
+// Empty session ID may happen by browser bugs
+
// Could also be set with a cookie like "PHPSESSID=; path=/"
session_id('');
-// Will still start the session and return true
+// Start the session with empty string should result in new session ID
var_dump(session_start());
-// Returns an empty string
+// Returns newly created session ID
var_dump(session_id());
?>
--EXPECTF--
-Warning: session_start(): Cannot start session with empty session ID in %s on line %d
-bool(false)
-string(0) ""
+bool(true)
+string(40) "%s"
diff --git a/ext/session/tests/bug69111.phpt b/ext/session/tests/bug69111.phpt
index c6d10c74a0..ce14dc750c 100644
--- a/ext/session/tests/bug69111.phpt
+++ b/ext/session/tests/bug69111.phpt
@@ -1,7 +1,5 @@
--TEST--
Bug #69111 Crash in SessionHandler::read()
---XFAIL--
-It is still a leak
--SKIPIF--
<?php include('skipif.inc'); ?>
--FILE--
@@ -19,4 +17,9 @@ $sh->write("foo", "bar");
var_dump($sh->read(@$id));
?>
--EXPECTF--
+Warning: SessionHandler::open(): Session is not active in %s on line 10
+
+Warning: SessionHandler::write(): Session is not active in %s on line 11
+
+Warning: SessionHandler::read(): Session is not active in %s on line 12
bool(false)
diff --git a/ext/session/tests/bug70133.phpt b/ext/session/tests/bug70133.phpt
new file mode 100644
index 0000000000..3e019e483b
--- /dev/null
+++ b/ext/session/tests/bug70133.phpt
@@ -0,0 +1,41 @@
+--TEST--
+Bug #70133 (Extended SessionHandler::read is ignoring $session_id when calling parent)
+--SKIPIF--
+<?php include('skipif.inc'); ?>
+--INI--
+session.save_handler=files
+session.save_path=
+session.use_strict_mode=0
+--FILE--
+<?php
+
+class CustomReadHandler extends \SessionHandler {
+
+ public function read($session_id) {
+ return parent::read('mycustomsession');
+ }
+}
+
+ob_start();
+
+session_set_save_handler(new CustomReadHandler(), true);
+
+session_id('mycustomsession');
+session_start();
+$_SESSION['foo'] = 'hoge';
+var_dump(session_id());
+session_commit();
+
+session_id('otherid');
+session_start();
+var_dump($_SESSION);
+var_dump(session_id());
+
+?>
+--EXPECT--
+string(15) "mycustomsession"
+array(1) {
+ ["foo"]=>
+ string(4) "hoge"
+}
+string(7) "otherid"
diff --git a/ext/session/tests/bug71162.phpt b/ext/session/tests/bug71162.phpt
new file mode 100644
index 0000000000..722889d417
--- /dev/null
+++ b/ext/session/tests/bug71162.phpt
@@ -0,0 +1,79 @@
+--TEST--
+updateTimestamp never called when session data is empty
+--INI--
+session.use_strict_mode=0
+session.save_handler=files
+--XFAIL--
+Current session module is designed to write empty session always. In addition, current session module only supports SessionHandlerInterface only from PHP 7.0.
+--FILE--
+<?php
+class MySessionHandler extends SessionHandler implements SessionUpdateTimestampHandlerInterface
+{
+ public function open($path, $sessname) {
+ return TRUE;
+ }
+
+ public function close() {
+ return TRUE;
+ }
+
+ public function read($sessid) {
+ return '';
+ }
+
+ public function write($sessid, $sessdata) {
+ echo __FUNCTION__, PHP_EOL;
+ return TRUE;
+ }
+
+ public function destroy($sessid) {
+ return TRUE;
+ }
+
+ public function gc($maxlifetime) {
+ return TRUE;
+ }
+
+ public function create_sid() {
+ return sha1(random_bytes(32));
+ }
+
+ public function validateId($sid) {
+ return TRUE;
+ }
+
+ public function updateTimestamp($sessid, $sessdata) {
+ echo __FUNCTION__, PHP_EOL;
+ return TRUE;
+ }
+}
+
+ob_start();
+$handler = new MySessionHandler();
+session_set_save_handler($handler);
+
+session_id(sha1(''));
+var_dump(session_id());
+var_dump(session_start(['lazy_write'=>1]));
+session_commit();
+
+session_id(sha1(''));
+var_dump(session_id());
+var_dump(session_start(['lazy_write'=>1]));
+session_commit();
+
+session_id(sha1(''));
+var_dump(session_id());
+var_dump(session_start(['lazy_write'=>0]));
+session_commit();
+?>
+--EXPECT--
+string(40) "da39a3ee5e6b4b0d3255bfef95601890afd80709"
+bool(true)
+write
+string(40) "da39a3ee5e6b4b0d3255bfef95601890afd80709"
+bool(true)
+updateTimestamp
+string(40) "da39a3ee5e6b4b0d3255bfef95601890afd80709"
+bool(true)
+write
diff --git a/ext/session/tests/bug72681.phpt b/ext/session/tests/bug72681.phpt
index ceca6ecc33..4752767d50 100644
--- a/ext/session/tests/bug72681.phpt
+++ b/ext/session/tests/bug72681.phpt
@@ -6,12 +6,17 @@ Bug #72681: PHP Session Data Injection Vulnerability
<?php
ini_set('session.serialize_handler', 'php');
session_start();
-$GLOBALS['ryat'] = $GLOBALS;
+$GLOBALS['ryat'] = $_SESSION;
$_SESSION['ryat'] = 'ryat|O:8:"stdClass":0:{}';
session_write_close();
session_start();
+var_dump($ryat);
var_dump($_SESSION);
?>
--EXPECT--
array(0) {
}
+array(1) {
+ ["ryat"]=>
+ string(24) "ryat|O:8:"stdClass":0:{}"
+}
diff --git a/ext/session/tests/bug74892.phpt b/ext/session/tests/bug74892.phpt
new file mode 100644
index 0000000000..a4c977828a
--- /dev/null
+++ b/ext/session/tests/bug74892.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Bug #74892 Url Rewriting (trans_sid) not working on urls that start with #
+--FILE--
+<?php
+ini_set('session.use_cookies', '0');
+ini_set('session.use_only_cookies',0);
+ini_set('session.use_trans_sid',1);
+ini_set('session.trans_sid_hosts','php.net');
+session_id('sessionidhere');
+session_start();
+
+?>
+<p><a href="index.php">Click This Anchor Tag!</a></p>
+<p><a href="index.php#place">External link with anchor</a></p>
+<p><a href="http://php.net#foo">External link with anchor 2</a></p>
+<p><a href="#place">Internal link</a></p>
+===DONE===
+--EXPECT--
+<p><a href="index.php?PHPSESSID=sessionidhere">Click This Anchor Tag!</a></p>
+<p><a href="index.php?PHPSESSID=sessionidhere#place">External link with anchor</a></p>
+<p><a href="http://php.net?PHPSESSID=sessionidhere#foo">External link with anchor 2</a></p>
+<p><a href="#place">Internal link</a></p>
+===DONE===
diff --git a/ext/session/tests/rfc1867_sid_invalid.phpt b/ext/session/tests/rfc1867_sid_invalid.phpt
index b434556c61..ac288d4109 100644
--- a/ext/session/tests/rfc1867_sid_invalid.phpt
+++ b/ext/session/tests/rfc1867_sid_invalid.phpt
@@ -9,6 +9,7 @@ session.save_path=
session.name=PHPSESSID
session.use_cookies=1
session.use_only_cookies=0
+session.use_strict_mode=0
session.auto_start=0
session.upload_progress.enabled=1
session.upload_progress.cleanup=0
@@ -48,13 +49,13 @@ session_destroy();
--EXPECTF--
Warning: Unknown: The session id is too long or contains illegal characters, valid characters are a-z, A-Z, 0-9 and '-,' in Unknown on line 0
-Warning: Unknown: The session id is too long or contains illegal characters, valid characters are a-z, A-Z, 0-9 and '-,' in Unknown on line 0
+Warning: Unknown: Failed to read session data: files (path: ) in Unknown on line 0
Warning: Unknown: Failed to write session data (files). Please verify that the current setting of session.save_path is correct () in Unknown on line 0
Warning: Unknown: The session id is too long or contains illegal characters, valid characters are a-z, A-Z, 0-9 and '-,' in Unknown on line 0
-Warning: Unknown: The session id is too long or contains illegal characters, valid characters are a-z, A-Z, 0-9 and '-,' in Unknown on line 0
+Warning: Unknown: Failed to read session data: files (path: ) in Unknown on line 0
Warning: Unknown: Failed to write session data (files). Please verify that the current setting of session.save_path is correct () in Unknown on line 0
string(%d) "%s"
diff --git a/ext/session/tests/session_basic2.phpt b/ext/session/tests/session_basic2.phpt
index 179b82971e..dd992a87a0 100644
--- a/ext/session/tests/session_basic2.phpt
+++ b/ext/session/tests/session_basic2.phpt
@@ -4,11 +4,11 @@ Test basic function : variation2
session.use_strict_mode=1
session.save_handler=files
session.hash_bits_per_character=4
-session.hash_function=0
session.gc_probability=1
session.gc_divisor=1000
session.gc_maxlifetime=300
session.save_path=
+session.sid_length=32
session.name=PHPSESSID
--SKIPIF--
<?php include('skipif.inc'); ?>
diff --git a/ext/session/tests/session_basic3.phpt b/ext/session/tests/session_basic3.phpt
index 3cc90a8eef..0337151cf0 100644
--- a/ext/session/tests/session_basic3.phpt
+++ b/ext/session/tests/session_basic3.phpt
@@ -12,8 +12,7 @@ session.gc_divisor=1000
session.gc_maxlifetime=300
session.save_path=
session.name=PHPSESSID
---XFAIL--
-Waiting url_scanner_ex.re fix. https://bugs.php.net/bug.php?id=68970
+url_rewriter.hosts=
--SKIPIF--
<?php include('skipif.inc'); ?>
--FILE--
@@ -206,15 +205,15 @@ echo '
<input type="text" name="test1"></input>
<input type="text" name="test2" />
</form>
-<form action="http://php.net/script.php" method="post">
+<form method="post" action="http://php.net/script.php">
<input type="text" name="test1"></input>
<input type="text" name="test2" />
</form>
-<form action="https://php.net/script.php" method="post">
+<form method="post" action="https://php.net/script.php">
<input type="text" name="test1"></input>
<input type="text" name="test2" />
</form>
-<form action="//php.net/script.php" method="post">
+<form method="post" action="//php.net/script.php">
<input type="text" name="test1"></input>
<input type="text" name="test2" />
</form>
@@ -233,15 +232,6 @@ ob_end_flush();
*** Test trans sid ***
<a href="/?PHPSESSID=testid">test</a>
-<a href="/?PHPSESSID=testid#bar">test</a>
-<a href="/?foo&PHPSESSID=testid">test</a>
-<a href="/?foo&PHPSESSID=testid#bar">test</a>
-<a href="/?foo=var&PHPSESSID=testid">test</a>
-<a href="/?foo=var&PHPSESSID=testid#bar">test</a>
-<a href="file.php?PHPSESSID=testid">test</a>
-<a href="file.php?foo&PHPSESSID=testid">test</a>
-<a href="file.php?foo=var&PHPSESSID=testid">test</a>
-<a href="/?PHPSESSID=testid">test</a>
<a href="/path?PHPSESSID=testid">test</a>
<a href="/path/?PHPSESSID=testid">test</a>
<a href="/path/?foo=var&PHPSESSID=testid">test</a>
@@ -257,14 +247,23 @@ ob_end_flush();
<a href="../path/?PHPSESSID=testid#bar">test</a>
<a href="../path/?foo=var&PHPSESSID=testid#bar">test</a>
-<a href="/?foo">test</a>
-<a href="/?foo#bar">test</a>
-<a href="/?foo=var">test</a>
-<a href="/?foo=var#bar">test</a>
-<a href="../?foo">test</a>
-<a href="../?foo#bar">test</a>
-<a href="../?foo=var">test</a>
-<a href="../?foo=var#bar">test</a>
+<a href="/?foo&PHPSESSID=testid">test</a>
+<a href="/?foo&PHPSESSID=testid#bar">test</a>
+<a href="/?foo=var&PHPSESSID=testid">test</a>
+<a href="/?foo=var&PHPSESSID=testid#bar">test</a>
+<a href="../?foo&PHPSESSID=testid">test</a>
+<a href="../?foo&PHPSESSID=testid#bar">test</a>
+<a href="../?foo=var&PHPSESSID=testid">test</a>
+<a href="../?foo=var&PHPSESSID=testid#bar">test</a>
+
+<a href="file.php?PHPSESSID=testid">test</a>
+<a href="file.php?foo&PHPSESSID=testid">test</a>
+<a href="file.php?foo=var&PHPSESSID=testid">test</a>
+<a href="file.php?foo=var&PHPSESSID=testid#bar">test</a>
+<a href="../file.php?PHPSESSID=testid">test</a>
+<a href="../file.php?foo&PHPSESSID=testid">test</a>
+<a href="../file.php?foo=var&PHPSESSID=testid">test</a>
+<a href="../file.php?foo=var&PHPSESSID=testid#bar">test</a>
<a href="http://php.net">test</a>
<a href="http://php.net/">test</a>
@@ -317,31 +316,31 @@ ob_end_flush();
<a href="//php.net/some/path/file.php?foo=var">test</a>
<a href="//php.net/some/path/file.php?foo=var#bar">test</a>
-<form action="script.php" method="post"><input type="hidden" name="PHPSESSID" value="testid" /><input type="hidden" name="PHPSESSID" value="testid" />
+<form action="script.php" method="post"><input type="hidden" name="PHPSESSID" value="testid" />
<input type="text" name="test1"></input>
<input type="text" name="test2" />
</form>
-<form action="../script.php" method="post"><input type="hidden" name="PHPSESSID" value="testid" /><input type="hidden" name="PHPSESSID" value="testid" />
+<form action="../script.php" method="post"><input type="hidden" name="PHPSESSID" value="testid" />
<input type="text" name="test1"></input>
<input type="text" name="test2" />
</form>
-<form action="/path/script.php" method="post"><input type="hidden" name="PHPSESSID" value="testid" /><input type="hidden" name="PHPSESSID" value="testid" />
+<form action="/path/script.php" method="post"><input type="hidden" name="PHPSESSID" value="testid" />
<input type="text" name="test1"></input>
<input type="text" name="test2" />
</form>
-<form action="../path/script.php" method="post"><input type="hidden" name="PHPSESSID" value="testid" /><input type="hidden" name="PHPSESSID" value="testid" />
+<form action="../path/script.php" method="post"><input type="hidden" name="PHPSESSID" value="testid" />
<input type="text" name="test1"></input>
<input type="text" name="test2" />
</form>
-<form action="http://php.net/script.php" method="post"><input type="hidden" name="PHPSESSID" value="testid" /><input type="hidden" name="PHPSESSID" value="testid" />
+<form method="post" action="http://php.net/script.php">
<input type="text" name="test1"></input>
<input type="text" name="test2" />
</form>
-<form action="https://php.net/script.php" method="post"><input type="hidden" name="PHPSESSID" value="testid" /><input type="hidden" name="PHPSESSID" value="testid" />
+<form method="post" action="https://php.net/script.php">
<input type="text" name="test1"></input>
<input type="text" name="test2" />
</form>
-<form action="//php.net/script.php" method="post"><input type="hidden" name="PHPSESSID" value="testid" /><input type="hidden" name="PHPSESSID" value="testid" />
+<form method="post" action="//php.net/script.php">
<input type="text" name="test1"></input>
<input type="text" name="test2" />
</form>
@@ -349,4 +348,4 @@ NULL
*** Cleanup ***
bool(true)
string(6) "testid"
-bool(true) \ No newline at end of file
+bool(true)
diff --git a/ext/session/tests/session_basic4.phpt b/ext/session/tests/session_basic4.phpt
new file mode 100644
index 0000000000..0a0f9749f8
--- /dev/null
+++ b/ext/session/tests/session_basic4.phpt
@@ -0,0 +1,67 @@
+--TEST--
+Test basic function : variation4 use_trans_sid
+--INI--
+session.use_strict_mode=0
+session.use_only_cookies=0
+session.use_trans_sid=1
+session.save_handler=files
+session.hash_bits_per_character=4
+session.hash_function=0
+session.gc_probability=1
+session.gc_divisor=1000
+session.gc_maxlifetime=300
+session.save_path=
+session.name=PHPSESSID
+session.trans_sid_tags="a=href,area=href,frame=src,form="
+url_rewriter.tags="a=href,area=href,frame=src,form="
+--SKIPIF--
+<?php include('skipif.inc'); ?>
+--FILE--
+<?php
+
+ob_start();
+
+/*
+ * Prototype : session.use_trans_sid=1
+ * Description : Test basic functionality.
+ * Source code : ext/session/session.c
+ */
+
+echo "*** Testing basic session functionality : variation4 use_trans_sid ***\n";
+
+echo "*** Test trans sid ***\n";
+output_add_rewrite_var('testvar1','testvalue1');
+
+session_id('test1');
+session_start();
+
+echo '
+<a href="/">
+<form action="" method="post">
+</form>
+';
+
+session_commit();
+
+output_add_rewrite_var('testvar2','testvalue2');
+
+session_id('test2');
+session_start();
+echo '
+<a href="/">
+<form action="" method="post">
+</form>
+';
+
+
+--EXPECT--
+*** Testing basic session functionality : variation4 use_trans_sid ***
+*** Test trans sid ***
+
+<a href="/?PHPSESSID=test2&testvar1=testvalue1&testvar2=testvalue2">
+<form action="" method="post"><input type="hidden" name="testvar1" value="testvalue1" /><input type="hidden" name="testvar2" value="testvalue2" /><input type="hidden" name="PHPSESSID" value="test2" />
+</form>
+
+<a href="/?PHPSESSID=test2&testvar1=testvalue1&testvar2=testvalue2">
+<form action="" method="post"><input type="hidden" name="testvar1" value="testvalue1" /><input type="hidden" name="testvar2" value="testvalue2" /><input type="hidden" name="PHPSESSID" value="test2" />
+</form>
diff --git a/ext/session/tests/session_basic5.phpt b/ext/session/tests/session_basic5.phpt
new file mode 100644
index 0000000000..7e3bb7fc21
--- /dev/null
+++ b/ext/session/tests/session_basic5.phpt
@@ -0,0 +1,446 @@
+--TEST--
+Test basic function : variation5 use_trans_sid
+--INI--
+session.use_strict_mode=0
+session.use_only_cookies=0
+session.use_trans_sid=1
+session.save_handler=files
+session.hash_bits_per_character=4
+session.hash_function=0
+session.gc_probability=1
+session.gc_divisor=1000
+session.gc_maxlifetime=300
+session.save_path=
+session.name=PHPSESSID
+--SKIPIF--
+<?php include('skipif.inc'); ?>
+--FILE--
+<?php
+ob_start();
+
+$_SERVER['HTTP_HOST'] = 'php.net';
+ini_set('session.trans_sid_hosts','php.net,example.com');
+
+/*
+ * Prototype : session.use_trans_sid=1
+ * Description : Test basic functionality.
+ * Source code : ext/session/session.c
+ */
+
+echo "*** Testing basic session functionality : variation5 use_trans_sid ***\n";
+echo "*** Test trans sid ***\n";
+
+$session_id = 'testid';
+session_id($session_id);
+session_start();
+// Should add session ID to allowed hosts only for SECURITY
+echo '
+<a href="/">test</a>
+<a href="/path">test</a>
+<a href="/path/">test</a>
+<a href="/path/?foo=var">test</a>
+<a href="../">test</a>
+<a href="../path">test</a>
+<a href="../path/">test</a>
+<a href="../path/?foo=var">test</a>
+
+<a href="/#bar">test</a>
+<a href="/path/#bar">test</a>
+<a href="/path/?foo=var#bar">test</a>
+<a href="../#bar">test</a>
+<a href="../path/#bar">test</a>
+<a href="../path/?foo=var#bar">test</a>
+
+<a href="/?foo">test</a>
+<a href="/?foo#bar">test</a>
+<a href="/?foo=var">test</a>
+<a href="/?foo=var#bar">test</a>
+<a href="../?foo">test</a>
+<a href="../?foo#bar">test</a>
+<a href="../?foo=var">test</a>
+<a href="../?foo=var#bar">test</a>
+
+<a href="file.php">test</a>
+<a href="file.php?foo">test</a>
+<a href="file.php?foo=var">test</a>
+<a href="file.php?foo=var#bar">test</a>
+<a href="../file.php">test</a>
+<a href="../file.php?foo">test</a>
+<a href="../file.php?foo=var">test</a>
+<a href="../file.php?foo=var#bar">test</a>
+
+<a href="http://php.net">test</a>
+<a href="http://php.net/">test</a>
+<a href="http://php.net/#bar">test</a>
+<a href="http://php.net/?foo">test</a>
+<a href="http://php.net/?foo#bar">test</a>
+<a href="http://php.net/?foo=var">test</a>
+<a href="http://php.net/?foo=var#bar">test</a>
+<a href="http://php.net/file.php">test</a>
+<a href="http://php.net/file.php#bar">test</a>
+<a href="http://php.net/file.php?foo">test</a>
+<a href="http://php.net/file.php?foo#bar">test</a>
+<a href="http://php.net/file.php?foo=var">test</a>
+<a href="http://php.net/file.php?foo=var#bar">test</a>
+<a href="http://php.net/some/path/file.php">test</a>
+<a href="http://php.net/some/path/file.php?foo">test</a>
+<a href="http://php.net/some/path/file.php?foo=var">test</a>
+<a href="http://php.net/some/path/file.php?foo=var#bar">test</a>
+
+<a href="https://php.net">test</a>
+<a href="https://php.net/">test</a>
+<a href="https://php.net/?foo=var#bar">test</a>
+<a href="https://php.net/file.php">test</a>
+<a href="https://php.net/file.php?foo=var#bar">test</a>
+<a href="https://php.net/some/path/file.php">test</a>
+<a href="https://php.net/some/path/file.php?foo=var#bar">test</a>
+<a href="https://php.net:8443">test</a>
+<a href="https://php.net:8443/">test</a>
+<a href="https://php.net:8443/?foo=var#bar">test</a>
+<a href="https://php.net:8443/file.php">test</a>
+<a href="https://php.net:8443/file.php?foo=var#bar">test</a>
+<a href="https://php.net:8443/some/path/file.php">test</a>
+<a href="https://php.net:8443/some/path/file.php?foo=var#bar">test</a>
+
+<a href="//php.net">test</a>
+<a href="//php.net/">test</a>
+<a href="//php.net/#bar">test</a>
+<a href="//php.net/?foo">test</a>
+<a href="//php.net/?foo#bar">test</a>
+<a href="//php.net/?foo=var">test</a>
+<a href="//php.net/?foo=var#bar">test</a>
+<a href="//php.net/file.php">test</a>
+<a href="//php.net/file.php#bar">test</a>
+<a href="//php.net/file.php?foo">test</a>
+<a href="//php.net/file.php?foo#bar">test</a>
+<a href="//php.net/file.php?foo=var">test</a>
+<a href="//php.net/file.php?foo=var#bar">test</a>
+<a href="//php.net/some/path/file.php">test</a>
+<a href="//php.net/some/path/file.php?foo">test</a>
+<a href="//php.net/some/path/file.php?foo=var">test</a>
+<a href="//php.net/some/path/file.php?foo=var#bar">test</a>
+
+<form action="script.php" method="post">
+ <input type="text" name="test1"></input>
+ <input type="text" name="test2" />
+</form>
+<form action="../script.php" method="post">r
+ <input type="text" name="test1"></input>
+ <input type="text" name="test2" />
+</form>
+<form action="/path/script.php" method="post">
+ <input type="text" name="test1"></input>
+ <input type="text" name="test2" />
+</form>
+<form action="../path/script.php" method="post">
+ <input type="text" name="test1"></input>
+ <input type="text" name="test2" />
+</form>
+<form method="post" action="http://php.net/script.php">
+ <input type="text" name="test1"></input>
+ <input type="text" name="test2" />
+</form>
+<form method="post" action="https://php.net/script.php">
+ <input type="text" name="test1"></input>
+ <input type="text" name="test2" />
+</form>
+<form method="post" action="//php.net/script.php">
+ <input type="text" name="test1"></input>
+ <input type="text" name="test2" />
+</form>
+
+
+<a href="http://bad.com">test</a>
+<a href="http://bad.com/">test</a>
+<a href="http://bad.com/#bar">test</a>
+<a href="http://bad.com/?foo">test</a>
+<a href="http://bad.com/?foo#bar">test</a>
+<a href="http://bad.com/?foo=var">test</a>
+<a href="http://bad.com/?foo=var#bar">test</a>
+<a href="http://bad.com/file.php">test</a>
+<a href="http://bad.com/file.php#bar">test</a>
+<a href="http://bad.com/file.php?foo">test</a>
+<a href="http://bad.com/file.php?foo#bar">test</a>
+<a href="http://bad.com/file.php?foo=var">test</a>
+<a href="http://bad.com/file.php?foo=var#bar">test</a>
+<a href="http://bad.com/some/path/file.php">test</a>
+<a href="http://bad.com/some/path/file.php?foo">test</a>
+<a href="http://bad.com/some/path/file.php?foo=var">test</a>
+<a href="http://bad.com/some/path/file.php?foo=var#bar">test</a>
+
+<a href="https://bad.com">test</a>
+<a href="https://bad.com/">test</a>
+<a href="https://bad.com/?foo=var#bar">test</a>
+<a href="https://bad.com/file.php">test</a>
+<a href="https://bad.com/file.php?foo=var#bar">test</a>
+<a href="https://bad.com/some/path/file.php">test</a>
+<a href="https://bad.com/some/path/file.php?foo=var#bar">test</a>
+<a href="https://bad.com:8443">test</a>
+<a href="https://bad.com:8443/">test</a>
+<a href="https://bad.com:8443/?foo=var#bar">test</a>
+<a href="https://bad.com:8443/file.php">test</a>
+<a href="https://bad.com:8443/file.php?foo=var#bar">test</a>
+<a href="https://bad.com:8443/some/path/file.php">test</a>
+<a href="https://bad.com:8443/some/path/file.php?foo=var#bar">test</a>
+
+<a href="//bad.com">test</a>
+<a href="//bad.com/">test</a>
+<a href="//bad.com/#bar">test</a>
+<a href="//bad.com/?foo">test</a>
+<a href="//bad.com/?foo#bar">test</a>
+<a href="//bad.com/?foo=var">test</a>
+<a href="//bad.com/?foo=var#bar">test</a>
+<a href="//bad.com/file.php">test</a>
+<a href="//bad.com/file.php#bar">test</a>
+<a href="//bad.com/file.php?foo">test</a>
+<a href="//bad.com/file.php?foo#bar">test</a>
+<a href="//bad.com/file.php?foo=var">test</a>
+<a href="//bad.com/file.php?foo=var#bar">test</a>
+<a href="//bad.com/some/path/file.php">test</a>
+<a href="//bad.com/some/path/file.php?foo">test</a>
+<a href="//bad.com/some/path/file.php?foo=var">test</a>
+<a href="//bad.com/some/path/file.php?foo=var#bar">test</a>
+
+<form action="//bad.com/script.php" method="post">
+ <input type="text" name="test1"></input>
+ <input type="text" name="test2" />
+</form>
+<form action="https://bad.com/foo/../script.php" method="post">
+ <input type="text" name="test1"></input>
+ <input type="text" name="test2" />
+</form>
+<form action="https://bad.com//path/script.php" method="post">
+ <input type="text" name="test1"></input>
+ <input type="text" name="test2" />
+</form>
+<form action="https://bad.com/foo/bar../path/script.php" method="post">
+ <input type="text" name="test1"></input>
+ <input type="text" name="test2" />
+</form>
+<form method="post" action="http://bad.com/script.php">
+ <input type="text" name="test1"></input>
+ <input type="text" name="test2" />
+</form>
+<form method="post" action="https://bad.com/script.php">
+ <input type="text" name="test1"></input>
+ <input type="text" name="test2" />
+</form>
+<form method="post" action="//bad.com/script.php">
+ <input type="text" name="test1"></input>
+ <input type="text" name="test2" />
+</form>
+
+';
+var_dump(session_commit());
+
+echo "*** Cleanup ***\n";
+var_dump(session_start());
+var_dump(session_id());
+var_dump(session_destroy());
+
+ob_end_flush();
+?>
+--EXPECT--
+*** Testing basic session functionality : variation5 use_trans_sid ***
+*** Test trans sid ***
+
+<a href="/?PHPSESSID=testid">test</a>
+<a href="/path?PHPSESSID=testid">test</a>
+<a href="/path/?PHPSESSID=testid">test</a>
+<a href="/path/?foo=var&PHPSESSID=testid">test</a>
+<a href="../?PHPSESSID=testid">test</a>
+<a href="../path?PHPSESSID=testid">test</a>
+<a href="../path/?PHPSESSID=testid">test</a>
+<a href="../path/?foo=var&PHPSESSID=testid">test</a>
+
+<a href="/?PHPSESSID=testid#bar">test</a>
+<a href="/path/?PHPSESSID=testid#bar">test</a>
+<a href="/path/?foo=var&PHPSESSID=testid#bar">test</a>
+<a href="../?PHPSESSID=testid#bar">test</a>
+<a href="../path/?PHPSESSID=testid#bar">test</a>
+<a href="../path/?foo=var&PHPSESSID=testid#bar">test</a>
+
+<a href="/?foo&PHPSESSID=testid">test</a>
+<a href="/?foo&PHPSESSID=testid#bar">test</a>
+<a href="/?foo=var&PHPSESSID=testid">test</a>
+<a href="/?foo=var&PHPSESSID=testid#bar">test</a>
+<a href="../?foo&PHPSESSID=testid">test</a>
+<a href="../?foo&PHPSESSID=testid#bar">test</a>
+<a href="../?foo=var&PHPSESSID=testid">test</a>
+<a href="../?foo=var&PHPSESSID=testid#bar">test</a>
+
+<a href="file.php?PHPSESSID=testid">test</a>
+<a href="file.php?foo&PHPSESSID=testid">test</a>
+<a href="file.php?foo=var&PHPSESSID=testid">test</a>
+<a href="file.php?foo=var&PHPSESSID=testid#bar">test</a>
+<a href="../file.php?PHPSESSID=testid">test</a>
+<a href="../file.php?foo&PHPSESSID=testid">test</a>
+<a href="../file.php?foo=var&PHPSESSID=testid">test</a>
+<a href="../file.php?foo=var&PHPSESSID=testid#bar">test</a>
+
+<a href="http://php.net/?PHPSESSID=testid">test</a>
+<a href="http://php.net/?PHPSESSID=testid">test</a>
+<a href="http://php.net/?PHPSESSID=testid#bar">test</a>
+<a href="http://php.net/?foo&PHPSESSID=testid">test</a>
+<a href="http://php.net/?foo&PHPSESSID=testid#bar">test</a>
+<a href="http://php.net/?foo=var&PHPSESSID=testid">test</a>
+<a href="http://php.net/?foo=var&PHPSESSID=testid#bar">test</a>
+<a href="http://php.net/file.php?PHPSESSID=testid">test</a>
+<a href="http://php.net/file.php?PHPSESSID=testid#bar">test</a>
+<a href="http://php.net/file.php?foo&PHPSESSID=testid">test</a>
+<a href="http://php.net/file.php?foo&PHPSESSID=testid#bar">test</a>
+<a href="http://php.net/file.php?foo=var&PHPSESSID=testid">test</a>
+<a href="http://php.net/file.php?foo=var&PHPSESSID=testid#bar">test</a>
+<a href="http://php.net/some/path/file.php?PHPSESSID=testid">test</a>
+<a href="http://php.net/some/path/file.php?foo&PHPSESSID=testid">test</a>
+<a href="http://php.net/some/path/file.php?foo=var&PHPSESSID=testid">test</a>
+<a href="http://php.net/some/path/file.php?foo=var&PHPSESSID=testid#bar">test</a>
+
+<a href="https://php.net/?PHPSESSID=testid">test</a>
+<a href="https://php.net/?PHPSESSID=testid">test</a>
+<a href="https://php.net/?foo=var&PHPSESSID=testid#bar">test</a>
+<a href="https://php.net/file.php?PHPSESSID=testid">test</a>
+<a href="https://php.net/file.php?foo=var&PHPSESSID=testid#bar">test</a>
+<a href="https://php.net/some/path/file.php?PHPSESSID=testid">test</a>
+<a href="https://php.net/some/path/file.php?foo=var&PHPSESSID=testid#bar">test</a>
+<a href="https://php.net:8443/?PHPSESSID=testid">test</a>
+<a href="https://php.net:8443/?PHPSESSID=testid">test</a>
+<a href="https://php.net:8443/?foo=var&PHPSESSID=testid#bar">test</a>
+<a href="https://php.net:8443/file.php?PHPSESSID=testid">test</a>
+<a href="https://php.net:8443/file.php?foo=var&PHPSESSID=testid#bar">test</a>
+<a href="https://php.net:8443/some/path/file.php?PHPSESSID=testid">test</a>
+<a href="https://php.net:8443/some/path/file.php?foo=var&PHPSESSID=testid#bar">test</a>
+
+<a href="//php.net/?PHPSESSID=testid">test</a>
+<a href="//php.net/?PHPSESSID=testid">test</a>
+<a href="//php.net/?PHPSESSID=testid#bar">test</a>
+<a href="//php.net/?foo&PHPSESSID=testid">test</a>
+<a href="//php.net/?foo&PHPSESSID=testid#bar">test</a>
+<a href="//php.net/?foo=var&PHPSESSID=testid">test</a>
+<a href="//php.net/?foo=var&PHPSESSID=testid#bar">test</a>
+<a href="//php.net/file.php?PHPSESSID=testid">test</a>
+<a href="//php.net/file.php?PHPSESSID=testid#bar">test</a>
+<a href="//php.net/file.php?foo&PHPSESSID=testid">test</a>
+<a href="//php.net/file.php?foo&PHPSESSID=testid#bar">test</a>
+<a href="//php.net/file.php?foo=var&PHPSESSID=testid">test</a>
+<a href="//php.net/file.php?foo=var&PHPSESSID=testid#bar">test</a>
+<a href="//php.net/some/path/file.php?PHPSESSID=testid">test</a>
+<a href="//php.net/some/path/file.php?foo&PHPSESSID=testid">test</a>
+<a href="//php.net/some/path/file.php?foo=var&PHPSESSID=testid">test</a>
+<a href="//php.net/some/path/file.php?foo=var&PHPSESSID=testid#bar">test</a>
+
+<form action="script.php" method="post"><input type="hidden" name="PHPSESSID" value="testid" />
+ <input type="text" name="test1"></input>
+ <input type="text" name="test2" />
+</form>
+<form action="../script.php" method="post"><input type="hidden" name="PHPSESSID" value="testid" />r
+ <input type="text" name="test1"></input>
+ <input type="text" name="test2" />
+</form>
+<form action="/path/script.php" method="post"><input type="hidden" name="PHPSESSID" value="testid" />
+ <input type="text" name="test1"></input>
+ <input type="text" name="test2" />
+</form>
+<form action="../path/script.php" method="post"><input type="hidden" name="PHPSESSID" value="testid" />
+ <input type="text" name="test1"></input>
+ <input type="text" name="test2" />
+</form>
+<form method="post" action="http://php.net/script.php"><input type="hidden" name="PHPSESSID" value="testid" />
+ <input type="text" name="test1"></input>
+ <input type="text" name="test2" />
+</form>
+<form method="post" action="https://php.net/script.php"><input type="hidden" name="PHPSESSID" value="testid" />
+ <input type="text" name="test1"></input>
+ <input type="text" name="test2" />
+</form>
+<form method="post" action="//php.net/script.php"><input type="hidden" name="PHPSESSID" value="testid" />
+ <input type="text" name="test1"></input>
+ <input type="text" name="test2" />
+</form>
+
+
+<a href="http://bad.com">test</a>
+<a href="http://bad.com/">test</a>
+<a href="http://bad.com/#bar">test</a>
+<a href="http://bad.com/?foo">test</a>
+<a href="http://bad.com/?foo#bar">test</a>
+<a href="http://bad.com/?foo=var">test</a>
+<a href="http://bad.com/?foo=var#bar">test</a>
+<a href="http://bad.com/file.php">test</a>
+<a href="http://bad.com/file.php#bar">test</a>
+<a href="http://bad.com/file.php?foo">test</a>
+<a href="http://bad.com/file.php?foo#bar">test</a>
+<a href="http://bad.com/file.php?foo=var">test</a>
+<a href="http://bad.com/file.php?foo=var#bar">test</a>
+<a href="http://bad.com/some/path/file.php">test</a>
+<a href="http://bad.com/some/path/file.php?foo">test</a>
+<a href="http://bad.com/some/path/file.php?foo=var">test</a>
+<a href="http://bad.com/some/path/file.php?foo=var#bar">test</a>
+
+<a href="https://bad.com">test</a>
+<a href="https://bad.com/">test</a>
+<a href="https://bad.com/?foo=var#bar">test</a>
+<a href="https://bad.com/file.php">test</a>
+<a href="https://bad.com/file.php?foo=var#bar">test</a>
+<a href="https://bad.com/some/path/file.php">test</a>
+<a href="https://bad.com/some/path/file.php?foo=var#bar">test</a>
+<a href="https://bad.com:8443">test</a>
+<a href="https://bad.com:8443/">test</a>
+<a href="https://bad.com:8443/?foo=var#bar">test</a>
+<a href="https://bad.com:8443/file.php">test</a>
+<a href="https://bad.com:8443/file.php?foo=var#bar">test</a>
+<a href="https://bad.com:8443/some/path/file.php">test</a>
+<a href="https://bad.com:8443/some/path/file.php?foo=var#bar">test</a>
+
+<a href="//bad.com">test</a>
+<a href="//bad.com/">test</a>
+<a href="//bad.com/#bar">test</a>
+<a href="//bad.com/?foo">test</a>
+<a href="//bad.com/?foo#bar">test</a>
+<a href="//bad.com/?foo=var">test</a>
+<a href="//bad.com/?foo=var#bar">test</a>
+<a href="//bad.com/file.php">test</a>
+<a href="//bad.com/file.php#bar">test</a>
+<a href="//bad.com/file.php?foo">test</a>
+<a href="//bad.com/file.php?foo#bar">test</a>
+<a href="//bad.com/file.php?foo=var">test</a>
+<a href="//bad.com/file.php?foo=var#bar">test</a>
+<a href="//bad.com/some/path/file.php">test</a>
+<a href="//bad.com/some/path/file.php?foo">test</a>
+<a href="//bad.com/some/path/file.php?foo=var">test</a>
+<a href="//bad.com/some/path/file.php?foo=var#bar">test</a>
+
+<form action="//bad.com/script.php" method="post">
+ <input type="text" name="test1"></input>
+ <input type="text" name="test2" />
+</form>
+<form action="https://bad.com/foo/../script.php" method="post">
+ <input type="text" name="test1"></input>
+ <input type="text" name="test2" />
+</form>
+<form action="https://bad.com//path/script.php" method="post">
+ <input type="text" name="test1"></input>
+ <input type="text" name="test2" />
+</form>
+<form action="https://bad.com/foo/bar../path/script.php" method="post">
+ <input type="text" name="test1"></input>
+ <input type="text" name="test2" />
+</form>
+<form method="post" action="http://bad.com/script.php">
+ <input type="text" name="test1"></input>
+ <input type="text" name="test2" />
+</form>
+<form method="post" action="https://bad.com/script.php">
+ <input type="text" name="test1"></input>
+ <input type="text" name="test2" />
+</form>
+<form method="post" action="//bad.com/script.php">
+ <input type="text" name="test1"></input>
+ <input type="text" name="test2" />
+</form>
+
+NULL
+*** Cleanup ***
+bool(true)
+string(6) "testid"
+bool(true)
diff --git a/ext/session/tests/session_create_id_basic.phpt b/ext/session/tests/session_create_id_basic.phpt
new file mode 100644
index 0000000000..4f5cc3e41e
--- /dev/null
+++ b/ext/session/tests/session_create_id_basic.phpt
@@ -0,0 +1,58 @@
+--TEST--
+Test session_create_id() function : basic functionality
+--INI--
+session.save_handler=files
+session.sid_length=32
+--SKIPIF--
+<?php include('skipif.inc'); ?>
+--FILE--
+<?php
+
+ob_start();
+
+/*
+ * Prototype : string session_create_id([string $prefix])
+ * Description : Create new session ID with prefix optionally.
+ * Source code : ext/session/session.c
+ */
+
+echo "*** Testing session_create_id() : basic functionality ***\n";
+
+// No session
+var_dump(session_create_id());
+var_dump(session_create_id('ABCD'));
+
+ini_set('session.use_strict_mode', true);
+$sid = session_create_id('XYZ');
+var_dump($sid);
+var_dump(session_id($sid));
+session_start();
+var_dump(session_id());
+var_dump(session_id() === $sid);
+session_destroy();
+
+ini_set('session.use_strict_mode', false);
+$sid = session_create_id('XYZ');
+var_dump($sid);
+var_dump(session_id($sid));
+session_start();
+var_dump(session_id());
+var_dump(session_id() === $sid);
+session_destroy();
+
+echo "Done";
+ob_end_flush();
+?>
+--EXPECTF--
+*** Testing session_create_id() : basic functionality ***
+string(32) "%s"
+string(36) "ABCD%s"
+string(35) "XYZ%s"
+string(0) ""
+string(32) "%s"
+bool(false)
+string(35) "XYZ%s"
+string(0) ""
+string(35) "XYZ%s"
+bool(true)
+Done
diff --git a/ext/session/tests/session_gc_basic.phpt b/ext/session/tests/session_gc_basic.phpt
new file mode 100644
index 0000000000..86e9156ce6
--- /dev/null
+++ b/ext/session/tests/session_gc_basic.phpt
@@ -0,0 +1,40 @@
+--TEST--
+Test session_gc() function : basic functionality
+--SKIPIF--
+<?php include('skipif.inc'); ?>
+--FILE--
+<?php
+
+ob_start();
+
+/*
+ * Prototype : int session_gc(void)
+ * Description : Perform GC
+ * Source code : ext/session/session.c
+ */
+
+echo "*** Testing session_gc() : basic functionality ***\n";
+
+var_dump(session_gc());
+
+var_dump(session_start());
+var_dump(session_gc(), session_gc() >= -1);
+var_dump(session_destroy());
+var_dump(session_id());
+
+echo "Done";
+ob_end_flush();
+?>
+--EXPECTF--
+*** Testing session_gc() : basic functionality ***
+
+Warning: session_gc(): Session is not active in %s on line %d
+bool(false)
+bool(true)
+int(%d)
+bool(true)
+bool(true)
+string(0) ""
+Done
+
+
diff --git a/ext/session/tests/session_hash_function_basic.phpt b/ext/session/tests/session_hash_function_basic.phpt
deleted file mode 100644
index a9c921581b..0000000000
--- a/ext/session/tests/session_hash_function_basic.phpt
+++ /dev/null
@@ -1,52 +0,0 @@
---TEST--
-Test session.hash_function ini setting : basic functionality
---SKIPIF--
-<?php include('skipif.inc'); ?>
---INI--
-session.hash_bits_per_character=4
---FILE--
-<?php
-
-ob_start();
-
-echo "*** Testing session.hash_function : basic functionality ***\n";
-
-var_dump(ini_set('session.hash_function', 'md5'));
-var_dump(session_start());
-var_dump(!empty(session_id()), session_id());
-var_dump(session_destroy());
-
-var_dump(ini_set('session.hash_function', 'sha1'));
-var_dump(session_start());
-var_dump(!empty(session_id()), session_id());
-var_dump(session_destroy());
-
-var_dump(ini_set('session.hash_function', 'none')); // Should fail
-var_dump(session_start());
-var_dump(!empty(session_id()), session_id());
-var_dump(session_destroy());
-
-
-echo "Done";
-ob_end_flush();
-?>
---EXPECTF--
-*** Testing session.hash_function : basic functionality ***
-string(1) "0"
-bool(true)
-bool(true)
-string(32) "%s"
-bool(true)
-string(3) "md5"
-bool(true)
-bool(true)
-string(40) "%s"
-bool(true)
-
-Warning: ini_set(): session.configuration 'session.hash_function' must be existing hash function. none does not exist. in %s%esession_hash_function_basic.php on line 17
-bool(false)
-bool(true)
-bool(true)
-string(40) "%s"
-bool(true)
-Done
diff --git a/ext/session/tests/session_id_basic2.phpt b/ext/session/tests/session_id_basic2.phpt
new file mode 100644
index 0000000000..fd26c0e9ed
--- /dev/null
+++ b/ext/session/tests/session_id_basic2.phpt
@@ -0,0 +1,38 @@
+--TEST--
+Test session_id() function : basic functionality
+--SKIPIF--
+<?php include('skipif.inc'); ?>
+--FILE--
+<?php
+
+ob_start();
+
+/*
+ * Prototype : string session_id([string $id])
+ * Description : Get and/or set the current session id
+ * Source code : ext/session/session.c
+ */
+
+echo "*** Testing session_id() : basic functionality ***\n";
+
+ini_set('session.sid_bits_per_chracter', 6);
+ini_set('session.sid_length', 120);
+session_start();
+var_dump(session_id());
+session_commit();
+
+ini_set('session.sid_bits_per_chracter', 4);
+ini_set('session.sid_length', 22);
+session_start();
+session_regenerate_id();
+var_dump(session_id());
+session_commit();
+
+echo "Done";
+?>
+--EXPECTF--
+*** Testing session_id() : basic functionality ***
+string(120) "%s"
+string(22) "%s"
+Done
+
diff --git a/ext/session/tests/session_id_error4.phpt b/ext/session/tests/session_id_error4.phpt
deleted file mode 100644
index 6c1fdbcd6b..0000000000
--- a/ext/session/tests/session_id_error4.phpt
+++ /dev/null
@@ -1,37 +0,0 @@
---TEST--
-Test session_id() function : error functionality
---SKIPIF--
-<?php include('skipif.inc'); ?>
---INI--
-session.hash_function=0
-session.hash_bits_per_character=4
---FILE--
-<?php
-
-ob_start();
-
-/*
- * Prototype : string session_id([string $id])
- * Description : Get and/or set the current session id
- * Source code : ext/session/session.c
- */
-
-echo "*** Testing session_id() : error functionality ***\n";
-
-var_dump(ini_set("session.hash_function", -1));
-var_dump(session_id());
-var_dump(session_start());
-var_dump(session_id());
-var_dump(session_destroy());
-
-echo "Done";
-ob_end_flush();
-?>
---EXPECTF--
-*** Testing session_id() : error functionality ***
-string(1) "0"
-string(0) ""
-bool(true)
-string(40) "%s"
-bool(true)
-Done
diff --git a/ext/session/tests/session_id_variation1.phpt b/ext/session/tests/session_id_variation1.phpt
deleted file mode 100644
index 983ca29170..0000000000
--- a/ext/session/tests/session_id_variation1.phpt
+++ /dev/null
@@ -1,48 +0,0 @@
---TEST--
-Test session_id() function : variation
---SKIPIF--
-<?php include('skipif.inc'); ?>
---INI--
-session.hash_function=0
---FILE--
-<?php
-
-ob_start();
-
-/*
- * Prototype : string session_id([string $id])
- * Description : Get and/or set the current session id
- * Source code : ext/session/session.c
- */
-
-echo "*** Testing session_id() : variation ***\n";
-
-var_dump(ini_set("session.hash_function", 0));
-var_dump(session_id());
-var_dump(session_start());
-var_dump(session_id());
-var_dump(session_destroy());
-
-var_dump(ini_set("session.hash_function", 1));
-var_dump(session_id());
-var_dump(session_start());
-var_dump(session_id());
-var_dump(session_destroy());
-
-echo "Done";
-ob_end_flush();
-?>
---EXPECTF--
-*** Testing session_id() : variation ***
-string(1) "0"
-string(0) ""
-bool(true)
-string(%d) "%s"
-bool(true)
-string(1) "0"
-string(0) ""
-bool(true)
-string(%d) "%s"
-bool(true)
-Done
-
diff --git a/ext/session/tests/session_id_variation2.phpt b/ext/session/tests/session_id_variation2.phpt
deleted file mode 100644
index f69aa44c0d..0000000000
--- a/ext/session/tests/session_id_variation2.phpt
+++ /dev/null
@@ -1,61 +0,0 @@
---TEST--
-Test session_id() function : variation
---SKIPIF--
-<?php include('skipif.inc'); ?>
---INI--
-session.hash_function=0
-session.entropy_file=
-session.entropy_length=0
---FILE--
-<?php
-
-ob_start();
-
-/*
- * Prototype : string session_id([string $id])
- * Description : Get and/or set the current session id
- * Source code : ext/session/session.c
- */
-
-echo "*** Testing session_id() : variation ***\n";
-
-$directory = dirname(__FILE__);
-$filename = ($directory."/entropy.txt");
-var_dump(ini_set("session.entropy_file", $filename));
-var_dump(file_put_contents($filename, "Hello World!"));
-var_dump(ini_set("session.entropy_length", filesize($filename)));
-
-var_dump(ini_set("session.hash_function", 0));
-var_dump(session_id());
-var_dump(session_start());
-var_dump(session_id());
-var_dump(session_destroy());
-
-var_dump(ini_set("session.hash_function", 1));
-var_dump(session_id());
-var_dump(session_start());
-var_dump(session_id());
-var_dump(session_destroy());
-var_dump(unlink($filename));
-
-echo "Done";
-ob_end_flush();
-?>
---EXPECTF--
-*** Testing session_id() : variation ***
-string(0) ""
-int(12)
-string(1) "0"
-string(1) "0"
-string(0) ""
-bool(true)
-string(%d) "%s"
-bool(true)
-string(1) "0"
-string(0) ""
-bool(true)
-string(%d) "%s"
-bool(true)
-bool(true)
-Done
-
diff --git a/ext/session/tests/session_save_path_variation2.phpt b/ext/session/tests/session_save_path_variation2.phpt
index 4cf44b75a4..60675aec3c 100644
--- a/ext/session/tests/session_save_path_variation2.phpt
+++ b/ext/session/tests/session_save_path_variation2.phpt
@@ -33,8 +33,12 @@ ob_end_flush();
string(5) "/blah"
Warning: session_start(): open(%sblah%e%s, O_RDWR) failed: No such file or directory (2) in %s on line %d
-bool(true)
+
+Warning: session_start(): Failed to read session data: files (path: %sblah) in %s on line %d
+bool(false)
string(5) "/blah"
-bool(true)
+
+Warning: session_destroy(): Trying to destroy uninitialized session in %s on line %d
+bool(false)
string(5) "/blah"
Done
diff --git a/ext/session/tests/session_save_path_variation3.phpt b/ext/session/tests/session_save_path_variation3.phpt
index b064f30183..1d290d95b3 100644
--- a/ext/session/tests/session_save_path_variation3.phpt
+++ b/ext/session/tests/session_save_path_variation3.phpt
@@ -33,8 +33,12 @@ ob_end_flush();
string(5) "/blah"
Warning: session_start(): open(%s, O_RDWR) failed: No such file or directory (2) in %s on line %d
-bool(true)
+
+Warning: session_start(): Failed to read session data: files (path: %sblah) in %s on line %d
+bool(false)
string(5) "/blah"
-bool(true)
+
+Warning: session_destroy(): Trying to destroy uninitialized session in %s on line %d
+bool(false)
string(5) "/blah"
Done
diff --git a/ext/session/tests/session_save_path_variation4.phpt b/ext/session/tests/session_save_path_variation4.phpt
index 80db6caf7d..a4c4e995d3 100644
--- a/ext/session/tests/session_save_path_variation4.phpt
+++ b/ext/session/tests/session_save_path_variation4.phpt
@@ -57,4 +57,3 @@ string(0) ""
Warning: session_start(): open_basedir restriction in effect. File(%s) is not within the allowed path(s): (.) in %s on line %d
Fatal error: session_start(): Failed to initialize storage module: files (path: ) in %s on line %d
-
diff --git a/ext/session/tests/session_set_save_handler_class_002.phpt b/ext/session/tests/session_set_save_handler_class_002.phpt
index b75a7e6390..880bc33425 100644
--- a/ext/session/tests/session_set_save_handler_class_002.phpt
+++ b/ext/session/tests/session_set_save_handler_class_002.phpt
@@ -34,7 +34,7 @@ class MySession2 extends SessionHandler {
}
public function read($id) {
- return @file_get_contents($this->path . $id);
+ return (string)@file_get_contents($this->path . $id);
}
public function write($id, $data) {
diff --git a/ext/session/tests/session_set_save_handler_class_005.phpt b/ext/session/tests/session_set_save_handler_class_005.phpt
index 5be735306a..1b8c1ce645 100644
--- a/ext/session/tests/session_set_save_handler_class_005.phpt
+++ b/ext/session/tests/session_set_save_handler_class_005.phpt
@@ -33,7 +33,7 @@ class MySession6 extends SessionHandler {
$handler = new MySession6;
session_set_save_handler($handler);
-session_start();
+var_dump(session_start());
var_dump(session_id(), ini_get('session.save_handler'), $_SESSION);
@@ -45,13 +45,12 @@ session_unset();
*** Testing session_set_save_handler() : incomplete implementation ***
Warning: SessionHandler::read(): Parent session handler is not open in %ssession_set_save_handler_class_005.php on line %d
+
+Warning: SessionHandler::close(): Parent session handler is not open in %ssession_set_save_handler_class_005.php on line %d
+
+Warning: session_start(): Failed to read session data: user (%s) in %ssession_set_save_handler_class_005.php on line %d
+bool(false)
string(%d) "%s"
string(4) "user"
array(0) {
}
-
-Warning: SessionHandler::write(): Parent session handler is not open in %ssession_set_save_handler_class_005.php on line %d
-
-Warning: session_write_close(): Failed to write session data %s in %ssession_set_save_handler_class_005.php on line %d
-
-Warning: SessionHandler::close(): Parent session handler is not open in %ssession_set_save_handler_class_005.php on line %d
diff --git a/ext/session/tests/session_set_save_handler_class_012.phpt b/ext/session/tests/session_set_save_handler_class_012.phpt
index 91e751bdfc..0ce03f865e 100644
--- a/ext/session/tests/session_set_save_handler_class_012.phpt
+++ b/ext/session/tests/session_set_save_handler_class_012.phpt
@@ -38,7 +38,7 @@ class MySession extends SessionHandler {
$oldHandler = ini_get('session.save_handler');
$handler = new MySession;
session_set_save_handler($handler);
-session_start();
+var_dump(session_start());
var_dump(session_id(), $oldHandler, ini_get('session.save_handler'), $handler->i, $_SESSION);
@@ -50,15 +50,14 @@ Warning: SessionHandler::open() expects exactly 2 parameters, 0 given in %s on l
Read %s
Warning: SessionHandler::read(): Parent session handler is not open in %s on line %d
+
+Warning: SessionHandler::close(): Parent session handler is not open in %s on line %d
+
+Warning: session_start(): Failed to read session data: user (%s) in %s on line %d
+bool(false)
string(%d) "%s"
string(5) "files"
string(4) "user"
int(2)
array(0) {
}
-
-Warning: SessionHandler::write(): Parent session handler is not open in Unknown on line 0
-
-Warning: session_write_close(): Failed to write session data %s in %s on line %d
-
-Warning: SessionHandler::close(): Parent session handler is not open in Unknown on line 0
diff --git a/ext/session/tests/session_set_save_handler_class_016.phpt b/ext/session/tests/session_set_save_handler_class_016.phpt
index 521bd86f31..4095813c9d 100644
--- a/ext/session/tests/session_set_save_handler_class_016.phpt
+++ b/ext/session/tests/session_set_save_handler_class_016.phpt
@@ -10,10 +10,10 @@ session.name=PHPSESSID
ob_start();
-/*
+/*
* Prototype : bool session_set_save_handler(SessionHandlerInterface $handler [, bool $register_shutdown_function = true])
* Description : Sets user-level session storage functions
- * Source code : ext/session/session.c
+ * Source code : ext/session/session.c
*/
echo "*** Testing session_set_save_handler() function: class with create_sid ***\n";
@@ -34,7 +34,7 @@ class MySession2 extends SessionHandler {
}
public function read($id) {
- return @file_get_contents($this->path . $id);
+ return (string)@file_get_contents($this->path . $id);
}
public function write($id, $data) {
diff --git a/ext/session/tests/session_set_save_handler_class_017.phpt b/ext/session/tests/session_set_save_handler_class_017.phpt
index 6f42d7809a..b8e7d7a7ad 100644
--- a/ext/session/tests/session_set_save_handler_class_017.phpt
+++ b/ext/session/tests/session_set_save_handler_class_017.phpt
@@ -34,7 +34,7 @@ class MySession2 extends SessionHandler {
}
public function read($id) {
- return @file_get_contents($this->path . $id);
+ return (string)@file_get_contents($this->path . $id);
}
public function write($id, $data) {
diff --git a/ext/session/tests/session_set_save_handler_error4.phpt b/ext/session/tests/session_set_save_handler_error4.phpt
index be3429b084..4267195ee1 100644
--- a/ext/session/tests/session_set_save_handler_error4.phpt
+++ b/ext/session/tests/session_set_save_handler_error4.phpt
@@ -24,7 +24,7 @@ session_set_save_handler("callback", "callback", "callback", "echo", "callback",
session_set_save_handler("callback", "callback", "callback", "callback", "echo", "callback");
session_set_save_handler("callback", "callback", "callback", "callback", "callback", "echo");
session_set_save_handler("callback", "callback", "callback", "callback", "callback", "callback");
-session_start();
+var_dump(session_start());
ob_end_flush();
?>
--EXPECTF--
@@ -39,3 +39,6 @@ Warning: session_set_save_handler(): Argument 4 is not a valid callback in %s on
Warning: session_set_save_handler(): Argument 5 is not a valid callback in %s on line %d
Warning: session_set_save_handler(): Argument 6 is not a valid callback in %s on line %d
+
+Warning: session_start(): Failed to read session data: user (%s) in %s on line %d
+bool(false)
diff --git a/ext/session/tests/session_set_save_handler_iface_001.phpt b/ext/session/tests/session_set_save_handler_iface_001.phpt
index 03ee42865c..6943d59cbe 100644
--- a/ext/session/tests/session_set_save_handler_iface_001.phpt
+++ b/ext/session/tests/session_set_save_handler_iface_001.phpt
@@ -34,7 +34,7 @@ class MySession2 implements SessionHandlerInterface {
}
public function read($id) {
- return @file_get_contents($this->path . $id);
+ return (string)@file_get_contents($this->path . $id);
}
public function write($id, $data) {
diff --git a/ext/session/tests/session_set_save_handler_iface_002.phpt b/ext/session/tests/session_set_save_handler_iface_002.phpt
index 40c9ac6825..204d88c785 100644
--- a/ext/session/tests/session_set_save_handler_iface_002.phpt
+++ b/ext/session/tests/session_set_save_handler_iface_002.phpt
@@ -43,7 +43,7 @@ class MySession2 implements MySessionHandlerInterface {
}
public function read($id) {
- return @file_get_contents($this->path . $id);
+ return (string)@file_get_contents($this->path . $id);
}
public function write($id, $data) {
diff --git a/ext/session/tests/session_set_save_handler_sid_002.phpt b/ext/session/tests/session_set_save_handler_sid_002.phpt
index f9a72aebca..8da4c589c6 100644
--- a/ext/session/tests/session_set_save_handler_sid_002.phpt
+++ b/ext/session/tests/session_set_save_handler_sid_002.phpt
@@ -3,6 +3,7 @@ Test session_set_save_handler() function: create_sid
--INI--
session.save_handler=files
session.name=PHPSESSID
+session.save_path=/tmp
--SKIPIF--
<?php include('skipif.inc'); ?>
--FILE--
@@ -74,4 +75,13 @@ session_unset();
--EXPECTF--
*** Testing session_set_save_handler() function: create_sid ***
-Fatal error: session_start(): Session id must be a string in %s on line %d
+Fatal error: Uncaught Error: Session id must be a string in %s:%d
+Stack trace:
+#0 %s(%d): session_start()
+#1 {main}
+
+Next Error: Failed to create session ID: user (path: %s) in %s:%d
+Stack trace:
+#0 %s(%d): session_start()
+#1 {main}
+ thrown in %s on line %d
diff --git a/ext/session/tests/session_set_save_handler_variation6.phpt b/ext/session/tests/session_set_save_handler_variation6.phpt
index 3f3f7a165a..573d40cf99 100644
--- a/ext/session/tests/session_set_save_handler_variation6.phpt
+++ b/ext/session/tests/session_set_save_handler_variation6.phpt
@@ -1,6 +1,7 @@
--TEST--
Test session_set_save_handler() function : test lazy_write
--INI--
+session.use_strict_mode=0
session.lazy_write=1
session.save_path=
session.name=PHPSESSID
diff --git a/ext/session/tests/sessionhandler_open_001.phpt b/ext/session/tests/sessionhandler_open_001.phpt
index 6ade9e00a5..e6e913a6a5 100644
--- a/ext/session/tests/sessionhandler_open_001.phpt
+++ b/ext/session/tests/sessionhandler_open_001.phpt
@@ -16,4 +16,11 @@ print "Done!\n";
?>
--EXPECTF--
+Warning: SessionHandler::open(): Session is not active in %s on line 5
+
+Warning: SessionHandler::open(): Session is not active in %s on line 6
+
+Warning: SessionHandler::open(): Session is not active in %s on line 7
+
+Warning: SessionHandler::open(): Session is not active in %s on line 8
Done!
diff --git a/ext/shmop/php_shmop.h b/ext/shmop/php_shmop.h
index 3c079c2cd2..9ac90f013d 100644
--- a/ext/shmop/php_shmop.h
+++ b/ext/shmop/php_shmop.h
@@ -38,7 +38,7 @@ PHP_FUNCTION(shmop_write);
PHP_FUNCTION(shmop_delete);
#ifdef PHP_WIN32
-typedef int key_t;
+# include "win32/ipc.h"
#endif
struct php_shmop
diff --git a/ext/shmop/shmop.c b/ext/shmop/shmop.c
index 521a3dcbb1..0d8984e6cc 100644
--- a/ext/shmop/shmop.c
+++ b/ext/shmop/shmop.c
@@ -305,7 +305,7 @@ PHP_FUNCTION(shmop_size)
PHP_FUNCTION(shmop_write)
{
struct php_shmop *shmop;
- int writesize;
+ zend_long writesize;
zend_long offset;
zend_string *data;
zval *shmid;
@@ -328,7 +328,7 @@ PHP_FUNCTION(shmop_write)
RETURN_FALSE;
}
- writesize = (ZSTR_LEN(data) < shmop->size - offset) ? ZSTR_LEN(data) : shmop->size - offset;
+ writesize = ((zend_long)ZSTR_LEN(data) < shmop->size - offset) ? (zend_long)ZSTR_LEN(data) : shmop->size - offset;
memcpy(shmop->addr + offset, ZSTR_VAL(data), writesize);
RETURN_LONG(writesize);
diff --git a/ext/simplexml/php_simplexml.h b/ext/simplexml/php_simplexml.h
index f5eb7f2f42..1ae8983989 100644
--- a/ext/simplexml/php_simplexml.h
+++ b/ext/simplexml/php_simplexml.h
@@ -45,9 +45,6 @@ extern zend_module_entry simplexml_module_entry;
PHP_MINIT_FUNCTION(simplexml);
PHP_MSHUTDOWN_FUNCTION(simplexml);
-#ifdef HAVE_SPL
-PHP_RINIT_FUNCTION(simplexml);
-#endif
PHP_MINFO_FUNCTION(simplexml);
typedef enum {
diff --git a/ext/simplexml/simplexml.c b/ext/simplexml/simplexml.c
index 0637e06af8..50413b12ac 100644
--- a/ext/simplexml/simplexml.c
+++ b/ext/simplexml/simplexml.c
@@ -247,8 +247,8 @@ static zval *sxe_prop_dim_read(zval *object, zval *member, zend_bool elements, z
if (!member) {
if (sxe->iter.type == SXE_ITER_ATTRLIST) {
/* This happens when the user did: $sxe[]->foo = $value */
- php_error_docref(NULL, E_ERROR, "Cannot create unnamed attribute");
- return NULL;
+ zend_throw_error(NULL, "Cannot create unnamed attribute");
+ return &EG(uninitialized_zval);
}
goto long_dim;
} else {
@@ -284,8 +284,8 @@ long_dim:
if (!member && node && node->parent &&
node->parent->type == XML_DOCUMENT_NODE) {
/* This happens when the user did: $sxe[]->foo = $value */
- php_error_docref(NULL, E_ERROR, "Cannot create unnamed attribute");
- return NULL;
+ zend_throw_error(NULL, "Cannot create unnamed attribute");
+ return &EG(uninitialized_zval);
}
}
@@ -330,7 +330,7 @@ long_dim:
}
if (sxe->iter.type == SXE_ITER_NONE) {
if (member && Z_LVAL_P(member) > 0) {
- php_error_docref(NULL, E_WARNING, "Cannot add element %s number %pd when only 0 such elements exist", mynode->name, Z_LVAL_P(member));
+ php_error_docref(NULL, E_WARNING, "Cannot add element %s number " ZEND_LONG_FMT " when only 0 such elements exist", mynode->name, Z_LVAL_P(member));
}
} else if (member) {
node = sxe_get_element_by_offset(sxe, Z_LVAL_P(member), node, &cnt);
@@ -341,7 +341,7 @@ long_dim:
_node_as_zval(sxe, node, rv, SXE_ITER_NONE, NULL, sxe->iter.nsprefix, sxe->iter.isprefix);
} else if (type == BP_VAR_W || type == BP_VAR_RW) {
if (member && cnt < Z_LVAL_P(member)) {
- php_error_docref(NULL, E_WARNING, "Cannot add element %s number %pd when only %pd such elements exist", mynode->name, Z_LVAL_P(member), cnt);
+ php_error_docref(NULL, E_WARNING, "Cannot add element %s number " ZEND_LONG_FMT " when only " ZEND_LONG_FMT " such elements exist", mynode->name, Z_LVAL_P(member), cnt);
}
node = xmlNewTextChild(mynode->parent, mynode->ns, mynode->name, NULL);
_node_as_zval(sxe, node, rv, SXE_ITER_NONE, NULL, sxe->iter.nsprefix, sxe->iter.isprefix);
@@ -466,7 +466,7 @@ static int sxe_prop_dim_write(zval *object, zval *member, zval *value, zend_bool
* and could also be E_PARSE, but we use this only during parsing
* and this is during runtime.
*/
- php_error_docref(NULL, E_ERROR, "Cannot create unnamed attribute");
+ zend_throw_error(NULL, "Cannot create unnamed attribute");
return FAILURE;
}
goto long_dim;
@@ -515,7 +515,7 @@ long_dim:
* and could also be E_PARSE, but we use this only during parsing
* and this is during runtime.
*/
- php_error_docref(NULL, E_ERROR, "Cannot create unnamed attribute");
+ zend_throw_error(NULL, "Cannot create unnamed attribute");
return FAILURE;
}
if (attribs && !node && sxe->iter.type == SXE_ITER_ELEMENT) {
@@ -588,7 +588,10 @@ long_dim:
if (elements) {
if (!member || Z_TYPE_P(member) == IS_LONG) {
if (node->type == XML_ATTRIBUTE_NODE) {
- php_error_docref(NULL, E_ERROR, "Cannot create duplicate attribute");
+ zend_throw_error(NULL, "Cannot create duplicate attribute");
+ if (new_value) {
+ zval_ptr_dtor(value);
+ }
return FAILURE;
}
@@ -596,7 +599,7 @@ long_dim:
newnode = node;
++counter;
if (member && Z_LVAL_P(member) > 0) {
- php_error_docref(NULL, E_WARNING, "Cannot add element %s number %pd when only 0 such elements exist", mynode->name, Z_LVAL_P(member));
+ php_error_docref(NULL, E_WARNING, "Cannot add element %s number " ZEND_LONG_FMT " when only 0 such elements exist", mynode->name, Z_LVAL_P(member));
retval = FAILURE;
}
} else if (member) {
@@ -644,14 +647,14 @@ next_iter:
}
} else if (!member || Z_TYPE_P(member) == IS_LONG) {
if (member && cnt < Z_LVAL_P(member)) {
- php_error_docref(NULL, E_WARNING, "Cannot add element %s number %pd when only %pd such elements exist", mynode->name, Z_LVAL_P(member), cnt);
+ php_error_docref(NULL, E_WARNING, "Cannot add element %s number " ZEND_LONG_FMT " when only " ZEND_LONG_FMT " such elements exist", mynode->name, Z_LVAL_P(member), cnt);
retval = FAILURE;
}
newnode = xmlNewTextChild(mynode->parent, mynode->ns, mynode->name, value ? (xmlChar *)Z_STRVAL_P(value) : NULL);
}
} else if (attribs) {
if (Z_TYPE_P(member) == IS_LONG) {
- php_error_docref(NULL, E_WARNING, "Cannot change attribute number %pd when only %d attributes exist", Z_LVAL_P(member), nodendx);
+ php_error_docref(NULL, E_WARNING, "Cannot change attribute number " ZEND_LONG_FMT " when only %d attributes exist", Z_LVAL_P(member), nodendx);
retval = FAILURE;
} else {
newnode = (xmlNodePtr)xmlNewProp(node, (xmlChar *)Z_STRVAL_P(member), value ? (xmlChar *)Z_STRVAL_P(value) : NULL);
@@ -713,7 +716,6 @@ static zval *sxe_property_get_adr(zval *object, zval *member, int fetch_type, vo
_node_as_zval(sxe, node, &ret, type, name, sxe->iter.nsprefix, sxe->iter.isprefix);
- sxe = Z_SXEOBJ_P(&ret);
if (!Z_ISUNDEF(sxe->tmp)) {
zval_ptr_dtor(&sxe->tmp);
}
@@ -1988,12 +1990,8 @@ static int sxe_count_elements(zval *object, zend_long *count) /* {{{ */
zval rv;
zend_call_method_with_0_params(object, intern->zo.ce, &intern->fptr_count, "count", &rv);
if (!Z_ISUNDEF(rv)) {
- if (!Z_ISUNDEF(intern->tmp)) {
- zval_ptr_dtor(&intern->tmp);
- }
- ZVAL_LONG(&intern->tmp, zval_get_long(&rv));
+ *count = zval_get_long(&rv);
zval_ptr_dtor(&rv);
- *count = Z_LVAL(intern->tmp);
return SUCCESS;
}
return FAILURE;
@@ -2053,7 +2051,9 @@ static zend_object_handlers sxe_object_handlers = { /* {{{ */
sxe_count_elements,
sxe_get_debug_info,
NULL,
- sxe_get_gc
+ sxe_get_gc,
+ NULL,
+ NULL
};
/* }}} */
@@ -2355,6 +2355,7 @@ zend_object_iterator_funcs php_sxe_iterator_funcs = { /* {{{ */
php_sxe_iterator_current_key,
php_sxe_iterator_move_forward,
php_sxe_iterator_rewind,
+ NULL
};
/* }}} */
diff --git a/ext/simplexml/sxe.c b/ext/simplexml/sxe.c
index 30b4526910..e1fbc63872 100644
--- a/ext/simplexml/sxe.c
+++ b/ext/simplexml/sxe.c
@@ -189,7 +189,7 @@ static const zend_function_entry funcs_SimpleXMLIterator[] = {
PHP_ME(ce_SimpleXMLIterator, next, arginfo_simplexmliterator__void, ZEND_ACC_PUBLIC)
PHP_ME(ce_SimpleXMLIterator, hasChildren, arginfo_simplexmliterator__void, ZEND_ACC_PUBLIC)
PHP_ME(ce_SimpleXMLIterator, getChildren, arginfo_simplexmliterator__void, ZEND_ACC_PUBLIC)
- {NULL, NULL, NULL}
+ PHP_FE_END
};
/* }}} */
diff --git a/ext/simplexml/tests/001-mb.phpt b/ext/simplexml/tests/001-mb.phpt
new file mode 100644
index 0000000000..ee4d6f085f
--- /dev/null
+++ b/ext/simplexml/tests/001-mb.phpt
@@ -0,0 +1,43 @@
+--TEST--
+SimpleXML: Simple document
+--SKIPIF--
+<?php if (!extension_loaded("simplexml")) print "skip"; ?>
+--FILE--
+<?php
+
+var_dump(simplexml_load_file(dirname(__FILE__).'/sxe私はガラスを食べられます.xml'));
+
+?>
+===DONE===
+--EXPECTF--
+object(SimpleXMLElement)#%d (2) {
+ ["@attributes"]=>
+ array(1) {
+ ["id"]=>
+ string(5) "elem1"
+ }
+ ["elem1"]=>
+ object(SimpleXMLElement)#%d (3) {
+ ["@attributes"]=>
+ array(1) {
+ ["attr1"]=>
+ string(5) "first"
+ }
+ ["comment"]=>
+ object(SimpleXMLElement)#%d (0) {
+ }
+ ["elem2"]=>
+ object(SimpleXMLElement)#%d (1) {
+ ["elem3"]=>
+ object(SimpleXMLElement)#%d (1) {
+ ["elem4"]=>
+ object(SimpleXMLElement)#%d (1) {
+ ["test"]=>
+ object(SimpleXMLElement)#%d (0) {
+ }
+ }
+ }
+ }
+ }
+}
+===DONE===
diff --git a/ext/simplexml/tests/012.phpt b/ext/simplexml/tests/012.phpt
index 2fc9bec41e..abbb10b8d3 100644
--- a/ext/simplexml/tests/012.phpt
+++ b/ext/simplexml/tests/012.phpt
@@ -37,4 +37,7 @@ Warning: main(): Cannot write or create unnamed attribute in %s012.php on line %
<?xml version="1.0" encoding="ISO-8859-1"?>
<foo attr="new value"/>
-Fatal error: main(): Cannot create unnamed attribute in %s012.php on line %d
+Fatal error: Uncaught Error: Cannot create unnamed attribute in %s012.php:%d
+Stack trace:
+#0 {main}
+ thrown in %s012.php on line %d
diff --git a/ext/simplexml/tests/sxe私はガラスを食べられます.xml b/ext/simplexml/tests/sxe私はガラスを食べられます.xml
new file mode 100644
index 0000000000..909b4e652c
--- /dev/null
+++ b/ext/simplexml/tests/sxe私はガラスを食べられます.xml
@@ -0,0 +1,17 @@
+<?xml version='1.0'?>
+<!DOCTYPE sxe SYSTEM "notfound.dtd" [
+<!ENTITY % incent SYSTEM "sxe.ent">
+%incent;
+]>
+<sxe id="elem1">
+ <elem1 attr1='first'>
+ <!-- comment -->
+ <elem2>
+ <elem3>
+ <elem4>
+ <?test processing instruction ?>
+ </elem4>
+ </elem3>
+ </elem2>
+ </elem1>
+</sxe> \ No newline at end of file
diff --git a/ext/snmp/config.w32 b/ext/snmp/config.w32
index 24622434c9..0848d5701e 100644
--- a/ext/snmp/config.w32
+++ b/ext/snmp/config.w32
@@ -4,10 +4,10 @@
ARG_WITH("snmp", "SNMP support", "no");
if (PHP_SNMP != "no") {
- if (CHECK_HEADER_ADD_INCLUDE("snmp.h", "CFLAGS_SNMP", PHP_PHP_BUILD + "\\include\\net-snmp;" + PHP_SNMP)) {
+ if (CHECK_HEADER_ADD_INCLUDE("snmp.h", "CFLAGS_SNMP", PHP_PHP_BUILD + "\\include\\net-snmp;" + PHP_SNMP) &&
+ SETUP_OPENSSL("snmp", PHP_SNMP) > 0) {
if (CHECK_LIB("netsnmp.lib", "snmp", PHP_SNMP)) {
EXTENSION('snmp', 'snmp.c');
- CHECK_LIB("libeay32.lib", "snmp", PHP_SNMP)
AC_DEFINE('HAVE_SNMP', 1);
AC_DEFINE("HAVE_NET_SNMP", 1);
} else {
diff --git a/ext/snmp/snmp.c b/ext/snmp/snmp.c
index 1605f0c6eb..cc6b4ea455 100644
--- a/ext/snmp/snmp.c
+++ b/ext/snmp/snmp.c
@@ -33,10 +33,7 @@
#include "php_snmp.h"
#include "zend_exceptions.h"
-
-#if HAVE_SPL
#include "ext/spl/spl_exceptions.h"
-#endif
#if HAVE_SNMP
@@ -1749,7 +1746,7 @@ PHP_FUNCTION(snmp_set_valueretrieval)
SNMP_G(valueretrieval) = method;
RETURN_TRUE;
} else {
- php_error_docref(NULL, E_WARNING, "Unknown SNMP value retrieval method '%pd'", method);
+ php_error_docref(NULL, E_WARNING, "Unknown SNMP value retrieval method '" ZEND_LONG_FMT "'", method);
RETURN_FALSE;
}
}
@@ -2082,12 +2079,11 @@ static HashTable *php_snmp_get_properties(zval *object)
HashTable *props;
zval rv;
zend_string *key;
- zend_ulong num_key;
obj = Z_SNMP_P(object);
props = zend_std_get_properties(object);
- ZEND_HASH_FOREACH_KEY_PTR(&php_snmp_properties, num_key, key, hnd) {
+ ZEND_HASH_FOREACH_STR_KEY_PTR(&php_snmp_properties, key, hnd) {
if (!hnd->read_func || hnd->read_func(obj, &rv) != SUCCESS) {
ZVAL_NULL(&rv);
}
@@ -2188,7 +2184,7 @@ static int php_snmp_write_max_oids(php_snmp_object *snmp_object, zval *newval)
if (Z_LVAL_P(newval) > 0) {
snmp_object->max_oids = Z_LVAL_P(newval);
} else {
- php_error_docref(NULL, E_WARNING, "max_oids should be positive integer or NULL, got %pd", Z_LVAL_P(newval));
+ php_error_docref(NULL, E_WARNING, "max_oids should be positive integer or NULL, got " ZEND_LONG_FMT, Z_LVAL_P(newval));
}
if (newval == &ztmp) {
@@ -2215,7 +2211,7 @@ static int php_snmp_write_valueretrieval(php_snmp_object *snmp_object, zval *new
if (Z_LVAL_P(newval) >= 0 && Z_LVAL_P(newval) <= (SNMP_VALUE_LIBRARY|SNMP_VALUE_PLAIN|SNMP_VALUE_OBJECT)) {
snmp_object->valueretrieval = Z_LVAL_P(newval);
} else {
- php_error_docref(NULL, E_WARNING, "Unknown SNMP value retrieval method '%pd'", Z_LVAL_P(newval));
+ php_error_docref(NULL, E_WARNING, "Unknown SNMP value retrieval method '" ZEND_LONG_FMT "'", Z_LVAL_P(newval));
ret = FAILURE;
}
@@ -2265,7 +2261,7 @@ static int php_snmp_write_oid_output_format(php_snmp_object *snmp_object, zval *
snmp_object->oid_output_format = Z_LVAL_P(newval);
break;
default:
- php_error_docref(NULL, E_WARNING, "Unknown SNMP output print format '%pd'", Z_LVAL_P(newval));
+ php_error_docref(NULL, E_WARNING, "Unknown SNMP output print format '" ZEND_LONG_FMT "'", Z_LVAL_P(newval));
ret = FAILURE;
break;
}
@@ -2417,11 +2413,7 @@ PHP_MINIT_FUNCTION(snmp)
/* Register SNMPException class */
INIT_CLASS_ENTRY(cex, "SNMPException", NULL);
-#ifdef HAVE_SPL
php_snmp_exception_ce = zend_register_internal_class_ex(&cex, spl_ce_RuntimeException);
-#else
- php_snmp_exception_ce = zend_register_internal_class_ex(&cex, zend_ce_exception);
-#endif
return SUCCESS;
}
@@ -2453,14 +2445,10 @@ PHP_MINFO_FUNCTION(snmp)
/* {{{ snmp_module_deps[]
*/
-#if ZEND_MODULE_API_NO >= 20050922
static const zend_module_dep snmp_module_deps[] = {
-#ifdef HAVE_SPL
ZEND_MOD_REQUIRED("spl")
-#endif
ZEND_MOD_END
};
-#endif
/* }}} */
/* {{{ snmp_module_entry
diff --git a/ext/soap/php_encoding.c b/ext/soap/php_encoding.c
index 47afe2703c..d5461f80d0 100644
--- a/ext/soap/php_encoding.c
+++ b/ext/soap/php_encoding.c
@@ -141,94 +141,94 @@ static void set_ns_and_type(xmlNodePtr node, encodeTypePtr type);
}
encode defaultEncoding[] = {
- {{UNKNOWN_TYPE, NULL, NULL, NULL}, guess_zval_convert, guess_xml_convert},
-
- {{IS_NULL, "nil", XSI_NAMESPACE, NULL}, to_zval_null, to_xml_null},
- {{IS_STRING, XSD_STRING_STRING, XSD_NAMESPACE, NULL}, to_zval_string, to_xml_string},
- {{IS_LONG, XSD_INT_STRING, XSD_NAMESPACE, NULL}, to_zval_long, to_xml_long},
- {{IS_DOUBLE, XSD_FLOAT_STRING, XSD_NAMESPACE, NULL}, to_zval_double, to_xml_double},
- {{IS_FALSE, XSD_BOOLEAN_STRING, XSD_NAMESPACE, NULL}, to_zval_bool, to_xml_bool},
- {{IS_TRUE, XSD_BOOLEAN_STRING, XSD_NAMESPACE, NULL}, to_zval_bool, to_xml_bool},
- {{IS_CONSTANT, XSD_STRING_STRING, XSD_NAMESPACE, NULL}, to_zval_string, to_xml_string},
- {{IS_ARRAY, SOAP_ENC_ARRAY_STRING, SOAP_1_1_ENC_NAMESPACE, NULL}, to_zval_array, guess_array_map},
- {{IS_OBJECT, SOAP_ENC_OBJECT_STRING, SOAP_1_1_ENC_NAMESPACE, NULL}, to_zval_object, to_xml_object},
- {{IS_ARRAY, SOAP_ENC_ARRAY_STRING, SOAP_1_2_ENC_NAMESPACE, NULL}, to_zval_array, guess_array_map},
- {{IS_OBJECT, SOAP_ENC_OBJECT_STRING, SOAP_1_2_ENC_NAMESPACE, NULL}, to_zval_object, to_xml_object},
-
- {{XSD_STRING, XSD_STRING_STRING, XSD_NAMESPACE, NULL}, to_zval_string, to_xml_string},
- {{XSD_BOOLEAN, XSD_BOOLEAN_STRING, XSD_NAMESPACE, NULL}, to_zval_bool, to_xml_bool},
- {{XSD_DECIMAL, XSD_DECIMAL_STRING, XSD_NAMESPACE, NULL}, to_zval_stringc, to_xml_string},
- {{XSD_FLOAT, XSD_FLOAT_STRING, XSD_NAMESPACE, NULL}, to_zval_double, to_xml_double},
- {{XSD_DOUBLE, XSD_DOUBLE_STRING, XSD_NAMESPACE, NULL}, to_zval_double, to_xml_double},
-
- {{XSD_DATETIME, XSD_DATETIME_STRING, XSD_NAMESPACE, NULL}, to_zval_stringc, to_xml_datetime},
- {{XSD_TIME, XSD_TIME_STRING, XSD_NAMESPACE, NULL}, to_zval_stringc, to_xml_time},
- {{XSD_DATE, XSD_DATE_STRING, XSD_NAMESPACE, NULL}, to_zval_stringc, to_xml_date},
- {{XSD_GYEARMONTH, XSD_GYEARMONTH_STRING, XSD_NAMESPACE, NULL}, to_zval_stringc, to_xml_gyearmonth},
- {{XSD_GYEAR, XSD_GYEAR_STRING, XSD_NAMESPACE, NULL}, to_zval_stringc, to_xml_gyear},
- {{XSD_GMONTHDAY, XSD_GMONTHDAY_STRING, XSD_NAMESPACE, NULL}, to_zval_stringc, to_xml_gmonthday},
- {{XSD_GDAY, XSD_GDAY_STRING, XSD_NAMESPACE, NULL}, to_zval_stringc, to_xml_gday},
- {{XSD_GMONTH, XSD_GMONTH_STRING, XSD_NAMESPACE, NULL}, to_zval_stringc, to_xml_gmonth},
- {{XSD_DURATION, XSD_DURATION_STRING, XSD_NAMESPACE, NULL}, to_zval_stringc, to_xml_duration},
-
- {{XSD_HEXBINARY, XSD_HEXBINARY_STRING, XSD_NAMESPACE, NULL}, to_zval_hexbin, to_xml_hexbin},
- {{XSD_BASE64BINARY, XSD_BASE64BINARY_STRING, XSD_NAMESPACE, NULL}, to_zval_base64, to_xml_base64},
-
- {{XSD_LONG, XSD_LONG_STRING, XSD_NAMESPACE, NULL}, to_zval_long, to_xml_long},
- {{XSD_INT, XSD_INT_STRING, XSD_NAMESPACE, NULL}, to_zval_long, to_xml_long},
- {{XSD_SHORT, XSD_SHORT_STRING, XSD_NAMESPACE, NULL}, to_zval_long, to_xml_long},
- {{XSD_BYTE, XSD_BYTE_STRING, XSD_NAMESPACE, NULL}, to_zval_long, to_xml_long},
- {{XSD_NONPOSITIVEINTEGER, XSD_NONPOSITIVEINTEGER_STRING, XSD_NAMESPACE, NULL}, to_zval_long, to_xml_long},
- {{XSD_POSITIVEINTEGER, XSD_POSITIVEINTEGER_STRING, XSD_NAMESPACE, NULL}, to_zval_long, to_xml_long},
- {{XSD_NONNEGATIVEINTEGER, XSD_NONNEGATIVEINTEGER_STRING, XSD_NAMESPACE, NULL}, to_zval_long, to_xml_long},
- {{XSD_NEGATIVEINTEGER, XSD_NEGATIVEINTEGER_STRING, XSD_NAMESPACE, NULL}, to_zval_long, to_xml_long},
- {{XSD_UNSIGNEDBYTE, XSD_UNSIGNEDBYTE_STRING, XSD_NAMESPACE, NULL}, to_zval_long, to_xml_long},
- {{XSD_UNSIGNEDSHORT, XSD_UNSIGNEDSHORT_STRING, XSD_NAMESPACE, NULL}, to_zval_long, to_xml_long},
- {{XSD_UNSIGNEDINT, XSD_UNSIGNEDINT_STRING, XSD_NAMESPACE, NULL}, to_zval_long, to_xml_long},
- {{XSD_UNSIGNEDLONG, XSD_UNSIGNEDLONG_STRING, XSD_NAMESPACE, NULL}, to_zval_long, to_xml_long},
- {{XSD_INTEGER, XSD_INTEGER_STRING, XSD_NAMESPACE, NULL}, to_zval_long, to_xml_long},
-
- {{XSD_ANYTYPE, XSD_ANYTYPE_STRING, XSD_NAMESPACE, NULL}, guess_zval_convert, guess_xml_convert},
- {{XSD_UR_TYPE, XSD_UR_TYPE_STRING, XSD_NAMESPACE, NULL}, guess_zval_convert, guess_xml_convert},
- {{XSD_ANYURI, XSD_ANYURI_STRING, XSD_NAMESPACE, NULL}, to_zval_stringc, to_xml_string},
- {{XSD_QNAME, XSD_QNAME_STRING, XSD_NAMESPACE, NULL}, to_zval_stringc, to_xml_string},
- {{XSD_NOTATION, XSD_NOTATION_STRING, XSD_NAMESPACE, NULL}, to_zval_stringc, to_xml_string},
- {{XSD_NORMALIZEDSTRING, XSD_NORMALIZEDSTRING_STRING, XSD_NAMESPACE, NULL}, to_zval_stringr, to_xml_string},
- {{XSD_TOKEN, XSD_TOKEN_STRING, XSD_NAMESPACE, NULL}, to_zval_stringc, to_xml_string},
- {{XSD_LANGUAGE, XSD_LANGUAGE_STRING, XSD_NAMESPACE, NULL}, to_zval_stringc, to_xml_string},
- {{XSD_NMTOKEN, XSD_NMTOKEN_STRING, XSD_NAMESPACE, NULL}, to_zval_stringc, to_xml_string},
- {{XSD_NMTOKENS, XSD_NMTOKENS_STRING, XSD_NAMESPACE, NULL}, to_zval_stringc, to_xml_list1},
- {{XSD_NAME, XSD_NAME_STRING, XSD_NAMESPACE, NULL}, to_zval_stringc, to_xml_string},
- {{XSD_NCNAME, XSD_NCNAME_STRING, XSD_NAMESPACE, NULL}, to_zval_stringc, to_xml_string},
- {{XSD_ID, XSD_ID_STRING, XSD_NAMESPACE, NULL}, to_zval_stringc, to_xml_string},
- {{XSD_IDREF, XSD_IDREF_STRING, XSD_NAMESPACE, NULL}, to_zval_stringc, to_xml_string},
- {{XSD_IDREFS, XSD_IDREFS_STRING, XSD_NAMESPACE, NULL}, to_zval_stringc, to_xml_list1},
- {{XSD_ENTITY, XSD_ENTITY_STRING, XSD_NAMESPACE, NULL}, to_zval_stringc, to_xml_string},
- {{XSD_ENTITIES, XSD_ENTITIES_STRING, XSD_NAMESPACE, NULL}, to_zval_stringc, to_xml_list1},
-
- {{APACHE_MAP, APACHE_MAP_STRING, APACHE_NAMESPACE, NULL}, to_zval_map, to_xml_map},
-
- {{SOAP_ENC_OBJECT, SOAP_ENC_OBJECT_STRING, SOAP_1_1_ENC_NAMESPACE, NULL}, to_zval_object, to_xml_object},
- {{SOAP_ENC_ARRAY, SOAP_ENC_ARRAY_STRING, SOAP_1_1_ENC_NAMESPACE, NULL}, to_zval_array, to_xml_array},
- {{SOAP_ENC_OBJECT, SOAP_ENC_OBJECT_STRING, SOAP_1_2_ENC_NAMESPACE, NULL}, to_zval_object, to_xml_object},
- {{SOAP_ENC_ARRAY, SOAP_ENC_ARRAY_STRING, SOAP_1_2_ENC_NAMESPACE, NULL}, to_zval_array, to_xml_array},
+ {{UNKNOWN_TYPE, NULL, NULL, NULL, NULL}, guess_zval_convert, guess_xml_convert},
+
+ {{IS_NULL, "nil", XSI_NAMESPACE, NULL, NULL}, to_zval_null, to_xml_null},
+ {{IS_STRING, XSD_STRING_STRING, XSD_NAMESPACE, NULL, NULL}, to_zval_string, to_xml_string},
+ {{IS_LONG, XSD_INT_STRING, XSD_NAMESPACE, NULL, NULL}, to_zval_long, to_xml_long},
+ {{IS_DOUBLE, XSD_FLOAT_STRING, XSD_NAMESPACE, NULL, NULL}, to_zval_double, to_xml_double},
+ {{IS_FALSE, XSD_BOOLEAN_STRING, XSD_NAMESPACE, NULL, NULL}, to_zval_bool, to_xml_bool},
+ {{IS_TRUE, XSD_BOOLEAN_STRING, XSD_NAMESPACE, NULL, NULL}, to_zval_bool, to_xml_bool},
+ {{IS_CONSTANT, XSD_STRING_STRING, XSD_NAMESPACE, NULL, NULL}, to_zval_string, to_xml_string},
+ {{IS_ARRAY, SOAP_ENC_ARRAY_STRING, SOAP_1_1_ENC_NAMESPACE, NULL, NULL}, to_zval_array, guess_array_map},
+ {{IS_OBJECT, SOAP_ENC_OBJECT_STRING, SOAP_1_1_ENC_NAMESPACE, NULL, NULL}, to_zval_object, to_xml_object},
+ {{IS_ARRAY, SOAP_ENC_ARRAY_STRING, SOAP_1_2_ENC_NAMESPACE, NULL, NULL}, to_zval_array, guess_array_map},
+ {{IS_OBJECT, SOAP_ENC_OBJECT_STRING, SOAP_1_2_ENC_NAMESPACE, NULL, NULL}, to_zval_object, to_xml_object},
+
+ {{XSD_STRING, XSD_STRING_STRING, XSD_NAMESPACE, NULL, NULL}, to_zval_string, to_xml_string},
+ {{XSD_BOOLEAN, XSD_BOOLEAN_STRING, XSD_NAMESPACE, NULL, NULL}, to_zval_bool, to_xml_bool},
+ {{XSD_DECIMAL, XSD_DECIMAL_STRING, XSD_NAMESPACE, NULL, NULL}, to_zval_stringc, to_xml_string},
+ {{XSD_FLOAT, XSD_FLOAT_STRING, XSD_NAMESPACE, NULL, NULL}, to_zval_double, to_xml_double},
+ {{XSD_DOUBLE, XSD_DOUBLE_STRING, XSD_NAMESPACE, NULL, NULL}, to_zval_double, to_xml_double},
+
+ {{XSD_DATETIME, XSD_DATETIME_STRING, XSD_NAMESPACE, NULL, NULL}, to_zval_stringc, to_xml_datetime},
+ {{XSD_TIME, XSD_TIME_STRING, XSD_NAMESPACE, NULL, NULL}, to_zval_stringc, to_xml_time},
+ {{XSD_DATE, XSD_DATE_STRING, XSD_NAMESPACE, NULL, NULL}, to_zval_stringc, to_xml_date},
+ {{XSD_GYEARMONTH, XSD_GYEARMONTH_STRING, XSD_NAMESPACE, NULL, NULL}, to_zval_stringc, to_xml_gyearmonth},
+ {{XSD_GYEAR, XSD_GYEAR_STRING, XSD_NAMESPACE, NULL, NULL}, to_zval_stringc, to_xml_gyear},
+ {{XSD_GMONTHDAY, XSD_GMONTHDAY_STRING, XSD_NAMESPACE, NULL, NULL}, to_zval_stringc, to_xml_gmonthday},
+ {{XSD_GDAY, XSD_GDAY_STRING, XSD_NAMESPACE, NULL, NULL}, to_zval_stringc, to_xml_gday},
+ {{XSD_GMONTH, XSD_GMONTH_STRING, XSD_NAMESPACE, NULL, NULL}, to_zval_stringc, to_xml_gmonth},
+ {{XSD_DURATION, XSD_DURATION_STRING, XSD_NAMESPACE, NULL, NULL}, to_zval_stringc, to_xml_duration},
+
+ {{XSD_HEXBINARY, XSD_HEXBINARY_STRING, XSD_NAMESPACE, NULL, NULL}, to_zval_hexbin, to_xml_hexbin},
+ {{XSD_BASE64BINARY, XSD_BASE64BINARY_STRING, XSD_NAMESPACE, NULL, NULL}, to_zval_base64, to_xml_base64},
+
+ {{XSD_LONG, XSD_LONG_STRING, XSD_NAMESPACE, NULL, NULL}, to_zval_long, to_xml_long},
+ {{XSD_INT, XSD_INT_STRING, XSD_NAMESPACE, NULL, NULL}, to_zval_long, to_xml_long},
+ {{XSD_SHORT, XSD_SHORT_STRING, XSD_NAMESPACE, NULL, NULL}, to_zval_long, to_xml_long},
+ {{XSD_BYTE, XSD_BYTE_STRING, XSD_NAMESPACE, NULL, NULL}, to_zval_long, to_xml_long},
+ {{XSD_NONPOSITIVEINTEGER, XSD_NONPOSITIVEINTEGER_STRING, XSD_NAMESPACE, NULL, NULL}, to_zval_long, to_xml_long},
+ {{XSD_POSITIVEINTEGER, XSD_POSITIVEINTEGER_STRING, XSD_NAMESPACE, NULL, NULL}, to_zval_long, to_xml_long},
+ {{XSD_NONNEGATIVEINTEGER, XSD_NONNEGATIVEINTEGER_STRING, XSD_NAMESPACE, NULL, NULL}, to_zval_long, to_xml_long},
+ {{XSD_NEGATIVEINTEGER, XSD_NEGATIVEINTEGER_STRING, XSD_NAMESPACE, NULL, NULL}, to_zval_long, to_xml_long},
+ {{XSD_UNSIGNEDBYTE, XSD_UNSIGNEDBYTE_STRING, XSD_NAMESPACE, NULL, NULL}, to_zval_long, to_xml_long},
+ {{XSD_UNSIGNEDSHORT, XSD_UNSIGNEDSHORT_STRING, XSD_NAMESPACE, NULL, NULL}, to_zval_long, to_xml_long},
+ {{XSD_UNSIGNEDINT, XSD_UNSIGNEDINT_STRING, XSD_NAMESPACE, NULL, NULL}, to_zval_long, to_xml_long},
+ {{XSD_UNSIGNEDLONG, XSD_UNSIGNEDLONG_STRING, XSD_NAMESPACE, NULL, NULL}, to_zval_long, to_xml_long},
+ {{XSD_INTEGER, XSD_INTEGER_STRING, XSD_NAMESPACE, NULL, NULL}, to_zval_long, to_xml_long},
+
+ {{XSD_ANYTYPE, XSD_ANYTYPE_STRING, XSD_NAMESPACE, NULL, NULL}, guess_zval_convert, guess_xml_convert},
+ {{XSD_UR_TYPE, XSD_UR_TYPE_STRING, XSD_NAMESPACE, NULL, NULL}, guess_zval_convert, guess_xml_convert},
+ {{XSD_ANYURI, XSD_ANYURI_STRING, XSD_NAMESPACE, NULL, NULL}, to_zval_stringc, to_xml_string},
+ {{XSD_QNAME, XSD_QNAME_STRING, XSD_NAMESPACE, NULL, NULL}, to_zval_stringc, to_xml_string},
+ {{XSD_NOTATION, XSD_NOTATION_STRING, XSD_NAMESPACE, NULL, NULL}, to_zval_stringc, to_xml_string},
+ {{XSD_NORMALIZEDSTRING, XSD_NORMALIZEDSTRING_STRING, XSD_NAMESPACE, NULL, NULL}, to_zval_stringr, to_xml_string},
+ {{XSD_TOKEN, XSD_TOKEN_STRING, XSD_NAMESPACE, NULL, NULL}, to_zval_stringc, to_xml_string},
+ {{XSD_LANGUAGE, XSD_LANGUAGE_STRING, XSD_NAMESPACE, NULL, NULL}, to_zval_stringc, to_xml_string},
+ {{XSD_NMTOKEN, XSD_NMTOKEN_STRING, XSD_NAMESPACE, NULL, NULL}, to_zval_stringc, to_xml_string},
+ {{XSD_NMTOKENS, XSD_NMTOKENS_STRING, XSD_NAMESPACE, NULL, NULL}, to_zval_stringc, to_xml_list1},
+ {{XSD_NAME, XSD_NAME_STRING, XSD_NAMESPACE, NULL, NULL}, to_zval_stringc, to_xml_string},
+ {{XSD_NCNAME, XSD_NCNAME_STRING, XSD_NAMESPACE, NULL, NULL}, to_zval_stringc, to_xml_string},
+ {{XSD_ID, XSD_ID_STRING, XSD_NAMESPACE, NULL, NULL}, to_zval_stringc, to_xml_string},
+ {{XSD_IDREF, XSD_IDREF_STRING, XSD_NAMESPACE, NULL, NULL}, to_zval_stringc, to_xml_string},
+ {{XSD_IDREFS, XSD_IDREFS_STRING, XSD_NAMESPACE, NULL, NULL}, to_zval_stringc, to_xml_list1},
+ {{XSD_ENTITY, XSD_ENTITY_STRING, XSD_NAMESPACE, NULL, NULL}, to_zval_stringc, to_xml_string},
+ {{XSD_ENTITIES, XSD_ENTITIES_STRING, XSD_NAMESPACE, NULL, NULL}, to_zval_stringc, to_xml_list1},
+
+ {{APACHE_MAP, APACHE_MAP_STRING, APACHE_NAMESPACE, NULL, NULL}, to_zval_map, to_xml_map},
+
+ {{SOAP_ENC_OBJECT, SOAP_ENC_OBJECT_STRING, SOAP_1_1_ENC_NAMESPACE, NULL, NULL}, to_zval_object, to_xml_object},
+ {{SOAP_ENC_ARRAY, SOAP_ENC_ARRAY_STRING, SOAP_1_1_ENC_NAMESPACE, NULL, NULL}, to_zval_array, to_xml_array},
+ {{SOAP_ENC_OBJECT, SOAP_ENC_OBJECT_STRING, SOAP_1_2_ENC_NAMESPACE, NULL, NULL}, to_zval_object, to_xml_object},
+ {{SOAP_ENC_ARRAY, SOAP_ENC_ARRAY_STRING, SOAP_1_2_ENC_NAMESPACE, NULL, NULL}, to_zval_array, to_xml_array},
/* support some of the 1999 data types */
- {{XSD_STRING, XSD_STRING_STRING, XSD_1999_NAMESPACE, NULL}, to_zval_string, to_xml_string},
- {{XSD_BOOLEAN, XSD_BOOLEAN_STRING, XSD_1999_NAMESPACE, NULL}, to_zval_bool, to_xml_bool},
- {{XSD_DECIMAL, XSD_DECIMAL_STRING, XSD_1999_NAMESPACE, NULL}, to_zval_stringc, to_xml_string},
- {{XSD_FLOAT, XSD_FLOAT_STRING, XSD_1999_NAMESPACE, NULL}, to_zval_double, to_xml_double},
- {{XSD_DOUBLE, XSD_DOUBLE_STRING, XSD_1999_NAMESPACE, NULL}, to_zval_double, to_xml_double},
+ {{XSD_STRING, XSD_STRING_STRING, XSD_1999_NAMESPACE, NULL, NULL}, to_zval_string, to_xml_string},
+ {{XSD_BOOLEAN, XSD_BOOLEAN_STRING, XSD_1999_NAMESPACE, NULL, NULL}, to_zval_bool, to_xml_bool},
+ {{XSD_DECIMAL, XSD_DECIMAL_STRING, XSD_1999_NAMESPACE, NULL, NULL}, to_zval_stringc, to_xml_string},
+ {{XSD_FLOAT, XSD_FLOAT_STRING, XSD_1999_NAMESPACE, NULL, NULL}, to_zval_double, to_xml_double},
+ {{XSD_DOUBLE, XSD_DOUBLE_STRING, XSD_1999_NAMESPACE, NULL, NULL}, to_zval_double, to_xml_double},
- {{XSD_LONG, XSD_LONG_STRING, XSD_1999_NAMESPACE, NULL}, to_zval_long, to_xml_long},
- {{XSD_INT, XSD_INT_STRING, XSD_1999_NAMESPACE, NULL}, to_zval_long, to_xml_long},
- {{XSD_SHORT, XSD_SHORT_STRING, XSD_1999_NAMESPACE, NULL}, to_zval_long, to_xml_long},
- {{XSD_BYTE, XSD_BYTE_STRING, XSD_1999_NAMESPACE, NULL}, to_zval_long, to_xml_long},
- {{XSD_1999_TIMEINSTANT, XSD_1999_TIMEINSTANT_STRING, XSD_1999_NAMESPACE, NULL}, to_zval_stringc, to_xml_string},
+ {{XSD_LONG, XSD_LONG_STRING, XSD_1999_NAMESPACE, NULL, NULL}, to_zval_long, to_xml_long},
+ {{XSD_INT, XSD_INT_STRING, XSD_1999_NAMESPACE, NULL, NULL}, to_zval_long, to_xml_long},
+ {{XSD_SHORT, XSD_SHORT_STRING, XSD_1999_NAMESPACE, NULL, NULL}, to_zval_long, to_xml_long},
+ {{XSD_BYTE, XSD_BYTE_STRING, XSD_1999_NAMESPACE, NULL, NULL}, to_zval_long, to_xml_long},
+ {{XSD_1999_TIMEINSTANT, XSD_1999_TIMEINSTANT_STRING, XSD_1999_NAMESPACE, NULL, NULL}, to_zval_stringc, to_xml_string},
- {{XSD_ANYXML, "<anyXML>", "<anyXML>", NULL}, to_zval_any, to_xml_any},
+ {{XSD_ANYXML, "<anyXML>", "<anyXML>", NULL, NULL}, to_zval_any, to_xml_any},
- {{END_KNOWN_TYPES, NULL, NULL, NULL}, guess_zval_convert, guess_xml_convert}
+ {{END_KNOWN_TYPES, NULL, NULL, NULL, NULL}, guess_zval_convert, guess_xml_convert}
};
int numDefaultEncodings = sizeof(defaultEncoding)/sizeof(encode);
@@ -799,7 +799,7 @@ static zval *to_zval_base64(zval *ret, encodeTypePtr type, xmlNodePtr data)
static zval *to_zval_hexbin(zval *ret, encodeTypePtr type, xmlNodePtr data)
{
zend_string *str;
- int i, j;
+ size_t i, j;
unsigned char c;
ZVAL_NULL(ret);
@@ -960,7 +960,7 @@ static xmlNodePtr to_xml_hexbin(encodeTypePtr type, zval *data, int style, xmlNo
xmlNodePtr ret, text;
unsigned char *str;
zval tmp;
- int i, j;
+ size_t i, j;
ret = xmlNewNode(NULL, BAD_CAST("BOGUS"));
xmlAddChild(parent, ret);
@@ -1179,13 +1179,8 @@ static xmlNodePtr to_xml_null(encodeTypePtr type, zval *data, int style, xmlNode
static void set_zval_property(zval* object, char* name, zval* val)
{
- zend_class_entry *old_scope;
-
- old_scope = EG(scope);
- EG(scope) = Z_OBJCE_P(object);
- add_property_zval(object, name, val);
+ zend_update_property(Z_OBJCE_P(object), object, name, strlen(name), val);
if (Z_REFCOUNTED_P(val)) Z_DELREF_P(val);
- EG(scope) = old_scope;
}
static zval* get_zval_property(zval* object, char* name, zval *rv)
@@ -1196,15 +1191,15 @@ static zval* get_zval_property(zval* object, char* name, zval *rv)
zend_class_entry *old_scope;
ZVAL_STRING(&member, name);
- old_scope = EG(scope);
- EG(scope) = Z_OBJCE_P(object);
+ old_scope = EG(fake_scope);
+ EG(fake_scope) = Z_OBJCE_P(object);
data = Z_OBJ_HT_P(object)->read_property(object, &member, BP_VAR_IS, NULL, rv);
if (data == &EG(uninitialized_zval)) {
/* Hack for bug #32455 */
zend_property_info *property_info;
property_info = zend_get_property_info(Z_OBJCE_P(object), Z_STR(member), 1);
- EG(scope) = old_scope;
+ EG(fake_scope) = old_scope;
if (property_info != ZEND_WRONG_PROPERTY_INFO && property_info &&
zend_hash_exists(Z_OBJPROP_P(object), property_info->name)) {
zval_ptr_dtor(&member);
@@ -1215,7 +1210,7 @@ static zval* get_zval_property(zval* object, char* name, zval *rv)
return NULL;
}
zval_ptr_dtor(&member);
- EG(scope) = old_scope;
+ EG(fake_scope) = old_scope;
ZVAL_DEREF(data);
return data;
} else if (Z_TYPE_P(object) == IS_ARRAY) {
@@ -1231,10 +1226,10 @@ static void unset_zval_property(zval* object, char* name)
zend_class_entry *old_scope;
ZVAL_STRING(&member, name);
- old_scope = EG(scope);
- EG(scope) = Z_OBJCE_P(object);
+ old_scope = EG(fake_scope);
+ EG(fake_scope) = Z_OBJCE_P(object);
Z_OBJ_HT_P(object)->unset_property(object, &member, NULL);
- EG(scope) = old_scope;
+ EG(fake_scope) = old_scope;
zval_ptr_dtor(&member);
} else if (Z_TYPE_P(object) == IS_ARRAY) {
zend_hash_str_del(Z_ARRVAL_P(object), name, strlen(name));
@@ -2451,7 +2446,7 @@ iterator_done:
smart_str_0(&array_type);
set_ns_prop(xmlParam, SOAP_1_1_ENC_NAMESPACE, "arrayType", ZSTR_VAL(array_type.s));
} else {
- int i = 0;
+ size_t i = 0;
while (i < ZSTR_LEN(array_size.s)) {
if (ZSTR_VAL(array_size.s)[i] == ',') {ZSTR_VAL(array_size.s)[i] = ' ';}
++i;
@@ -2903,7 +2898,7 @@ static xmlNodePtr to_xml_datetime_ex(encodeTypePtr type, zval *data, char *forma
ta = php_localtime_r(&timestamp, &tmbuf);
/*ta = php_gmtime_r(&timestamp, &tmbuf);*/
if (!ta) {
- soap_error1(E_ERROR, "Encoding: Invalid timestamp %pd", Z_LVAL_P(data));
+ soap_error1(E_ERROR, "Encoding: Invalid timestamp " ZEND_LONG_FMT, Z_LVAL_P(data));
}
buf = (char *) emalloc(buf_len);
@@ -3502,7 +3497,11 @@ static int is_map(zval *array)
{
zend_ulong index;
zend_string *key;
- int i = 0;
+ zend_ulong i = 0;
+
+ if (HT_IS_PACKED(Z_ARRVAL_P(array)) && HT_IS_WITHOUT_HOLES(Z_ARRVAL_P(array))) {
+ return FALSE;
+ }
ZEND_HASH_FOREACH_KEY(Z_ARRVAL_P(array), index, key) {
if (key || index != i) {
diff --git a/ext/soap/php_http.c b/ext/soap/php_http.c
index 3a890d7c36..c0b5f61216 100644
--- a/ext/soap/php_http.c
+++ b/ext/soap/php_http.c
@@ -22,7 +22,7 @@
#include "php_soap.h"
#include "ext/standard/base64.h"
#include "ext/standard/md5.h"
-#include "ext/standard/php_rand.h"
+#include "ext/standard/php_random.h"
static char *get_http_header_value(char *headers, char *type);
static zend_string *get_http_body(php_stream *socketd, int close, char *headers);
@@ -343,7 +343,7 @@ int make_http_soap_request(zval *this_ptr,
zend_string *request;
smart_str soap_headers = {0};
smart_str soap_headers_z = {0};
- int err;
+ size_t err;
php_url *phpurl = NULL;
php_stream *stream;
zval *trace, *tmp;
@@ -646,11 +646,15 @@ try_again:
if ((digest = zend_hash_str_find(Z_OBJPROP_P(this_ptr), "_digest", sizeof("_digest")-1)) != NULL) {
if (Z_TYPE_P(digest) == IS_ARRAY) {
char HA1[33], HA2[33], response[33], cnonce[33], nc[9];
+ zend_long nonce;
PHP_MD5_CTX md5ctx;
unsigned char hash[16];
+ php_random_bytes_throw(&nonce, sizeof(nonce));
+ nonce &= 0x7fffffff;
+
PHP_MD5Init(&md5ctx);
- snprintf(cnonce, sizeof(cnonce), ZEND_LONG_FMT, php_rand());
+ snprintf(cnonce, sizeof(cnonce), ZEND_LONG_FMT, nonce);
PHP_MD5Update(&md5ctx, (unsigned char*)cnonce, strlen(cnonce));
PHP_MD5Final(hash, &md5ctx);
make_digest(cnonce, hash);
@@ -658,7 +662,7 @@ try_again:
if ((tmp = zend_hash_str_find(Z_ARRVAL_P(digest), "nc", sizeof("nc")-1)) != NULL &&
Z_TYPE_P(tmp) == IS_LONG) {
Z_LVAL_P(tmp)++;
- snprintf(nc, sizeof(nc), "%08ld", Z_LVAL_P(tmp));
+ snprintf(nc, sizeof(nc), "%08" ZEND_LONG_FMT_SPEC, Z_LVAL_P(tmp));
} else {
add_assoc_long(digest, "nc", 1);
strcpy(nc, "00000001");
@@ -987,10 +991,10 @@ try_again:
sempos = strstr(options, ";");
if (strstr(options,"path=") == options) {
eqpos = options + sizeof("path=")-1;
- add_index_stringl(&zcookie, 1, eqpos, sempos?(sempos-eqpos):strlen(eqpos));
+ add_index_stringl(&zcookie, 1, eqpos, sempos?(size_t)(sempos-eqpos):strlen(eqpos));
} else if (strstr(options,"domain=") == options) {
eqpos = options + sizeof("domain=")-1;
- add_index_stringl(&zcookie, 2, eqpos, sempos?(sempos-eqpos):strlen(eqpos));
+ add_index_stringl(&zcookie, 2, eqpos, sempos?(size_t)(sempos-eqpos):strlen(eqpos));
} else if (strstr(options,"secure") == options) {
add_index_bool(&zcookie, 3, 1);
}
diff --git a/ext/soap/php_packet_soap.c b/ext/soap/php_packet_soap.c
index 0f347edc5e..128e02a314 100644
--- a/ext/soap/php_packet_soap.c
+++ b/ext/soap/php_packet_soap.c
@@ -385,7 +385,7 @@ int parse_packet_soap(zval *this_ptr, char *buffer, int buffer_size, sdlFunction
} else {
zend_refcounted *garbage = Z_COUNTED_P(return_value);
ZVAL_COPY(return_value, tmp);
- _zval_dtor_func(garbage ZEND_FILE_LINE_CC);
+ zval_dtor_func(garbage);
}
}
}
diff --git a/ext/soap/php_sdl.c b/ext/soap/php_sdl.c
index c53fa8a758..b8f1911f69 100644
--- a/ext/soap/php_sdl.c
+++ b/ext/soap/php_sdl.c
@@ -177,7 +177,7 @@ encodePtr get_encoder_ex(sdlPtr sdl, const char *nscat, int len)
return NULL;
}
-sdlBindingPtr get_binding_from_type(sdlPtr sdl, int type)
+sdlBindingPtr get_binding_from_type(sdlPtr sdl, sdlBindingType type)
{
sdlBindingPtr binding;
@@ -227,7 +227,7 @@ static int is_wsdl_element(xmlNodePtr node)
void sdl_set_uri_credentials(sdlCtx *ctx, char *uri)
{
char *s;
- int l1, l2;
+ size_t l1, l2;
zval context;
zval *header = NULL;
@@ -235,11 +235,11 @@ void sdl_set_uri_credentials(sdlCtx *ctx, char *uri)
s = strstr(ctx->sdl->source, "://");
if (!s) return;
s = strchr(s+3, '/');
- l1 = s ? (s - ctx->sdl->source) : strlen(ctx->sdl->source);
+ l1 = s ? (size_t)(s - ctx->sdl->source) : strlen(ctx->sdl->source);
s = strstr((char*)uri, "://");
if (!s) return;
s = strchr(s+3, '/');
- l2 = s ? (s - (char*)uri) : strlen((char*)uri);
+ l2 = s ? (size_t)(s - (char*)uri) : strlen((char*)uri);
if (l1 != l2) {
/* check for http://...:80/ */
if (l1 > 11 &&
@@ -3156,7 +3156,7 @@ sdlPtr get_sdl(zval *this_ptr, char *uri, zend_long cache_wsdl)
char fn[MAXPATHLEN];
sdlPtr sdl = NULL;
char* old_error_code = SOAP_GLOBAL(error_code);
- int uri_len = 0;
+ size_t uri_len = 0;
php_stream_context *context=NULL;
zval *tmp, *proxy_host, *proxy_port, orig_context, new_context;
smart_str headers = {0};
@@ -3339,7 +3339,7 @@ cache_in_memory:
SOAP_GLOBAL(mem_cache) = malloc(sizeof(HashTable));
zend_hash_init(SOAP_GLOBAL(mem_cache), 0, NULL, delete_psdl, 1);
} else if (SOAP_GLOBAL(cache_limit) > 0 &&
- SOAP_GLOBAL(cache_limit) <= zend_hash_num_elements(SOAP_GLOBAL(mem_cache))) {
+ SOAP_GLOBAL(cache_limit) <= (zend_long)zend_hash_num_elements(SOAP_GLOBAL(mem_cache))) {
/* in-memory cache overflow */
sdl_cache_bucket *q;
time_t latest = t;
diff --git a/ext/soap/php_sdl.h b/ext/soap/php_sdl.h
index 0678daec2d..2c3aeb4ec2 100644
--- a/ext/soap/php_sdl.h
+++ b/ext/soap/php_sdl.h
@@ -260,7 +260,7 @@ encodePtr get_encoder_from_prefix(sdlPtr sdl, xmlNodePtr data, const xmlChar *ty
encodePtr get_encoder(sdlPtr sdl, const char *ns, const char *type);
encodePtr get_encoder_ex(sdlPtr sdl, const char *nscat, int len);
-sdlBindingPtr get_binding_from_type(sdlPtr sdl, int type);
+sdlBindingPtr get_binding_from_type(sdlPtr sdl, sdlBindingType type);
sdlBindingPtr get_binding_from_name(sdlPtr sdl, char *name, char *ns);
void delete_sdl(void *handle);
diff --git a/ext/soap/soap.c b/ext/soap/soap.c
index 1751f4d2f9..1f857590cd 100644
--- a/ext/soap/soap.c
+++ b/ext/soap/soap.c
@@ -955,9 +955,7 @@ PHP_METHOD(SoapFault, __toString)
line = zend_read_property(soap_fault_class_entry, this_ptr, "line", sizeof("line")-1, 1, &rv4);
fci.size = sizeof(fci);
- fci.function_table = &Z_OBJCE_P(getThis())->function_table;
ZVAL_STRINGL(&fci.function_name, "gettraceasstring", sizeof("gettraceasstring")-1);
- fci.symbol_table = NULL;
fci.object = Z_OBJ(EX(This));
fci.retval = &trace;
fci.param_count = 0;
@@ -974,7 +972,7 @@ PHP_METHOD(SoapFault, __toString)
line_val = zval_get_long(line);
convert_to_string(&trace);
- str = strpprintf(0, "SoapFault exception: [%s] %s in %s:%pd\nStack trace:\n%s",
+ str = strpprintf(0, "SoapFault exception: [%s] %s in %s:" ZEND_LONG_FMT "\nStack trace:\n%s",
ZSTR_VAL(faultcode_val), ZSTR_VAL(faultstring_val), ZSTR_VAL(file_val), line_val,
Z_STRLEN(trace) ? Z_STRVAL(trace) : "#0 {main}\n");
@@ -1280,7 +1278,7 @@ PHP_METHOD(SoapServer, setPersistence)
value == SOAP_PERSISTENCE_REQUEST) {
service->soap_class.persistence = value;
} else {
- php_error_docref(NULL, E_WARNING, "Tried to set persistence with bogus value (%pd)", value);
+ php_error_docref(NULL, E_WARNING, "Tried to set persistence with bogus value (" ZEND_LONG_FMT ")", value);
return;
}
} else {
@@ -2164,7 +2162,7 @@ static void soap_error_handler(int error_num, const char *error_filename, const
zval fault;
char* code = SOAP_GLOBAL(error_code);
char buffer[1024];
- int buffer_len;
+ size_t buffer_len;
#ifdef va_copy
va_list argcopy;
#endif
@@ -2179,7 +2177,7 @@ static void soap_error_handler(int error_num, const char *error_filename, const
buffer_len = vslprintf(buffer, sizeof(buffer)-1, format, args);
#endif
buffer[sizeof(buffer)-1]=0;
- if (buffer_len > sizeof(buffer) - 1 || buffer_len < 0) {
+ if (buffer_len > sizeof(buffer) - 1 || buffer_len == (size_t)-1) {
buffer_len = sizeof(buffer) - 1;
}
@@ -2245,7 +2243,7 @@ static void soap_error_handler(int error_num, const char *error_filename, const
!service->send_errors) {
strcpy(buffer, "Internal Error");
} else {
- int buffer_len;
+ size_t buffer_len;
zval outbuflen;
#ifdef va_copy
@@ -2256,7 +2254,7 @@ static void soap_error_handler(int error_num, const char *error_filename, const
buffer_len = vslprintf(buffer, sizeof(buffer)-1, format, args);
#endif
buffer[sizeof(buffer)-1]=0;
- if (buffer_len > sizeof(buffer) - 1 || buffer_len < 0) {
+ if (buffer_len > sizeof(buffer) - 1 || buffer_len == (size_t)-1) {
buffer_len = sizeof(buffer) - 1;
}
diff --git a/ext/soap/tests/bugs/bug38005.phpt b/ext/soap/tests/bugs/bug38005.phpt
index 6a4fb2580b..219696c263 100644
--- a/ext/soap/tests/bugs/bug38005.phpt
+++ b/ext/soap/tests/bugs/bug38005.phpt
@@ -6,7 +6,7 @@ Bug #38005 (SoapFault faultstring doesn't follow encoding rules)
soap.wsdl_cache_enabled=0
--FILE--
<?php
-function Test($param) {
+function Test($param=NULL) {
return new SoapFault('Test', 'This is our fault: ');
}
diff --git a/ext/soap/tests/soap12/soap12-test.inc b/ext/soap/tests/soap12/soap12-test.inc
index fbdc855a7e..e27712241f 100644
--- a/ext/soap/tests/soap12/soap12-test.inc
+++ b/ext/soap/tests/soap12/soap12-test.inc
@@ -90,7 +90,7 @@ class Soap12test {
return count($input);
}
- function isNil($input) {
+ function isNil($input=NULL) {
return is_null($input);
}
diff --git a/ext/sockets/conversions.c b/ext/sockets/conversions.c
index f2621f65d0..46286a468d 100644
--- a/ext/sockets/conversions.c
+++ b/ext/sockets/conversions.c
@@ -225,7 +225,7 @@ static unsigned from_array_iterate(const zval *arr,
/* Note i starts at 1, not 0! */
i = 1;
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(arr), elem) {
- if (snprintf(buf, sizeof(buf), "element #%u", i) >= sizeof(buf)) {
+ if ((size_t)snprintf(buf, sizeof(buf), "element #%u", i) >= sizeof(buf)) {
memcpy(buf, "element", sizeof("element"));
}
zend_llist_add_element(&ctx->keys, &bufp);
@@ -656,7 +656,7 @@ static void from_zval_write_sun_path(const zval *path, char *sockaddr_un_c, ser_
}
if (ZSTR_LEN(path_str) >= sizeof(saddr->sun_path)) {
do_from_zval_err(ctx, "the path is too long, the maximum permitted "
- "length is %ld", sizeof(saddr->sun_path) - 1);
+ "length is %zd", sizeof(saddr->sun_path) - 1);
return;
}
@@ -937,7 +937,7 @@ static void from_zval_write_control_array(const zval *arr, char *msghdr_c, ser_c
break;
}
- if (snprintf(buf, sizeof(buf), "element #%u", (unsigned)i++) >= sizeof(buf)) {
+ if ((size_t)snprintf(buf, sizeof(buf), "element #%u", (unsigned)i++) >= sizeof(buf)) {
memcpy(buf, "element", sizeof("element"));
}
zend_llist_add_element(&ctx->keys, &bufp);
@@ -965,7 +965,7 @@ static void to_zval_read_cmsg_data(const char *cmsghdr_c, zval *zv, res_context
}
if (CMSG_LEN(entry->size) > cmsg->cmsg_len) {
do_to_zval_err(ctx, "the cmsghdr structure is unexpectedly small; "
- "expected a length of at least %pd, but got %pd",
+ "expected a length of at least " ZEND_LONG_FMT ", but got " ZEND_LONG_FMT,
(zend_long)CMSG_LEN(entry->size), (zend_long)cmsg->cmsg_len);
return;
}
@@ -1019,7 +1019,7 @@ static void to_zval_read_control_array(const char *msghdr_c, zval *zv, res_conte
ZVAL_NULL(&tmp);
elem = zend_hash_next_index_insert(Z_ARRVAL_P(zv), &tmp);
- if (snprintf(buf, sizeof(buf), "element #%u", (unsigned)i++) >= sizeof(buf)) {
+ if ((size_t)snprintf(buf, sizeof(buf), "element #%u", (unsigned)i++) >= sizeof(buf)) {
memcpy(buf, "element", sizeof("element"));
}
zend_llist_add_element(&ctx->keys, &bufp);
@@ -1061,9 +1061,9 @@ static void from_zval_write_msghdr_buffer_size(const zval *elem, char *msghdr_c,
return;
}
- if (lval < 0 || lval > MAX_USER_BUFF_SIZE) {
- do_from_zval_err(ctx, "the buffer size must be between 1 and %pd; "
- "given %pd", (zend_long)MAX_USER_BUFF_SIZE, lval);
+ if (lval < 0 || (zend_ulong)lval > MAX_USER_BUFF_SIZE) {
+ do_from_zval_err(ctx, "the buffer size must be between 1 and " ZEND_LONG_FMT "; "
+ "given " ZEND_LONG_FMT, (zend_long)MAX_USER_BUFF_SIZE, lval);
return;
}
@@ -1236,9 +1236,9 @@ static void from_zval_write_ifindex(const zval *zv, char *uinteger, ser_context
unsigned ret = 0;
if (Z_TYPE_P(zv) == IS_LONG) {
- if (Z_LVAL_P(zv) < 0 || Z_LVAL_P(zv) > UINT_MAX) { /* allow 0 (unspecified interface) */
+ if (Z_LVAL_P(zv) < 0 || (zend_ulong)Z_LVAL_P(zv) > UINT_MAX) { /* allow 0 (unspecified interface) */
do_from_zval_err(ctx, "the interface index cannot be negative or "
- "larger than %u; given %pd", UINT_MAX, Z_LVAL_P(zv));
+ "larger than %u; given " ZEND_LONG_FMT, UINT_MAX, Z_LVAL_P(zv));
} else {
ret = (unsigned)Z_LVAL_P(zv);
}
@@ -1400,7 +1400,7 @@ void to_zval_read_fd_array(const char *data, zval *zv, res_context *ctx)
if (*cmsg_len < data_offset) {
do_to_zval_err(ctx, "length of cmsg is smaller than its data member "
- "offset (%pd vs %pd)", (zend_long)*cmsg_len, (zend_long)data_offset);
+ "offset (" ZEND_LONG_FMT " vs " ZEND_LONG_FMT ")", (zend_long)*cmsg_len, (zend_long)data_offset);
return;
}
num_elems = (*cmsg_len - data_offset) / sizeof(int);
diff --git a/ext/sockets/multicast.c b/ext/sockets/multicast.c
index 4e5d399a87..24f374c55d 100644
--- a/ext/sockets/multicast.c
+++ b/ext/sockets/multicast.c
@@ -90,10 +90,10 @@ static int php_get_if_index_from_zval(zval *val, unsigned *out)
int ret;
if (Z_TYPE_P(val) == IS_LONG) {
- if (Z_LVAL_P(val) < 0 || Z_LVAL_P(val) > UINT_MAX) {
+ if (Z_LVAL_P(val) < 0 || (zend_ulong)Z_LVAL_P(val) > UINT_MAX) {
php_error_docref(NULL, E_WARNING,
"the interface index cannot be negative or larger than %u;"
- " given %pd", UINT_MAX, Z_LVAL_P(val));
+ " given " ZEND_LONG_FMT, UINT_MAX, Z_LVAL_P(val));
ret = FAILURE;
} else {
*out = Z_LVAL_P(val);
diff --git a/ext/sockets/sendrecvmsg.c b/ext/sockets/sendrecvmsg.c
index d7035412d2..ecb6fbe62f 100644
--- a/ext/sockets/sendrecvmsg.c
+++ b/ext/sockets/sendrecvmsg.c
@@ -71,7 +71,7 @@ inline ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags)
#define LONG_CHECK_VALID_INT(l) \
do { \
if ((l) < INT_MIN && (l) > INT_MAX) { \
- php_error_docref0(NULL, E_WARNING, "The value %pd does not fit inside " \
+ php_error_docref0(NULL, E_WARNING, "The value " ZEND_LONG_FMT " does not fit inside " \
"the boundaries of a native integer", (l)); \
return; \
} \
@@ -299,16 +299,16 @@ PHP_FUNCTION(socket_cmsg_space)
entry = get_ancillary_reg_entry(level, type);
if (entry == NULL) {
- php_error_docref0(NULL, E_WARNING, "The pair level %pd/type %pd is "
+ php_error_docref0(NULL, E_WARNING, "The pair level " ZEND_LONG_FMT "/type " ZEND_LONG_FMT " is "
"not supported by PHP", level, type);
return;
}
- if (entry->var_el_size > 0 && n > (ZEND_LONG_MAX - (zend_long)entry->size -
- (zend_long)CMSG_SPACE(0) - 15L) / entry->var_el_size) {
+ if (entry->var_el_size > 0 && n > (zend_long)((ZEND_LONG_MAX - entry->size -
+ CMSG_SPACE(0) - 15L) / entry->var_el_size)) {
/* the -15 is to account for any padding CMSG_SPACE may add after the data */
php_error_docref0(NULL, E_WARNING, "The value for the "
- "third argument (%pd) is too large", n);
+ "third argument (" ZEND_LONG_FMT ") is too large", n);
return;
}
diff --git a/ext/sockets/sockaddr_conv.c b/ext/sockets/sockaddr_conv.c
index 1ce109ee8c..57996612d2 100644
--- a/ext/sockets/sockaddr_conv.c
+++ b/ext/sockets/sockaddr_conv.c
@@ -66,7 +66,7 @@ int php_set_inet6_addr(struct sockaddr_in6 *sin6, char *string, php_socket *php_
unsigned scope_id = 0;
if (IS_LONG == is_numeric_string(scope, strlen(scope), &lval, &dval, 0)) {
- if (lval > 0 && lval <= UINT_MAX) {
+ if (lval > 0 && (zend_ulong)lval <= UINT_MAX) {
scope_id = lval;
}
} else {
diff --git a/ext/sockets/sockets.c b/ext/sockets/sockets.c
index 0268a471dd..38420658d1 100644
--- a/ext/sockets/sockets.c
+++ b/ext/sockets/sockets.c
@@ -1377,12 +1377,12 @@ PHP_FUNCTION(socket_create)
&& arg1 != AF_INET6
#endif
&& arg1 != AF_INET) {
- php_error_docref(NULL, E_WARNING, "invalid socket domain [%pd] specified for argument 1, assuming AF_INET", arg1);
+ php_error_docref(NULL, E_WARNING, "invalid socket domain [" ZEND_LONG_FMT "] specified for argument 1, assuming AF_INET", arg1);
arg1 = AF_INET;
}
if (arg2 > 10) {
- php_error_docref(NULL, E_WARNING, "invalid socket type [%pd] specified for argument 2, assuming SOCK_STREAM", arg2);
+ php_error_docref(NULL, E_WARNING, "invalid socket type [" ZEND_LONG_FMT "] specified for argument 2, assuming SOCK_STREAM", arg2);
arg2 = SOCK_STREAM;
}
@@ -1659,9 +1659,9 @@ PHP_FUNCTION(socket_send)
RETURN_FALSE;
}
- retval = send(php_sock->bsd_socket, buf, (buf_len < len ? buf_len : len), flags);
+ retval = send(php_sock->bsd_socket, buf, (buf_len < (size_t)len ? buf_len : (size_t)len), flags);
- if (retval == -1) {
+ if (retval == (size_t)-1) {
PHP_SOCKET_ERROR(php_sock, "unable to write to socket", errno);
RETURN_FALSE;
}
@@ -1827,7 +1827,7 @@ PHP_FUNCTION(socket_sendto)
s_un.sun_family = AF_UNIX;
snprintf(s_un.sun_path, 108, "%s", addr);
- retval = sendto(php_sock->bsd_socket, buf, (len > buf_len) ? buf_len : len, flags, (struct sockaddr *) &s_un, SUN_LEN(&s_un));
+ retval = sendto(php_sock->bsd_socket, buf, ((size_t)len > buf_len) ? buf_len : (size_t)len, flags, (struct sockaddr *) &s_un, SUN_LEN(&s_un));
break;
case AF_INET:
@@ -1843,7 +1843,7 @@ PHP_FUNCTION(socket_sendto)
RETURN_FALSE;
}
- retval = sendto(php_sock->bsd_socket, buf, (len > buf_len) ? buf_len : len, flags, (struct sockaddr *) &sin, sizeof(sin));
+ retval = sendto(php_sock->bsd_socket, buf, ((size_t)len > buf_len) ? buf_len : (size_t)len, flags, (struct sockaddr *) &sin, sizeof(sin));
break;
#if HAVE_IPV6
case AF_INET6:
@@ -1859,7 +1859,7 @@ PHP_FUNCTION(socket_sendto)
RETURN_FALSE;
}
- retval = sendto(php_sock->bsd_socket, buf, (len > buf_len) ? buf_len : len, flags, (struct sockaddr *) &sin6, sizeof(sin6));
+ retval = sendto(php_sock->bsd_socket, buf, ((size_t)len > buf_len) ? buf_len : (size_t)len, flags, (struct sockaddr *) &sin6, sizeof(sin6));
break;
#endif
default:
@@ -2153,12 +2153,12 @@ PHP_FUNCTION(socket_create_pair)
&& domain != AF_INET6
#endif
&& domain != AF_UNIX) {
- php_error_docref(NULL, E_WARNING, "invalid socket domain [%pd] specified for argument 1, assuming AF_INET", domain);
+ php_error_docref(NULL, E_WARNING, "invalid socket domain [" ZEND_LONG_FMT "] specified for argument 1, assuming AF_INET", domain);
domain = AF_INET;
}
if (type > 10) {
- php_error_docref(NULL, E_WARNING, "invalid socket type [%pd] specified for argument 2, assuming SOCK_STREAM", type);
+ php_error_docref(NULL, E_WARNING, "invalid socket type [" ZEND_LONG_FMT "] specified for argument 2, assuming SOCK_STREAM", type);
type = SOCK_STREAM;
}
diff --git a/ext/sockets/tests/socket_send.phpt b/ext/sockets/tests/socket_send.phpt
index ceeb397979..4093ad47cf 100644
--- a/ext/sockets/tests/socket_send.phpt
+++ b/ext/sockets/tests/socket_send.phpt
@@ -4,6 +4,7 @@ int socket_send ( resource $socket , string $buf , int $len , int $flags );
marcosptf - <marcosptf@yahoo.com.br> - #phparty7 - @phpsp - novatec/2015 - sao paulo - br
--SKIPIF--
<?php
+if (getenv("SKIP_ONLINE_TESTS")) die("skip online test");
if (!extension_loaded('sockets')) {
die('SKIP sockets extension not available.');
}
diff --git a/ext/sockets/tests/socket_shutdown.phpt b/ext/sockets/tests/socket_shutdown.phpt
index 77cbc8f32c..747016b795 100644
--- a/ext/sockets/tests/socket_shutdown.phpt
+++ b/ext/sockets/tests/socket_shutdown.phpt
@@ -4,6 +4,7 @@ bool socket_shutdown ( resource $socket [, int $how = 2 ] ) ;
marcosptf - <marcosptf@yahoo.com.br> - #phparty7 - @phpsp - novatec/2015 - sao paulo - br
--SKIPIF--
<?php
+if (getenv("SKIP_ONLINE_TESTS")) die("skip online test");
if (!extension_loaded('sockets')) {
die('SKIP sockets extension not available.');
}
diff --git a/ext/spl/php_spl.c b/ext/spl/php_spl.c
index b8e10a563c..38588907a1 100644
--- a/ext/spl/php_spl.c
+++ b/ext/spl/php_spl.c
@@ -39,8 +39,7 @@
#include "spl_heap.h"
#include "zend_exceptions.h"
#include "zend_interfaces.h"
-#include "ext/standard/php_rand.h"
-#include "ext/standard/php_lcg.h"
+#include "ext/standard/php_mt_rand.h"
#include "main/snprintf.h"
#ifdef COMPILE_DL_SPL
@@ -771,10 +770,6 @@ PHPAPI zend_string *php_spl_object_hash(zval *obj) /* {{{*/
intptr_t hash_handle, hash_handlers;
if (!SPL_G(hash_mask_init)) {
- if (!BG(mt_rand_is_seeded)) {
- php_mt_srand((uint32_t)GENERATE_SEED());
- }
-
SPL_G(hash_mask_handle) = (intptr_t)(php_mt_rand() >> 1);
SPL_G(hash_mask_handlers) = (intptr_t)(php_mt_rand() >> 1);
SPL_G(hash_mask_init) = 1;
@@ -783,7 +778,7 @@ PHPAPI zend_string *php_spl_object_hash(zval *obj) /* {{{*/
hash_handle = SPL_G(hash_mask_handle)^(intptr_t)Z_OBJ_HANDLE_P(obj);
hash_handlers = SPL_G(hash_mask_handlers);
- return strpprintf(32, "%016lx%016lx", hash_handle, hash_handlers);
+ return strpprintf(32, "%016zx%016zx", hash_handle, hash_handlers);
}
/* }}} */
diff --git a/ext/spl/spl_array.c b/ext/spl/spl_array.c
index b8eddf061f..81bbe48bd4 100644
--- a/ext/spl/spl_array.c
+++ b/ext/spl/spl_array.c
@@ -351,7 +351,7 @@ fetch_dim_string:
}
return retval;
case IS_RESOURCE:
- zend_error(E_NOTICE, "Resource ID#%pd used as offset, casting to integer (%pd)", Z_RES_P(offset)->handle, Z_RES_P(offset)->handle);
+ zend_error(E_NOTICE, "Resource ID#%d used as offset, casting to integer (%d)", Z_RES_P(offset)->handle, Z_RES_P(offset)->handle);
index = Z_RES_P(offset)->handle;
goto num_index;
case IS_DOUBLE:
@@ -369,13 +369,13 @@ num_index:
if ((retval = zend_hash_index_find(ht, index)) == NULL) {
switch (type) {
case BP_VAR_R:
- zend_error(E_NOTICE, "Undefined offset: %pd", index);
+ zend_error(E_NOTICE, "Undefined offset: " ZEND_LONG_FMT, index);
case BP_VAR_UNSET:
case BP_VAR_IS:
retval = &EG(uninitialized_zval);
break;
case BP_VAR_RW:
- zend_error(E_NOTICE, "Undefined offset: %pd", index);
+ zend_error(E_NOTICE, "Undefined offset: " ZEND_LONG_FMT, index);
case BP_VAR_W: {
zval value;
ZVAL_UNDEF(&value);
@@ -594,7 +594,7 @@ try_again:
num_index:
ht = spl_array_get_hash_table(intern);
if (zend_hash_index_del(ht, index) == FAILURE) {
- zend_error(E_NOTICE,"Undefined offset: %pd", index);
+ zend_error(E_NOTICE,"Undefined offset: " ZEND_LONG_FMT, index);
}
break;
case IS_REFERENCE:
@@ -770,7 +770,7 @@ void spl_array_iterator_append(zval *object, zval *append_value) /* {{{ */
}
if (spl_array_is_object(intern)) {
- php_error_docref(NULL, E_RECOVERABLE_ERROR, "Cannot append properties to objects, use %s::offsetSet() instead", ZSTR_VAL(Z_OBJCE_P(object)->name));
+ zend_throw_error(NULL, "Cannot append properties to objects, use %s::offsetSet() instead", ZSTR_VAL(Z_OBJCE_P(object)->name));
return;
}
@@ -901,6 +901,11 @@ static zval *spl_array_get_property_ptr_ptr(zval *object, zval *member, int type
if ((intern->ar_flags & SPL_ARRAY_ARRAY_AS_PROPS) != 0
&& !std_object_handlers.has_property(object, member, 2, NULL)) {
+ /* If object has offsetGet() overridden, then fallback to read_property,
+ * which will call offsetGet(). */
+ if (intern->fptr_offset_get) {
+ return NULL;
+ }
return spl_array_get_dimension_ptr(1, intern, member, type);
}
return std_object_handlers.get_property_ptr_ptr(object, member, type, cache_slot);
@@ -1160,7 +1165,8 @@ zend_object_iterator_funcs spl_array_it_funcs = {
spl_array_it_get_current_data,
spl_array_it_get_current_key,
spl_array_it_move_forward,
- spl_array_it_rewind
+ spl_array_it_rewind,
+ NULL
};
zend_object_iterator *spl_array_get_iterator(zend_class_entry *ce, zval *object, int by_ref) /* {{{ */
@@ -1367,10 +1373,10 @@ SPL_METHOD(Array, seek)
return; /* ok */
}
}
- zend_throw_exception_ex(spl_ce_OutOfBoundsException, 0, "Seek position %pd is out of range", opos);
+ zend_throw_exception_ex(spl_ce_OutOfBoundsException, 0, "Seek position " ZEND_LONG_FMT " is out of range", opos);
} /* }}} */
-int static spl_array_object_count_elements_helper(spl_array_object *intern, zend_long *count) /* {{{ */
+static int spl_array_object_count_elements_helper(spl_array_object *intern, zend_long *count) /* {{{ */
{
HashTable *aht = spl_array_get_hash_table(intern);
HashPosition pos, *pos_ptr;
@@ -1836,7 +1842,7 @@ SPL_METHOD(Array, unserialize)
outexcept:
PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
- zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "Error at offset %pd of %d bytes", (zend_long)((char*)p - buf), buf_len);
+ zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "Error at offset " ZEND_LONG_FMT " of %zd bytes", (zend_long)((char*)p - buf), buf_len);
return;
} /* }}} */
diff --git a/ext/spl/spl_directory.c b/ext/spl/spl_directory.c
index 41df66107d..6491613115 100644
--- a/ext/spl/spl_directory.c
+++ b/ext/spl/spl_directory.c
@@ -355,8 +355,8 @@ static zend_object *spl_filesystem_object_clone(zval *zobject)
intern->u.dir.index = index;
break;
case SPL_FS_FILE:
- php_error_docref(NULL, E_ERROR, "An object of class %s cannot be cloned", ZSTR_VAL(old_object->ce->name));
- break;
+ zend_throw_error(NULL, "An object of class %s cannot be cloned", ZSTR_VAL(old_object->ce->name));
+ return new_object;
}
intern->file_class = source->file_class;
@@ -676,7 +676,8 @@ void spl_filesystem_object_construct(INTERNAL_FUNCTION_PARAMETERS, zend_long cto
{
spl_filesystem_object *intern;
char *path;
- size_t parsed, len;
+ int parsed;
+ size_t len;
zend_long flags;
zend_error_handling error_handling;
@@ -834,7 +835,7 @@ SPL_METHOD(DirectoryIterator, seek)
zval_ptr_dtor(&retval);
}
if (!valid) {
- zend_throw_exception_ex(spl_ce_OutOfBoundsException, 0, "Seek position %ld is out of range", pos);
+ zend_throw_exception_ex(spl_ce_OutOfBoundsException, 0, "Seek position " ZEND_LONG_FMT " is out of range", pos);
return;
}
zend_call_method_with_0_params(&EX(This), Z_OBJCE(EX(This)), &intern->u.dir.func_next, "next", NULL);
@@ -1608,7 +1609,8 @@ zend_object_iterator_funcs spl_filesystem_dir_it_funcs = {
spl_filesystem_dir_it_current_data,
spl_filesystem_dir_it_current_key,
spl_filesystem_dir_it_move_forward,
- spl_filesystem_dir_it_rewind
+ spl_filesystem_dir_it_rewind,
+ NULL
};
/* }}} */
@@ -1806,7 +1808,8 @@ zend_object_iterator_funcs spl_filesystem_tree_it_funcs = {
spl_filesystem_tree_it_current_data,
spl_filesystem_tree_it_current_key,
spl_filesystem_tree_it_move_forward,
- spl_filesystem_tree_it_rewind
+ spl_filesystem_tree_it_rewind,
+ NULL
};
/* }}} */
@@ -1848,7 +1851,7 @@ static int spl_filesystem_object_cast(zval *readobj, zval *writeobj, int type)
ZVAL_STRINGL(retval_ptr, intern->file_name, intern->file_name_len);
zval_ptr_dtor(readobj);
- ZVAL_COPY_VALUE(writeobj, retval_ptr);
+ ZVAL_NEW_STR(writeobj, Z_STR_P(retval_ptr));
} else {
ZVAL_STRINGL(writeobj, intern->file_name, intern->file_name_len);
}
@@ -1860,7 +1863,7 @@ static int spl_filesystem_object_cast(zval *readobj, zval *writeobj, int type)
ZVAL_STRING(retval_ptr, intern->u.dir.entry.d_name);
zval_ptr_dtor(readobj);
- ZVAL_COPY_VALUE(writeobj, retval_ptr);
+ ZVAL_NEW_STR(writeobj, Z_STR_P(retval_ptr));
} else {
ZVAL_STRING(writeobj, intern->u.dir.entry.d_name);
}
@@ -2075,13 +2078,11 @@ static int spl_filesystem_file_call(spl_filesystem_object *intern, zend_function
ZVAL_UNDEF(&retval);
fci.size = sizeof(fci);
- fci.function_table = EG(function_table);
fci.object = NULL;
fci.retval = &retval;
fci.param_count = num_args;
fci.params = params;
fci.no_separation = 1;
- fci.symbol_table = NULL;
ZVAL_STR(&fci.function_name, func_ptr->common.function_name);
fcic.initialized = 1;
@@ -2324,7 +2325,7 @@ SPL_METHOD(SplTempFileObject, __construct)
intern->file_name = "php://memory";
intern->file_name_len = 12;
} else if (ZEND_NUM_ARGS()) {
- intern->file_name_len = slprintf(tmp_fname, sizeof(tmp_fname), "php://temp/maxmemory:%pd", max_memory);
+ intern->file_name_len = slprintf(tmp_fname, sizeof(tmp_fname), "php://temp/maxmemory:" ZEND_LONG_FMT, max_memory);
intern->file_name = tmp_fname;
} else {
intern->file_name = "php://temp";
@@ -2887,7 +2888,7 @@ SPL_METHOD(SplFileObject, fwrite)
if (ZEND_NUM_ARGS() > 1) {
if (length >= 0) {
- str_len = MAX(0, MIN((size_t)length, str_len));
+ str_len = MIN((size_t)length, str_len);
} else {
/* Negative length given, nothing to write */
str_len = 0;
@@ -2971,7 +2972,7 @@ SPL_METHOD(SplFileObject, seek)
}
if (line_pos < 0) {
- zend_throw_exception_ex(spl_ce_LogicException, 0, "Can't seek file %s to negative line %pd", intern->file_name, line_pos);
+ zend_throw_exception_ex(spl_ce_LogicException, 0, "Can't seek file %s to negative line " ZEND_LONG_FMT, intern->file_name, line_pos);
RETURN_FALSE;
}
diff --git a/ext/spl/spl_dllist.c b/ext/spl/spl_dllist.c
index c205de5fed..667a6bad7b 100644
--- a/ext/spl/spl_dllist.c
+++ b/ext/spl/spl_dllist.c
@@ -1223,7 +1223,7 @@ SPL_METHOD(SplDoublyLinkedList, unserialize)
error:
PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
- zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "Error at offset %pd of %d bytes", (zend_long)((char*)p - buf), buf_len);
+ zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "Error at offset %zd of %zd bytes", ((char*)p - buf), buf_len);
return;
} /* }}} */
@@ -1291,7 +1291,8 @@ zend_object_iterator_funcs spl_dllist_it_funcs = {
spl_dllist_it_get_current_data,
spl_dllist_it_get_current_key,
spl_dllist_it_move_forward,
- spl_dllist_it_rewind
+ spl_dllist_it_rewind,
+ NULL
}; /* }}} */
zend_object_iterator *spl_dllist_get_iterator(zend_class_entry *ce, zval *object, int by_ref) /* {{{ */
diff --git a/ext/spl/spl_engine.h b/ext/spl/spl_engine.h
index e7b54b5712..6b50c6bd45 100644
--- a/ext/spl/spl_engine.h
+++ b/ext/spl/spl_engine.h
@@ -62,10 +62,8 @@ static inline void spl_instantiate_arg_n(zend_class_entry *pce, zval *retval, in
spl_instantiate(pce, retval);
fci.size = sizeof(zend_fcall_info);
- fci.function_table = &pce->function_table;
ZVAL_STR(&fci.function_name, func->common.function_name);
fci.object = Z_OBJ_P(retval);
- fci.symbol_table = NULL;
fci.retval = &dummy;
fci.param_count = argc;
fci.params = argv;
@@ -73,7 +71,7 @@ static inline void spl_instantiate_arg_n(zend_class_entry *pce, zval *retval, in
fcc.initialized = 1;
fcc.function_handler = func;
- fcc.calling_scope = EG(scope);
+ fcc.calling_scope = zend_get_executed_scope();
fcc.called_scope = pce;
fcc.object = Z_OBJ_P(retval);
diff --git a/ext/spl/spl_fixedarray.c b/ext/spl/spl_fixedarray.c
index 4426bb6e81..3dd5b0656f 100644
--- a/ext/spl/spl_fixedarray.c
+++ b/ext/spl/spl_fixedarray.c
@@ -49,7 +49,7 @@ typedef struct _spl_fixedarray { /* {{{ */
/* }}} */
typedef struct _spl_fixedarray_object { /* {{{ */
- spl_fixedarray *array;
+ spl_fixedarray array;
zend_function *fptr_offset_get;
zend_function *fptr_offset_set;
zend_function *fptr_offset_has;
@@ -148,13 +148,8 @@ static HashTable* spl_fixedarray_object_get_gc(zval *obj, zval **table, int *n)
spl_fixedarray_object *intern = Z_SPLFIXEDARRAY_P(obj);
HashTable *ht = zend_std_get_properties(obj);
- if (intern->array) {
- *table = intern->array->elements;
- *n = (int)intern->array->size;
- } else {
- *table = NULL;
- *n = 0;
- }
+ *table = intern->array.elements;
+ *n = (int)intern->array.size;
return ht;
}
@@ -166,21 +161,21 @@ static HashTable* spl_fixedarray_object_get_properties(zval *obj) /* {{{{ */
HashTable *ht = zend_std_get_properties(obj);
zend_long i = 0;
- if (intern->array) {
+ if (intern->array.size > 0) {
zend_long j = zend_hash_num_elements(ht);
- for (i = 0; i < intern->array->size; i++) {
- if (!Z_ISUNDEF(intern->array->elements[i])) {
- zend_hash_index_update(ht, i, &intern->array->elements[i]);
- if (Z_REFCOUNTED(intern->array->elements[i])){
- Z_ADDREF(intern->array->elements[i]);
+ for (i = 0; i < intern->array.size; i++) {
+ if (!Z_ISUNDEF(intern->array.elements[i])) {
+ zend_hash_index_update(ht, i, &intern->array.elements[i]);
+ if (Z_REFCOUNTED(intern->array.elements[i])){
+ Z_ADDREF(intern->array.elements[i]);
}
} else {
zend_hash_index_update(ht, i, &EG(uninitialized_zval));
}
}
- if (j > intern->array->size) {
- for (i = intern->array->size; i < j; ++i) {
+ if (j > intern->array.size) {
+ for (i = intern->array.size; i < j; ++i) {
zend_hash_index_del(ht, i);
}
}
@@ -195,15 +190,14 @@ static void spl_fixedarray_object_free_storage(zend_object *object) /* {{{ */
spl_fixedarray_object *intern = spl_fixed_array_from_obj(object);
zend_long i;
- if (intern->array) {
- for (i = 0; i < intern->array->size; i++) {
- zval_ptr_dtor(&(intern->array->elements[i]));
+ if (intern->array.size > 0) {
+ for (i = 0; i < intern->array.size; i++) {
+ zval_ptr_dtor(&(intern->array.elements[i]));
}
- if (intern->array->size > 0 && intern->array->elements) {
- efree(intern->array->elements);
+ if (intern->array.size > 0 && intern->array.elements) {
+ efree(intern->array.elements);
}
- efree(intern->array);
}
zend_object_std_dtor(&intern->std);
@@ -229,14 +223,8 @@ static zend_object *spl_fixedarray_object_new_ex(zend_class_entry *class_type, z
if (orig && clone_orig) {
spl_fixedarray_object *other = Z_SPLFIXEDARRAY_P(orig);
intern->ce_get_iterator = other->ce_get_iterator;
- if (!other->array) {
- /* leave a empty object, will be dtor later by CLONE handler */
- zend_throw_exception(spl_ce_RuntimeException, "The instance wasn't initialized properly", 0);
- } else {
- intern->array = emalloc(sizeof(spl_fixedarray));
- spl_fixedarray_init(intern->array, other->array->size);
- spl_fixedarray_copy(intern->array, other->array);
- }
+ spl_fixedarray_init(&intern->array, other->array.size);
+ spl_fixedarray_copy(&intern->array, &other->array);
}
while (parent) {
@@ -341,13 +329,13 @@ static inline zval *spl_fixedarray_object_read_dimension_helper(spl_fixedarray_o
index = Z_LVAL_P(offset);
}
- if (index < 0 || intern->array == NULL || index >= intern->array->size) {
+ if (index < 0 || index >= intern->array.size) {
zend_throw_exception(spl_ce_RuntimeException, "Index invalid or out of range", 0);
return NULL;
- } else if (Z_ISUNDEF(intern->array->elements[index])) {
+ } else if (Z_ISUNDEF(intern->array.elements[index])) {
return NULL;
} else {
- return &intern->array->elements[index];
+ return &intern->array.elements[index];
}
}
/* }}} */
@@ -376,7 +364,7 @@ static zval *spl_fixedarray_object_read_dimension(zval *object, zval *offset, in
if (intern->fptr_offset_get) {
zval tmp;
if (!offset) {
- ZVAL_UNDEF(&tmp);
+ ZVAL_NULL(&tmp);
offset = &tmp;
} else {
SEPARATE_ARG_IF_REF(offset);
@@ -409,15 +397,15 @@ static inline void spl_fixedarray_object_write_dimension_helper(spl_fixedarray_o
index = Z_LVAL_P(offset);
}
- if (index < 0 || intern->array == NULL || index >= intern->array->size) {
+ if (index < 0 || index >= intern->array.size) {
zend_throw_exception(spl_ce_RuntimeException, "Index invalid or out of range", 0);
return;
} else {
- if (!Z_ISUNDEF(intern->array->elements[index])) {
- zval_ptr_dtor(&(intern->array->elements[index]));
+ if (!Z_ISUNDEF(intern->array.elements[index])) {
+ zval_ptr_dtor(&(intern->array.elements[index]));
}
ZVAL_DEREF(value);
- ZVAL_COPY(&intern->array->elements[index], value);
+ ZVAL_COPY(&intern->array.elements[index], value);
}
}
/* }}} */
@@ -457,12 +445,12 @@ static inline void spl_fixedarray_object_unset_dimension_helper(spl_fixedarray_o
index = Z_LVAL_P(offset);
}
- if (index < 0 || intern->array == NULL || index >= intern->array->size) {
+ if (index < 0 || index >= intern->array.size) {
zend_throw_exception(spl_ce_RuntimeException, "Index invalid or out of range", 0);
return;
} else {
- zval_ptr_dtor(&(intern->array->elements[index]));
- ZVAL_UNDEF(&intern->array->elements[index]);
+ zval_ptr_dtor(&(intern->array.elements[index]));
+ ZVAL_UNDEF(&intern->array.elements[index]);
}
}
/* }}} */
@@ -496,13 +484,13 @@ static inline int spl_fixedarray_object_has_dimension_helper(spl_fixedarray_obje
index = Z_LVAL_P(offset);
}
- if (index < 0 || intern->array == NULL || index >= intern->array->size) {
+ if (index < 0 || index >= intern->array.size) {
retval = 0;
} else {
- if (Z_ISUNDEF(intern->array->elements[index])) {
+ if (Z_ISUNDEF(intern->array.elements[index])) {
retval = 0;
} else if (check_empty) {
- if (zend_is_true(&intern->array->elements[index])) {
+ if (zend_is_true(&intern->array.elements[index])) {
retval = 1;
} else {
retval = 0;
@@ -550,14 +538,12 @@ static int spl_fixedarray_object_count_elements(zval *object, zend_long *count)
if (!Z_ISUNDEF(rv)) {
*count = zval_get_long(&rv);
zval_ptr_dtor(&rv);
- return SUCCESS;
+ } else {
+ *count = 0;
}
- } else if (intern->array) {
- *count = intern->array->size;
- return SUCCESS;
+ } else {
+ *count = intern->array.size;
}
-
- *count = 0;
return SUCCESS;
}
/* }}} */
@@ -581,13 +567,12 @@ SPL_METHOD(SplFixedArray, __construct)
intern = Z_SPLFIXEDARRAY_P(object);
- if (intern->array) {
+ if (intern->array.size > 0) {
/* called __construct() twice, bail out */
return;
}
- intern->array = emalloc(sizeof(spl_fixedarray));
- spl_fixedarray_init(intern->array, size);
+ spl_fixedarray_init(&intern->array, size);
}
/* }}} */
@@ -603,18 +588,17 @@ SPL_METHOD(SplFixedArray, __wakeup)
return;
}
- if (!intern->array) {
+ if (intern->array.size == 0) {
int index = 0;
int size = zend_hash_num_elements(intern_ht);
- intern->array = emalloc(sizeof(spl_fixedarray));
- spl_fixedarray_init(intern->array, size);
+ spl_fixedarray_init(&intern->array, size);
ZEND_HASH_FOREACH_VAL(intern_ht, data) {
if (Z_REFCOUNTED_P(data)) {
Z_ADDREF_P(data);
}
- ZVAL_COPY_VALUE(&intern->array->elements[index], data);
+ ZVAL_COPY_VALUE(&intern->array.elements[index], data);
index++;
} ZEND_HASH_FOREACH_END();
@@ -637,10 +621,7 @@ SPL_METHOD(SplFixedArray, count)
}
intern = Z_SPLFIXEDARRAY_P(object);
- if (intern->array) {
- RETURN_LONG(intern->array->size);
- }
- RETURN_LONG(0);
+ RETURN_LONG(intern->array.size);
}
/* }}} */
@@ -657,13 +638,13 @@ SPL_METHOD(SplFixedArray, toArray)
intern = Z_SPLFIXEDARRAY_P(getThis());
array_init(return_value);
- if (intern->array) {
+ if (intern->array.size > 0) {
int i = 0;
- for (; i < intern->array->size; i++) {
- if (!Z_ISUNDEF(intern->array->elements[i])) {
- zend_hash_index_update(Z_ARRVAL_P(return_value), i, &intern->array->elements[i]);
- if (Z_REFCOUNTED(intern->array->elements[i])) {
- Z_ADDREF(intern->array->elements[i]);
+ for (; i < intern->array.size; i++) {
+ if (!Z_ISUNDEF(intern->array.elements[i])) {
+ zend_hash_index_update(Z_ARRVAL_P(return_value), i, &intern->array.elements[i]);
+ if (Z_REFCOUNTED(intern->array.elements[i])) {
+ Z_ADDREF(intern->array.elements[i]);
}
} else {
zend_hash_index_update(Z_ARRVAL_P(return_value), i, &EG(uninitialized_zval));
@@ -678,7 +659,7 @@ SPL_METHOD(SplFixedArray, toArray)
SPL_METHOD(SplFixedArray, fromArray)
{
zval *data;
- spl_fixedarray *array;
+ spl_fixedarray array;
spl_fixedarray_object *intern;
int num;
zend_bool save_indexes = 1;
@@ -687,7 +668,6 @@ SPL_METHOD(SplFixedArray, fromArray)
return;
}
- array = ecalloc(1, sizeof(spl_fixedarray));
num = zend_hash_num_elements(Z_ARRVAL_P(data));
if (num > 0 && save_indexes) {
@@ -698,7 +678,6 @@ SPL_METHOD(SplFixedArray, fromArray)
ZEND_HASH_FOREACH_KEY(Z_ARRVAL_P(data), num_index, str_index) {
if (str_index != NULL || (zend_long)num_index < 0) {
- efree(array);
zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0, "array must contain only positive integer keys");
return;
}
@@ -710,30 +689,29 @@ SPL_METHOD(SplFixedArray, fromArray)
tmp = max_index + 1;
if (tmp <= 0) {
- efree(array);
zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0, "integer overflow detected");
return;
}
- spl_fixedarray_init(array, tmp);
+ spl_fixedarray_init(&array, tmp);
ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(data), num_index, str_index, element) {
ZVAL_DEREF(element);
- ZVAL_COPY(&array->elements[num_index], element);
+ ZVAL_COPY(&array.elements[num_index], element);
} ZEND_HASH_FOREACH_END();
} else if (num > 0 && !save_indexes) {
zval *element;
zend_long i = 0;
- spl_fixedarray_init(array, num);
+ spl_fixedarray_init(&array, num);
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(data), element) {
ZVAL_DEREF(element);
- ZVAL_COPY(&array->elements[i], element);
+ ZVAL_COPY(&array.elements[i], element);
i++;
} ZEND_HASH_FOREACH_END();
} else {
- spl_fixedarray_init(array, 0);
+ spl_fixedarray_init(&array, 0);
}
object_init_ex(return_value, spl_ce_SplFixedArray);
@@ -755,10 +733,7 @@ SPL_METHOD(SplFixedArray, getSize)
}
intern = Z_SPLFIXEDARRAY_P(object);
- if (intern->array) {
- RETURN_LONG(intern->array->size);
- }
- RETURN_LONG(0);
+ RETURN_LONG(intern->array.size);
}
/* }}} */
@@ -780,11 +755,8 @@ SPL_METHOD(SplFixedArray, setSize)
}
intern = Z_SPLFIXEDARRAY_P(object);
- if (!intern->array) {
- intern->array = ecalloc(1, sizeof(spl_fixedarray));
- }
- spl_fixedarray_resize(intern->array, size);
+ spl_fixedarray_resize(&intern->array, size);
RETURN_TRUE;
}
/* }}} */
@@ -888,7 +860,7 @@ static int spl_fixedarray_it_valid(zend_object_iterator *iter) /* {{{ */
return zend_user_it_valid(iter);
}
- if (object->current >= 0 && object->array && object->current < object->array->size) {
+ if (object->current >= 0 && object->current < object->array.size) {
return SUCCESS;
}
@@ -982,7 +954,7 @@ SPL_METHOD(SplFixedArray, valid)
return;
}
- RETURN_BOOL(intern->current >= 0 && intern->array && intern->current < intern->array->size);
+ RETURN_BOOL(intern->current >= 0 && intern->current < intern->array.size);
}
/* }}} */
@@ -1033,7 +1005,8 @@ zend_object_iterator_funcs spl_fixedarray_it_funcs = {
spl_fixedarray_it_get_current_data,
spl_fixedarray_it_get_current_key,
spl_fixedarray_it_move_forward,
- spl_fixedarray_it_rewind
+ spl_fixedarray_it_rewind,
+ NULL
};
zend_object_iterator *spl_fixedarray_get_iterator(zend_class_entry *ce, zval *object, int by_ref) /* {{{ */
diff --git a/ext/spl/spl_heap.c b/ext/spl/spl_heap.c
index b0690f275e..06e4773677 100644
--- a/ext/spl/spl_heap.c
+++ b/ext/spl/spl_heap.c
@@ -1069,7 +1069,8 @@ zend_object_iterator_funcs spl_heap_it_funcs = {
spl_heap_it_get_current_data,
spl_heap_it_get_current_key,
spl_heap_it_move_forward,
- spl_heap_it_rewind
+ spl_heap_it_rewind,
+ NULL
};
zend_object_iterator_funcs spl_pqueue_it_funcs = {
@@ -1078,7 +1079,8 @@ zend_object_iterator_funcs spl_pqueue_it_funcs = {
spl_pqueue_it_get_current_data,
spl_heap_it_get_current_key,
spl_heap_it_move_forward,
- spl_heap_it_rewind
+ spl_heap_it_rewind,
+ NULL
};
zend_object_iterator *spl_heap_get_iterator(zend_class_entry *ce, zval *object, int by_ref) /* {{{ */
@@ -1153,11 +1155,11 @@ ZEND_END_ARG_INFO()
static const zend_function_entry spl_funcs_SplMinHeap[] = {
SPL_ME(SplMinHeap, compare, arginfo_heap_compare, ZEND_ACC_PROTECTED)
- {NULL, NULL, NULL}
+ PHP_FE_END
};
static const zend_function_entry spl_funcs_SplMaxHeap[] = {
SPL_ME(SplMaxHeap, compare, arginfo_heap_compare, ZEND_ACC_PROTECTED)
- {NULL, NULL, NULL}
+ PHP_FE_END
};
static const zend_function_entry spl_funcs_SplPriorityQueue[] = {
@@ -1176,7 +1178,7 @@ static const zend_function_entry spl_funcs_SplPriorityQueue[] = {
SPL_ME(SplHeap, valid, arginfo_splheap_void, ZEND_ACC_PUBLIC)
SPL_ME(SplHeap, recoverFromCorruption, arginfo_splheap_void, ZEND_ACC_PUBLIC)
SPL_ME(SplHeap, isCorrupted, arginfo_splheap_void, ZEND_ACC_PUBLIC)
- {NULL, NULL, NULL}
+ PHP_FE_END
};
static const zend_function_entry spl_funcs_SplHeap[] = {
@@ -1193,7 +1195,7 @@ static const zend_function_entry spl_funcs_SplHeap[] = {
SPL_ME(SplHeap, recoverFromCorruption, arginfo_splheap_void, ZEND_ACC_PUBLIC)
SPL_ME(SplHeap, isCorrupted, arginfo_splheap_void, ZEND_ACC_PUBLIC)
ZEND_FENTRY(compare, NULL, NULL, ZEND_ACC_PROTECTED|ZEND_ACC_ABSTRACT)
- {NULL, NULL, NULL}
+ PHP_FE_END
};
/* }}} */
diff --git a/ext/spl/spl_iterators.c b/ext/spl/spl_iterators.c
index db0888a118..772d5ceabb 100644
--- a/ext/spl/spl_iterators.c
+++ b/ext/spl/spl_iterators.c
@@ -472,7 +472,8 @@ zend_object_iterator_funcs spl_recursive_it_iterator_funcs = {
spl_recursive_it_get_current_data,
spl_recursive_it_get_current_key,
spl_recursive_it_move_forward,
- spl_recursive_it_rewind
+ spl_recursive_it_rewind,
+ NULL
};
static void spl_recursive_it_it_construct(INTERNAL_FUNCTION_PARAMETERS, zend_class_entry *ce_base, zend_class_entry *ce_inner, recursive_it_it_type rit_type)
@@ -1399,10 +1400,6 @@ int spl_dual_it_call_method(char *method, INTERNAL_FUNCTION_PARAMETERS)
intern = Z_SPLDUAL_IT_P(getThis());
ZVAL_STRING(&func, method, 0);
- if (!zend_is_callable(&func, 0, &method)) {
- php_error_docref(NULL, E_ERROR, "Method %s::%s() does not exist", intern->inner.ce->name, method);
- return FAILURE;
- }
p = EG(argument_stack).top_element-2;
arg_count = (zend_ulong) *p;
@@ -1421,7 +1418,7 @@ int spl_dual_it_call_method(char *method, INTERNAL_FUNCTION_PARAMETERS)
success = SUCCESS;
} else {
- php_error_docref(NULL, E_ERROR, "Unable to call %s::%s()", intern->inner.ce->name, method);
+ zend_throw_error(NULL, "Unable to call %s::%s()", intern->inner.ce->name, method);
success = FAILURE;
}
@@ -1556,7 +1553,7 @@ static spl_dual_it_object* spl_dual_it_construct(INTERNAL_FUNCTION_PARAMETERS, z
return NULL;
}
if (mode < 0 || mode >= REGIT_MODE_MAX) {
- zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0, "Illegal mode %pd", mode);
+ zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0, "Illegal mode " ZEND_LONG_FMT, mode);
return NULL;
}
intern->u.regex.mode = mode;
@@ -1649,13 +1646,6 @@ SPL_METHOD(dual_it, getInnerIterator)
}
} /* }}} */
-static inline void spl_dual_it_require(spl_dual_it_object *intern)
-{
- if (!intern->inner.iterator) {
- php_error_docref(NULL, E_ERROR, "The inner constructor wasn't initialized with an iterator instance");
- }
-}
-
static inline void spl_dual_it_free(spl_dual_it_object *intern)
{
if (intern->inner.iterator && intern->inner.iterator->funcs->invalidate_current) {
@@ -1728,8 +1718,9 @@ static inline void spl_dual_it_next(spl_dual_it_object *intern, int do_free)
{
if (do_free) {
spl_dual_it_free(intern);
- } else {
- spl_dual_it_require(intern);
+ } else if (!intern->inner.iterator) {
+ zend_throw_error(NULL, "The inner constructor wasn't initialized with an iterator instance");
+ return;
}
intern->inner.iterator->funcs->move_forward(intern->inner.iterator);
intern->current.pos++;
@@ -2163,7 +2154,7 @@ SPL_METHOD(RegexIterator, setMode)
}
if (mode < 0 || mode >= REGIT_MODE_MAX) {
- zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0, "Illegal mode %pd", mode);
+ zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0, "Illegal mode " ZEND_LONG_FMT, mode);
return;/* NULL */
}
@@ -2501,11 +2492,11 @@ static inline void spl_limit_it_seek(spl_dual_it_object *intern, zend_long pos)
spl_dual_it_free(intern);
if (pos < intern->u.limit.offset) {
- zend_throw_exception_ex(spl_ce_OutOfBoundsException, 0, "Cannot seek to %pd which is below the offset %pd", pos, intern->u.limit.offset);
+ zend_throw_exception_ex(spl_ce_OutOfBoundsException, 0, "Cannot seek to " ZEND_LONG_FMT " which is below the offset " ZEND_LONG_FMT, pos, intern->u.limit.offset);
return;
}
if (pos >= intern->u.limit.offset + intern->u.limit.count && intern->u.limit.count != -1) {
- zend_throw_exception_ex(spl_ce_OutOfBoundsException, 0, "Cannot seek to %pd which is behind offset %pd plus count %pd", pos, intern->u.limit.offset, intern->u.limit.count);
+ zend_throw_exception_ex(spl_ce_OutOfBoundsException, 0, "Cannot seek to " ZEND_LONG_FMT " which is behind offset " ZEND_LONG_FMT " plus count " ZEND_LONG_FMT, pos, intern->u.limit.offset, intern->u.limit.count);
return;
}
if (pos != intern->current.pos && instanceof_function(intern->inner.ce, spl_ce_SeekableIterator)) {
@@ -2932,7 +2923,7 @@ SPL_METHOD(CachingIterator, getCache)
SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
if (!(intern->u.caching.flags & CIT_FULL_CACHE)) {
- zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "%v does not use a full cache (see CachingIterator::__construct)", ZSTR_VAL(Z_OBJCE_P(getThis())->name));
+ zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "%s does not use a full cache (see CachingIterator::__construct)", ZSTR_VAL(Z_OBJCE_P(getThis())->name));
return;
}
@@ -3002,7 +2993,7 @@ SPL_METHOD(CachingIterator, count)
SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
if (!(intern->u.caching.flags & CIT_FULL_CACHE)) {
- zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "%v does not use a full cache (see CachingIterator::__construct)", ZSTR_VAL(Z_OBJCE_P(getThis())->name));
+ zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "%s does not use a full cache (see CachingIterator::__construct)", ZSTR_VAL(Z_OBJCE_P(getThis())->name));
return;
}
@@ -3375,7 +3366,7 @@ SPL_METHOD(AppendIterator, __construct)
Append an iterator */
SPL_METHOD(AppendIterator, append)
{
- spl_dual_it_object *intern;
+ spl_dual_it_object *intern, *appender;
zval *it;
SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
@@ -3387,6 +3378,11 @@ SPL_METHOD(AppendIterator, append)
spl_array_iterator_append(&intern->u.append.zarrayit, it);
intern->u.append.iterator->funcs->move_forward(intern->u.append.iterator);
}else{
+ appender = Z_SPLDUAL_IT_P(it);
+ if (appender->dit_type == DIT_AppendIterator) {
+ spl_array_iterator_append(&intern->u.append.zarrayit, &appender->u.append.zarrayit);
+ return;
+ }
spl_array_iterator_append(&intern->u.append.zarrayit, it);
}
diff --git a/ext/spl/spl_observer.c b/ext/spl/spl_observer.c
index 462c638b25..de33bd5a6b 100644
--- a/ext/spl/spl_observer.c
+++ b/ext/spl/spl_observer.c
@@ -51,7 +51,7 @@ ZEND_END_ARG_INFO();
static const zend_function_entry spl_funcs_SplObserver[] = {
SPL_ABSTRACT_ME(SplObserver, update, arginfo_SplObserver_update)
- {NULL, NULL, NULL}
+ PHP_FE_END
};
ZEND_BEGIN_ARG_INFO(arginfo_SplSubject_attach, 0)
@@ -69,7 +69,7 @@ static const zend_function_entry spl_funcs_SplSubject[] = {
SPL_ABSTRACT_ME(SplSubject, attach, arginfo_SplSubject_attach)
SPL_ABSTRACT_ME(SplSubject, detach, arginfo_SplSubject_attach)
SPL_ABSTRACT_ME(SplSubject, notify, arginfo_SplSubject_void)
- {NULL, NULL, NULL}
+ PHP_FE_END
};
PHPAPI zend_class_entry *spl_ce_SplObserver;
@@ -117,32 +117,34 @@ void spl_SplObjectStorage_free_storage(zend_object *object) /* {{{ */
} /* }}} */
-static zend_string *spl_object_storage_get_hash(spl_SplObjectStorage *intern, zval *this, zval *obj) {
+static int spl_object_storage_get_hash(zend_hash_key *key, spl_SplObjectStorage *intern, zval *this, zval *obj) {
if (intern->fptr_get_hash) {
zval rv;
zend_call_method_with_1_params(this, intern->std.ce, &intern->fptr_get_hash, "getHash", &rv, obj);
if (!Z_ISUNDEF(rv)) {
if (Z_TYPE(rv) == IS_STRING) {
- return Z_STR(rv);
+ key->key = Z_STR(rv);
+ return SUCCESS;
} else {
zend_throw_exception(spl_ce_RuntimeException, "Hash needs to be a string", 0);
zval_ptr_dtor(&rv);
- return NULL;
+ return FAILURE;
}
} else {
- return NULL;
+ return FAILURE;
}
} else {
- zend_string *hash = zend_string_alloc(sizeof(zend_object*), 0);
- memcpy(ZSTR_VAL(hash), (void*)&Z_OBJ_P(obj), sizeof(zend_object*));
- ZSTR_VAL(hash)[ZSTR_LEN(hash)] = '\0';
- return hash;
+ key->key = NULL;
+ key->h = Z_OBJ_HANDLE_P(obj);
+ return SUCCESS;
}
}
-static void spl_object_storage_free_hash(spl_SplObjectStorage *intern, zend_string *hash) {
- zend_string_release(hash);
+static void spl_object_storage_free_hash(spl_SplObjectStorage *intern, zend_hash_key *key) {
+ if (key->key) {
+ zend_string_release(key->key);
+ }
}
static void spl_object_storage_dtor(zval *element) /* {{{ */
@@ -153,21 +155,24 @@ static void spl_object_storage_dtor(zval *element) /* {{{ */
efree(el);
} /* }}} */
-spl_SplObjectStorageElement* spl_object_storage_get(spl_SplObjectStorage *intern, zend_string *hash) /* {{{ */
+static spl_SplObjectStorageElement* spl_object_storage_get(spl_SplObjectStorage *intern, zend_hash_key *key) /* {{{ */
{
- return (spl_SplObjectStorageElement*)zend_hash_find_ptr(&intern->storage, hash);
+ if (key->key) {
+ return zend_hash_find_ptr(&intern->storage, key->key);
+ } else {
+ return zend_hash_index_find_ptr(&intern->storage, key->h);
+ }
} /* }}} */
spl_SplObjectStorageElement *spl_object_storage_attach(spl_SplObjectStorage *intern, zval *this, zval *obj, zval *inf) /* {{{ */
{
spl_SplObjectStorageElement *pelement, element;
- zend_string *hash = spl_object_storage_get_hash(intern, this, obj);
-
- if (!hash) {
+ zend_hash_key key;
+ if (spl_object_storage_get_hash(&key, intern, this, obj) == FAILURE) {
return NULL;
}
- pelement = spl_object_storage_get(intern, hash);
+ pelement = spl_object_storage_get(intern, &key);
if (pelement) {
zval_ptr_dtor(&pelement->inf);
@@ -176,7 +181,7 @@ spl_SplObjectStorageElement *spl_object_storage_attach(spl_SplObjectStorage *int
} else {
ZVAL_NULL(&pelement->inf);
}
- spl_object_storage_free_hash(intern, hash);
+ spl_object_storage_free_hash(intern, &key);
return pelement;
}
@@ -186,20 +191,28 @@ spl_SplObjectStorageElement *spl_object_storage_attach(spl_SplObjectStorage *int
} else {
ZVAL_NULL(&element.inf);
}
- pelement = zend_hash_update_mem(&intern->storage, hash, &element, sizeof(spl_SplObjectStorageElement));
- spl_object_storage_free_hash(intern, hash);
+ if (key.key) {
+ pelement = zend_hash_update_mem(&intern->storage, key.key, &element, sizeof(spl_SplObjectStorageElement));
+ } else {
+ pelement = zend_hash_index_update_mem(&intern->storage, key.h, &element, sizeof(spl_SplObjectStorageElement));
+ }
+ spl_object_storage_free_hash(intern, &key);
return pelement;
} /* }}} */
-int spl_object_storage_detach(spl_SplObjectStorage *intern, zval *this, zval *obj) /* {{{ */
+static int spl_object_storage_detach(spl_SplObjectStorage *intern, zval *this, zval *obj) /* {{{ */
{
int ret = FAILURE;
- zend_string *hash = spl_object_storage_get_hash(intern, this, obj);
- if (!hash) {
+ zend_hash_key key;
+ if (spl_object_storage_get_hash(&key, intern, this, obj) == FAILURE) {
return ret;
}
- ret = zend_hash_del(&intern->storage, hash);
- spl_object_storage_free_hash(intern, hash);
+ if (key.key) {
+ ret = zend_hash_del(&intern->storage, key.key);
+ } else {
+ ret = zend_hash_index_del(&intern->storage, key.h);
+ }
+ spl_object_storage_free_hash(intern, &key);
return ret;
} /* }}}*/
@@ -369,13 +382,17 @@ static zend_object *spl_SplObjectStorage_new(zend_class_entry *class_type)
int spl_object_storage_contains(spl_SplObjectStorage *intern, zval *this, zval *obj) /* {{{ */
{
int found;
- zend_string *hash = spl_object_storage_get_hash(intern, this, obj);
- if (!hash) {
+ zend_hash_key key;
+ if (spl_object_storage_get_hash(&key, intern, this, obj) == FAILURE) {
return 0;
}
- found = zend_hash_exists(&intern->storage, hash);
- spl_object_storage_free_hash(intern, hash);
+ if (key.key) {
+ found = zend_hash_exists(&intern->storage, key.key);
+ } else {
+ found = zend_hash_index_exists(&intern->storage, key.h);
+ }
+ spl_object_storage_free_hash(intern, &key);
return found;
} /* }}} */
@@ -430,19 +447,18 @@ SPL_METHOD(SplObjectStorage, offsetGet)
zval *obj;
spl_SplObjectStorageElement *element;
spl_SplObjectStorage *intern = Z_SPLOBJSTORAGE_P(getThis());
- zend_string *hash;
+ zend_hash_key key;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "o", &obj) == FAILURE) {
return;
}
- hash = spl_object_storage_get_hash(intern, getThis(), obj);
- if (!hash) {
+ if (spl_object_storage_get_hash(&key, intern, getThis(), obj) == FAILURE) {
return;
}
- element = spl_object_storage_get(intern, hash);
- spl_object_storage_free_hash(intern, hash);
+ element = spl_object_storage_get(intern, &key);
+ spl_object_storage_free_hash(intern, &key);
if (!element) {
zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "Object not found");
@@ -777,7 +793,7 @@ SPL_METHOD(SplObjectStorage, unserialize)
while (count-- > 0) {
spl_SplObjectStorageElement *pelement;
- zend_string *hash;
+ zend_hash_key key;
if (*p != ';') {
goto outexcept;
@@ -803,14 +819,13 @@ SPL_METHOD(SplObjectStorage, unserialize)
goto outexcept;
}
- hash = spl_object_storage_get_hash(intern, getThis(), &entry);
- if (!hash) {
+ if (spl_object_storage_get_hash(&key, intern, getThis(), &entry) == FAILURE) {
zval_ptr_dtor(&entry);
zval_ptr_dtor(&inf);
goto outexcept;
}
- pelement = spl_object_storage_get(intern, hash);
- spl_object_storage_free_hash(intern, hash);
+ pelement = spl_object_storage_get(intern, &key);
+ spl_object_storage_free_hash(intern, &key);
if (pelement) {
if (!Z_ISUNDEF(pelement->inf)) {
var_push_dtor(&var_hash, &pelement->inf);
@@ -852,7 +867,7 @@ SPL_METHOD(SplObjectStorage, unserialize)
outexcept:
PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
- zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "Error at offset %pd of %d bytes", (zend_long)((char*)p - buf), buf_len);
+ zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "Error at offset %zd of %zd bytes", ((char*)p - buf), buf_len);
return;
} /* }}} */
@@ -911,7 +926,7 @@ static const zend_function_entry spl_funcs_SplObjectStorage[] = {
SPL_MA(SplObjectStorage, offsetSet, SplObjectStorage, attach, arginfo_attach, 0)
SPL_MA(SplObjectStorage, offsetUnset, SplObjectStorage, detach, arginfo_offsetGet, 0)
SPL_ME(SplObjectStorage, offsetGet, arginfo_offsetGet, 0)
- {NULL, NULL, NULL}
+ PHP_FE_END
};
typedef enum {
@@ -1220,7 +1235,7 @@ static const zend_function_entry spl_funcs_MultipleIterator[] = {
SPL_ME(MultipleIterator, key, arginfo_splobject_void, 0)
SPL_ME(MultipleIterator, current, arginfo_splobject_void, 0)
SPL_ME(MultipleIterator, next, arginfo_splobject_void, 0)
- {NULL, NULL, NULL}
+ PHP_FE_END
};
/* {{{ PHP_MINIT_FUNCTION(spl_observer) */
diff --git a/ext/spl/tests/array_013.phpt b/ext/spl/tests/array_013.phpt
index 3fda53884f..c2dc1f4989 100644
--- a/ext/spl/tests/array_013.phpt
+++ b/ext/spl/tests/array_013.phpt
@@ -76,4 +76,8 @@ one=>1
two=>2
===Append===
-Catchable fatal error: ArrayIterator::append(): Cannot append properties to objects, use ArrayIterator::offsetSet() instead in %sarray_013.php on line %d
+Fatal error: Uncaught Error: Cannot append properties to objects, use ArrayIterator::offsetSet() instead in %s:%d
+Stack trace:
+#0 %s(%d): ArrayIterator->append('three')
+#1 {main}
+ thrown in %s on line %d \ No newline at end of file
diff --git a/ext/spl/tests/bug62904.phpt b/ext/spl/tests/bug62904.phpt
index 7e392da9ab..8ce0d7bc79 100644
--- a/ext/spl/tests/bug62904.phpt
+++ b/ext/spl/tests/bug62904.phpt
@@ -10,10 +10,9 @@ class foo extends SplFixedArray {
$x = new foo(2);
-try {
- $z = clone $x;
-} catch (Exception $e) {
- var_dump($e->getMessage());
-}
+$z = clone $x;
+echo "No crash.";
+
--EXPECTF--
-string(40) "The instance wasn't initialized properly"
+No crash.
+
diff --git a/ext/spl/tests/bug64106.phpt b/ext/spl/tests/bug64106.phpt
index 855caef213..26203c4e2e 100644
--- a/ext/spl/tests/bug64106.phpt
+++ b/ext/spl/tests/bug64106.phpt
@@ -4,7 +4,7 @@ Bug #64106: Segfault on SplFixedArray[][x] = y when extended
<?php
class MyFixedArray extends SplFixedArray {
- public function offsetGet($offset) {}
+ public function offsetGet($offset) { var_dump($offset); }
}
$array = new MyFixedArray(10);
@@ -12,4 +12,6 @@ $array[][1] = 10;
?>
--EXPECTF--
+NULL
+
Notice: Indirect modification of overloaded element of MyFixedArray has no effect in %s on line %d
diff --git a/ext/spl/tests/bug72888.phpt b/ext/spl/tests/bug72888.phpt
new file mode 100644
index 0000000000..7d2fc6db08
--- /dev/null
+++ b/ext/spl/tests/bug72888.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Bug #72888 (Segfault on clone on splFileObject)
+--FILE--
+<?php
+$x = new SplFileObject(__FILE__);
+
+try {
+ $y=clone $x;
+} catch (Error $e) {
+ var_dump($e->getMessage());
+}
+var_dump($y);
+?>
+--EXPECTF--
+string(49) "An object of class SplFileObject cannot be cloned"
+
+Notice: Undefined variable: y in %sbug72888.php on line %d
+NULL
diff --git a/ext/spl/tests/bug73686.phpt b/ext/spl/tests/bug73686.phpt
new file mode 100644
index 0000000000..ae9a59c4aa
--- /dev/null
+++ b/ext/spl/tests/bug73686.phpt
@@ -0,0 +1,45 @@
+--TEST--
+Bug #73686 (Adding settype()ed values to ArrayObject results in references)
+--FILE--
+<?php
+
+$ao = new ArrayObject;
+
+foreach ([1, 2, 3] as $i => $var)
+{
+ settype($var, 'string');
+ $ao[$i] = $var;
+}
+var_dump($ao);
+
+$ao = new ArrayObject;
+
+foreach ([1, 2, 3] as $i => $var)
+{
+ $ao[$i] = &$var;
+}
+var_dump($ao);
+?>
+--EXPECTF--
+object(ArrayObject)#%d (1) {
+ ["storage":"ArrayObject":private]=>
+ array(3) {
+ [0]=>
+ string(1) "1"
+ [1]=>
+ string(1) "2"
+ [2]=>
+ string(1) "3"
+ }
+}
+object(ArrayObject)#%d (1) {
+ ["storage":"ArrayObject":private]=>
+ array(3) {
+ [0]=>
+ &int(3)
+ [1]=>
+ &int(3)
+ [2]=>
+ &int(3)
+ }
+}
diff --git a/ext/spl/tests/bug74058.phpt b/ext/spl/tests/bug74058.phpt
new file mode 100644
index 0000000000..a416d8f15a
--- /dev/null
+++ b/ext/spl/tests/bug74058.phpt
@@ -0,0 +1,81 @@
+--TEST--
+Bug #74058 (ArrayObject can not notice changes)
+--FILE--
+<?php
+
+class MyArrayObject extends ArrayObject
+{
+ public function __construct($input = [])
+ {
+ parent::__construct($input, ArrayObject::ARRAY_AS_PROPS);
+ }
+
+ public function offsetSet($x, $v)
+ {
+ echo "offsetSet('{$x}')\n";
+ return parent::offsetSet($x, $v);
+ }
+
+ public function offsetGet($x)
+ {
+ echo "offsetGet('{$x}')\n";
+ return parent::offsetGet($x);
+ }
+}
+
+class MyArray extends ArrayObject
+{
+ public function __construct($input = [])
+ {
+ parent::__construct($input);
+ }
+
+ public function offsetSet($x, $v)
+ {
+ echo "offsetSet('{$x}')\n";
+ return parent::offsetSet($x, $v);
+ }
+
+ public function offsetGet($x)
+ {
+ echo "offsetGet('{$x}')\n";
+ return parent::offsetGet($x);
+ }
+}
+
+$x = new MyArrayObject;
+$x->a1 = new stdClass();
+var_dump($x->a1);
+
+$x->a1->b = 'some value';
+var_dump($x->a1);
+
+$y = new MyArray();
+$y['a2'] = new stdClass();
+var_dump($y['a2']);
+
+$y['a2']->b = 'some value';
+var_dump($y['a2']);
+
+?>
+--EXPECTF--
+offsetSet('a1')
+offsetGet('a1')
+object(stdClass)#%s (0) {
+}
+offsetGet('a1')
+offsetGet('a1')
+object(stdClass)#%s (1) {
+ ["b"]=>
+ string(10) "some value"
+}
+offsetSet('a2')
+offsetGet('a2')
+object(stdClass)#%s (0) {
+}
+offsetGet('a2')
+offsetGet('a2')
+object(stdClass)#%s (1) {
+ ["b"]=>
+ string(10) "some value"
+}
diff --git a/ext/spl/tests/bug74977.phpt b/ext/spl/tests/bug74977.phpt
new file mode 100644
index 0000000000..09e16eedfe
--- /dev/null
+++ b/ext/spl/tests/bug74977.phpt
@@ -0,0 +1,13 @@
+--TEST--
+Bug #74977: Recursion leads to crash
+--FILE--
+<?php
+
+$iterator = new AppendIterator(array("A","A","A"));
+$iterator->append($iterator);
+var_dump($iterator);
+?>
+--EXPECTF--
+object(AppendIterator)#1 (0) {
+}
+
diff --git a/ext/spl/tests/spl_004.phpt b/ext/spl/tests/spl_004.phpt
index ac44b9d684..be0cb804c6 100644
--- a/ext/spl/tests/spl_004.phpt
+++ b/ext/spl/tests/spl_004.phpt
@@ -78,7 +78,7 @@ int(5)
int(6)
int(4)
===ERRORS===
-Error: Argument 3 passed to iterator_apply() must be of the type array, integer given
+Error: Argument 3 passed to iterator_apply() must be of the type array or null, integer given
Error: iterator_apply() expects parameter 2 to be a valid callback, function 'non_existing_function' not found or invalid function name
NULL
Error: iterator_apply() expects at most 3 parameters, 4 given
diff --git a/ext/sqlite3/libsqlite/sqlite3.c b/ext/sqlite3/libsqlite/sqlite3.c
index 9e962ce13d..4a87906e12 100644
--- a/ext/sqlite3/libsqlite/sqlite3.c
+++ b/ext/sqlite3/libsqlite/sqlite3.c
@@ -1,6 +1,6 @@
/******************************************************************************
** This file is an amalgamation of many separate C source files from SQLite
-** version 3.14.2. By combining all the individual C code files into this
+** version 3.15.1. By combining all the individual C code files into this
** single large file, the entire code can be compiled as a single translation
** unit. This allows many compilers to do optimizations that would not be
** possible if the files were compiled separately. Performance improvements
@@ -368,7 +368,8 @@ extern "C" {
** be held constant and Z will be incremented or else Y will be incremented
** and Z will be reset to zero.
**
-** Since version 3.6.18, SQLite source code has been stored in the
+** Since [version 3.6.18] ([dateof:3.6.18]),
+** SQLite source code has been stored in the
** <a href="http://www.fossil-scm.org/">Fossil configuration management
** system</a>. ^The SQLITE_SOURCE_ID macro evaluates to
** a string which identifies a particular check-in of SQLite
@@ -380,9 +381,9 @@ extern "C" {
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
-#define SQLITE_VERSION "3.14.2"
-#define SQLITE_VERSION_NUMBER 3014002
-#define SQLITE_SOURCE_ID "2016-09-12 18:50:49 29dbef4b8585f753861a36d6dd102ca634197bd6"
+#define SQLITE_VERSION "3.15.1"
+#define SQLITE_VERSION_NUMBER 3015001
+#define SQLITE_SOURCE_ID "2016-11-04 12:08:49 1136863c76576110e710dd5d69ab6bf347c65e36"
/*
** CAPI3REF: Run-Time Library Version Numbers
@@ -712,7 +713,8 @@ SQLITE_API int sqlite3_exec(
** [result codes]. However, experience has shown that many of
** these result codes are too coarse-grained. They do not provide as
** much information about problems as programmers might like. In an effort to
-** address this, newer versions of SQLite (version 3.3.8 and later) include
+** address this, newer versions of SQLite (version 3.3.8 [dateof:3.3.8]
+** and later) include
** support for additional result codes that provide more detailed information
** about errors. These [extended result codes] are enabled or disabled
** on a per database connection basis using the
@@ -1236,6 +1238,12 @@ struct sqlite3_io_methods {
** on whether or not the file has been renamed, moved, or deleted since it
** was first opened.
**
+** <li>[[SQLITE_FCNTL_WIN32_GET_HANDLE]]
+** The [SQLITE_FCNTL_WIN32_GET_HANDLE] opcode can be used to obtain the
+** underlying native file handle associated with a file handle. This file
+** control interprets its argument as a pointer to a native file handle and
+** writes the resulting value there.
+**
** <li>[[SQLITE_FCNTL_WIN32_SET_HANDLE]]
** The [SQLITE_FCNTL_WIN32_SET_HANDLE] opcode is used for debugging. This
** opcode causes the xFileControl method to swap the file handle with the one
@@ -1286,6 +1294,7 @@ struct sqlite3_io_methods {
#define SQLITE_FCNTL_RBU 26
#define SQLITE_FCNTL_VFS_POINTER 27
#define SQLITE_FCNTL_JOURNAL_POINTER 28
+#define SQLITE_FCNTL_WIN32_GET_HANDLE 29
/* deprecated names */
#define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE
@@ -2229,8 +2238,18 @@ struct sqlite3_mem_methods {
** be a NULL pointer, in which case the new setting is not reported back.
** </dd>
**
+** <dt>SQLITE_DBCONFIG_MAINDBNAME</dt>
+** <dd> ^This option is used to change the name of the "main" database
+** schema. ^The sole argument is a pointer to a constant UTF8 string
+** which will become the new schema name in place of "main". ^SQLite
+** does not make a copy of the new main schema name string, so the application
+** must ensure that the argument passed into this DBCONFIG option is unchanged
+** until after the database connection closes.
+** </dd>
+**
** </dl>
*/
+#define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */
#define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */
#define SQLITE_DBCONFIG_ENABLE_FKEY 1002 /* int int* */
#define SQLITE_DBCONFIG_ENABLE_TRIGGER 1003 /* int int* */
@@ -4301,7 +4320,8 @@ SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt*,int);
** other than [SQLITE_ROW] before any subsequent invocation of
** sqlite3_step(). Failure to reset the prepared statement using
** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from
-** sqlite3_step(). But after version 3.6.23.1, sqlite3_step() began
+** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1],
+** sqlite3_step() began
** calling [sqlite3_reset()] automatically in this circumstance rather
** than returning [SQLITE_MISUSE]. This is not considered a compatibility
** break because any application that ever receives an SQLITE_MISUSE error
@@ -5664,7 +5684,8 @@ SQLITE_API void *sqlite3_update_hook(
** and disabled if the argument is false.)^
**
** ^Cache sharing is enabled and disabled for an entire process.
-** This is a change as of SQLite version 3.5.0. In prior versions of SQLite,
+** This is a change as of SQLite [version 3.5.0] ([dateof:3.5.0]).
+** In prior versions of SQLite,
** sharing was enabled or disabled for each thread separately.
**
** ^(The cache sharing mode set by this interface effects all subsequent
@@ -5758,7 +5779,8 @@ SQLITE_API int sqlite3_db_release_memory(sqlite3*);
** from the heap.
** </ul>)^
**
-** Beginning with SQLite version 3.7.3, the soft heap limit is enforced
+** Beginning with SQLite [version 3.7.3] ([dateof:3.7.3]),
+** the soft heap limit is enforced
** regardless of whether or not the [SQLITE_ENABLE_MEMORY_MANAGEMENT]
** compile-time option is invoked. With [SQLITE_ENABLE_MEMORY_MANAGEMENT],
** the soft heap limit is enforced on every memory allocation. Without
@@ -6152,13 +6174,15 @@ struct sqlite3_module {
** the xUpdate method are automatically rolled back by SQLite.
**
** IMPORTANT: The estimatedRows field was added to the sqlite3_index_info
-** structure for SQLite version 3.8.2. If a virtual table extension is
+** structure for SQLite [version 3.8.2] ([dateof:3.8.2]).
+** If a virtual table extension is
** used with an SQLite version earlier than 3.8.2, the results of attempting
** to read or write the estimatedRows field are undefined (but are likely
** to included crashing the application). The estimatedRows field should
** therefore only be used if [sqlite3_libversion_number()] returns a
** value greater than or equal to 3008002. Similarly, the idxFlags field
-** was added for version 3.9.0. It may therefore only be used if
+** was added for [version 3.9.0] ([dateof:3.9.0]).
+** It may therefore only be used if
** sqlite3_libversion_number() returns a value greater than or equal to
** 3009000.
*/
@@ -6856,7 +6880,7 @@ SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex*);
#define SQLITE_MUTEX_STATIC_MEM 3 /* sqlite3_malloc() */
#define SQLITE_MUTEX_STATIC_MEM2 4 /* NOT USED */
#define SQLITE_MUTEX_STATIC_OPEN 4 /* sqlite3BtreeOpen() */
-#define SQLITE_MUTEX_STATIC_PRNG 5 /* sqlite3_random() */
+#define SQLITE_MUTEX_STATIC_PRNG 5 /* sqlite3_randomness() */
#define SQLITE_MUTEX_STATIC_LRU 6 /* lru page list */
#define SQLITE_MUTEX_STATIC_LRU2 7 /* NOT USED */
#define SQLITE_MUTEX_STATIC_PMEM 7 /* sqlite3PageMalloc() */
@@ -6960,6 +6984,7 @@ SQLITE_API int sqlite3_test_control(int op, ...);
#define SQLITE_TESTCTRL_SCRATCHMALLOC 17
#define SQLITE_TESTCTRL_LOCALTIME_FAULT 18
#define SQLITE_TESTCTRL_EXPLAIN_STMT 19 /* NOT USED */
+#define SQLITE_TESTCTRL_ONCE_RESET_THRESHOLD 19
#define SQLITE_TESTCTRL_NEVER_CORRUPT 20
#define SQLITE_TESTCTRL_VDBE_COVERAGE 21
#define SQLITE_TESTCTRL_BYTEORDER 22
@@ -8899,7 +8924,7 @@ int sqlite3session_attach(
** CAPI3REF: Set a table filter on a Session Object.
**
** The second argument (xFilter) is the "filter callback". For changes to rows
-** in tables that are not attached to the Session oject, the filter is called
+** in tables that are not attached to the Session object, the filter is called
** to determine whether changes to the table's rows should be tracked or not.
** If xFilter returns 0, changes is not tracked. Note that once a table is
** attached, xFilter will not be called again.
@@ -9165,7 +9190,7 @@ int sqlite3session_isempty(sqlite3_session *pSession);
** [sqlite3changeset_invert()] functions, all changes within the changeset
** that apply to a single table are grouped together. This means that when
** an application iterates through a changeset using an iterator created by
-** this function, all changes that relate to a single table are visted
+** this function, all changes that relate to a single table are visited
** consecutively. There is no chance that the iterator will visit a change
** the applies to table X, then one for table Y, and then later on visit
** another change for table X.
@@ -9252,7 +9277,7 @@ int sqlite3changeset_op(
** 0x01 if the corresponding column is part of the tables primary key, or
** 0x00 if it is not.
**
-** If argumet pnCol is not NULL, then *pnCol is set to the number of columns
+** If argument pnCol is not NULL, then *pnCol is set to the number of columns
** in the table.
**
** If this function is called when the iterator does not point to a valid
@@ -9469,12 +9494,12 @@ int sqlite3changeset_concat(
/*
-** Changegroup handle.
+** CAPI3REF: Changegroup Handle
*/
typedef struct sqlite3_changegroup sqlite3_changegroup;
/*
-** CAPI3REF: Combine two or more changesets into a single changeset.
+** CAPI3REF: Create A New Changegroup Object
**
** An sqlite3_changegroup object is used to combine two or more changesets
** (or patchsets) into a single changeset (or patchset). A single changegroup
@@ -9511,6 +9536,8 @@ typedef struct sqlite3_changegroup sqlite3_changegroup;
int sqlite3changegroup_new(sqlite3_changegroup **pp);
/*
+** CAPI3REF: Add A Changeset To A Changegroup
+**
** Add all changes within the changeset (or patchset) in buffer pData (size
** nData bytes) to the changegroup.
**
@@ -9525,7 +9552,7 @@ int sqlite3changegroup_new(sqlite3_changegroup **pp);
** apply to the same row as a change already present in the changegroup if
** the two rows have the same primary key.
**
-** Changes to rows that that do not already appear in the changegroup are
+** Changes to rows that do not already appear in the changegroup are
** simply copied into it. Or, if both the new changeset and the changegroup
** contain changes that apply to a single row, the final contents of the
** changegroup depends on the type of each change, as follows:
@@ -9586,6 +9613,8 @@ int sqlite3changegroup_new(sqlite3_changegroup **pp);
int sqlite3changegroup_add(sqlite3_changegroup*, int nData, void *pData);
/*
+** CAPI3REF: Obtain A Composite Changeset From A Changegroup
+**
** Obtain a buffer containing a changeset (or patchset) representing the
** current contents of the changegroup. If the inputs to the changegroup
** were themselves changesets, the output is a changeset. Or, if the
@@ -9614,7 +9643,7 @@ int sqlite3changegroup_output(
);
/*
-** Delete a changegroup object.
+** CAPI3REF: Delete A Changegroup Object
*/
void sqlite3changegroup_delete(sqlite3_changegroup*);
@@ -11391,9 +11420,9 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*);
#define TK_LIMIT 129
#define TK_WHERE 130
#define TK_INTO 131
-#define TK_INTEGER 132
-#define TK_FLOAT 133
-#define TK_BLOB 134
+#define TK_FLOAT 132
+#define TK_BLOB 133
+#define TK_INTEGER 134
#define TK_VARIABLE 135
#define TK_CASE 136
#define TK_WHEN 137
@@ -11417,10 +11446,12 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*);
#define TK_UMINUS 155
#define TK_UPLUS 156
#define TK_REGISTER 157
-#define TK_ASTERISK 158
-#define TK_SPAN 159
-#define TK_SPACE 160
-#define TK_ILLEGAL 161
+#define TK_VECTOR 158
+#define TK_SELECT_COLUMN 159
+#define TK_ASTERISK 160
+#define TK_SPAN 161
+#define TK_SPACE 162
+#define TK_ILLEGAL 163
/* The token codes above must all fit in 8 bits */
#define TKFLG_MASK 0xff
@@ -12079,7 +12110,9 @@ SQLITE_PRIVATE int sqlite3BtreeIsInReadTrans(Btree*);
SQLITE_PRIVATE int sqlite3BtreeIsInBackup(Btree*);
SQLITE_PRIVATE void *sqlite3BtreeSchema(Btree *, int, void(*)(void *));
SQLITE_PRIVATE int sqlite3BtreeSchemaLocked(Btree *pBtree);
+#ifndef SQLITE_OMIT_SHARED_CACHE
SQLITE_PRIVATE int sqlite3BtreeLockTable(Btree *pBtree, int iTab, u8 isWriteLock);
+#endif
SQLITE_PRIVATE int sqlite3BtreeSavepoint(Btree *, int, int);
SQLITE_PRIVATE const char *sqlite3BtreeGetFilename(Btree *);
@@ -12282,8 +12315,10 @@ SQLITE_PRIVATE int sqlite3BtreeData(BtCursor*, u32 offset, u32 amt, void*);
SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck(Btree*, int *aRoot, int nRoot, int, int*);
SQLITE_PRIVATE struct Pager *sqlite3BtreePager(Btree*);
+#ifndef SQLITE_OMIT_INCRBLOB
SQLITE_PRIVATE int sqlite3BtreePutData(BtCursor*, u32 offset, u32 amt, void*);
SQLITE_PRIVATE void sqlite3BtreeIncrblobCursor(BtCursor *);
+#endif
SQLITE_PRIVATE void sqlite3BtreeClearCursor(BtCursor *);
SQLITE_PRIVATE int sqlite3BtreeSetVersion(Btree *pBt, int iVersion);
SQLITE_PRIVATE int sqlite3BtreeCursorHasHint(BtCursor*, unsigned int mask);
@@ -12444,7 +12479,6 @@ struct SubProgram {
int nOp; /* Elements in aOp[] */
int nMem; /* Number of memory cells required */
int nCsr; /* Number of cursors required */
- int nOnce; /* Number of OP_Once instructions */
void *token; /* id that may be used to recursive triggers */
SubProgram *pNext; /* Next sub-program already visited */
};
@@ -12567,7 +12601,7 @@ typedef struct VdbeOpList VdbeOpList;
#define OP_Le 39 /* same as TK_LE, synopsis: IF r[P3]<=r[P1] */
#define OP_Lt 40 /* same as TK_LT, synopsis: IF r[P3]<r[P1] */
#define OP_Ge 41 /* same as TK_GE, synopsis: IF r[P3]>=r[P1] */
-#define OP_Last 42
+#define OP_ElseNotEq 42 /* same as TK_ESCAPE */
#define OP_BitAnd 43 /* same as TK_BITAND, synopsis: r[P3]=r[P1]&r[P2] */
#define OP_BitOr 44 /* same as TK_BITOR, synopsis: r[P3]=r[P1]|r[P2] */
#define OP_ShiftLeft 45 /* same as TK_LSHIFT, synopsis: r[P3]=r[P2]<<r[P1] */
@@ -12578,115 +12612,116 @@ typedef struct VdbeOpList VdbeOpList;
#define OP_Divide 50 /* same as TK_SLASH, synopsis: r[P3]=r[P2]/r[P1] */
#define OP_Remainder 51 /* same as TK_REM, synopsis: r[P3]=r[P2]%r[P1] */
#define OP_Concat 52 /* same as TK_CONCAT, synopsis: r[P3]=r[P2]+r[P1] */
-#define OP_SorterSort 53
+#define OP_Last 53
#define OP_BitNot 54 /* same as TK_BITNOT, synopsis: r[P1]= ~r[P1] */
-#define OP_Sort 55
-#define OP_Rewind 56
-#define OP_IdxLE 57 /* synopsis: key=r[P3@P4] */
-#define OP_IdxGT 58 /* synopsis: key=r[P3@P4] */
-#define OP_IdxLT 59 /* synopsis: key=r[P3@P4] */
-#define OP_IdxGE 60 /* synopsis: key=r[P3@P4] */
-#define OP_RowSetRead 61 /* synopsis: r[P3]=rowset(P1) */
-#define OP_RowSetTest 62 /* synopsis: if r[P3] in rowset(P1) goto P2 */
-#define OP_Program 63
-#define OP_FkIfZero 64 /* synopsis: if fkctr[P1]==0 goto P2 */
-#define OP_IfPos 65 /* synopsis: if r[P1]>0 then r[P1]-=P3, goto P2 */
-#define OP_IfNotZero 66 /* synopsis: if r[P1]!=0 then r[P1]-=P3, goto P2 */
-#define OP_DecrJumpZero 67 /* synopsis: if (--r[P1])==0 goto P2 */
-#define OP_IncrVacuum 68
-#define OP_VNext 69
-#define OP_Init 70 /* synopsis: Start at P2 */
-#define OP_Return 71
-#define OP_EndCoroutine 72
-#define OP_HaltIfNull 73 /* synopsis: if r[P3]=null halt */
-#define OP_Halt 74
-#define OP_Integer 75 /* synopsis: r[P2]=P1 */
-#define OP_Int64 76 /* synopsis: r[P2]=P4 */
-#define OP_String 77 /* synopsis: r[P2]='P4' (len=P1) */
-#define OP_Null 78 /* synopsis: r[P2..P3]=NULL */
-#define OP_SoftNull 79 /* synopsis: r[P1]=NULL */
-#define OP_Blob 80 /* synopsis: r[P2]=P4 (len=P1) */
-#define OP_Variable 81 /* synopsis: r[P2]=parameter(P1,P4) */
-#define OP_Move 82 /* synopsis: r[P2@P3]=r[P1@P3] */
-#define OP_Copy 83 /* synopsis: r[P2@P3+1]=r[P1@P3+1] */
-#define OP_SCopy 84 /* synopsis: r[P2]=r[P1] */
-#define OP_IntCopy 85 /* synopsis: r[P2]=r[P1] */
-#define OP_ResultRow 86 /* synopsis: output=r[P1@P2] */
-#define OP_CollSeq 87
-#define OP_Function0 88 /* synopsis: r[P3]=func(r[P2@P5]) */
-#define OP_Function 89 /* synopsis: r[P3]=func(r[P2@P5]) */
-#define OP_AddImm 90 /* synopsis: r[P1]=r[P1]+P2 */
-#define OP_RealAffinity 91
-#define OP_Cast 92 /* synopsis: affinity(r[P1]) */
-#define OP_Permutation 93
-#define OP_Compare 94 /* synopsis: r[P1@P3] <-> r[P2@P3] */
-#define OP_Column 95 /* synopsis: r[P3]=PX */
-#define OP_Affinity 96 /* synopsis: affinity(r[P1@P2]) */
+#define OP_SorterSort 55
+#define OP_Sort 56
+#define OP_Rewind 57
+#define OP_IdxLE 58 /* synopsis: key=r[P3@P4] */
+#define OP_IdxGT 59 /* synopsis: key=r[P3@P4] */
+#define OP_IdxLT 60 /* synopsis: key=r[P3@P4] */
+#define OP_IdxGE 61 /* synopsis: key=r[P3@P4] */
+#define OP_RowSetRead 62 /* synopsis: r[P3]=rowset(P1) */
+#define OP_RowSetTest 63 /* synopsis: if r[P3] in rowset(P1) goto P2 */
+#define OP_Program 64
+#define OP_FkIfZero 65 /* synopsis: if fkctr[P1]==0 goto P2 */
+#define OP_IfPos 66 /* synopsis: if r[P1]>0 then r[P1]-=P3, goto P2 */
+#define OP_IfNotZero 67 /* synopsis: if r[P1]!=0 then r[P1]-=P3, goto P2 */
+#define OP_DecrJumpZero 68 /* synopsis: if (--r[P1])==0 goto P2 */
+#define OP_IncrVacuum 69
+#define OP_VNext 70
+#define OP_Init 71 /* synopsis: Start at P2 */
+#define OP_Return 72
+#define OP_EndCoroutine 73
+#define OP_HaltIfNull 74 /* synopsis: if r[P3]=null halt */
+#define OP_Halt 75
+#define OP_Integer 76 /* synopsis: r[P2]=P1 */
+#define OP_Int64 77 /* synopsis: r[P2]=P4 */
+#define OP_String 78 /* synopsis: r[P2]='P4' (len=P1) */
+#define OP_Null 79 /* synopsis: r[P2..P3]=NULL */
+#define OP_SoftNull 80 /* synopsis: r[P1]=NULL */
+#define OP_Blob 81 /* synopsis: r[P2]=P4 (len=P1) */
+#define OP_Variable 82 /* synopsis: r[P2]=parameter(P1,P4) */
+#define OP_Move 83 /* synopsis: r[P2@P3]=r[P1@P3] */
+#define OP_Copy 84 /* synopsis: r[P2@P3+1]=r[P1@P3+1] */
+#define OP_SCopy 85 /* synopsis: r[P2]=r[P1] */
+#define OP_IntCopy 86 /* synopsis: r[P2]=r[P1] */
+#define OP_ResultRow 87 /* synopsis: output=r[P1@P2] */
+#define OP_CollSeq 88
+#define OP_Function0 89 /* synopsis: r[P3]=func(r[P2@P5]) */
+#define OP_Function 90 /* synopsis: r[P3]=func(r[P2@P5]) */
+#define OP_AddImm 91 /* synopsis: r[P1]=r[P1]+P2 */
+#define OP_RealAffinity 92
+#define OP_Cast 93 /* synopsis: affinity(r[P1]) */
+#define OP_Permutation 94
+#define OP_Compare 95 /* synopsis: r[P1@P3] <-> r[P2@P3] */
+#define OP_Column 96 /* synopsis: r[P3]=PX */
#define OP_String8 97 /* same as TK_STRING, synopsis: r[P2]='P4' */
-#define OP_MakeRecord 98 /* synopsis: r[P3]=mkrec(r[P1@P2]) */
-#define OP_Count 99 /* synopsis: r[P2]=count() */
-#define OP_ReadCookie 100
-#define OP_SetCookie 101
-#define OP_ReopenIdx 102 /* synopsis: root=P2 iDb=P3 */
-#define OP_OpenRead 103 /* synopsis: root=P2 iDb=P3 */
-#define OP_OpenWrite 104 /* synopsis: root=P2 iDb=P3 */
-#define OP_OpenAutoindex 105 /* synopsis: nColumn=P2 */
-#define OP_OpenEphemeral 106 /* synopsis: nColumn=P2 */
-#define OP_SorterOpen 107
-#define OP_SequenceTest 108 /* synopsis: if( cursor[P1].ctr++ ) pc = P2 */
-#define OP_OpenPseudo 109 /* synopsis: P3 columns in r[P2] */
-#define OP_Close 110
-#define OP_ColumnsUsed 111
-#define OP_Sequence 112 /* synopsis: r[P2]=cursor[P1].ctr++ */
-#define OP_NewRowid 113 /* synopsis: r[P2]=rowid */
-#define OP_Insert 114 /* synopsis: intkey=r[P3] data=r[P2] */
-#define OP_InsertInt 115 /* synopsis: intkey=P3 data=r[P2] */
-#define OP_Delete 116
-#define OP_ResetCount 117
-#define OP_SorterCompare 118 /* synopsis: if key(P1)!=trim(r[P3],P4) goto P2 */
-#define OP_SorterData 119 /* synopsis: r[P2]=data */
-#define OP_RowKey 120 /* synopsis: r[P2]=key */
-#define OP_RowData 121 /* synopsis: r[P2]=data */
-#define OP_Rowid 122 /* synopsis: r[P2]=rowid */
-#define OP_NullRow 123
-#define OP_SorterInsert 124
-#define OP_IdxInsert 125 /* synopsis: key=r[P2] */
-#define OP_IdxDelete 126 /* synopsis: key=r[P2@P3] */
-#define OP_Seek 127 /* synopsis: Move P3 to P1.rowid */
-#define OP_IdxRowid 128 /* synopsis: r[P2]=rowid */
-#define OP_Destroy 129
-#define OP_Clear 130
-#define OP_ResetSorter 131
-#define OP_CreateIndex 132 /* synopsis: r[P2]=root iDb=P1 */
-#define OP_Real 133 /* same as TK_FLOAT, synopsis: r[P2]=P4 */
-#define OP_CreateTable 134 /* synopsis: r[P2]=root iDb=P1 */
-#define OP_ParseSchema 135
-#define OP_LoadAnalysis 136
-#define OP_DropTable 137
-#define OP_DropIndex 138
-#define OP_DropTrigger 139
-#define OP_IntegrityCk 140
-#define OP_RowSetAdd 141 /* synopsis: rowset(P1)=r[P2] */
-#define OP_Param 142
-#define OP_FkCounter 143 /* synopsis: fkctr[P1]+=P2 */
-#define OP_MemMax 144 /* synopsis: r[P1]=max(r[P1],r[P2]) */
-#define OP_OffsetLimit 145 /* synopsis: if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1) */
-#define OP_AggStep0 146 /* synopsis: accum=r[P3] step(r[P2@P5]) */
-#define OP_AggStep 147 /* synopsis: accum=r[P3] step(r[P2@P5]) */
-#define OP_AggFinal 148 /* synopsis: accum=r[P1] N=P2 */
-#define OP_Expire 149
-#define OP_TableLock 150 /* synopsis: iDb=P1 root=P2 write=P3 */
-#define OP_VBegin 151
-#define OP_VCreate 152
-#define OP_VDestroy 153
-#define OP_VOpen 154
-#define OP_VColumn 155 /* synopsis: r[P3]=vcolumn(P2) */
-#define OP_VRename 156
-#define OP_Pagecount 157
-#define OP_MaxPgcnt 158
-#define OP_CursorHint 159
-#define OP_Noop 160
-#define OP_Explain 161
+#define OP_Affinity 98 /* synopsis: affinity(r[P1@P2]) */
+#define OP_MakeRecord 99 /* synopsis: r[P3]=mkrec(r[P1@P2]) */
+#define OP_Count 100 /* synopsis: r[P2]=count() */
+#define OP_ReadCookie 101
+#define OP_SetCookie 102
+#define OP_ReopenIdx 103 /* synopsis: root=P2 iDb=P3 */
+#define OP_OpenRead 104 /* synopsis: root=P2 iDb=P3 */
+#define OP_OpenWrite 105 /* synopsis: root=P2 iDb=P3 */
+#define OP_OpenAutoindex 106 /* synopsis: nColumn=P2 */
+#define OP_OpenEphemeral 107 /* synopsis: nColumn=P2 */
+#define OP_SorterOpen 108
+#define OP_SequenceTest 109 /* synopsis: if( cursor[P1].ctr++ ) pc = P2 */
+#define OP_OpenPseudo 110 /* synopsis: P3 columns in r[P2] */
+#define OP_Close 111
+#define OP_ColumnsUsed 112
+#define OP_Sequence 113 /* synopsis: r[P2]=cursor[P1].ctr++ */
+#define OP_NewRowid 114 /* synopsis: r[P2]=rowid */
+#define OP_Insert 115 /* synopsis: intkey=r[P3] data=r[P2] */
+#define OP_InsertInt 116 /* synopsis: intkey=P3 data=r[P2] */
+#define OP_Delete 117
+#define OP_ResetCount 118
+#define OP_SorterCompare 119 /* synopsis: if key(P1)!=trim(r[P3],P4) goto P2 */
+#define OP_SorterData 120 /* synopsis: r[P2]=data */
+#define OP_RowKey 121 /* synopsis: r[P2]=key */
+#define OP_RowData 122 /* synopsis: r[P2]=data */
+#define OP_Rowid 123 /* synopsis: r[P2]=rowid */
+#define OP_NullRow 124
+#define OP_SorterInsert 125
+#define OP_IdxInsert 126 /* synopsis: key=r[P2] */
+#define OP_IdxDelete 127 /* synopsis: key=r[P2@P3] */
+#define OP_Seek 128 /* synopsis: Move P3 to P1.rowid */
+#define OP_IdxRowid 129 /* synopsis: r[P2]=rowid */
+#define OP_Destroy 130
+#define OP_Clear 131
+#define OP_Real 132 /* same as TK_FLOAT, synopsis: r[P2]=P4 */
+#define OP_ResetSorter 133
+#define OP_CreateIndex 134 /* synopsis: r[P2]=root iDb=P1 */
+#define OP_CreateTable 135 /* synopsis: r[P2]=root iDb=P1 */
+#define OP_ParseSchema 136
+#define OP_LoadAnalysis 137
+#define OP_DropTable 138
+#define OP_DropIndex 139
+#define OP_DropTrigger 140
+#define OP_IntegrityCk 141
+#define OP_RowSetAdd 142 /* synopsis: rowset(P1)=r[P2] */
+#define OP_Param 143
+#define OP_FkCounter 144 /* synopsis: fkctr[P1]+=P2 */
+#define OP_MemMax 145 /* synopsis: r[P1]=max(r[P1],r[P2]) */
+#define OP_OffsetLimit 146 /* synopsis: if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1) */
+#define OP_AggStep0 147 /* synopsis: accum=r[P3] step(r[P2@P5]) */
+#define OP_AggStep 148 /* synopsis: accum=r[P3] step(r[P2@P5]) */
+#define OP_AggFinal 149 /* synopsis: accum=r[P1] N=P2 */
+#define OP_Expire 150
+#define OP_TableLock 151 /* synopsis: iDb=P1 root=P2 write=P3 */
+#define OP_VBegin 152
+#define OP_VCreate 153
+#define OP_VDestroy 154
+#define OP_VOpen 155
+#define OP_VColumn 156 /* synopsis: r[P3]=vcolumn(P2) */
+#define OP_VRename 157
+#define OP_Pagecount 158
+#define OP_MaxPgcnt 159
+#define OP_CursorHint 160
+#define OP_Noop 161
+#define OP_Explain 162
/* Properties such as "out2" or "jump" that are specified in
** comments following the "case" for each opcode in the vdbe.c
@@ -12706,20 +12741,20 @@ typedef struct VdbeOpList VdbeOpList;
/* 32 */ 0x09, 0x09, 0x03, 0x03, 0x0b, 0x0b, 0x0b, 0x0b,\
/* 40 */ 0x0b, 0x0b, 0x01, 0x26, 0x26, 0x26, 0x26, 0x26,\
/* 48 */ 0x26, 0x26, 0x26, 0x26, 0x26, 0x01, 0x12, 0x01,\
-/* 56 */ 0x01, 0x01, 0x01, 0x01, 0x01, 0x23, 0x0b, 0x01,\
-/* 64 */ 0x01, 0x03, 0x03, 0x03, 0x01, 0x01, 0x01, 0x02,\
-/* 72 */ 0x02, 0x08, 0x00, 0x10, 0x10, 0x10, 0x10, 0x00,\
-/* 80 */ 0x10, 0x10, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00,\
-/* 88 */ 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00,\
-/* 96 */ 0x00, 0x10, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00,\
+/* 56 */ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x23, 0x0b,\
+/* 64 */ 0x01, 0x01, 0x03, 0x03, 0x03, 0x01, 0x01, 0x01,\
+/* 72 */ 0x02, 0x02, 0x08, 0x00, 0x10, 0x10, 0x10, 0x10,\
+/* 80 */ 0x00, 0x10, 0x10, 0x00, 0x00, 0x10, 0x10, 0x00,\
+/* 88 */ 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00,\
+/* 96 */ 0x00, 0x10, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00,\
/* 104 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
-/* 112 */ 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
-/* 120 */ 0x00, 0x00, 0x10, 0x00, 0x04, 0x04, 0x00, 0x00,\
-/* 128 */ 0x10, 0x10, 0x00, 0x00, 0x10, 0x10, 0x10, 0x00,\
-/* 136 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x10, 0x00,\
-/* 144 */ 0x04, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
-/* 152 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x00,\
-/* 160 */ 0x00, 0x00,}
+/* 112 */ 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,\
+/* 120 */ 0x00, 0x00, 0x00, 0x10, 0x00, 0x04, 0x04, 0x00,\
+/* 128 */ 0x00, 0x10, 0x10, 0x00, 0x10, 0x00, 0x10, 0x10,\
+/* 136 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x10,\
+/* 144 */ 0x00, 0x04, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00,\
+/* 152 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10,\
+/* 160 */ 0x00, 0x00, 0x00,}
/* The sqlite3P2Values() routine is able to run faster if it knows
** the value of the largest JUMP opcode. The smaller the maximum
@@ -12727,7 +12762,7 @@ typedef struct VdbeOpList VdbeOpList;
** generated this include file strives to group all JUMP opcodes
** together near the beginning of the list.
*/
-#define SQLITE_MX_JUMP_OPCODE 70 /* Maximum JUMP opcode */
+#define SQLITE_MX_JUMP_OPCODE 71 /* Maximum JUMP opcode */
/************** End of opcodes.h *********************************************/
/************** Continuing where we left off in vdbe.h ***********************/
@@ -13060,10 +13095,13 @@ SQLITE_PRIVATE int sqlite3PagerWalSupported(Pager *pPager);
SQLITE_PRIVATE int sqlite3PagerWalCallback(Pager *pPager);
SQLITE_PRIVATE int sqlite3PagerOpenWal(Pager *pPager, int *pisOpen);
SQLITE_PRIVATE int sqlite3PagerCloseWal(Pager *pPager);
+SQLITE_PRIVATE int sqlite3PagerUseWal(Pager *pPager);
# ifdef SQLITE_ENABLE_SNAPSHOT
SQLITE_PRIVATE int sqlite3PagerSnapshotGet(Pager *pPager, sqlite3_snapshot **ppSnapshot);
SQLITE_PRIVATE int sqlite3PagerSnapshotOpen(Pager *pPager, sqlite3_snapshot *pSnapshot);
# endif
+#else
+# define sqlite3PagerUseWal(x) 0
#endif
#ifdef SQLITE_ENABLE_ZIPVFS
@@ -13696,7 +13734,7 @@ SQLITE_PRIVATE void sqlite3OsCloseFree(sqlite3_file *);
** databases may be attached.
*/
struct Db {
- char *zName; /* Name of this database */
+ char *zDbSName; /* Name of this database. (schema name, not filename) */
Btree *pBt; /* The B*Tree structure for this database file */
u8 safety_level; /* How aggressive at syncing data to disk */
u8 bSyncSet; /* True if "PRAGMA synchronous=N" has been run */
@@ -14332,6 +14370,7 @@ struct CollSeq {
** operator is NULL. It is added to certain comparison operators to
** prove that the operands are always NOT NULL.
*/
+#define SQLITE_KEEPNULL 0x08 /* Used by vector == or <> */
#define SQLITE_JUMPIFNULL 0x10 /* jumps if either operand is NULL */
#define SQLITE_STOREP2 0x20 /* Store result in reg[P2] rather than jump */
#define SQLITE_NULLEQ 0x80 /* NULL=NULL */
@@ -14896,9 +14935,11 @@ struct Expr {
int iTable; /* TK_COLUMN: cursor number of table holding column
** TK_REGISTER: register number
** TK_TRIGGER: 1 -> new, 0 -> old
- ** EP_Unlikely: 134217728 times likelihood */
+ ** EP_Unlikely: 134217728 times likelihood
+ ** TK_SELECT: 1st register of result vector */
ynVar iColumn; /* TK_COLUMN: column index. -1 for rowid.
- ** TK_VARIABLE: variable number (always >= 1). */
+ ** TK_VARIABLE: variable number (always >= 1).
+ ** TK_SELECT_COLUMN: column of the result vector */
i16 iAgg; /* Which entry in pAggInfo->aCol[] or ->aFunc[] */
i16 iRightJoinTable; /* If EP_FromJoin, the right table of the join */
u8 op2; /* TK_REGISTER: original value of Expr.op
@@ -14934,6 +14975,7 @@ struct Expr {
#define EP_CanBeNull 0x100000 /* Can be null despite NOT NULL constraint */
#define EP_Subquery 0x200000 /* Tree contains a TK_SELECT operator */
#define EP_Alias 0x400000 /* Is an alias for a result set column */
+#define EP_Leaf 0x800000 /* Expr.pLeft, .pRight, .u.pSelect all NULL */
/*
** Combinations of two or more EP_* flags
@@ -15379,7 +15421,7 @@ struct Select {
*/
struct SelectDest {
u8 eDest; /* How to dispose of the results. On of SRT_* above. */
- char affSdst; /* Affinity used when eDest==SRT_Set */
+ char *zAffSdst; /* Affinity used when eDest==SRT_Set */
int iSDParm; /* A parameter used by the eDest disposal method */
int iSdst; /* Base register where results are written */
int nSdst; /* Number of registers allocated */
@@ -15485,36 +15527,23 @@ struct Parse {
u8 okConstFactor; /* OK to factor out constants */
u8 disableLookaside; /* Number of times lookaside has been disabled */
u8 nColCache; /* Number of entries in aColCache[] */
- int aTempReg[8]; /* Holding area for temporary registers */
int nRangeReg; /* Size of the temporary register block */
int iRangeReg; /* First register in temporary register block */
int nErr; /* Number of errors seen */
int nTab; /* Number of previously allocated VDBE cursors */
int nMem; /* Number of memory cells used so far */
- int nSet; /* Number of sets used so far */
- int nOnce; /* Number of OP_Once instructions so far */
int nOpAlloc; /* Number of slots allocated for Vdbe.aOp[] */
int szOpAlloc; /* Bytes of memory space allocated for Vdbe.aOp[] */
- int iFixedOp; /* Never back out opcodes iFixedOp-1 or earlier */
int ckBase; /* Base register of data during check constraints */
int iSelfTab; /* Table of an index whose exprs are being coded */
int iCacheLevel; /* ColCache valid when aColCache[].iLevel<=iCacheLevel */
int iCacheCnt; /* Counter used to generate aColCache[].lru values */
int nLabel; /* Number of labels used */
int *aLabel; /* Space to hold the labels */
- struct yColCache {
- int iTable; /* Table cursor number */
- i16 iColumn; /* Table column number */
- u8 tempReg; /* iReg is a temp register that needs to be freed */
- int iLevel; /* Nesting level */
- int iReg; /* Reg with value of this column. 0 means none. */
- int lru; /* Least recently used entry has the smallest value */
- } aColCache[SQLITE_N_COLCACHE]; /* One for each column cache entry */
ExprList *pConstExpr;/* Constant expressions */
Token constraintName;/* Name of the constraint currently being parsed */
yDbMask writeMask; /* Start a write transaction on these databases */
yDbMask cookieMask; /* Bitmask of schema verified databases */
- int cookieValue[SQLITE_MAX_ATTACHED+2]; /* Values of cookies to verify */
int regRowid; /* Register holding rowid of CREATE TABLE entry */
int regRoot; /* Register holding root page number for new objects */
int nMaxArg; /* Max args passed to user function by sub-program */
@@ -15527,8 +15556,6 @@ struct Parse {
TableLock *aTableLock; /* Required table locks for shared-cache mode */
#endif
AutoincInfo *pAinc; /* Information about AUTOINCREMENT counters */
-
- /* Information used while coding trigger programs. */
Parse *pToplevel; /* Parse structure for main program (or NULL) */
Table *pTriggerTab; /* Table triggers are being coded for */
int addrCrTab; /* Address of OP_CreateTable opcode on CREATE TABLE */
@@ -15539,6 +15566,25 @@ struct Parse {
u8 eOrconf; /* Default ON CONFLICT policy for trigger steps */
u8 disableTriggers; /* True to disable triggers */
+ /**************************************************************************
+ ** Fields above must be initialized to zero. The fields that follow,
+ ** down to the beginning of the recursive section, do not need to be
+ ** initialized as they will be set before being used. The boundary is
+ ** determined by offsetof(Parse,aColCache).
+ **************************************************************************/
+
+ struct yColCache {
+ int iTable; /* Table cursor number */
+ i16 iColumn; /* Table column number */
+ u8 tempReg; /* iReg is a temp register that needs to be freed */
+ int iLevel; /* Nesting level */
+ int iReg; /* Reg with value of this column. 0 means none. */
+ int lru; /* Least recently used entry has the smallest value */
+ } aColCache[SQLITE_N_COLCACHE]; /* One for each column cache entry */
+ int aTempReg[8]; /* Holding area for temporary registers */
+ Token sNameToken; /* Token with unqualified schema object name */
+ Token sLastToken; /* The last token parsed */
+
/************************************************************************
** Above is constant between recursions. Below is reset before and after
** each recursion. The boundary between these two regions is determined
@@ -15554,7 +15600,6 @@ struct Parse {
u8 declareVtab; /* True if inside sqlite3_declare_vtab() */
int nVtabLock; /* Number of virtual tables to lock */
#endif
- int nAlias; /* Number of aliased result set columns */
int nHeight; /* Expression tree height of current sub-select */
#ifndef SQLITE_OMIT_EXPLAIN
int iSelectId; /* ID of current select for EXPLAIN output */
@@ -15566,8 +15611,6 @@ struct Parse {
Table *pNewTable; /* A table being constructed by CREATE TABLE */
Trigger *pNewTrigger; /* Trigger under construct by a CREATE TRIGGER */
const char *zAuthContext; /* The 6th parameter to db->xAuth callbacks */
- Token sNameToken; /* Token with unqualified schema object name */
- Token sLastToken; /* The last token parsed */
#ifndef SQLITE_OMIT_VIRTUALTABLE
Token sArg; /* Complete text of a module argument */
Table **apVtabLock; /* Pointer to virtual tables needing locking */
@@ -15579,6 +15622,14 @@ struct Parse {
};
/*
+** Sizes and pointers of various parts of the Parse object.
+*/
+#define PARSE_HDR_SZ offsetof(Parse,aColCache) /* Recursive part w/o aColCache*/
+#define PARSE_RECURSE_SZ offsetof(Parse,nVar) /* Recursive part */
+#define PARSE_TAIL_SZ (sizeof(Parse)-PARSE_RECURSE_SZ) /* Non-recursive part */
+#define PARSE_TAIL(X) (((char*)(X))+PARSE_RECURSE_SZ) /* Pointer to tail */
+
+/*
** Return true if currently inside an sqlite3_declare_vtab() call.
*/
#ifdef SQLITE_OMIT_VIRTUALTABLE
@@ -15826,6 +15877,7 @@ struct Sqlite3Config {
int (*xTestCallback)(int); /* Invoked by sqlite3FaultSim() */
#endif
int bLocaltimeFault; /* True to fail localtime() calls */
+ int iOnceResetThreshold; /* When to reset OP_Once counters */
};
/*
@@ -16111,6 +16163,7 @@ SQLITE_PRIVATE void *sqlite3TestTextToPtr(const char*);
#if defined(SQLITE_DEBUG)
SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView*, const Expr*, u8);
+SQLITE_PRIVATE void sqlite3TreeViewBareExprList(TreeView*, const ExprList*, const char*);
SQLITE_PRIVATE void sqlite3TreeViewExprList(TreeView*, const ExprList*, u8, const char*);
SQLITE_PRIVATE void sqlite3TreeViewSelect(TreeView*, const Select*, u8);
SQLITE_PRIVATE void sqlite3TreeViewWith(TreeView*, const With*, u8);
@@ -16139,9 +16192,10 @@ SQLITE_PRIVATE Expr *sqlite3PExpr(Parse*, int, Expr*, Expr*, const Token*);
SQLITE_PRIVATE void sqlite3PExprAddSelect(Parse*, Expr*, Select*);
SQLITE_PRIVATE Expr *sqlite3ExprAnd(sqlite3*,Expr*, Expr*);
SQLITE_PRIVATE Expr *sqlite3ExprFunction(Parse*,ExprList*, Token*);
-SQLITE_PRIVATE void sqlite3ExprAssignVarNumber(Parse*, Expr*);
+SQLITE_PRIVATE void sqlite3ExprAssignVarNumber(Parse*, Expr*, u32);
SQLITE_PRIVATE void sqlite3ExprDelete(sqlite3*, Expr*);
SQLITE_PRIVATE ExprList *sqlite3ExprListAppend(Parse*,ExprList*,Expr*);
+SQLITE_PRIVATE ExprList *sqlite3ExprListAppendVector(Parse*,ExprList*,IdList*,Expr*);
SQLITE_PRIVATE void sqlite3ExprListSetSortOrder(ExprList*,int);
SQLITE_PRIVATE void sqlite3ExprListSetName(Parse*,ExprList*,Token*,int);
SQLITE_PRIVATE void sqlite3ExprListSetSpan(Parse*,ExprList*,ExprSpan*);
@@ -16177,7 +16231,6 @@ SQLITE_PRIVATE void sqlite3EndTable(Parse*,Token*,Token*,u8,Select*);
SQLITE_PRIVATE int sqlite3ParseUri(const char*,const char*,unsigned int*,
sqlite3_vfs**,char**,char **);
SQLITE_PRIVATE Btree *sqlite3DbNameToBtree(sqlite3*,const char*);
-SQLITE_PRIVATE int sqlite3CodeOnce(Parse *);
#ifdef SQLITE_OMIT_BUILTIN_TEST
# define sqlite3FaultSim(X) SQLITE_OK
@@ -16300,8 +16353,8 @@ SQLITE_PRIVATE Table *sqlite3LocateTableItem(Parse*,u32 flags,struct SrcList_ite
SQLITE_PRIVATE Index *sqlite3FindIndex(sqlite3*,const char*, const char*);
SQLITE_PRIVATE void sqlite3UnlinkAndDeleteTable(sqlite3*,int,const char*);
SQLITE_PRIVATE void sqlite3UnlinkAndDeleteIndex(sqlite3*,int,const char*);
-SQLITE_PRIVATE void sqlite3Vacuum(Parse*);
-SQLITE_PRIVATE int sqlite3RunVacuum(char**, sqlite3*);
+SQLITE_PRIVATE void sqlite3Vacuum(Parse*,Token*);
+SQLITE_PRIVATE int sqlite3RunVacuum(char**, sqlite3*, int);
SQLITE_PRIVATE char *sqlite3NameFromToken(sqlite3*, Token*);
SQLITE_PRIVATE int sqlite3ExprCompare(Expr*, Expr*, int);
SQLITE_PRIVATE int sqlite3ExprListCompare(ExprList*, ExprList*, int);
@@ -16477,6 +16530,7 @@ SQLITE_PRIVATE const char *sqlite3IndexAffinityStr(sqlite3*, Index*);
SQLITE_PRIVATE void sqlite3TableAffinity(Vdbe*, Table*, int);
SQLITE_PRIVATE char sqlite3CompareAffinity(Expr *pExpr, char aff2);
SQLITE_PRIVATE int sqlite3IndexAffinityOk(Expr *pExpr, char idx_affinity);
+SQLITE_PRIVATE char sqlite3TableColumnAffinity(Table*,int);
SQLITE_PRIVATE char sqlite3ExprAffinity(Expr *pExpr);
SQLITE_PRIVATE int sqlite3Atoi64(const char*, i64*, int, u8);
SQLITE_PRIVATE int sqlite3DecOrHexToI64(const char*, i64*);
@@ -16542,7 +16596,7 @@ SQLITE_PRIVATE void sqlite3AlterRenameTable(Parse*, SrcList*, Token*);
SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *, int *);
SQLITE_PRIVATE void sqlite3NestedParse(Parse*, const char*, ...);
SQLITE_PRIVATE void sqlite3ExpirePreparedStatements(sqlite3*);
-SQLITE_PRIVATE int sqlite3CodeSubselect(Parse *, Expr *, int, int);
+SQLITE_PRIVATE int sqlite3CodeSubselect(Parse*, Expr *, int, int);
SQLITE_PRIVATE void sqlite3SelectPrep(Parse*, Select*, NameContext*);
SQLITE_PRIVATE void sqlite3SelectWrongNumTermsError(Parse *pParse, Select *p);
SQLITE_PRIVATE int sqlite3MatchSpanName(const char*, const char*, const char*, const char*);
@@ -16597,12 +16651,20 @@ SQLITE_PRIVATE Expr *sqlite3CreateColumnExpr(sqlite3 *, SrcList *, int, int);
SQLITE_PRIVATE void sqlite3BackupRestart(sqlite3_backup *);
SQLITE_PRIVATE void sqlite3BackupUpdate(sqlite3_backup *, Pgno, const u8 *);
+#ifndef SQLITE_OMIT_SUBQUERY
+SQLITE_PRIVATE int sqlite3ExprCheckIN(Parse*, Expr*);
+#else
+# define sqlite3ExprCheckIN(x,y) SQLITE_OK
+#endif
+
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
SQLITE_PRIVATE void sqlite3AnalyzeFunctions(void);
-SQLITE_PRIVATE int sqlite3Stat4ProbeSetValue(Parse*,Index*,UnpackedRecord**,Expr*,u8,int,int*);
+SQLITE_PRIVATE int sqlite3Stat4ProbeSetValue(
+ Parse*,Index*,UnpackedRecord**,Expr*,int,int,int*);
SQLITE_PRIVATE int sqlite3Stat4ValueFromExpr(Parse*, Expr*, u8, sqlite3_value**);
SQLITE_PRIVATE void sqlite3Stat4ProbeFree(UnpackedRecord*);
SQLITE_PRIVATE int sqlite3Stat4Column(sqlite3*, const void*, int, int, sqlite3_value**);
+SQLITE_PRIVATE char sqlite3IndexColumnAffinity(sqlite3*, Index*, int);
#endif
/*
@@ -16755,7 +16817,7 @@ SQLITE_PRIVATE void sqlite3EndBenignMalloc(void);
#define IN_INDEX_NOOP_OK 0x0001 /* OK to return IN_INDEX_NOOP */
#define IN_INDEX_MEMBERSHIP 0x0002 /* IN operator used for membership test */
#define IN_INDEX_LOOP 0x0004 /* IN operator used as a loop */
-SQLITE_PRIVATE int sqlite3FindInIndex(Parse *, Expr *, u32, int*);
+SQLITE_PRIVATE int sqlite3FindInIndex(Parse *, Expr *, u32, int*, int*);
SQLITE_PRIVATE int sqlite3JournalOpen(sqlite3_vfs *, const char *, sqlite3_file *, int, int);
SQLITE_PRIVATE int sqlite3JournalSize(sqlite3_vfs *);
@@ -16860,6 +16922,11 @@ SQLITE_PRIVATE int sqlite3ThreadJoin(SQLiteThread*, void**);
SQLITE_PRIVATE int sqlite3DbstatRegister(sqlite3*);
#endif
+SQLITE_PRIVATE int sqlite3ExprVectorSize(Expr *pExpr);
+SQLITE_PRIVATE int sqlite3ExprIsVector(Expr *pExpr);
+SQLITE_PRIVATE Expr *sqlite3VectorFieldSubexpr(Expr*, int);
+SQLITE_PRIVATE Expr *sqlite3ExprForVectorField(Parse*,Expr*,int);
+
#endif /* SQLITEINT_H */
/************** End of sqliteInt.h *******************************************/
@@ -16945,16 +17012,13 @@ SQLITE_PRIVATE const unsigned char sqlite3UpperToLower[] = {
**
** (x & ~(map[x]&0x20))
**
-** Standard function tolower() is implemented using the sqlite3UpperToLower[]
+** The equivalent of tolower() is implemented using the sqlite3UpperToLower[]
** array. tolower() is used more often than toupper() by SQLite.
**
-** Bit 0x40 is set if the character non-alphanumeric and can be used in an
+** Bit 0x40 is set if the character is non-alphanumeric and can be used in an
** SQLite identifier. Identifiers are alphanumerics, "_", "$", and any
** non-ASCII UTF character. Hence the test for whether or not a character is
** part of an identifier is 0x46.
-**
-** SQLite's versions are identical to the standard versions assuming a
-** locale of "C". They are implemented as macros in sqliteInt.h.
*/
#ifdef SQLITE_ASCII
SQLITE_PRIVATE const unsigned char sqlite3CtypeMap[256] = {
@@ -17027,7 +17091,7 @@ SQLITE_PRIVATE const unsigned char sqlite3CtypeMap[256] = {
#endif
/* Statement journals spill to disk when their size exceeds the following
-** threashold (in bytes). 0 means that statement journals are created and
+** threshold (in bytes). 0 means that statement journals are created and
** written to disk immediately (the default behavior for SQLite versions
** before 3.12.0). -1 means always keep the entire statement journal in
** memory. (The statement journal is also always held entirely in memory
@@ -17091,7 +17155,8 @@ SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config = {
#ifndef SQLITE_OMIT_BUILTIN_TEST
0, /* xTestCallback */
#endif
- 0 /* bLocaltimeFault */
+ 0, /* bLocaltimeFault */
+ 0x7ffffffe /* iOnceResetThreshold */
};
/*
@@ -17114,7 +17179,7 @@ SQLITE_PRIVATE const Token sqlite3IntTokens[] = {
** The value of the "pending" byte must be 0x40000000 (1 byte past the
** 1-gibabyte boundary) in a compatible database. SQLite never uses
** the database page that contains the pending byte. It never attempts
-** to read or write that page. The pending byte page is set assign
+** to read or write that page. The pending byte page is set aside
** for use by the VFS layers as space for managing file locks.
**
** During testing, it is often desirable to move the pending byte to
@@ -17674,9 +17739,6 @@ typedef unsigned Bool;
/* Opaque type used by code in vdbesort.c */
typedef struct VdbeSorter VdbeSorter;
-/* Opaque type used by the explainer */
-typedef struct Explain Explain;
-
/* Elements of the linked list at Vdbe.pAuxData */
typedef struct AuxData AuxData;
@@ -17751,6 +17813,12 @@ struct VdbeCursor {
** aType[] and nField+1 array slots for aOffset[] */
};
+
+/*
+** A value for VdbeCursor.cacheStatus that means the cache is always invalid.
+*/
+#define CACHE_STALE 0
+
/*
** When a sub-program is executed (OP_Program), a structure of this type
** is allocated to store the current value of the program counter, as
@@ -17779,7 +17847,6 @@ struct VdbeFrame {
Op *aOp; /* Program instructions for parent frame */
i64 *anExec; /* Event counters from parent frame */
Mem *aMem; /* Array of memory cells for parent frame */
- u8 *aOnceFlag; /* Array of OP_Once flags for parent frame */
VdbeCursor **apCsr; /* Array of Vdbe cursors for parent frame */
void *token; /* Copy of SubProgram.token */
i64 lastRowid; /* Last insert rowid (sqlite3.lastRowid) */
@@ -17788,7 +17855,6 @@ struct VdbeFrame {
int pc; /* Program Counter in parent (calling) frame */
int nOp; /* Size of aOp array */
int nMem; /* Number of entries in aMem */
- int nOnceFlag; /* Number of entries in aOnceFlag */
int nChildMem; /* Number of memory cells for child frame */
int nChildCsr; /* Number of cursors for child frame */
int nChange; /* Statement changes (Vdbe.nChange) */
@@ -17798,11 +17864,6 @@ struct VdbeFrame {
#define VdbeFrameMem(p) ((Mem *)&((u8 *)p)[ROUND8(sizeof(VdbeFrame))])
/*
-** A value for VdbeCursor.cacheValid that means the cache is always invalid.
-*/
-#define CACHE_STALE 0
-
-/*
** Internally, the vdbe manipulates nearly all SQL values as Mem
** structures. Each Mem struct may cache multiple representations (string,
** integer etc.) of the same value.
@@ -17942,18 +18003,6 @@ struct sqlite3_context {
sqlite3_value *argv[1]; /* Argument set */
};
-/*
-** An Explain object accumulates indented output which is helpful
-** in describing recursive data structures.
-*/
-struct Explain {
- Vdbe *pVdbe; /* Attach the explanation to this Vdbe */
- StrAccum str; /* The string being accumulated */
- int nIndent; /* Number of elements in aIndent */
- u16 aIndent[100]; /* Levels of indentation */
- char zBase[100]; /* Initial space */
-};
-
/* A bitfield type for use inside of structures. Always follow with :N where
** N is the number of bits.
*/
@@ -17978,34 +18027,47 @@ struct ScanStatus {
*/
struct Vdbe {
sqlite3 *db; /* The database connection that owns this statement */
+ Vdbe *pPrev,*pNext; /* Linked list of VDBEs with the same Vdbe.db */
+ Parse *pParse; /* Parsing context used to create this Vdbe */
+ ynVar nVar; /* Number of entries in aVar[] */
+ ynVar nzVar; /* Number of entries in azVar[] */
+ u32 magic; /* Magic number for sanity checking */
+ int nMem; /* Number of memory locations currently allocated */
+ int nCursor; /* Number of slots in apCsr[] */
+ u32 cacheCtr; /* VdbeCursor row cache generation counter */
+ int pc; /* The program counter */
+ int rc; /* Value to return */
+ int nChange; /* Number of db changes made since last reset */
+ int iStatement; /* Statement number (or 0 if has not opened stmt) */
+ i64 iCurrentTime; /* Value of julianday('now') for this statement */
+ i64 nFkConstraint; /* Number of imm. FK constraints this VM */
+ i64 nStmtDefCons; /* Number of def. constraints when stmt started */
+ i64 nStmtDefImmCons; /* Number of def. imm constraints when stmt started */
+
+ /* When allocating a new Vdbe object, all of the fields below should be
+ ** initialized to zero or NULL */
+
Op *aOp; /* Space to hold the virtual machine's program */
Mem *aMem; /* The memory locations */
Mem **apArg; /* Arguments to currently executing user function */
Mem *aColName; /* Column names to return */
Mem *pResultSet; /* Pointer to an array of results */
- Parse *pParse; /* Parsing context used to create this Vdbe */
- int nMem; /* Number of memory locations currently allocated */
- int nOp; /* Number of instructions in the program */
- int nCursor; /* Number of slots in apCsr[] */
- u32 magic; /* Magic number for sanity checking */
char *zErrMsg; /* Error message written here */
- Vdbe *pPrev,*pNext; /* Linked list of VDBEs with the same Vdbe.db */
VdbeCursor **apCsr; /* One element of this array for each open cursor */
Mem *aVar; /* Values for the OP_Variable opcode. */
char **azVar; /* Name of variables */
- ynVar nVar; /* Number of entries in aVar[] */
- ynVar nzVar; /* Number of entries in azVar[] */
- u32 cacheCtr; /* VdbeCursor row cache generation counter */
- int pc; /* The program counter */
- int rc; /* Value to return */
+#ifndef SQLITE_OMIT_TRACE
+ i64 startTime; /* Time when query started - used for profiling */
+#endif
+ int nOp; /* Number of instructions in the program */
#ifdef SQLITE_DEBUG
int rcApp; /* errcode set by sqlite3_result_error_code() */
#endif
u16 nResColumn; /* Number of columns in one row of the result set */
u8 errorAction; /* Recovery action to do in case of an error */
+ u8 minWriteFileFormat; /* Minimum file format for writable database files */
bft expired:1; /* True if the VM needs to be recompiled */
bft doingRerun:1; /* True if rerunning after an auto-reprepare */
- u8 minWriteFileFormat; /* Minimum file format for writable database files */
bft explain:2; /* True if EXPLAIN present on SQL command */
bft changeCntOn:1; /* True to update the change-counter */
bft runOnlyOnce:1; /* Automatically expire on reset */
@@ -18013,18 +18075,9 @@ struct Vdbe {
bft readOnly:1; /* True for statements that do not write */
bft bIsReader:1; /* True for statements that read */
bft isPrepareV2:1; /* True if prepared with prepare_v2() */
- int nChange; /* Number of db changes made since last reset */
yDbMask btreeMask; /* Bitmask of db->aDb[] entries referenced */
yDbMask lockMask; /* Subset of btreeMask that requires a lock */
- int iStatement; /* Statement number (or 0 if has not opened stmt) */
u32 aCounter[5]; /* Counters used by sqlite3_stmt_status() */
-#ifndef SQLITE_OMIT_TRACE
- i64 startTime; /* Time when query started - used for profiling */
-#endif
- i64 iCurrentTime; /* Value of julianday('now') for this statement */
- i64 nFkConstraint; /* Number of imm. FK constraints this VM */
- i64 nStmtDefCons; /* Number of def. constraints when stmt started */
- i64 nStmtDefImmCons; /* Number of def. imm constraints when stmt started */
char *zSql; /* Text of the SQL statement that generated this */
void *pFree; /* Free this when deleting the vdbe */
VdbeFrame *pFrame; /* Parent frame */
@@ -18032,8 +18085,6 @@ struct Vdbe {
int nFrame; /* Number of frames in pFrame list */
u32 expmask; /* Binding to these vars invalidates VM */
SubProgram *pProgram; /* Linked list of all sub-programs used by VM */
- int nOnceFlag; /* Size of array aOnceFlag[] */
- u8 *aOnceFlag; /* Flags for OP_Once */
AuxData *pAuxData; /* Linked list of auxdata allocations */
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
i64 *anExec; /* Number of times each op has been executed */
@@ -18045,10 +18096,11 @@ struct Vdbe {
/*
** The following are allowed values for Vdbe.magic
*/
-#define VDBE_MAGIC_INIT 0x26bceaa5 /* Building a VDBE program */
-#define VDBE_MAGIC_RUN 0xbdf20da3 /* VDBE is ready to execute */
-#define VDBE_MAGIC_HALT 0x519c2973 /* VDBE has completed execution */
-#define VDBE_MAGIC_DEAD 0xb606c3c8 /* The VDBE has been deallocated */
+#define VDBE_MAGIC_INIT 0x16bceaa5 /* Building a VDBE program */
+#define VDBE_MAGIC_RUN 0x2df20da3 /* VDBE is ready to execute */
+#define VDBE_MAGIC_HALT 0x319c2973 /* VDBE has completed execution */
+#define VDBE_MAGIC_RESET 0x48fa9f76 /* Reset and ready to run again */
+#define VDBE_MAGIC_DEAD 0x5606c3c8 /* The VDBE has been deallocated */
/*
** Structure used to store the context required by the
@@ -18065,8 +18117,8 @@ struct PreUpdate {
int iNewReg; /* Register for new.* values */
i64 iKey1; /* First key value passed to hook */
i64 iKey2; /* Second key value passed to hook */
- int iPKey; /* If not negative index of IPK column */
Mem *aNew; /* Array of new.* values */
+ Table *pTab; /* Schema object being upated */
};
/*
@@ -25874,7 +25926,7 @@ SQLITE_PRIVATE void sqlite3TreeViewWith(TreeView *pView, const With *pWith, u8 m
/*
-** Generate a human-readable description of a the Select object.
+** Generate a human-readable description of a Select object.
*/
SQLITE_PRIVATE void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 moreToFollow){
int n = 0;
@@ -26205,6 +26257,15 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m
sqlite3TreeViewExpr(pView, pExpr->pRight, 0);
break;
}
+ case TK_VECTOR: {
+ sqlite3TreeViewBareExprList(pView, pExpr->x.pList, "VECTOR");
+ break;
+ }
+ case TK_SELECT_COLUMN: {
+ sqlite3TreeViewLine(pView, "SELECT-COLUMN %d", pExpr->iColumn);
+ sqlite3TreeViewSelect(pView, pExpr->pLeft->x.pSelect, 0);
+ break;
+ }
default: {
sqlite3TreeViewLine(pView, "op=%d", pExpr->op);
break;
@@ -26221,21 +26282,20 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m
sqlite3TreeViewPop(pView);
}
+
/*
** Generate a human-readable explanation of an expression list.
*/
-SQLITE_PRIVATE void sqlite3TreeViewExprList(
+SQLITE_PRIVATE void sqlite3TreeViewBareExprList(
TreeView *pView,
const ExprList *pList,
- u8 moreToFollow,
const char *zLabel
){
- int i;
- pView = sqlite3TreeViewPush(pView, moreToFollow);
if( zLabel==0 || zLabel[0]==0 ) zLabel = "LIST";
if( pList==0 ){
sqlite3TreeViewLine(pView, "%s (empty)", zLabel);
}else{
+ int i;
sqlite3TreeViewLine(pView, "%s", zLabel);
for(i=0; i<pList->nExpr; i++){
int j = pList->a[i].u.x.iOrderByCol;
@@ -26247,6 +26307,15 @@ SQLITE_PRIVATE void sqlite3TreeViewExprList(
if( j ) sqlite3TreeViewPop(pView);
}
}
+}
+SQLITE_PRIVATE void sqlite3TreeViewExprList(
+ TreeView *pView,
+ const ExprList *pList,
+ u8 moreToFollow,
+ const char *zLabel
+){
+ pView = sqlite3TreeViewPush(pView, moreToFollow);
+ sqlite3TreeViewBareExprList(pView, pList, zLabel);
sqlite3TreeViewPop(pView);
}
@@ -28508,36 +28577,21 @@ SQLITE_PRIVATE int sqlite3SubInt64(i64 *pA, i64 iB){
return sqlite3AddInt64(pA, -iB);
}
}
-#define TWOPOWER32 (((i64)1)<<32)
-#define TWOPOWER31 (((i64)1)<<31)
SQLITE_PRIVATE int sqlite3MulInt64(i64 *pA, i64 iB){
i64 iA = *pA;
- i64 iA1, iA0, iB1, iB0, r;
-
- iA1 = iA/TWOPOWER32;
- iA0 = iA % TWOPOWER32;
- iB1 = iB/TWOPOWER32;
- iB0 = iB % TWOPOWER32;
- if( iA1==0 ){
- if( iB1==0 ){
- *pA *= iB;
- return 0;
- }
- r = iA0*iB1;
- }else if( iB1==0 ){
- r = iA1*iB0;
- }else{
- /* If both iA1 and iB1 are non-zero, overflow will result */
- return 1;
- }
- testcase( r==(-TWOPOWER31)-1 );
- testcase( r==(-TWOPOWER31) );
- testcase( r==TWOPOWER31 );
- testcase( r==TWOPOWER31-1 );
- if( r<(-TWOPOWER31) || r>=TWOPOWER31 ) return 1;
- r *= TWOPOWER32;
- if( sqlite3AddInt64(&r, iA0*iB0) ) return 1;
- *pA = r;
+ if( iB>0 ){
+ if( iA>LARGEST_INT64/iB ) return 1;
+ if( iA<SMALLEST_INT64/iB ) return 1;
+ }else if( iB<0 ){
+ if( iA>0 ){
+ if( iB<SMALLEST_INT64/iA ) return 1;
+ }else if( iA<0 ){
+ if( iB==SMALLEST_INT64 ) return 1;
+ if( iA==SMALLEST_INT64 ) return 1;
+ if( -iA>LARGEST_INT64/-iB ) return 1;
+ }
+ }
+ *pA = iA*iB;
return 0;
}
@@ -28732,7 +28786,11 @@ static unsigned int strHash(const char *z){
unsigned int h = 0;
unsigned char c;
while( (c = (unsigned char)*z++)!=0 ){ /*OPTIMIZATION-IF-TRUE*/
- h = (h<<3) ^ h ^ sqlite3UpperToLower[c];
+ /* Knuth multiplicative hashing. (Sorting & Searching, p. 510).
+ ** 0x9e3779b1 is 2654435761 which is the closest prime number to
+ ** (2**32)*golden_ratio, where golden_ratio = (sqrt(5) - 1)/2. */
+ h += sqlite3UpperToLower[c];
+ h *= 0x9e3779b1;
}
return h;
}
@@ -28998,7 +29056,7 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){
/* 39 */ "Le" OpHelp("IF r[P3]<=r[P1]"),
/* 40 */ "Lt" OpHelp("IF r[P3]<r[P1]"),
/* 41 */ "Ge" OpHelp("IF r[P3]>=r[P1]"),
- /* 42 */ "Last" OpHelp(""),
+ /* 42 */ "ElseNotEq" OpHelp(""),
/* 43 */ "BitAnd" OpHelp("r[P3]=r[P1]&r[P2]"),
/* 44 */ "BitOr" OpHelp("r[P3]=r[P1]|r[P2]"),
/* 45 */ "ShiftLeft" OpHelp("r[P3]=r[P2]<<r[P1]"),
@@ -29009,115 +29067,116 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){
/* 50 */ "Divide" OpHelp("r[P3]=r[P2]/r[P1]"),
/* 51 */ "Remainder" OpHelp("r[P3]=r[P2]%r[P1]"),
/* 52 */ "Concat" OpHelp("r[P3]=r[P2]+r[P1]"),
- /* 53 */ "SorterSort" OpHelp(""),
+ /* 53 */ "Last" OpHelp(""),
/* 54 */ "BitNot" OpHelp("r[P1]= ~r[P1]"),
- /* 55 */ "Sort" OpHelp(""),
- /* 56 */ "Rewind" OpHelp(""),
- /* 57 */ "IdxLE" OpHelp("key=r[P3@P4]"),
- /* 58 */ "IdxGT" OpHelp("key=r[P3@P4]"),
- /* 59 */ "IdxLT" OpHelp("key=r[P3@P4]"),
- /* 60 */ "IdxGE" OpHelp("key=r[P3@P4]"),
- /* 61 */ "RowSetRead" OpHelp("r[P3]=rowset(P1)"),
- /* 62 */ "RowSetTest" OpHelp("if r[P3] in rowset(P1) goto P2"),
- /* 63 */ "Program" OpHelp(""),
- /* 64 */ "FkIfZero" OpHelp("if fkctr[P1]==0 goto P2"),
- /* 65 */ "IfPos" OpHelp("if r[P1]>0 then r[P1]-=P3, goto P2"),
- /* 66 */ "IfNotZero" OpHelp("if r[P1]!=0 then r[P1]-=P3, goto P2"),
- /* 67 */ "DecrJumpZero" OpHelp("if (--r[P1])==0 goto P2"),
- /* 68 */ "IncrVacuum" OpHelp(""),
- /* 69 */ "VNext" OpHelp(""),
- /* 70 */ "Init" OpHelp("Start at P2"),
- /* 71 */ "Return" OpHelp(""),
- /* 72 */ "EndCoroutine" OpHelp(""),
- /* 73 */ "HaltIfNull" OpHelp("if r[P3]=null halt"),
- /* 74 */ "Halt" OpHelp(""),
- /* 75 */ "Integer" OpHelp("r[P2]=P1"),
- /* 76 */ "Int64" OpHelp("r[P2]=P4"),
- /* 77 */ "String" OpHelp("r[P2]='P4' (len=P1)"),
- /* 78 */ "Null" OpHelp("r[P2..P3]=NULL"),
- /* 79 */ "SoftNull" OpHelp("r[P1]=NULL"),
- /* 80 */ "Blob" OpHelp("r[P2]=P4 (len=P1)"),
- /* 81 */ "Variable" OpHelp("r[P2]=parameter(P1,P4)"),
- /* 82 */ "Move" OpHelp("r[P2@P3]=r[P1@P3]"),
- /* 83 */ "Copy" OpHelp("r[P2@P3+1]=r[P1@P3+1]"),
- /* 84 */ "SCopy" OpHelp("r[P2]=r[P1]"),
- /* 85 */ "IntCopy" OpHelp("r[P2]=r[P1]"),
- /* 86 */ "ResultRow" OpHelp("output=r[P1@P2]"),
- /* 87 */ "CollSeq" OpHelp(""),
- /* 88 */ "Function0" OpHelp("r[P3]=func(r[P2@P5])"),
- /* 89 */ "Function" OpHelp("r[P3]=func(r[P2@P5])"),
- /* 90 */ "AddImm" OpHelp("r[P1]=r[P1]+P2"),
- /* 91 */ "RealAffinity" OpHelp(""),
- /* 92 */ "Cast" OpHelp("affinity(r[P1])"),
- /* 93 */ "Permutation" OpHelp(""),
- /* 94 */ "Compare" OpHelp("r[P1@P3] <-> r[P2@P3]"),
- /* 95 */ "Column" OpHelp("r[P3]=PX"),
- /* 96 */ "Affinity" OpHelp("affinity(r[P1@P2])"),
+ /* 55 */ "SorterSort" OpHelp(""),
+ /* 56 */ "Sort" OpHelp(""),
+ /* 57 */ "Rewind" OpHelp(""),
+ /* 58 */ "IdxLE" OpHelp("key=r[P3@P4]"),
+ /* 59 */ "IdxGT" OpHelp("key=r[P3@P4]"),
+ /* 60 */ "IdxLT" OpHelp("key=r[P3@P4]"),
+ /* 61 */ "IdxGE" OpHelp("key=r[P3@P4]"),
+ /* 62 */ "RowSetRead" OpHelp("r[P3]=rowset(P1)"),
+ /* 63 */ "RowSetTest" OpHelp("if r[P3] in rowset(P1) goto P2"),
+ /* 64 */ "Program" OpHelp(""),
+ /* 65 */ "FkIfZero" OpHelp("if fkctr[P1]==0 goto P2"),
+ /* 66 */ "IfPos" OpHelp("if r[P1]>0 then r[P1]-=P3, goto P2"),
+ /* 67 */ "IfNotZero" OpHelp("if r[P1]!=0 then r[P1]-=P3, goto P2"),
+ /* 68 */ "DecrJumpZero" OpHelp("if (--r[P1])==0 goto P2"),
+ /* 69 */ "IncrVacuum" OpHelp(""),
+ /* 70 */ "VNext" OpHelp(""),
+ /* 71 */ "Init" OpHelp("Start at P2"),
+ /* 72 */ "Return" OpHelp(""),
+ /* 73 */ "EndCoroutine" OpHelp(""),
+ /* 74 */ "HaltIfNull" OpHelp("if r[P3]=null halt"),
+ /* 75 */ "Halt" OpHelp(""),
+ /* 76 */ "Integer" OpHelp("r[P2]=P1"),
+ /* 77 */ "Int64" OpHelp("r[P2]=P4"),
+ /* 78 */ "String" OpHelp("r[P2]='P4' (len=P1)"),
+ /* 79 */ "Null" OpHelp("r[P2..P3]=NULL"),
+ /* 80 */ "SoftNull" OpHelp("r[P1]=NULL"),
+ /* 81 */ "Blob" OpHelp("r[P2]=P4 (len=P1)"),
+ /* 82 */ "Variable" OpHelp("r[P2]=parameter(P1,P4)"),
+ /* 83 */ "Move" OpHelp("r[P2@P3]=r[P1@P3]"),
+ /* 84 */ "Copy" OpHelp("r[P2@P3+1]=r[P1@P3+1]"),
+ /* 85 */ "SCopy" OpHelp("r[P2]=r[P1]"),
+ /* 86 */ "IntCopy" OpHelp("r[P2]=r[P1]"),
+ /* 87 */ "ResultRow" OpHelp("output=r[P1@P2]"),
+ /* 88 */ "CollSeq" OpHelp(""),
+ /* 89 */ "Function0" OpHelp("r[P3]=func(r[P2@P5])"),
+ /* 90 */ "Function" OpHelp("r[P3]=func(r[P2@P5])"),
+ /* 91 */ "AddImm" OpHelp("r[P1]=r[P1]+P2"),
+ /* 92 */ "RealAffinity" OpHelp(""),
+ /* 93 */ "Cast" OpHelp("affinity(r[P1])"),
+ /* 94 */ "Permutation" OpHelp(""),
+ /* 95 */ "Compare" OpHelp("r[P1@P3] <-> r[P2@P3]"),
+ /* 96 */ "Column" OpHelp("r[P3]=PX"),
/* 97 */ "String8" OpHelp("r[P2]='P4'"),
- /* 98 */ "MakeRecord" OpHelp("r[P3]=mkrec(r[P1@P2])"),
- /* 99 */ "Count" OpHelp("r[P2]=count()"),
- /* 100 */ "ReadCookie" OpHelp(""),
- /* 101 */ "SetCookie" OpHelp(""),
- /* 102 */ "ReopenIdx" OpHelp("root=P2 iDb=P3"),
- /* 103 */ "OpenRead" OpHelp("root=P2 iDb=P3"),
- /* 104 */ "OpenWrite" OpHelp("root=P2 iDb=P3"),
- /* 105 */ "OpenAutoindex" OpHelp("nColumn=P2"),
- /* 106 */ "OpenEphemeral" OpHelp("nColumn=P2"),
- /* 107 */ "SorterOpen" OpHelp(""),
- /* 108 */ "SequenceTest" OpHelp("if( cursor[P1].ctr++ ) pc = P2"),
- /* 109 */ "OpenPseudo" OpHelp("P3 columns in r[P2]"),
- /* 110 */ "Close" OpHelp(""),
- /* 111 */ "ColumnsUsed" OpHelp(""),
- /* 112 */ "Sequence" OpHelp("r[P2]=cursor[P1].ctr++"),
- /* 113 */ "NewRowid" OpHelp("r[P2]=rowid"),
- /* 114 */ "Insert" OpHelp("intkey=r[P3] data=r[P2]"),
- /* 115 */ "InsertInt" OpHelp("intkey=P3 data=r[P2]"),
- /* 116 */ "Delete" OpHelp(""),
- /* 117 */ "ResetCount" OpHelp(""),
- /* 118 */ "SorterCompare" OpHelp("if key(P1)!=trim(r[P3],P4) goto P2"),
- /* 119 */ "SorterData" OpHelp("r[P2]=data"),
- /* 120 */ "RowKey" OpHelp("r[P2]=key"),
- /* 121 */ "RowData" OpHelp("r[P2]=data"),
- /* 122 */ "Rowid" OpHelp("r[P2]=rowid"),
- /* 123 */ "NullRow" OpHelp(""),
- /* 124 */ "SorterInsert" OpHelp(""),
- /* 125 */ "IdxInsert" OpHelp("key=r[P2]"),
- /* 126 */ "IdxDelete" OpHelp("key=r[P2@P3]"),
- /* 127 */ "Seek" OpHelp("Move P3 to P1.rowid"),
- /* 128 */ "IdxRowid" OpHelp("r[P2]=rowid"),
- /* 129 */ "Destroy" OpHelp(""),
- /* 130 */ "Clear" OpHelp(""),
- /* 131 */ "ResetSorter" OpHelp(""),
- /* 132 */ "CreateIndex" OpHelp("r[P2]=root iDb=P1"),
- /* 133 */ "Real" OpHelp("r[P2]=P4"),
- /* 134 */ "CreateTable" OpHelp("r[P2]=root iDb=P1"),
- /* 135 */ "ParseSchema" OpHelp(""),
- /* 136 */ "LoadAnalysis" OpHelp(""),
- /* 137 */ "DropTable" OpHelp(""),
- /* 138 */ "DropIndex" OpHelp(""),
- /* 139 */ "DropTrigger" OpHelp(""),
- /* 140 */ "IntegrityCk" OpHelp(""),
- /* 141 */ "RowSetAdd" OpHelp("rowset(P1)=r[P2]"),
- /* 142 */ "Param" OpHelp(""),
- /* 143 */ "FkCounter" OpHelp("fkctr[P1]+=P2"),
- /* 144 */ "MemMax" OpHelp("r[P1]=max(r[P1],r[P2])"),
- /* 145 */ "OffsetLimit" OpHelp("if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1)"),
- /* 146 */ "AggStep0" OpHelp("accum=r[P3] step(r[P2@P5])"),
- /* 147 */ "AggStep" OpHelp("accum=r[P3] step(r[P2@P5])"),
- /* 148 */ "AggFinal" OpHelp("accum=r[P1] N=P2"),
- /* 149 */ "Expire" OpHelp(""),
- /* 150 */ "TableLock" OpHelp("iDb=P1 root=P2 write=P3"),
- /* 151 */ "VBegin" OpHelp(""),
- /* 152 */ "VCreate" OpHelp(""),
- /* 153 */ "VDestroy" OpHelp(""),
- /* 154 */ "VOpen" OpHelp(""),
- /* 155 */ "VColumn" OpHelp("r[P3]=vcolumn(P2)"),
- /* 156 */ "VRename" OpHelp(""),
- /* 157 */ "Pagecount" OpHelp(""),
- /* 158 */ "MaxPgcnt" OpHelp(""),
- /* 159 */ "CursorHint" OpHelp(""),
- /* 160 */ "Noop" OpHelp(""),
- /* 161 */ "Explain" OpHelp(""),
+ /* 98 */ "Affinity" OpHelp("affinity(r[P1@P2])"),
+ /* 99 */ "MakeRecord" OpHelp("r[P3]=mkrec(r[P1@P2])"),
+ /* 100 */ "Count" OpHelp("r[P2]=count()"),
+ /* 101 */ "ReadCookie" OpHelp(""),
+ /* 102 */ "SetCookie" OpHelp(""),
+ /* 103 */ "ReopenIdx" OpHelp("root=P2 iDb=P3"),
+ /* 104 */ "OpenRead" OpHelp("root=P2 iDb=P3"),
+ /* 105 */ "OpenWrite" OpHelp("root=P2 iDb=P3"),
+ /* 106 */ "OpenAutoindex" OpHelp("nColumn=P2"),
+ /* 107 */ "OpenEphemeral" OpHelp("nColumn=P2"),
+ /* 108 */ "SorterOpen" OpHelp(""),
+ /* 109 */ "SequenceTest" OpHelp("if( cursor[P1].ctr++ ) pc = P2"),
+ /* 110 */ "OpenPseudo" OpHelp("P3 columns in r[P2]"),
+ /* 111 */ "Close" OpHelp(""),
+ /* 112 */ "ColumnsUsed" OpHelp(""),
+ /* 113 */ "Sequence" OpHelp("r[P2]=cursor[P1].ctr++"),
+ /* 114 */ "NewRowid" OpHelp("r[P2]=rowid"),
+ /* 115 */ "Insert" OpHelp("intkey=r[P3] data=r[P2]"),
+ /* 116 */ "InsertInt" OpHelp("intkey=P3 data=r[P2]"),
+ /* 117 */ "Delete" OpHelp(""),
+ /* 118 */ "ResetCount" OpHelp(""),
+ /* 119 */ "SorterCompare" OpHelp("if key(P1)!=trim(r[P3],P4) goto P2"),
+ /* 120 */ "SorterData" OpHelp("r[P2]=data"),
+ /* 121 */ "RowKey" OpHelp("r[P2]=key"),
+ /* 122 */ "RowData" OpHelp("r[P2]=data"),
+ /* 123 */ "Rowid" OpHelp("r[P2]=rowid"),
+ /* 124 */ "NullRow" OpHelp(""),
+ /* 125 */ "SorterInsert" OpHelp(""),
+ /* 126 */ "IdxInsert" OpHelp("key=r[P2]"),
+ /* 127 */ "IdxDelete" OpHelp("key=r[P2@P3]"),
+ /* 128 */ "Seek" OpHelp("Move P3 to P1.rowid"),
+ /* 129 */ "IdxRowid" OpHelp("r[P2]=rowid"),
+ /* 130 */ "Destroy" OpHelp(""),
+ /* 131 */ "Clear" OpHelp(""),
+ /* 132 */ "Real" OpHelp("r[P2]=P4"),
+ /* 133 */ "ResetSorter" OpHelp(""),
+ /* 134 */ "CreateIndex" OpHelp("r[P2]=root iDb=P1"),
+ /* 135 */ "CreateTable" OpHelp("r[P2]=root iDb=P1"),
+ /* 136 */ "ParseSchema" OpHelp(""),
+ /* 137 */ "LoadAnalysis" OpHelp(""),
+ /* 138 */ "DropTable" OpHelp(""),
+ /* 139 */ "DropIndex" OpHelp(""),
+ /* 140 */ "DropTrigger" OpHelp(""),
+ /* 141 */ "IntegrityCk" OpHelp(""),
+ /* 142 */ "RowSetAdd" OpHelp("rowset(P1)=r[P2]"),
+ /* 143 */ "Param" OpHelp(""),
+ /* 144 */ "FkCounter" OpHelp("fkctr[P1]+=P2"),
+ /* 145 */ "MemMax" OpHelp("r[P1]=max(r[P1],r[P2])"),
+ /* 146 */ "OffsetLimit" OpHelp("if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1)"),
+ /* 147 */ "AggStep0" OpHelp("accum=r[P3] step(r[P2@P5])"),
+ /* 148 */ "AggStep" OpHelp("accum=r[P3] step(r[P2@P5])"),
+ /* 149 */ "AggFinal" OpHelp("accum=r[P1] N=P2"),
+ /* 150 */ "Expire" OpHelp(""),
+ /* 151 */ "TableLock" OpHelp("iDb=P1 root=P2 write=P3"),
+ /* 152 */ "VBegin" OpHelp(""),
+ /* 153 */ "VCreate" OpHelp(""),
+ /* 154 */ "VDestroy" OpHelp(""),
+ /* 155 */ "VOpen" OpHelp(""),
+ /* 156 */ "VColumn" OpHelp("r[P3]=vcolumn(P2)"),
+ /* 157 */ "VRename" OpHelp(""),
+ /* 158 */ "Pagecount" OpHelp(""),
+ /* 159 */ "MaxPgcnt" OpHelp(""),
+ /* 160 */ "CursorHint" OpHelp(""),
+ /* 161 */ "Noop" OpHelp(""),
+ /* 162 */ "Explain" OpHelp(""),
};
return azName[i];
}
@@ -34855,6 +34914,27 @@ static UnixUnusedFd *findReusableFd(const char *zPath, int flags){
}
/*
+** Find the mode, uid and gid of file zFile.
+*/
+static int getFileMode(
+ const char *zFile, /* File name */
+ mode_t *pMode, /* OUT: Permissions of zFile */
+ uid_t *pUid, /* OUT: uid of zFile. */
+ gid_t *pGid /* OUT: gid of zFile. */
+){
+ struct stat sStat; /* Output of stat() on database file */
+ int rc = SQLITE_OK;
+ if( 0==osStat(zFile, &sStat) ){
+ *pMode = sStat.st_mode & 0777;
+ *pUid = sStat.st_uid;
+ *pGid = sStat.st_gid;
+ }else{
+ rc = SQLITE_IOERR_FSTAT;
+ }
+ return rc;
+}
+
+/*
** This function is called by unixOpen() to determine the unix permissions
** to create new files with. If no error occurs, then SQLITE_OK is returned
** and a value suitable for passing as the third argument to open(2) is
@@ -34889,7 +34969,6 @@ static int findCreateFileMode(
if( flags & (SQLITE_OPEN_WAL|SQLITE_OPEN_MAIN_JOURNAL) ){
char zDb[MAX_PATHNAME+1]; /* Database file path */
int nDb; /* Number of valid bytes in zDb */
- struct stat sStat; /* Output of stat() on database file */
/* zPath is a path to a WAL or journal file. The following block derives
** the path to the associated database file from zPath. This block handles
@@ -34920,15 +34999,18 @@ static int findCreateFileMode(
memcpy(zDb, zPath, nDb);
zDb[nDb] = '\0';
- if( 0==osStat(zDb, &sStat) ){
- *pMode = sStat.st_mode & 0777;
- *pUid = sStat.st_uid;
- *pGid = sStat.st_gid;
- }else{
- rc = SQLITE_IOERR_FSTAT;
- }
+ rc = getFileMode(zDb, pMode, pUid, pGid);
}else if( flags & SQLITE_OPEN_DELETEONCLOSE ){
*pMode = 0600;
+ }else if( flags & SQLITE_OPEN_URI ){
+ /* If this is a main database file and the file was opened using a URI
+ ** filename, check for the "modeof" parameter. If present, interpret
+ ** its value as a filename and try to copy the mode, uid and gid from
+ ** that file. */
+ const char *z = sqlite3_uri_parameter(zPath, "modeof");
+ if( z ){
+ rc = getFileMode(z, pMode, pUid, pGid);
+ }
}
return rc;
}
@@ -40604,6 +40686,12 @@ static int winFileControl(sqlite3_file *id, int op, void *pArg){
OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h));
return SQLITE_OK;
}
+ case SQLITE_FCNTL_WIN32_GET_HANDLE: {
+ LPHANDLE phFile = (LPHANDLE)pArg;
+ *phFile = pFile->h;
+ OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h));
+ return SQLITE_OK;
+ }
#ifdef SQLITE_TEST
case SQLITE_FCNTL_WIN32_SET_HANDLE: {
LPHANDLE phFile = (LPHANDLE)pArg;
@@ -43951,7 +44039,7 @@ static SQLITE_NOINLINE PgHdr *pcacheFetchFinishWithInit(
assert( pPage!=0 );
pPgHdr = (PgHdr*)pPage->pExtra;
assert( pPgHdr->pPage==0 );
- memset(pPgHdr, 0, sizeof(PgHdr));
+ memset(&pPgHdr->pDirty, 0, sizeof(PgHdr) - offsetof(PgHdr,pDirty));
pPgHdr->pPage = pPage;
pPgHdr->pData = pPage->pBuf;
pPgHdr->pExtra = (void *)&pPgHdr[1];
@@ -44645,7 +44733,7 @@ static int pcache1InitBulk(PCache1 *pCache){
szBulk = -1024 * (i64)pcache1.nInitPage;
}
if( szBulk > pCache->szAlloc*(i64)pCache->nMax ){
- szBulk = pCache->szAlloc*pCache->nMax;
+ szBulk = pCache->szAlloc*(i64)pCache->nMax;
}
zBulk = pCache->pBulk = sqlite3Malloc( szBulk );
sqlite3EndBenignMalloc();
@@ -47094,9 +47182,10 @@ static const unsigned char aJournalMagic[] = {
** rollback journal. Otherwise false.
*/
#ifndef SQLITE_OMIT_WAL
-static int pagerUseWal(Pager *pPager){
+SQLITE_PRIVATE int sqlite3PagerUseWal(Pager *pPager){
return (pPager->pWal!=0);
}
+# define pagerUseWal(x) sqlite3PagerUseWal(x)
#else
# define pagerUseWal(x) 0
# define pagerRollbackWal(x) 0
@@ -52933,7 +53022,11 @@ SQLITE_PRIVATE int sqlite3PagerOpenSavepoint(Pager *pPager, int nSavepoint){
** savepoint. If no errors occur, SQLITE_OK is returned.
*/
SQLITE_PRIVATE int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint){
- int rc = pPager->errCode; /* Return code */
+ int rc = pPager->errCode;
+
+#ifdef SQLITE_ENABLE_ZIPVFS
+ if( op==SAVEPOINT_RELEASE ) rc = SQLITE_OK;
+#endif
assert( op==SAVEPOINT_RELEASE || op==SAVEPOINT_ROLLBACK );
assert( iSavepoint>=0 || op==SAVEPOINT_ROLLBACK );
@@ -52974,6 +53067,20 @@ SQLITE_PRIVATE int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint){
rc = pagerPlaybackSavepoint(pPager, pSavepoint);
assert(rc!=SQLITE_DONE);
}
+
+#ifdef SQLITE_ENABLE_ZIPVFS
+ /* If the cache has been modified but the savepoint cannot be rolled
+ ** back journal_mode=off, put the pager in the error state. This way,
+ ** if the VFS used by this pager includes ZipVFS, the entire transaction
+ ** can be rolled back at the ZipVFS level. */
+ else if(
+ pPager->journalMode==PAGER_JOURNALMODE_OFF
+ && pPager->eState>=PAGER_WRITER_CACHEMOD
+ ){
+ pPager->errCode = SQLITE_ABORT;
+ pPager->eState = PAGER_ERROR;
+ }
+#endif
}
return rc;
@@ -58899,7 +59006,7 @@ static int btreeMoveto(
){
int rc; /* Status code */
UnpackedRecord *pIdxKey; /* Unpacked index key */
- char aSpace[200]; /* Temp space for pIdxKey - to avoid a malloc */
+ char aSpace[384]; /* Temp space for pIdxKey - to avoid a malloc */
char *pFree = 0;
if( pKey ){
@@ -59741,8 +59848,11 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){
if( data[iPtr+1]==0 && data[iPtr]==0 ){
iFreeBlk = 0; /* Shortcut for the case when the freelist is empty */
}else{
- while( (iFreeBlk = get2byte(&data[iPtr]))>0 && iFreeBlk<iStart ){
- if( iFreeBlk<iPtr+4 ) return SQLITE_CORRUPT_BKPT;
+ while( (iFreeBlk = get2byte(&data[iPtr]))<iStart ){
+ if( iFreeBlk<iPtr+4 ){
+ if( iFreeBlk==0 ) break;
+ return SQLITE_CORRUPT_BKPT;
+ }
iPtr = iFreeBlk;
}
if( iFreeBlk>iLast ) return SQLITE_CORRUPT_BKPT;
@@ -62734,7 +62844,7 @@ static int accessPayload(
&& (bEnd || a==ovflSize) /* (6) */
&& pBt->inTransaction==TRANS_READ /* (4) */
&& (fd = sqlite3PagerFile(pBt->pPager))->pMethods /* (3) */
- && pBt->pPage1->aData[19]==0x01 /* (5) */
+ && 0==sqlite3PagerUseWal(pBt->pPager) /* (5) */
&& &pBuf[-4]>=pBufStart /* (7) */
){
u8 aSave[4];
@@ -64233,8 +64343,6 @@ static int fillInCell(
nHeader += putVarint32(&pCell[nHeader], nPayload);
nHeader += putVarint(&pCell[nHeader], *(u64*)&pX->nKey);
}else{
- assert( pX->nData==0 );
- assert( pX->nZero==0 );
assert( pX->nKey<=0x7fffffff && pX->pKey!=0 );
nSrc = nPayload = (int)pX->nKey;
pSrc = pX->pKey;
@@ -67934,22 +68042,16 @@ static Btree *findBtree(sqlite3 *pErrorDb, sqlite3 *pDb, const char *zDb){
int i = sqlite3FindDbName(pDb, zDb);
if( i==1 ){
- Parse *pParse;
+ Parse sParse;
int rc = 0;
- pParse = sqlite3StackAllocZero(pErrorDb, sizeof(*pParse));
- if( pParse==0 ){
- sqlite3ErrorWithMsg(pErrorDb, SQLITE_NOMEM, "out of memory");
- rc = SQLITE_NOMEM_BKPT;
- }else{
- pParse->db = pDb;
- if( sqlite3OpenTempDatabase(pParse) ){
- sqlite3ErrorWithMsg(pErrorDb, pParse->rc, "%s", pParse->zErrMsg);
- rc = SQLITE_ERROR;
- }
- sqlite3DbFree(pErrorDb, pParse->zErrMsg);
- sqlite3ParserReset(pParse);
- sqlite3StackFree(pErrorDb, pParse);
+ memset(&sParse, 0, sizeof(sParse));
+ sParse.db = pDb;
+ if( sqlite3OpenTempDatabase(&sParse) ){
+ sqlite3ErrorWithMsg(pErrorDb, sParse.rc, "%s", sParse.zErrMsg);
+ rc = SQLITE_ERROR;
}
+ sqlite3DbFree(pErrorDb, sParse.zErrMsg);
+ sqlite3ParserReset(&sParse);
if( rc ){
return 0;
}
@@ -68047,7 +68149,6 @@ SQLITE_API sqlite3_backup *sqlite3_backup_init(
p->isAttached = 0;
if( 0==p->pSrc || 0==p->pDest
- || setDestPgsz(p)==SQLITE_NOMEM
|| checkReadTransaction(pDestDb, p->pDest)!=SQLITE_OK
){
/* One (or both) of the named databases did not exist or an OOM
@@ -68235,14 +68336,6 @@ SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage){
rc = SQLITE_OK;
}
- /* Lock the destination database, if it is not locked already. */
- if( SQLITE_OK==rc && p->bDestLocked==0
- && SQLITE_OK==(rc = sqlite3BtreeBeginTrans(p->pDest, 2))
- ){
- p->bDestLocked = 1;
- sqlite3BtreeGetMeta(p->pDest, BTREE_SCHEMA_VERSION, &p->iDestSchema);
- }
-
/* If there is no open read-transaction on the source database, open
** one now. If a transaction is opened here, then it will be closed
** before this function exits.
@@ -68252,6 +68345,24 @@ SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage){
bCloseTrans = 1;
}
+ /* If the destination database has not yet been locked (i.e. if this
+ ** is the first call to backup_step() for the current backup operation),
+ ** try to set its page size to the same as the source database. This
+ ** is especially important on ZipVFS systems, as in that case it is
+ ** not possible to create a database file that uses one page size by
+ ** writing to it with another. */
+ if( p->bDestLocked==0 && rc==SQLITE_OK && setDestPgsz(p)==SQLITE_NOMEM ){
+ rc = SQLITE_NOMEM;
+ }
+
+ /* Lock the destination database, if it is not locked already. */
+ if( SQLITE_OK==rc && p->bDestLocked==0
+ && SQLITE_OK==(rc = sqlite3BtreeBeginTrans(p->pDest, 2))
+ ){
+ p->bDestLocked = 1;
+ sqlite3BtreeGetMeta(p->pDest, BTREE_SCHEMA_VERSION, &p->iDestSchema);
+ }
+
/* Do not allow backup if the destination database is in WAL mode
** and the page sizes are different between source and destination */
pgszSrc = sqlite3BtreeGetPageSize(p->pSrc);
@@ -68840,18 +68951,18 @@ SQLITE_PRIVATE int sqlite3VdbeMemClearAndResize(Mem *pMem, int szNew){
** Return SQLITE_OK on success or SQLITE_NOMEM if malloc fails.
*/
SQLITE_PRIVATE int sqlite3VdbeMemMakeWriteable(Mem *pMem){
- int f;
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
assert( (pMem->flags&MEM_RowSet)==0 );
- ExpandBlob(pMem);
- f = pMem->flags;
- if( (f&(MEM_Str|MEM_Blob)) && (pMem->szMalloc==0 || pMem->z!=pMem->zMalloc) ){
- if( sqlite3VdbeMemGrow(pMem, pMem->n + 2, 1) ){
- return SQLITE_NOMEM_BKPT;
+ if( (pMem->flags & (MEM_Str|MEM_Blob))!=0 ){
+ if( ExpandBlob(pMem) ) return SQLITE_NOMEM;
+ if( pMem->szMalloc==0 || pMem->z!=pMem->zMalloc ){
+ if( sqlite3VdbeMemGrow(pMem, pMem->n + 2, 1) ){
+ return SQLITE_NOMEM_BKPT;
+ }
+ pMem->z[pMem->n] = 0;
+ pMem->z[pMem->n+1] = 0;
+ pMem->flags |= MEM_Term;
}
- pMem->z[pMem->n] = 0;
- pMem->z[pMem->n+1] = 0;
- pMem->flags |= MEM_Term;
}
pMem->flags &= ~MEM_Ephem;
#ifdef SQLITE_DEBUG
@@ -68867,25 +68978,24 @@ SQLITE_PRIVATE int sqlite3VdbeMemMakeWriteable(Mem *pMem){
*/
#ifndef SQLITE_OMIT_INCRBLOB
SQLITE_PRIVATE int sqlite3VdbeMemExpandBlob(Mem *pMem){
- if( pMem->flags & MEM_Zero ){
- int nByte;
- assert( pMem->flags&MEM_Blob );
- assert( (pMem->flags&MEM_RowSet)==0 );
- assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
-
- /* Set nByte to the number of bytes required to store the expanded blob. */
- nByte = pMem->n + pMem->u.nZero;
- if( nByte<=0 ){
- nByte = 1;
- }
- if( sqlite3VdbeMemGrow(pMem, nByte, 1) ){
- return SQLITE_NOMEM_BKPT;
- }
+ int nByte;
+ assert( pMem->flags & MEM_Zero );
+ assert( pMem->flags&MEM_Blob );
+ assert( (pMem->flags&MEM_RowSet)==0 );
+ assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
- memset(&pMem->z[pMem->n], 0, pMem->u.nZero);
- pMem->n += pMem->u.nZero;
- pMem->flags &= ~(MEM_Zero|MEM_Term);
+ /* Set nByte to the number of bytes required to store the expanded blob. */
+ nByte = pMem->n + pMem->u.nZero;
+ if( nByte<=0 ){
+ nByte = 1;
}
+ if( sqlite3VdbeMemGrow(pMem, nByte, 1) ){
+ return SQLITE_NOMEM_BKPT;
+ }
+
+ memset(&pMem->z[pMem->n], 0, pMem->u.nZero);
+ pMem->n += pMem->u.nZero;
+ pMem->flags &= ~(MEM_Zero|MEM_Term);
return SQLITE_OK;
}
#endif
@@ -68945,6 +69055,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemStringify(Mem *pMem, u8 enc, u8 bForce){
if( sqlite3VdbeMemClearAndResize(pMem, nByte) ){
+ pMem->enc = 0;
return SQLITE_NOMEM_BKPT;
}
@@ -69226,7 +69337,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemNumerify(Mem *pMem){
}
}
assert( (pMem->flags & (MEM_Int|MEM_Real|MEM_Null))!=0 );
- pMem->flags &= ~(MEM_Str|MEM_Blob);
+ pMem->flags &= ~(MEM_Str|MEM_Blob|MEM_Zero);
return SQLITE_OK;
}
@@ -69244,7 +69355,7 @@ SQLITE_PRIVATE void sqlite3VdbeMemCast(Mem *pMem, u8 aff, u8 encoding){
if( (pMem->flags & MEM_Blob)==0 ){
sqlite3ValueApplyAffinity(pMem, SQLITE_AFF_TEXT, encoding);
assert( pMem->flags & MEM_Str || pMem->db->mallocFailed );
- MemSetTypeFlag(pMem, MEM_Blob);
+ if( pMem->flags & MEM_Str ) MemSetTypeFlag(pMem, MEM_Blob);
}else{
pMem->flags &= ~(MEM_TypeMask&~MEM_Blob);
}
@@ -69669,9 +69780,6 @@ static SQLITE_NOINLINE const void *valueToText(sqlite3_value* pVal, u8 enc){
assert( (pVal->flags & (MEM_Null))==0 );
if( pVal->flags & (MEM_Blob|MEM_Str) ){
pVal->flags |= MEM_Str;
- if( pVal->flags & MEM_Zero ){
- sqlite3VdbeMemExpandBlob(pVal);
- }
if( pVal->enc != (enc & ~SQLITE_UTF16_ALIGNED) ){
sqlite3VdbeChangeEncoding(pVal, enc & ~SQLITE_UTF16_ALIGNED);
}
@@ -69924,10 +70032,7 @@ static int valueFromExpr(
const char *zNeg = "";
int rc = SQLITE_OK;
- if( !pExpr ){
- *ppVal = 0;
- return SQLITE_OK;
- }
+ assert( pExpr!=0 );
while( (op = pExpr->op)==TK_UPLUS || op==TK_SPAN ) pExpr = pExpr->pLeft;
if( NEVER(op==TK_REGISTER) ) op = pExpr->op2;
@@ -70051,7 +70156,7 @@ SQLITE_PRIVATE int sqlite3ValueFromExpr(
u8 affinity, /* Affinity to use */
sqlite3_value **ppVal /* Write the new value here */
){
- return valueFromExpr(db, pExpr, enc, affinity, ppVal, 0);
+ return pExpr ? valueFromExpr(db, pExpr, enc, affinity, ppVal, 0) : 0;
}
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
@@ -70171,9 +70276,9 @@ static int stat4ValueFromExpr(
** structures intended to be compared against sample index keys stored
** in the sqlite_stat4 table.
**
-** A single call to this function attempts to populates field iVal (leftmost
-** is 0 etc.) of the unpacked record with a value extracted from expression
-** pExpr. Extraction of values is possible if:
+** A single call to this function populates zero or more fields of the
+** record starting with field iVal (fields are numbered from left to
+** right starting with 0). A single field is populated if:
**
** * (pExpr==0). In this case the value is assumed to be an SQL NULL,
**
@@ -70182,10 +70287,14 @@ static int stat4ValueFromExpr(
** * The sqlite3ValueFromExpr() function is able to extract a value
** from the expression (i.e. the expression is a literal value).
**
-** If a value can be extracted, the affinity passed as the 5th argument
-** is applied to it before it is copied into the UnpackedRecord. Output
-** parameter *pbOk is set to true if a value is extracted, or false
-** otherwise.
+** Or, if pExpr is a TK_VECTOR, one field is populated for each of the
+** vector components that match either of the two latter criteria listed
+** above.
+**
+** Before any value is appended to the record, the affinity of the
+** corresponding column within index pIdx is applied to it. Before
+** this function returns, output parameter *pnExtract is set to the
+** number of values appended to the record.
**
** When this function is called, *ppRec must either point to an object
** allocated by an earlier call to this function, or must be NULL. If it
@@ -70201,22 +70310,33 @@ SQLITE_PRIVATE int sqlite3Stat4ProbeSetValue(
Index *pIdx, /* Index being probed */
UnpackedRecord **ppRec, /* IN/OUT: Probe record */
Expr *pExpr, /* The expression to extract a value from */
- u8 affinity, /* Affinity to use */
+ int nElem, /* Maximum number of values to append */
int iVal, /* Array element to populate */
- int *pbOk /* OUT: True if value was extracted */
+ int *pnExtract /* OUT: Values appended to the record */
){
- int rc;
- sqlite3_value *pVal = 0;
- struct ValueNewStat4Ctx alloc;
+ int rc = SQLITE_OK;
+ int nExtract = 0;
+
+ if( pExpr==0 || pExpr->op!=TK_SELECT ){
+ int i;
+ struct ValueNewStat4Ctx alloc;
- alloc.pParse = pParse;
- alloc.pIdx = pIdx;
- alloc.ppRec = ppRec;
- alloc.iVal = iVal;
+ alloc.pParse = pParse;
+ alloc.pIdx = pIdx;
+ alloc.ppRec = ppRec;
+
+ for(i=0; i<nElem; i++){
+ sqlite3_value *pVal = 0;
+ Expr *pElem = (pExpr ? sqlite3VectorFieldSubexpr(pExpr, i) : 0);
+ u8 aff = sqlite3IndexColumnAffinity(pParse->db, pIdx, iVal+i);
+ alloc.iVal = iVal+i;
+ rc = stat4ValueFromExpr(pParse, pElem, aff, &alloc, &pVal);
+ if( !pVal ) break;
+ nExtract++;
+ }
+ }
- rc = stat4ValueFromExpr(pParse, pExpr, affinity, &alloc, &pVal);
- assert( pVal==0 || pVal->db==pParse->db );
- *pbOk = (pVal!=0);
+ *pnExtract = nExtract;
return rc;
}
@@ -70379,8 +70499,9 @@ SQLITE_PRIVATE int sqlite3ValueBytes(sqlite3_value *pVal, u8 enc){
SQLITE_PRIVATE Vdbe *sqlite3VdbeCreate(Parse *pParse){
sqlite3 *db = pParse->db;
Vdbe *p;
- p = sqlite3DbMallocZero(db, sizeof(Vdbe) );
+ p = sqlite3DbMallocRawNN(db, sizeof(Vdbe) );
if( p==0 ) return 0;
+ memset(&p->aOp, 0, sizeof(Vdbe)-offsetof(Vdbe,aOp));
p->db = db;
if( db->pVdbe ){
db->pVdbe->pPrev = p;
@@ -70542,9 +70663,8 @@ SQLITE_PRIVATE int sqlite3VdbeAddOp3(Vdbe *p, int op, int p1, int p2, int p3){
if( p->db->flags & SQLITE_VdbeAddopTrace ){
int jj, kk;
Parse *pParse = p->pParse;
- for(jj=kk=0; jj<SQLITE_N_COLCACHE; jj++){
+ for(jj=kk=0; jj<pParse->nColCache; jj++){
struct yColCache *x = pParse->aColCache + jj;
- if( x->iLevel>pParse->iCacheLevel || x->iReg==0 ) continue;
printf(" r[%d]={%d:%d}", x->iReg, x->iTable, x->iColumn);
kk++;
}
@@ -70732,7 +70852,6 @@ SQLITE_PRIVATE void sqlite3VdbeResolveLabel(Vdbe *v, int x){
if( p->aLabel ){
p->aLabel[j] = v->nOp;
}
- p->iFixedOp = v->nOp - 1;
}
/*
@@ -71123,7 +71242,8 @@ SQLITE_PRIVATE void sqlite3VdbeChangeP3(Vdbe *p, u32 addr, int val){
sqlite3VdbeGetOp(p,addr)->p3 = val;
}
SQLITE_PRIVATE void sqlite3VdbeChangeP5(Vdbe *p, u8 p5){
- if( !p->db->mallocFailed ) p->aOp[p->nOp-1].p5 = p5;
+ assert( p->nOp>0 || p->db->mallocFailed );
+ if( p->nOp>0 ) p->aOp[p->nOp-1].p5 = p5;
}
/*
@@ -71131,7 +71251,6 @@ SQLITE_PRIVATE void sqlite3VdbeChangeP5(Vdbe *p, u8 p5){
** the address of the next instruction to be coded.
*/
SQLITE_PRIVATE void sqlite3VdbeJumpHere(Vdbe *p, int addr){
- p->pParse->iFixedOp = p->nOp - 1;
sqlite3VdbeChangeP2(p, addr, p->nOp);
}
@@ -71254,7 +71373,7 @@ SQLITE_PRIVATE int sqlite3VdbeChangeToNoop(Vdbe *p, int addr){
** then remove it. Return true if and only if an opcode was removed.
*/
SQLITE_PRIVATE int sqlite3VdbeDeletePriorOpcode(Vdbe *p, u8 op){
- if( (p->nOp-1)>(p->pParse->iFixedOp) && p->aOp[p->nOp-1].opcode==op ){
+ if( p->nOp>0 && p->aOp[p->nOp-1].opcode==op ){
return sqlite3VdbeChangeToNoop(p, p->nOp-1);
}else{
return 0;
@@ -71818,6 +71937,21 @@ SQLITE_PRIVATE void sqlite3VdbePrintOp(FILE *pOut, int pc, Op *pOp){
#endif
/*
+** Initialize an array of N Mem element.
+*/
+static void initMemArray(Mem *p, int N, sqlite3 *db, u16 flags){
+ while( (N--)>0 ){
+ p->db = db;
+ p->flags = flags;
+ p->szMalloc = 0;
+#ifdef SQLITE_DEBUG
+ p->pScopyFrom = 0;
+#endif
+ p++;
+ }
+}
+
+/*
** Release an array of N Mem elements
*/
static void releaseMemArray(Mem *p, int N){
@@ -72028,6 +72162,7 @@ SQLITE_PRIVATE int sqlite3VdbeList(
pMem->flags = MEM_Str|MEM_Term;
zP4 = displayP4(pOp, pMem->z, pMem->szMalloc);
if( zP4!=pMem->z ){
+ pMem->n = 0;
sqlite3VdbeMemSetStr(pMem, zP4, -1, SQLITE_UTF8, 0);
}else{
assert( pMem->z!=0 );
@@ -72170,7 +72305,7 @@ SQLITE_PRIVATE void sqlite3VdbeRewind(Vdbe *p){
int i;
#endif
assert( p!=0 );
- assert( p->magic==VDBE_MAGIC_INIT );
+ assert( p->magic==VDBE_MAGIC_INIT || p->magic==VDBE_MAGIC_RESET );
/* There should be at least one opcode.
*/
@@ -72227,7 +72362,6 @@ SQLITE_PRIVATE void sqlite3VdbeMakeReady(
int nMem; /* Number of VM memory registers */
int nCursor; /* Number of cursors required */
int nArg; /* Number of arguments in subprograms */
- int nOnce; /* Number of OP_Once instructions */
int n; /* Loop counter */
struct ReusableSpace x; /* Reusable bulk memory */
@@ -72242,8 +72376,6 @@ SQLITE_PRIVATE void sqlite3VdbeMakeReady(
nMem = pParse->nMem;
nCursor = pParse->nTab;
nArg = pParse->nMaxArg;
- nOnce = pParse->nOnce;
- if( nOnce==0 ) nOnce = 1; /* Ensure at least one byte in p->aOnceFlag[] */
/* Each cursor uses a memory cell. The first cursor (cursor 0) can
** use aMem[0] which is not otherwise used by the VDBE program. Allocate
@@ -72262,10 +72394,7 @@ SQLITE_PRIVATE void sqlite3VdbeMakeReady(
assert( EIGHT_BYTE_ALIGNMENT(x.pSpace) );
x.nFree = ROUNDDOWN8(pParse->szOpAlloc - n); /* Bytes of unused memory */
assert( x.nFree>=0 );
- if( x.nFree>0 ){
- memset(x.pSpace, 0, x.nFree);
- assert( EIGHT_BYTE_ALIGNMENT(&x.pSpace[x.nFree]) );
- }
+ assert( EIGHT_BYTE_ALIGNMENT(&x.pSpace[x.nFree]) );
resolveP2Values(p, &nArg);
p->usesStmtJournal = (u8)(pParse->isMultiWrite && pParse->mayAbort);
@@ -72290,36 +72419,34 @@ SQLITE_PRIVATE void sqlite3VdbeMakeReady(
p->aVar = allocSpace(&x, p->aVar, nVar*sizeof(Mem));
p->apArg = allocSpace(&x, p->apArg, nArg*sizeof(Mem*));
p->apCsr = allocSpace(&x, p->apCsr, nCursor*sizeof(VdbeCursor*));
- p->aOnceFlag = allocSpace(&x, p->aOnceFlag, nOnce);
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
p->anExec = allocSpace(&x, p->anExec, p->nOp*sizeof(i64));
#endif
if( x.nNeeded==0 ) break;
- x.pSpace = p->pFree = sqlite3DbMallocZero(db, x.nNeeded);
+ x.pSpace = p->pFree = sqlite3DbMallocRawNN(db, x.nNeeded);
x.nFree = x.nNeeded;
}while( !db->mallocFailed );
- p->nCursor = nCursor;
- p->nOnceFlag = nOnce;
- if( p->aVar ){
- p->nVar = (ynVar)nVar;
- for(n=0; n<nVar; n++){
- p->aVar[n].flags = MEM_Null;
- p->aVar[n].db = db;
- }
- }
p->nzVar = pParse->nzVar;
p->azVar = pParse->azVar;
pParse->nzVar = 0;
pParse->azVar = 0;
- if( p->aMem ){
+ p->explain = pParse->explain;
+ if( db->mallocFailed ){
+ p->nVar = 0;
+ p->nCursor = 0;
+ p->nMem = 0;
+ }else{
+ p->nCursor = nCursor;
+ p->nVar = (ynVar)nVar;
+ initMemArray(p->aVar, nVar, db, MEM_Null);
p->nMem = nMem;
- for(n=0; n<nMem; n++){
- p->aMem[n].flags = MEM_Undefined;
- p->aMem[n].db = db;
- }
+ initMemArray(p->aMem, nMem, db, MEM_Undefined);
+ memset(p->apCsr, 0, nCursor*sizeof(VdbeCursor*));
+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
+ memset(p->anExec, 0, p->nOp*sizeof(i64));
+#endif
}
- p->explain = pParse->explain;
sqlite3VdbeRewind(p);
}
@@ -72388,8 +72515,6 @@ SQLITE_PRIVATE int sqlite3VdbeFrameRestore(VdbeFrame *pFrame){
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
v->anExec = pFrame->anExec;
#endif
- v->aOnceFlag = pFrame->aOnceFlag;
- v->nOnceFlag = pFrame->nOnceFlag;
v->aOp = pFrame->aOp;
v->nOp = pFrame->nOp;
v->aMem = pFrame->aMem;
@@ -72473,13 +72598,9 @@ SQLITE_PRIVATE void sqlite3VdbeSetNumCols(Vdbe *p, int nResColumn){
sqlite3DbFree(db, p->aColName);
n = nResColumn*COLNAME_N;
p->nResColumn = (u16)nResColumn;
- p->aColName = pColName = (Mem*)sqlite3DbMallocZero(db, sizeof(Mem)*n );
+ p->aColName = pColName = (Mem*)sqlite3DbMallocRawNN(db, sizeof(Mem)*n );
if( p->aColName==0 ) return;
- while( n-- > 0 ){
- pColName->flags = MEM_Null;
- pColName->db = p->db;
- pColName++;
- }
+ initMemArray(p->aColName, n, p->db, MEM_Null);
}
/*
@@ -72930,7 +73051,6 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){
if( db->mallocFailed ){
p->rc = SQLITE_NOMEM_BKPT;
}
- if( p->aOnceFlag ) memset(p->aOnceFlag, 0, p->nOnceFlag);
closeAllCursors(p);
if( p->magic!=VDBE_MAGIC_RUN ){
return SQLITE_OK;
@@ -73242,7 +73362,7 @@ SQLITE_PRIVATE int sqlite3VdbeReset(Vdbe *p){
}
#endif
p->iCurrentTime = 0;
- p->magic = VDBE_MAGIC_INIT;
+ p->magic = VDBE_MAGIC_RESET;
return p->rc & db->errMask;
}
@@ -73306,19 +73426,21 @@ SQLITE_PRIVATE void sqlite3VdbeClearObject(sqlite3 *db, Vdbe *p){
SubProgram *pSub, *pNext;
int i;
assert( p->db==0 || p->db==db );
- releaseMemArray(p->aVar, p->nVar);
releaseMemArray(p->aColName, p->nResColumn*COLNAME_N);
for(pSub=p->pProgram; pSub; pSub=pNext){
pNext = pSub->pNext;
vdbeFreeOpArray(db, pSub->aOp, pSub->nOp);
sqlite3DbFree(db, pSub);
}
- for(i=p->nzVar-1; i>=0; i--) sqlite3DbFree(db, p->azVar[i]);
- sqlite3DbFree(db, p->azVar);
+ if( p->magic!=VDBE_MAGIC_INIT ){
+ releaseMemArray(p->aVar, p->nVar);
+ for(i=p->nzVar-1; i>=0; i--) sqlite3DbFree(db, p->azVar[i]);
+ sqlite3DbFree(db, p->azVar);
+ sqlite3DbFree(db, p->pFree);
+ }
vdbeFreeOpArray(db, p->aOp, p->nOp);
sqlite3DbFree(db, p->aColName);
sqlite3DbFree(db, p->zSql);
- sqlite3DbFree(db, p->pFree);
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
for(i=0; i<p->nScan; i++){
sqlite3DbFree(db, p->aScan[i].zName);
@@ -74075,14 +74197,48 @@ static int vdbeCompareMemString(
}
/*
+** The input pBlob is guaranteed to be a Blob that is not marked
+** with MEM_Zero. Return true if it could be a zero-blob.
+*/
+static int isAllZero(const char *z, int n){
+ int i;
+ for(i=0; i<n; i++){
+ if( z[i] ) return 0;
+ }
+ return 1;
+}
+
+/*
** Compare two blobs. Return negative, zero, or positive if the first
** is less than, equal to, or greater than the second, respectively.
** If one blob is a prefix of the other, then the shorter is the lessor.
*/
static SQLITE_NOINLINE int sqlite3BlobCompare(const Mem *pB1, const Mem *pB2){
- int c = memcmp(pB1->z, pB2->z, pB1->n>pB2->n ? pB2->n : pB1->n);
+ int c;
+ int n1 = pB1->n;
+ int n2 = pB2->n;
+
+ /* It is possible to have a Blob value that has some non-zero content
+ ** followed by zero content. But that only comes up for Blobs formed
+ ** by the OP_MakeRecord opcode, and such Blobs never get passed into
+ ** sqlite3MemCompare(). */
+ assert( (pB1->flags & MEM_Zero)==0 || n1==0 );
+ assert( (pB2->flags & MEM_Zero)==0 || n2==0 );
+
+ if( (pB1->flags|pB2->flags) & MEM_Zero ){
+ if( pB1->flags & pB2->flags & MEM_Zero ){
+ return pB1->u.nZero - pB2->u.nZero;
+ }else if( pB1->flags & MEM_Zero ){
+ if( !isAllZero(pB2->z, pB2->n) ) return -1;
+ return pB1->u.nZero - n2;
+ }else{
+ if( !isAllZero(pB1->z, pB1->n) ) return +1;
+ return n1 - pB2->u.nZero;
+ }
+ }
+ c = memcmp(pB1->z, pB2->z, n1>n2 ? n2 : n1);
if( c ) return c;
- return pB1->n - pB2->n;
+ return n1 - n2;
}
/*
@@ -74388,6 +74544,7 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip(
/* RHS is a blob */
else if( pRhs->flags & MEM_Blob ){
+ assert( (pRhs->flags & MEM_Zero)==0 || pRhs->n==0 );
getVarint32(&aKey1[idx1], serial_type);
testcase( serial_type==12 );
if( serial_type<12 || (serial_type & 0x01) ){
@@ -74399,6 +74556,12 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip(
if( (d1+nStr) > (unsigned)nKey1 ){
pPKey2->errCode = (u8)SQLITE_CORRUPT_BKPT;
return 0; /* Corruption */
+ }else if( pRhs->flags & MEM_Zero ){
+ if( !isAllZero((const char*)&aKey1[d1],nStr) ){
+ rc = 1;
+ }else{
+ rc = nStr - pRhs->u.nZero;
+ }
}else{
int nCmp = MIN(nStr, pRhs->n);
rc = memcmp(&aKey1[d1], pRhs->z, nCmp);
@@ -74469,7 +74632,7 @@ static int vdbeRecordCompareInt(
int res;
u32 y;
u64 x;
- i64 v = pPKey2->aMem[0].u.i;
+ i64 v;
i64 lhs;
vdbeAssertFieldCountWithinLimits(nKey1, pKey1, pPKey2->pKeyInfo);
@@ -74528,6 +74691,7 @@ static int vdbeRecordCompareInt(
return sqlite3VdbeRecordCompare(nKey1, pKey1, pPKey2);
}
+ v = pPKey2->aMem[0].u.i;
if( v>lhs ){
res = pPKey2->r1;
}else if( v<lhs ){
@@ -74931,7 +75095,7 @@ SQLITE_PRIVATE void sqlite3VdbePreUpdateHook(
preupdate.keyinfo.aSortOrder = (u8*)&fakeSortOrder;
preupdate.iKey1 = iKey1;
preupdate.iKey2 = iKey2;
- preupdate.iPKey = pTab->iPKey;
+ preupdate.pTab = pTab;
db->pPreUpdate = &preupdate;
db->xPreUpdateCallback(db->pPreUpdateArg, db, op, zDb, zTbl, iKey1, iKey2);
@@ -75122,7 +75286,7 @@ SQLITE_API int sqlite3_clear_bindings(sqlite3_stmt *pStmt){
SQLITE_API const void *sqlite3_value_blob(sqlite3_value *pVal){
Mem *p = (Mem*)pVal;
if( p->flags & (MEM_Blob|MEM_Str) ){
- if( sqlite3VdbeMemExpandBlob(p)!=SQLITE_OK ){
+ if( ExpandBlob(p)!=SQLITE_OK ){
assert( p->flags==MEM_Null && p->z==0 );
return 0;
}
@@ -75452,7 +75616,7 @@ static int doWalCallbacks(sqlite3 *db){
nEntry = sqlite3PagerWalCallback(sqlite3BtreePager(pBt));
sqlite3BtreeLeave(pBt);
if( db->xWalCallback && nEntry>0 && rc==SQLITE_OK ){
- rc = db->xWalCallback(db->pWalArg, db, db->aDb[i].zName, nEntry);
+ rc = db->xWalCallback(db->pWalArg, db, db->aDb[i].zDbSName, nEntry);
}
}
}
@@ -75905,14 +76069,13 @@ static Mem *columnMem(sqlite3_stmt *pStmt, int i){
Mem *pOut;
pVm = (Vdbe *)pStmt;
- if( pVm && pVm->pResultSet!=0 && i<pVm->nResColumn && i>=0 ){
- sqlite3_mutex_enter(pVm->db->mutex);
+ if( pVm==0 ) return (Mem*)columnNullValue();
+ assert( pVm->db );
+ sqlite3_mutex_enter(pVm->db->mutex);
+ if( pVm->pResultSet!=0 && i<pVm->nResColumn && i>=0 ){
pOut = &pVm->pResultSet[i];
}else{
- if( pVm && ALWAYS(pVm->db) ){
- sqlite3_mutex_enter(pVm->db->mutex);
- sqlite3Error(pVm->db, SQLITE_RANGE);
- }
+ sqlite3Error(pVm->db, SQLITE_RANGE);
pOut = (Mem*)columnNullValue();
}
return pOut;
@@ -75945,6 +76108,8 @@ static void columnMallocFailure(sqlite3_stmt *pStmt)
*/
Vdbe *p = (Vdbe *)pStmt;
if( p ){
+ assert( p->db!=0 );
+ assert( sqlite3_mutex_held(p->db->mutex) );
p->rc = sqlite3ApiExit(p->db, p->rc);
sqlite3_mutex_leave(p->db->mutex);
}
@@ -76521,7 +76686,7 @@ SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt){
*/
SQLITE_API int sqlite3_stmt_busy(sqlite3_stmt *pStmt){
Vdbe *v = (Vdbe*)pStmt;
- return v!=0 && v->pc>=0 && v->magic==VDBE_MAGIC_RUN;
+ return v!=0 && v->magic==VDBE_MAGIC_RUN && v->pc>=0;
}
/*
@@ -76662,9 +76827,14 @@ SQLITE_API int sqlite3_preupdate_old(sqlite3 *db, int iIdx, sqlite3_value **ppVa
if( iIdx>=p->pUnpacked->nField ){
*ppValue = (sqlite3_value *)columnNullValue();
}else{
+ Mem *pMem = *ppValue = &p->pUnpacked->aMem[iIdx];
*ppValue = &p->pUnpacked->aMem[iIdx];
- if( iIdx==p->iPKey ){
- sqlite3VdbeMemSetInt64(*ppValue, p->iKey1);
+ if( iIdx==p->pTab->iPKey ){
+ sqlite3VdbeMemSetInt64(pMem, p->iKey1);
+ }else if( p->pTab->aCol[iIdx].affinity==SQLITE_AFF_REAL ){
+ if( pMem->flags & MEM_Int ){
+ sqlite3VdbeMemRealify(pMem);
+ }
}
}
@@ -76728,7 +76898,7 @@ SQLITE_API int sqlite3_preupdate_new(sqlite3 *db, int iIdx, sqlite3_value **ppVa
UnpackedRecord *pUnpack = p->pNewUnpacked;
if( !pUnpack ){
Mem *pData = &p->v->aMem[p->iNewReg];
- rc = sqlite3VdbeMemExpandBlob(pData);
+ rc = ExpandBlob(pData);
if( rc!=SQLITE_OK ) goto preupdate_new_out;
pUnpack = vdbeUnpackRecord(&p->keyinfo, pData->n, pData->z);
if( !pUnpack ){
@@ -76741,7 +76911,7 @@ SQLITE_API int sqlite3_preupdate_new(sqlite3 *db, int iIdx, sqlite3_value **ppVa
pMem = (sqlite3_value *)columnNullValue();
}else{
pMem = &pUnpack->aMem[iIdx];
- if( iIdx==p->iPKey ){
+ if( iIdx==p->pTab->iPKey ){
sqlite3VdbeMemSetInt64(pMem, p->iKey2);
}
}
@@ -76762,7 +76932,7 @@ SQLITE_API int sqlite3_preupdate_new(sqlite3 *db, int iIdx, sqlite3_value **ppVa
assert( iIdx>=0 && iIdx<p->pCsr->nField );
pMem = &p->aNew[iIdx];
if( pMem->flags==0 ){
- if( iIdx==p->iPKey ){
+ if( iIdx==p->pTab->iPKey ){
sqlite3VdbeMemSetInt64(pMem, p->iKey2);
}else{
rc = sqlite3VdbeMemCopy(pMem, &p->v->aMem[p->iNewReg+1+iIdx]);
@@ -77710,7 +77880,7 @@ SQLITE_PRIVATE int sqlite3VdbeExec(
sqlite3 *db = p->db; /* The database */
u8 resetSchemaOnFault = 0; /* Reset schema after an error if positive */
u8 encoding = ENC(db); /* The database encoding */
- int iCompare = 0; /* Result of last OP_Compare operation */
+ int iCompare = 0; /* Result of last comparison */
unsigned nVmStep = 0; /* Number of virtual machine steps */
#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
unsigned nProgressLimit = 0;/* Invoke xProgress() when nVmStep reaches this */
@@ -78042,7 +78212,7 @@ case OP_Yield: { /* in1, jump */
}
/* Opcode: HaltIfNull P1 P2 P3 P4 P5
-** Synopsis: if r[P3]=null halt
+** Synopsis: if r[P3]=null halt
**
** Check the value in register P3. If it is NULL then Halt using
** parameter P1, P2, and P4 as if this were a Halt instruction. If the
@@ -78255,7 +78425,7 @@ case OP_String: { /* out2 */
}
/* Opcode: Null P1 P2 P3 * *
-** Synopsis: r[P2..P3]=NULL
+** Synopsis: r[P2..P3]=NULL
**
** Write a NULL into registers P2. If P3 greater than P2, then also write
** NULL into register P3 and every register in between P2 and P3. If P3
@@ -78273,18 +78443,20 @@ case OP_Null: { /* out2 */
cnt = pOp->p3-pOp->p2;
assert( pOp->p3<=(p->nMem+1 - p->nCursor) );
pOut->flags = nullFlag = pOp->p1 ? (MEM_Null|MEM_Cleared) : MEM_Null;
+ pOut->n = 0;
while( cnt>0 ){
pOut++;
memAboutToChange(p, pOut);
sqlite3VdbeMemSetNull(pOut);
pOut->flags = nullFlag;
+ pOut->n = 0;
cnt--;
}
break;
}
/* Opcode: SoftNull P1 * * * *
-** Synopsis: r[P1]=NULL
+** Synopsis: r[P1]=NULL
**
** Set register P1 to have the value NULL as seen by the OP_MakeRecord
** instruction, but do not free any string or blob memory associated with
@@ -78337,7 +78509,7 @@ case OP_Variable: { /* out2 */
}
/* Opcode: Move P1 P2 P3 * *
-** Synopsis: r[P2@P3]=r[P1@P3]
+** Synopsis: r[P2@P3]=r[P1@P3]
**
** Move the P3 values in register P1..P1+P3-1 over into
** registers P2..P2+P3-1. Registers P1..P1+P3-1 are
@@ -78447,7 +78619,7 @@ case OP_IntCopy: { /* out2 */
}
/* Opcode: ResultRow P1 P2 * * *
-** Synopsis: output=r[P1@P2]
+** Synopsis: output=r[P1@P2]
**
** The registers P1 through P1+P2-1 contain a single row of
** results. This opcode causes the sqlite3_step() call to terminate
@@ -78580,14 +78752,14 @@ case OP_Concat: { /* same as TK_CONCAT, in1, in2, out3 */
}
/* Opcode: Add P1 P2 P3 * *
-** Synopsis: r[P3]=r[P1]+r[P2]
+** Synopsis: r[P3]=r[P1]+r[P2]
**
** Add the value in register P1 to the value in register P2
** and store the result in register P3.
** If either input is NULL, the result is NULL.
*/
/* Opcode: Multiply P1 P2 P3 * *
-** Synopsis: r[P3]=r[P1]*r[P2]
+** Synopsis: r[P3]=r[P1]*r[P2]
**
**
** Multiply the value in register P1 by the value in register P2
@@ -78595,14 +78767,14 @@ case OP_Concat: { /* same as TK_CONCAT, in1, in2, out3 */
** If either input is NULL, the result is NULL.
*/
/* Opcode: Subtract P1 P2 P3 * *
-** Synopsis: r[P3]=r[P2]-r[P1]
+** Synopsis: r[P3]=r[P2]-r[P1]
**
** Subtract the value in register P1 from the value in register P2
** and store the result in register P3.
** If either input is NULL, the result is NULL.
*/
/* Opcode: Divide P1 P2 P3 * *
-** Synopsis: r[P3]=r[P2]/r[P1]
+** Synopsis: r[P3]=r[P2]/r[P1]
**
** Divide the value in register P1 by the value in register P2
** and store the result in register P3 (P3=P2/P1). If the value in
@@ -78610,7 +78782,7 @@ case OP_Concat: { /* same as TK_CONCAT, in1, in2, out3 */
** NULL, the result is NULL.
*/
/* Opcode: Remainder P1 P2 P3 * *
-** Synopsis: r[P3]=r[P2]%r[P1]
+** Synopsis: r[P3]=r[P2]%r[P1]
**
** Compute the remainder after integer register P2 is divided by
** register P1 and store the result in register P3.
@@ -78843,21 +79015,21 @@ case OP_Function: {
}
/* Opcode: BitAnd P1 P2 P3 * *
-** Synopsis: r[P3]=r[P1]&r[P2]
+** Synopsis: r[P3]=r[P1]&r[P2]
**
** Take the bit-wise AND of the values in register P1 and P2 and
** store the result in register P3.
** If either input is NULL, the result is NULL.
*/
/* Opcode: BitOr P1 P2 P3 * *
-** Synopsis: r[P3]=r[P1]|r[P2]
+** Synopsis: r[P3]=r[P1]|r[P2]
**
** Take the bit-wise OR of the values in register P1 and P2 and
** store the result in register P3.
** If either input is NULL, the result is NULL.
*/
/* Opcode: ShiftLeft P1 P2 P3 * *
-** Synopsis: r[P3]=r[P2]<<r[P1]
+** Synopsis: r[P3]=r[P2]<<r[P1]
**
** Shift the integer value in register P2 to the left by the
** number of bits specified by the integer in register P1.
@@ -78865,7 +79037,7 @@ case OP_Function: {
** If either input is NULL, the result is NULL.
*/
/* Opcode: ShiftRight P1 P2 P3 * *
-** Synopsis: r[P3]=r[P2]>>r[P1]
+** Synopsis: r[P3]=r[P2]>>r[P1]
**
** Shift the integer value in register P2 to the right by the
** number of bits specified by the integer in register P1.
@@ -78925,7 +79097,7 @@ case OP_ShiftRight: { /* same as TK_RSHIFT, in1, in2, out3 */
}
/* Opcode: AddImm P1 P2 * * *
-** Synopsis: r[P1]=r[P1]+P2
+** Synopsis: r[P1]=r[P1]+P2
**
** Add the constant P2 to the value in register P1.
** The result is always an integer.
@@ -79017,15 +79189,12 @@ case OP_Cast: { /* in1 */
}
#endif /* SQLITE_OMIT_CAST */
-/* Opcode: Lt P1 P2 P3 P4 P5
-** Synopsis: IF r[P3]<r[P1]
-**
-** Compare the values in register P1 and P3. If reg(P3)<reg(P1) then
-** jump to address P2.
+/* Opcode: Eq P1 P2 P3 P4 P5
+** Synopsis: IF r[P3]==r[P1]
**
-** If the SQLITE_JUMPIFNULL bit of P5 is set and either reg(P1) or
-** reg(P3) is NULL then take the jump. If the SQLITE_JUMPIFNULL
-** bit is clear then fall through if either operand is NULL.
+** Compare the values in register P1 and P3. If reg(P3)==reg(P1) then
+** jump to address P2. Or if the SQLITE_STOREP2 flag is set in P5, then
+** store the result of comparison in register P2.
**
** The SQLITE_AFF_MASK portion of P5 must be an affinity character -
** SQLITE_AFF_TEXT, SQLITE_AFF_INTEGER, and so forth. An attempt is made
@@ -79039,44 +79208,61 @@ case OP_Cast: { /* in1 */
** the values are compared. If both values are blobs then memcmp() is
** used to determine the results of the comparison. If both values
** are text, then the appropriate collating function specified in
-** P4 is used to do the comparison. If P4 is not specified then
+** P4 is used to do the comparison. If P4 is not specified then
** memcmp() is used to compare text string. If both values are
** numeric, then a numeric comparison is used. If the two values
** are of different types, then numbers are considered less than
** strings and strings are considered less than blobs.
**
-** If the SQLITE_STOREP2 bit of P5 is set, then do not jump. Instead,
-** store a boolean result (either 0, or 1, or NULL) in register P2.
+** If SQLITE_NULLEQ is set in P5 then the result of comparison is always either
+** true or false and is never NULL. If both operands are NULL then the result
+** of comparison is true. If either operand is NULL then the result is false.
+** If neither operand is NULL the result is the same as it would be if
+** the SQLITE_NULLEQ flag were omitted from P5.
**
-** If the SQLITE_NULLEQ bit is set in P5, then NULL values are considered
-** equal to one another, provided that they do not have their MEM_Cleared
-** bit set.
+** If both SQLITE_STOREP2 and SQLITE_KEEPNULL flags are set then the
+** content of r[P2] is only changed if the new value is NULL or 0 (false).
+** In other words, a prior r[P2] value will not be overwritten by 1 (true).
*/
/* Opcode: Ne P1 P2 P3 P4 P5
** Synopsis: IF r[P3]!=r[P1]
**
-** This works just like the Lt opcode except that the jump is taken if
-** the operands in registers P1 and P3 are not equal. See the Lt opcode for
+** This works just like the Eq opcode except that the jump is taken if
+** the operands in registers P1 and P3 are not equal. See the Eq opcode for
** additional information.
**
-** If SQLITE_NULLEQ is set in P5 then the result of comparison is always either
-** true or false and is never NULL. If both operands are NULL then the result
-** of comparison is false. If either operand is NULL then the result is true.
-** If neither operand is NULL the result is the same as it would be if
-** the SQLITE_NULLEQ flag were omitted from P5.
+** If both SQLITE_STOREP2 and SQLITE_KEEPNULL flags are set then the
+** content of r[P2] is only changed if the new value is NULL or 1 (true).
+** In other words, a prior r[P2] value will not be overwritten by 0 (false).
*/
-/* Opcode: Eq P1 P2 P3 P4 P5
-** Synopsis: IF r[P3]==r[P1]
+/* Opcode: Lt P1 P2 P3 P4 P5
+** Synopsis: IF r[P3]<r[P1]
**
-** This works just like the Lt opcode except that the jump is taken if
-** the operands in registers P1 and P3 are equal.
-** See the Lt opcode for additional information.
+** Compare the values in register P1 and P3. If reg(P3)<reg(P1) then
+** jump to address P2. Or if the SQLITE_STOREP2 flag is set in P5 store
+** the result of comparison (0 or 1 or NULL) into register P2.
**
-** If SQLITE_NULLEQ is set in P5 then the result of comparison is always either
-** true or false and is never NULL. If both operands are NULL then the result
-** of comparison is true. If either operand is NULL then the result is false.
-** If neither operand is NULL the result is the same as it would be if
-** the SQLITE_NULLEQ flag were omitted from P5.
+** If the SQLITE_JUMPIFNULL bit of P5 is set and either reg(P1) or
+** reg(P3) is NULL then the take the jump. If the SQLITE_JUMPIFNULL
+** bit is clear then fall through if either operand is NULL.
+**
+** The SQLITE_AFF_MASK portion of P5 must be an affinity character -
+** SQLITE_AFF_TEXT, SQLITE_AFF_INTEGER, and so forth. An attempt is made
+** to coerce both inputs according to this affinity before the
+** comparison is made. If the SQLITE_AFF_MASK is 0x00, then numeric
+** affinity is used. Note that the affinity conversions are stored
+** back into the input registers P1 and P3. So this opcode can cause
+** persistent changes to registers P1 and P3.
+**
+** Once any conversions have taken place, and neither value is NULL,
+** the values are compared. If both values are blobs then memcmp() is
+** used to determine the results of the comparison. If both values
+** are text, then the appropriate collating function specified in
+** P4 is used to do the comparison. If P4 is not specified then
+** memcmp() is used to compare text string. If both values are
+** numeric, then a numeric comparison is used. If the two values
+** are of different types, then numbers are considered less than
+** strings and strings are considered less than blobs.
*/
/* Opcode: Le P1 P2 P3 P4 P5
** Synopsis: IF r[P3]<=r[P1]
@@ -79105,7 +79291,7 @@ case OP_Lt: /* same as TK_LT, jump, in1, in3 */
case OP_Le: /* same as TK_LE, jump, in1, in3 */
case OP_Gt: /* same as TK_GT, jump, in1, in3 */
case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
- int res; /* Result of the comparison of pIn1 against pIn3 */
+ int res, res2; /* Result of the comparison of pIn1 against pIn3 */
char affinity; /* Affinity to use for comparison */
u16 flags1; /* Copy of initial value of pIn1->flags */
u16 flags3; /* Copy of initial value of pIn3->flags */
@@ -79128,9 +79314,9 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
&& (flags3&MEM_Null)!=0
&& (flags3&MEM_Cleared)==0
){
- res = 0; /* Results are equal */
+ res = 0; /* Operands are equal */
}else{
- res = 1; /* Results are not equal */
+ res = 1; /* Operands are not equal */
}
}else{
/* SQLITE_NULLEQ is clear and at least one operand is NULL,
@@ -79139,6 +79325,7 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
*/
if( pOp->p5 & SQLITE_STOREP2 ){
pOut = &aMem[pOp->p2];
+ iCompare = 1; /* Operands are not equal */
memAboutToChange(p, pOut);
MemSetTypeFlag(pOut, MEM_Null);
REGISTER_TRACE(pOp->p2, pOut);
@@ -79157,12 +79344,21 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
if( (flags1 | flags3)&MEM_Str ){
if( (flags1 & (MEM_Int|MEM_Real|MEM_Str))==MEM_Str ){
applyNumericAffinity(pIn1,0);
+ testcase( flags3!=pIn3->flags ); /* Possible if pIn1==pIn3 */
flags3 = pIn3->flags;
}
if( (flags3 & (MEM_Int|MEM_Real|MEM_Str))==MEM_Str ){
applyNumericAffinity(pIn3,0);
}
}
+ /* Handle the common case of integer comparison here, as an
+ ** optimization, to avoid a call to sqlite3MemCompare() */
+ if( (pIn1->flags & pIn3->flags & MEM_Int)!=0 ){
+ if( pIn3->u.i > pIn1->u.i ){ res = +1; goto compare_op; }
+ if( pIn3->u.i < pIn1->u.i ){ res = -1; goto compare_op; }
+ res = 0;
+ goto compare_op;
+ }
}else if( affinity==SQLITE_AFF_TEXT ){
if( (flags1 & MEM_Str)==0 && (flags1 & (MEM_Int|MEM_Real))!=0 ){
testcase( pIn1->flags & MEM_Int );
@@ -79170,7 +79366,7 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
sqlite3VdbeMemStringify(pIn1, encoding, 1);
testcase( (flags1&MEM_Dyn) != (pIn1->flags&MEM_Dyn) );
flags1 = (pIn1->flags & ~MEM_TypeMask) | (flags1 & MEM_TypeMask);
- flags3 = pIn3->flags;
+ assert( pIn1!=pIn3 );
}
if( (flags3 & MEM_Str)==0 && (flags3 & (MEM_Int|MEM_Real))!=0 ){
testcase( pIn3->flags & MEM_Int );
@@ -79181,23 +79377,16 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
}
}
assert( pOp->p4type==P4_COLLSEQ || pOp->p4.pColl==0 );
- if( flags1 & MEM_Zero ){
- sqlite3VdbeMemExpandBlob(pIn1);
- flags1 &= ~MEM_Zero;
- }
- if( flags3 & MEM_Zero ){
- sqlite3VdbeMemExpandBlob(pIn3);
- flags3 &= ~MEM_Zero;
- }
res = sqlite3MemCompare(pIn3, pIn1, pOp->p4.pColl);
}
+compare_op:
switch( pOp->opcode ){
- case OP_Eq: res = res==0; break;
- case OP_Ne: res = res!=0; break;
- case OP_Lt: res = res<0; break;
- case OP_Le: res = res<=0; break;
- case OP_Gt: res = res>0; break;
- default: res = res>=0; break;
+ case OP_Eq: res2 = res==0; break;
+ case OP_Ne: res2 = res; break;
+ case OP_Lt: res2 = res<0; break;
+ case OP_Le: res2 = res<=0; break;
+ case OP_Gt: res2 = res>0; break;
+ default: res2 = res>=0; break;
}
/* Undo any changes made by applyAffinity() to the input registers. */
@@ -79208,19 +79397,55 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
if( pOp->p5 & SQLITE_STOREP2 ){
pOut = &aMem[pOp->p2];
+ iCompare = res;
+ res2 = res2!=0; /* For this path res2 must be exactly 0 or 1 */
+ if( (pOp->p5 & SQLITE_KEEPNULL)!=0 ){
+ /* The KEEPNULL flag prevents OP_Eq from overwriting a NULL with 1
+ ** and prevents OP_Ne from overwriting NULL with 0. This flag
+ ** is only used in contexts where either:
+ ** (1) op==OP_Eq && (r[P2]==NULL || r[P2]==0)
+ ** (2) op==OP_Ne && (r[P2]==NULL || r[P2]==1)
+ ** Therefore it is not necessary to check the content of r[P2] for
+ ** NULL. */
+ assert( pOp->opcode==OP_Ne || pOp->opcode==OP_Eq );
+ assert( res2==0 || res2==1 );
+ testcase( res2==0 && pOp->opcode==OP_Eq );
+ testcase( res2==1 && pOp->opcode==OP_Eq );
+ testcase( res2==0 && pOp->opcode==OP_Ne );
+ testcase( res2==1 && pOp->opcode==OP_Ne );
+ if( (pOp->opcode==OP_Eq)==res2 ) break;
+ }
memAboutToChange(p, pOut);
MemSetTypeFlag(pOut, MEM_Int);
- pOut->u.i = res;
+ pOut->u.i = res2;
REGISTER_TRACE(pOp->p2, pOut);
}else{
VdbeBranchTaken(res!=0, (pOp->p5 & SQLITE_NULLEQ)?2:3);
- if( res ){
+ if( res2 ){
goto jump_to_p2;
}
}
break;
}
+/* Opcode: ElseNotEq * P2 * * *
+**
+** This opcode must immediately follow an OP_Lt or OP_Gt comparison operator.
+** If result of an OP_Eq comparison on the same two operands
+** would have be NULL or false (0), then then jump to P2.
+** If the result of an OP_Eq comparison on the two previous operands
+** would have been true (1), then fall through.
+*/
+case OP_ElseNotEq: { /* same as TK_ESCAPE, jump */
+ assert( pOp>aOp );
+ assert( pOp[-1].opcode==OP_Lt || pOp[-1].opcode==OP_Gt );
+ assert( pOp[-1].p5 & SQLITE_STOREP2 );
+ VdbeBranchTaken(iCompare!=0, 2);
+ if( iCompare!=0 ) goto jump_to_p2;
+ break;
+}
+
+
/* Opcode: Permutation * * * P4 *
**
** Set the permutation used by the OP_Compare operator to be the array
@@ -79416,22 +79641,18 @@ case OP_BitNot: { /* same as TK_BITNOT, in1, out2 */
/* Opcode: Once P1 P2 * * *
**
-** Check the "once" flag number P1. If it is set, jump to instruction P2.
-** Otherwise, set the flag and fall through to the next instruction.
-** In other words, this opcode causes all following opcodes up through P2
-** (but not including P2) to run just once and to be skipped on subsequent
-** times through the loop.
-**
-** All "once" flags are initially cleared whenever a prepared statement
-** first begins to run.
+** If the P1 value is equal to the P1 value on the OP_Init opcode at
+** instruction 0, then jump to P2. If the two P1 values differ, then
+** set the P1 value on this opcode to equal the P1 value on the OP_Init
+** and fall through.
*/
case OP_Once: { /* jump */
- assert( pOp->p1<p->nOnceFlag );
- VdbeBranchTaken(p->aOnceFlag[pOp->p1]!=0, 2);
- if( p->aOnceFlag[pOp->p1] ){
+ assert( p->aOp[0].opcode==OP_Init );
+ VdbeBranchTaken(p->aOp[0].p1==pOp->p1, 2);
+ if( p->aOp[0].p1==pOp->p1 ){
goto jump_to_p2;
}else{
- p->aOnceFlag[pOp->p1] = 1;
+ pOp->p1 = p->aOp[0].p1;
}
break;
}
@@ -79470,7 +79691,7 @@ case OP_IfNot: { /* jump, in1 */
}
/* Opcode: IsNull P1 P2 * * *
-** Synopsis: if r[P1]==NULL goto P2
+** Synopsis: if r[P1]==NULL goto P2
**
** Jump to P2 if the value in register P1 is NULL.
*/
@@ -79498,7 +79719,7 @@ case OP_NotNull: { /* same as TK_NOTNULL, jump, in1 */
}
/* Opcode: Column P1 P2 P3 P4 P5
-** Synopsis: r[P3]=PX
+** Synopsis: r[P3]=PX
**
** Interpret the data that cursor P1 points to as a structure built using
** the MakeRecord instruction. (See the MakeRecord opcode for additional
@@ -80264,12 +80485,12 @@ case OP_Transaction: {
rc = sqlite3BtreeBeginTrans(pBt, pOp->p2);
testcase( rc==SQLITE_BUSY_SNAPSHOT );
testcase( rc==SQLITE_BUSY_RECOVERY );
- if( (rc&0xff)==SQLITE_BUSY ){
- p->pc = (int)(pOp - aOp);
- p->rc = rc;
- goto vdbe_return;
- }
if( rc!=SQLITE_OK ){
+ if( (rc&0xff)==SQLITE_BUSY ){
+ p->pc = (int)(pOp - aOp);
+ p->rc = rc;
+ goto vdbe_return;
+ }
goto abort_due_to_error;
}
@@ -80296,10 +80517,9 @@ case OP_Transaction: {
}
/* Gather the schema version number for checking:
- ** IMPLEMENTATION-OF: R-32195-19465 The schema version is used by SQLite
- ** each time a query is executed to ensure that the internal cache of the
- ** schema used when compiling the SQL query matches the schema of the
- ** database against which the compiled query is actually executed.
+ ** IMPLEMENTATION-OF: R-03189-51135 As each SQL statement runs, the schema
+ ** version is checked to ensure that the schema has not changed since the
+ ** SQL statement was prepared.
*/
sqlite3BtreeGetMeta(pBt, BTREE_SCHEMA_VERSION, (u32 *)&iMeta);
iGen = db->aDb[pOp->p1].pSchema->iGeneration;
@@ -80960,7 +81180,6 @@ case OP_SeekGT: { /* jump, in3 */
#ifdef SQLITE_DEBUG
{ int i; for(i=0; i<r.nField; i++) assert( memIsValid(&r.aMem[i]) ); }
#endif
- ExpandBlob(r.aMem);
r.eqSeen = 0;
rc = sqlite3BtreeMovetoUnpacked(pC->uc.pCursor, &r, 0, 0, &res);
if( rc!=SQLITE_OK ){
@@ -81008,7 +81227,6 @@ seek_not_found:
}
break;
}
-
/* Opcode: Found P1 P2 P3 P4 *
** Synopsis: key=r[P3@P4]
@@ -81102,13 +81320,13 @@ case OP_Found: { /* jump, in3 */
r.pKeyInfo = pC->pKeyInfo;
r.nField = (u16)pOp->p4.i;
r.aMem = pIn3;
+#ifdef SQLITE_DEBUG
for(ii=0; ii<r.nField; ii++){
assert( memIsValid(&r.aMem[ii]) );
- ExpandBlob(&r.aMem[ii]);
-#ifdef SQLITE_DEBUG
+ assert( (r.aMem[ii].flags & MEM_Zero)==0 || r.aMem[ii].n==0 );
if( ii ) REGISTER_TRACE(pOp->p3+ii, &r.aMem[ii]);
-#endif
}
+#endif
pIdxKey = &r;
}else{
pIdxKey = sqlite3VdbeAllocUnpackedRecord(
@@ -81116,7 +81334,7 @@ case OP_Found: { /* jump, in3 */
);
if( pIdxKey==0 ) goto no_mem;
assert( pIn3->flags & MEM_Blob );
- ExpandBlob(pIn3);
+ (void)ExpandBlob(pIn3);
sqlite3VdbeRecordUnpack(pC->pKeyInfo, pIn3->n, pIn3->z, pIdxKey);
}
pIdxKey->default_rc = 0;
@@ -81441,7 +81659,7 @@ case OP_NewRowid: { /* out2 */
** for indices is OP_IdxInsert.
*/
/* Opcode: InsertInt P1 P2 P3 P4 P5
-** Synopsis: intkey=P3 data=r[P2]
+** Synopsis: intkey=P3 data=r[P2]
**
** This works exactly like OP_Insert except that the key is the
** integer value P3, not the value of the integer stored in register P3.
@@ -81483,7 +81701,7 @@ case OP_InsertInt: {
if( pOp->p4type==P4_TABLE && HAS_UPDATE_HOOK(db) ){
assert( pC->isTable );
assert( pC->iDb>=0 );
- zDb = db->aDb[pC->iDb].zName;
+ zDb = db->aDb[pC->iDb].zDbSName;
pTab = pOp->p4.pTab;
assert( HasRowid(pTab) );
op = ((pOp->p5 & OPFLAG_ISUPDATE) ? SQLITE_UPDATE : SQLITE_INSERT);
@@ -81557,7 +81775,7 @@ case OP_InsertInt: {
** P1 must not be pseudo-table. It has to be a real table with
** multiple rows.
**
-** If P4 is not NULL then it points to a Table struture. In this case either
+** If P4 is not NULL then it points to a Table object. In this case either
** the update or pre-update hook, or both, may be invoked. The P1 cursor must
** have been positioned using OP_NotFound prior to invoking this opcode in
** this case. Specifically, if one is configured, the pre-update hook is
@@ -81600,7 +81818,7 @@ case OP_Delete: {
if( pOp->p4type==P4_TABLE && HAS_UPDATE_HOOK(db) ){
assert( pC->iDb>=0 );
assert( pOp->p4.pTab!=0 );
- zDb = db->aDb[pC->iDb].zName;
+ zDb = db->aDb[pC->iDb].zDbSName;
pTab = pOp->p4.pTab;
if( (pOp->p5 & OPFLAG_SAVEPOSITION)!=0 && pC->isTable ){
pC->movetoTarget = sqlite3BtreeIntegerKey(pC->uc.pCursor);
@@ -81672,7 +81890,7 @@ case OP_ResetCount: {
}
/* Opcode: SorterCompare P1 P2 P3 P4
-** Synopsis: if key(P1)!=trim(r[P3],P4) goto P2
+** Synopsis: if key(P1)!=trim(r[P3],P4) goto P2
**
** P1 is a sorter cursor. This instruction compares a prefix of the
** record blob in register P3 against a prefix of the entry that
@@ -82148,9 +82366,6 @@ case OP_IdxInsert: { /* in2 */
}else{
x.nKey = pIn2->n;
x.pKey = pIn2->z;
- x.nData = 0;
- x.nZero = 0;
- x.pData = 0;
rc = sqlite3BtreeInsert(pC->uc.pCursor, &x, pOp->p3,
((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0)
);
@@ -82199,7 +82414,7 @@ case OP_IdxDelete: {
}
/* Opcode: Seek P1 * P3 P4 *
-** Synopsis: Move P3 to P1.rowid
+** Synopsis: Move P3 to P1.rowid
**
** P1 is an open index cursor and P3 is a cursor on the corresponding
** table. This opcode does a deferred seek of the P3 table cursor
@@ -82570,7 +82785,7 @@ case OP_ParseSchema: {
initData.pzErrMsg = &p->zErrMsg;
zSql = sqlite3MPrintf(db,
"SELECT name, rootpage, sql FROM '%q'.%s WHERE %s ORDER BY rowid",
- db->aDb[iDb].zName, zMaster, pOp->p4.z);
+ db->aDb[iDb].zDbSName, zMaster, pOp->p4.z);
if( zSql==0 ){
rc = SQLITE_NOMEM_BKPT;
}else{
@@ -82706,7 +82921,7 @@ case OP_IntegrityCk: {
#endif /* SQLITE_OMIT_INTEGRITY_CHECK */
/* Opcode: RowSetAdd P1 P2 * * *
-** Synopsis: rowset(P1)=r[P2]
+** Synopsis: rowset(P1)=r[P2]
**
** Insert the integer value held by register P2 into a boolean index
** held in register P1.
@@ -82726,7 +82941,7 @@ case OP_RowSetAdd: { /* in1, in2 */
}
/* Opcode: RowSetRead P1 P2 P3 * *
-** Synopsis: r[P3]=rowset(P1)
+** Synopsis: r[P3]=rowset(P1)
**
** Extract the smallest value from boolean index P1 and put that value into
** register P3. Or, if boolean index P1 is initially empty, leave P3
@@ -82875,8 +83090,7 @@ case OP_Program: { /* jump */
if( pProgram->nCsr==0 ) nMem++;
nByte = ROUND8(sizeof(VdbeFrame))
+ nMem * sizeof(Mem)
- + pProgram->nCsr * sizeof(VdbeCursor *)
- + pProgram->nOnce * sizeof(u8);
+ + pProgram->nCsr * sizeof(VdbeCursor *);
pFrame = sqlite3DbMallocZero(db, nByte);
if( !pFrame ){
goto no_mem;
@@ -82896,8 +83110,6 @@ case OP_Program: { /* jump */
pFrame->aOp = p->aOp;
pFrame->nOp = p->nOp;
pFrame->token = pProgram->token;
- pFrame->aOnceFlag = p->aOnceFlag;
- pFrame->nOnceFlag = p->nOnceFlag;
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
pFrame->anExec = p->anExec;
#endif
@@ -82931,13 +83143,10 @@ case OP_Program: { /* jump */
p->apCsr = (VdbeCursor **)&aMem[p->nMem];
p->aOp = aOp = pProgram->aOp;
p->nOp = pProgram->nOp;
- p->aOnceFlag = (u8 *)&p->apCsr[p->nCursor];
- p->nOnceFlag = pProgram->nOnce;
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
p->anExec = 0;
#endif
pOp = &aOp[-1];
- memset(p->aOnceFlag, 0, p->nOnceFlag);
break;
}
@@ -83399,15 +83608,14 @@ case OP_JournalMode: { /* out2 */
#endif /* SQLITE_OMIT_PRAGMA */
#if !defined(SQLITE_OMIT_VACUUM) && !defined(SQLITE_OMIT_ATTACH)
-/* Opcode: Vacuum * * * * *
+/* Opcode: Vacuum P1 * * * *
**
-** Vacuum the entire database. This opcode will cause other virtual
-** machines to be created and run. It may not be called from within
-** a transaction.
+** Vacuum the entire database P1. P1 is 0 for "main", and 2 or more
+** for an attached database. The "temp" database may not be vacuumed.
*/
case OP_Vacuum: {
assert( p->readOnly==0 );
- rc = sqlite3RunVacuum(&p->zErrMsg, db);
+ rc = sqlite3RunVacuum(&p->zErrMsg, db, pOp->p1);
if( rc ) goto abort_due_to_error;
break;
}
@@ -83905,8 +84113,8 @@ case OP_MaxPgcnt: { /* out2 */
#endif
-/* Opcode: Init * P2 * P4 *
-** Synopsis: Start at P2
+/* Opcode: Init P1 P2 * P4 *
+** Synopsis: Start at P2
**
** Programs contain a single instance of this opcode as the very first
** opcode.
@@ -83916,9 +84124,13 @@ case OP_MaxPgcnt: { /* out2 */
** Or if P4 is blank, use the string returned by sqlite3_sql().
**
** If P2 is not zero, jump to instruction P2.
+**
+** Increment the value of P1 so that OP_Once opcodes will jump the
+** first time they are evaluated for this run.
*/
case OP_Init: { /* jump */
char *zTrace;
+ int i;
/* If the P4 argument is not NULL, then it must be an SQL comment string.
** The "--" string is broken up to prevent false-positives with srcck1.c.
@@ -83930,6 +84142,7 @@ case OP_Init: { /* jump */
** sqlite3_expanded_sql(P) otherwise.
*/
assert( pOp->p4.z==0 || strncmp(pOp->p4.z, "-" "- ", 3)==0 );
+ assert( pOp==p->aOp ); /* Always instruction 0 */
#ifndef SQLITE_OMIT_TRACE
if( (db->mTrace & (SQLITE_TRACE_STMT|SQLITE_TRACE_LEGACY))!=0
@@ -83951,10 +84164,10 @@ case OP_Init: { /* jump */
#ifdef SQLITE_USE_FCNTL_TRACE
zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql);
if( zTrace ){
- int i;
- for(i=0; i<db->nDb; i++){
- if( DbMaskTest(p->btreeMask, i)==0 ) continue;
- sqlite3_file_control(db, db->aDb[i].zName, SQLITE_FCNTL_TRACE, zTrace);
+ int j;
+ for(j=0; j<db->nDb; j++){
+ if( DbMaskTest(p->btreeMask, j)==0 ) continue;
+ sqlite3_file_control(db, db->aDb[j].zDbSName, SQLITE_FCNTL_TRACE, zTrace);
}
}
#endif /* SQLITE_USE_FCNTL_TRACE */
@@ -83966,8 +84179,15 @@ case OP_Init: { /* jump */
}
#endif /* SQLITE_DEBUG */
#endif /* SQLITE_OMIT_TRACE */
- if( pOp->p2 ) goto jump_to_p2;
- break;
+ assert( pOp->p2>0 );
+ if( pOp->p1>=sqlite3GlobalConfig.iOnceResetThreshold ){
+ for(i=1; i<p->nOp; i++){
+ if( p->aOp[i].opcode==OP_Once ) p->aOp[i].p1 = 0;
+ }
+ pOp->p1 = 0;
+ }
+ pOp->p1++;
+ goto jump_to_p2;
}
#ifdef SQLITE_ENABLE_CURSOR_HINTS
@@ -84290,7 +84510,7 @@ SQLITE_API int sqlite3_blob_open(
goto blob_open_out;
}
pBlob->pTab = pTab;
- pBlob->zDb = db->aDb[sqlite3SchemaToIndex(db, pTab->pSchema)].zName;
+ pBlob->zDb = db->aDb[sqlite3SchemaToIndex(db, pTab->pSchema)].zDbSName;
/* Now search pTab for the exact column. */
for(iCol=0; iCol<pTab->nCol; iCol++) {
@@ -87842,17 +88062,17 @@ static SQLITE_NOINLINE int walkExpr(Walker *pWalker, Expr *pExpr){
testcase( ExprHasProperty(pExpr, EP_TokenOnly) );
testcase( ExprHasProperty(pExpr, EP_Reduced) );
rc = pWalker->xExprCallback(pWalker, pExpr);
- if( rc==WRC_Continue
- && !ExprHasProperty(pExpr,EP_TokenOnly) ){
- if( sqlite3WalkExpr(pWalker, pExpr->pLeft) ) return WRC_Abort;
- if( sqlite3WalkExpr(pWalker, pExpr->pRight) ) return WRC_Abort;
- if( ExprHasProperty(pExpr, EP_xIsSelect) ){
- if( sqlite3WalkSelect(pWalker, pExpr->x.pSelect) ) return WRC_Abort;
- }else{
- if( sqlite3WalkExprList(pWalker, pExpr->x.pList) ) return WRC_Abort;
- }
+ if( rc || ExprHasProperty(pExpr,(EP_TokenOnly|EP_Leaf)) ){
+ return rc & WRC_Abort;
}
- return rc & WRC_Abort;
+ if( pExpr->pLeft && walkExpr(pWalker, pExpr->pLeft) ) return WRC_Abort;
+ if( pExpr->pRight && walkExpr(pWalker, pExpr->pRight) ) return WRC_Abort;
+ if( ExprHasProperty(pExpr, EP_xIsSelect) ){
+ if( sqlite3WalkSelect(pWalker, pExpr->x.pSelect) ) return WRC_Abort;
+ }else if( pExpr->x.pList ){
+ if( sqlite3WalkExprList(pWalker, pExpr->x.pList) ) return WRC_Abort;
+ }
+ return WRC_Continue;
}
SQLITE_PRIVATE int sqlite3WalkExpr(Walker *pWalker, Expr *pExpr){
return pExpr ? walkExpr(pWalker,pExpr) : WRC_Continue;
@@ -88186,8 +88406,8 @@ static int lookupName(
zDb = 0;
}else{
for(i=0; i<db->nDb; i++){
- assert( db->aDb[i].zName );
- if( sqlite3StrICmp(db->aDb[i].zName,zDb)==0 ){
+ assert( db->aDb[i].zDbSName );
+ if( sqlite3StrICmp(db->aDb[i].zDbSName,zDb)==0 ){
pSchema = db->aDb[i].pSchema;
break;
}
@@ -88588,7 +88808,6 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
/* if( pSrcList==0 ) break; */
notValid(pParse, pNC, "the \".\" operator", NC_IdxExpr);
- /*notValid(pParse, pNC, "the \".\" operator", NC_PartIdx|NC_IsCheck, 1);*/
pRight = pExpr->pRight;
if( pRight->op==TK_ID ){
zDb = 0;
@@ -88611,14 +88830,12 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
int no_such_func = 0; /* True if no such function exists */
int wrong_num_args = 0; /* True if wrong number of arguments */
int is_agg = 0; /* True if is an aggregate function */
- int auth; /* Authorization to use the function */
int nId; /* Number of characters in function name */
const char *zId; /* The function name. */
FuncDef *pDef; /* Information about the function */
u8 enc = ENC(pParse->db); /* The database encoding */
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
- notValid(pParse, pNC, "functions", NC_PartIdx);
zId = pExpr->u.zToken;
nId = sqlite3Strlen30(zId);
pDef = sqlite3FindFunction(pParse->db, zId, n, enc, 0);
@@ -88655,15 +88872,17 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
}
}
#ifndef SQLITE_OMIT_AUTHORIZATION
- auth = sqlite3AuthCheck(pParse, SQLITE_FUNCTION, 0, pDef->zName, 0);
- if( auth!=SQLITE_OK ){
- if( auth==SQLITE_DENY ){
- sqlite3ErrorMsg(pParse, "not authorized to use function: %s",
- pDef->zName);
- pNC->nErr++;
+ {
+ int auth = sqlite3AuthCheck(pParse, SQLITE_FUNCTION, 0,pDef->zName,0);
+ if( auth!=SQLITE_OK ){
+ if( auth==SQLITE_DENY ){
+ sqlite3ErrorMsg(pParse, "not authorized to use function: %s",
+ pDef->zName);
+ pNC->nErr++;
+ }
+ pExpr->op = TK_NULL;
+ return WRC_Prune;
}
- pExpr->op = TK_NULL;
- return WRC_Prune;
}
#endif
if( pDef->funcFlags & (SQLITE_FUNC_CONSTANT|SQLITE_FUNC_SLOCHNG) ){
@@ -88676,7 +88895,8 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
/* Date/time functions that use 'now', and other functions like
** sqlite_version() that might change over time cannot be used
** in an index. */
- notValid(pParse, pNC, "non-deterministic functions", NC_IdxExpr);
+ notValid(pParse, pNC, "non-deterministic functions",
+ NC_IdxExpr|NC_PartIdx);
}
}
if( is_agg && (pNC->ncFlags & NC_AllowAgg)==0 ){
@@ -88741,6 +88961,33 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
notValid(pParse, pNC, "parameters", NC_IsCheck|NC_PartIdx|NC_IdxExpr);
break;
}
+ case TK_EQ:
+ case TK_NE:
+ case TK_LT:
+ case TK_LE:
+ case TK_GT:
+ case TK_GE:
+ case TK_IS:
+ case TK_ISNOT: {
+ int nLeft, nRight;
+ if( pParse->db->mallocFailed ) break;
+ assert( pExpr->pRight!=0 );
+ assert( pExpr->pLeft!=0 );
+ nLeft = sqlite3ExprVectorSize(pExpr->pLeft);
+ nRight = sqlite3ExprVectorSize(pExpr->pRight);
+ if( nLeft!=nRight ){
+ testcase( pExpr->op==TK_EQ );
+ testcase( pExpr->op==TK_NE );
+ testcase( pExpr->op==TK_LT );
+ testcase( pExpr->op==TK_LE );
+ testcase( pExpr->op==TK_GT );
+ testcase( pExpr->op==TK_GE );
+ testcase( pExpr->op==TK_IS );
+ testcase( pExpr->op==TK_ISNOT );
+ sqlite3ErrorMsg(pParse, "row value misused");
+ }
+ break;
+ }
}
return (pParse->nErr || pParse->db->mallocFailed) ? WRC_Abort : WRC_Continue;
}
@@ -89483,6 +89730,18 @@ SQLITE_PRIVATE void sqlite3ResolveSelfReference(
*/
/* #include "sqliteInt.h" */
+/* Forward declarations */
+static void exprCodeBetween(Parse*,Expr*,int,void(*)(Parse*,Expr*,int,int),int);
+static int exprCodeVector(Parse *pParse, Expr *p, int *piToFree);
+
+/*
+** Return the affinity character for a single column of a table.
+*/
+SQLITE_PRIVATE char sqlite3TableColumnAffinity(Table *pTab, int iCol){
+ assert( iCol<pTab->nCol );
+ return iCol>=0 ? pTab->aCol[iCol].affinity : SQLITE_AFF_INTEGER;
+}
+
/*
** Return the 'affinity' of the expression pExpr if any.
**
@@ -89508,21 +89767,21 @@ SQLITE_PRIVATE char sqlite3ExprAffinity(Expr *pExpr){
assert( pExpr->flags&EP_xIsSelect );
return sqlite3ExprAffinity(pExpr->x.pSelect->pEList->a[0].pExpr);
}
+ if( op==TK_REGISTER ) op = pExpr->op2;
#ifndef SQLITE_OMIT_CAST
if( op==TK_CAST ){
assert( !ExprHasProperty(pExpr, EP_IntValue) );
return sqlite3AffinityType(pExpr->u.zToken, 0);
}
#endif
- if( (op==TK_AGG_COLUMN || op==TK_COLUMN || op==TK_REGISTER)
- && pExpr->pTab!=0
- ){
- /* op==TK_REGISTER && pExpr->pTab!=0 happens when pExpr was originally
- ** a TK_COLUMN but was previously evaluated and cached in a register */
- int j = pExpr->iColumn;
- if( j<0 ) return SQLITE_AFF_INTEGER;
- assert( pExpr->pTab && j<pExpr->pTab->nCol );
- return pExpr->pTab->aCol[j].affinity;
+ if( op==TK_AGG_COLUMN || op==TK_COLUMN ){
+ return sqlite3TableColumnAffinity(pExpr->pTab, pExpr->iColumn);
+ }
+ if( op==TK_SELECT_COLUMN ){
+ assert( pExpr->pLeft->flags&EP_xIsSelect );
+ return sqlite3ExprAffinity(
+ pExpr->pLeft->x.pSelect->pEList->a[pExpr->iColumn].pExpr
+ );
}
return pExpr->affinity;
}
@@ -89688,7 +89947,7 @@ static char comparisonAffinity(Expr *pExpr){
aff = sqlite3CompareAffinity(pExpr->pRight, aff);
}else if( ExprHasProperty(pExpr, EP_xIsSelect) ){
aff = sqlite3CompareAffinity(pExpr->x.pSelect->pEList->a[0].pExpr, aff);
- }else if( !aff ){
+ }else if( NEVER(aff==0) ){
aff = SQLITE_AFF_BLOB;
}
return aff;
@@ -89778,6 +90037,270 @@ static int codeCompare(
return addr;
}
+/*
+** Return true if expression pExpr is a vector, or false otherwise.
+**
+** A vector is defined as any expression that results in two or more
+** columns of result. Every TK_VECTOR node is an vector because the
+** parser will not generate a TK_VECTOR with fewer than two entries.
+** But a TK_SELECT might be either a vector or a scalar. It is only
+** considered a vector if it has two or more result columns.
+*/
+SQLITE_PRIVATE int sqlite3ExprIsVector(Expr *pExpr){
+ return sqlite3ExprVectorSize(pExpr)>1;
+}
+
+/*
+** If the expression passed as the only argument is of type TK_VECTOR
+** return the number of expressions in the vector. Or, if the expression
+** is a sub-select, return the number of columns in the sub-select. For
+** any other type of expression, return 1.
+*/
+SQLITE_PRIVATE int sqlite3ExprVectorSize(Expr *pExpr){
+ u8 op = pExpr->op;
+ if( op==TK_REGISTER ) op = pExpr->op2;
+ if( op==TK_VECTOR ){
+ return pExpr->x.pList->nExpr;
+ }else if( op==TK_SELECT ){
+ return pExpr->x.pSelect->pEList->nExpr;
+ }else{
+ return 1;
+ }
+}
+
+#ifndef SQLITE_OMIT_SUBQUERY
+/*
+** Return a pointer to a subexpression of pVector that is the i-th
+** column of the vector (numbered starting with 0). The caller must
+** ensure that i is within range.
+**
+** If pVector is really a scalar (and "scalar" here includes subqueries
+** that return a single column!) then return pVector unmodified.
+**
+** pVector retains ownership of the returned subexpression.
+**
+** If the vector is a (SELECT ...) then the expression returned is
+** just the expression for the i-th term of the result set, and may
+** not be ready for evaluation because the table cursor has not yet
+** been positioned.
+*/
+SQLITE_PRIVATE Expr *sqlite3VectorFieldSubexpr(Expr *pVector, int i){
+ assert( i<sqlite3ExprVectorSize(pVector) );
+ if( sqlite3ExprIsVector(pVector) ){
+ assert( pVector->op2==0 || pVector->op==TK_REGISTER );
+ if( pVector->op==TK_SELECT || pVector->op2==TK_SELECT ){
+ return pVector->x.pSelect->pEList->a[i].pExpr;
+ }else{
+ return pVector->x.pList->a[i].pExpr;
+ }
+ }
+ return pVector;
+}
+#endif /* !defined(SQLITE_OMIT_SUBQUERY) */
+
+#ifndef SQLITE_OMIT_SUBQUERY
+/*
+** Compute and return a new Expr object which when passed to
+** sqlite3ExprCode() will generate all necessary code to compute
+** the iField-th column of the vector expression pVector.
+**
+** It is ok for pVector to be a scalar (as long as iField==0).
+** In that case, this routine works like sqlite3ExprDup().
+**
+** The caller owns the returned Expr object and is responsible for
+** ensuring that the returned value eventually gets freed.
+**
+** The caller retains ownership of pVector. If pVector is a TK_SELECT,
+** then the returned object will reference pVector and so pVector must remain
+** valid for the life of the returned object. If pVector is a TK_VECTOR
+** or a scalar expression, then it can be deleted as soon as this routine
+** returns.
+**
+** A trick to cause a TK_SELECT pVector to be deleted together with
+** the returned Expr object is to attach the pVector to the pRight field
+** of the returned TK_SELECT_COLUMN Expr object.
+*/
+SQLITE_PRIVATE Expr *sqlite3ExprForVectorField(
+ Parse *pParse, /* Parsing context */
+ Expr *pVector, /* The vector. List of expressions or a sub-SELECT */
+ int iField /* Which column of the vector to return */
+){
+ Expr *pRet;
+ if( pVector->op==TK_SELECT ){
+ assert( pVector->flags & EP_xIsSelect );
+ /* The TK_SELECT_COLUMN Expr node:
+ **
+ ** pLeft: pVector containing TK_SELECT
+ ** pRight: not used. But recursively deleted.
+ ** iColumn: Index of a column in pVector
+ ** pLeft->iTable: First in an array of register holding result, or 0
+ ** if the result is not yet computed.
+ **
+ ** sqlite3ExprDelete() specifically skips the recursive delete of
+ ** pLeft on TK_SELECT_COLUMN nodes. But pRight is followed, so pVector
+ ** can be attached to pRight to cause this node to take ownership of
+ ** pVector. Typically there will be multiple TK_SELECT_COLUMN nodes
+ ** with the same pLeft pointer to the pVector, but only one of them
+ ** will own the pVector.
+ */
+ pRet = sqlite3PExpr(pParse, TK_SELECT_COLUMN, 0, 0, 0);
+ if( pRet ){
+ pRet->iColumn = iField;
+ pRet->pLeft = pVector;
+ }
+ assert( pRet==0 || pRet->iTable==0 );
+ }else{
+ if( pVector->op==TK_VECTOR ) pVector = pVector->x.pList->a[iField].pExpr;
+ pRet = sqlite3ExprDup(pParse->db, pVector, 0);
+ }
+ return pRet;
+}
+#endif /* !define(SQLITE_OMIT_SUBQUERY) */
+
+/*
+** If expression pExpr is of type TK_SELECT, generate code to evaluate
+** it. Return the register in which the result is stored (or, if the
+** sub-select returns more than one column, the first in an array
+** of registers in which the result is stored).
+**
+** If pExpr is not a TK_SELECT expression, return 0.
+*/
+static int exprCodeSubselect(Parse *pParse, Expr *pExpr){
+ int reg = 0;
+#ifndef SQLITE_OMIT_SUBQUERY
+ if( pExpr->op==TK_SELECT ){
+ reg = sqlite3CodeSubselect(pParse, pExpr, 0, 0);
+ }
+#endif
+ return reg;
+}
+
+/*
+** Argument pVector points to a vector expression - either a TK_VECTOR
+** or TK_SELECT that returns more than one column. This function returns
+** the register number of a register that contains the value of
+** element iField of the vector.
+**
+** If pVector is a TK_SELECT expression, then code for it must have
+** already been generated using the exprCodeSubselect() routine. In this
+** case parameter regSelect should be the first in an array of registers
+** containing the results of the sub-select.
+**
+** If pVector is of type TK_VECTOR, then code for the requested field
+** is generated. In this case (*pRegFree) may be set to the number of
+** a temporary register to be freed by the caller before returning.
+**
+** Before returning, output parameter (*ppExpr) is set to point to the
+** Expr object corresponding to element iElem of the vector.
+*/
+static int exprVectorRegister(
+ Parse *pParse, /* Parse context */
+ Expr *pVector, /* Vector to extract element from */
+ int iField, /* Field to extract from pVector */
+ int regSelect, /* First in array of registers */
+ Expr **ppExpr, /* OUT: Expression element */
+ int *pRegFree /* OUT: Temp register to free */
+){
+ u8 op = pVector->op;
+ assert( op==TK_VECTOR || op==TK_REGISTER || op==TK_SELECT );
+ if( op==TK_REGISTER ){
+ *ppExpr = sqlite3VectorFieldSubexpr(pVector, iField);
+ return pVector->iTable+iField;
+ }
+ if( op==TK_SELECT ){
+ *ppExpr = pVector->x.pSelect->pEList->a[iField].pExpr;
+ return regSelect+iField;
+ }
+ *ppExpr = pVector->x.pList->a[iField].pExpr;
+ return sqlite3ExprCodeTemp(pParse, *ppExpr, pRegFree);
+}
+
+/*
+** Expression pExpr is a comparison between two vector values. Compute
+** the result of the comparison (1, 0, or NULL) and write that
+** result into register dest.
+**
+** The caller must satisfy the following preconditions:
+**
+** if pExpr->op==TK_IS: op==TK_EQ and p5==SQLITE_NULLEQ
+** if pExpr->op==TK_ISNOT: op==TK_NE and p5==SQLITE_NULLEQ
+** otherwise: op==pExpr->op and p5==0
+*/
+static void codeVectorCompare(
+ Parse *pParse, /* Code generator context */
+ Expr *pExpr, /* The comparison operation */
+ int dest, /* Write results into this register */
+ u8 op, /* Comparison operator */
+ u8 p5 /* SQLITE_NULLEQ or zero */
+){
+ Vdbe *v = pParse->pVdbe;
+ Expr *pLeft = pExpr->pLeft;
+ Expr *pRight = pExpr->pRight;
+ int nLeft = sqlite3ExprVectorSize(pLeft);
+ int i;
+ int regLeft = 0;
+ int regRight = 0;
+ u8 opx = op;
+ int addrDone = sqlite3VdbeMakeLabel(v);
+
+ assert( nLeft==sqlite3ExprVectorSize(pRight) );
+ assert( pExpr->op==TK_EQ || pExpr->op==TK_NE
+ || pExpr->op==TK_IS || pExpr->op==TK_ISNOT
+ || pExpr->op==TK_LT || pExpr->op==TK_GT
+ || pExpr->op==TK_LE || pExpr->op==TK_GE
+ );
+ assert( pExpr->op==op || (pExpr->op==TK_IS && op==TK_EQ)
+ || (pExpr->op==TK_ISNOT && op==TK_NE) );
+ assert( p5==0 || pExpr->op!=op );
+ assert( p5==SQLITE_NULLEQ || pExpr->op==op );
+
+ p5 |= SQLITE_STOREP2;
+ if( opx==TK_LE ) opx = TK_LT;
+ if( opx==TK_GE ) opx = TK_GT;
+
+ regLeft = exprCodeSubselect(pParse, pLeft);
+ regRight = exprCodeSubselect(pParse, pRight);
+
+ for(i=0; 1 /*Loop exits by "break"*/; i++){
+ int regFree1 = 0, regFree2 = 0;
+ Expr *pL, *pR;
+ int r1, r2;
+ assert( i>=0 && i<nLeft );
+ if( i>0 ) sqlite3ExprCachePush(pParse);
+ r1 = exprVectorRegister(pParse, pLeft, i, regLeft, &pL, &regFree1);
+ r2 = exprVectorRegister(pParse, pRight, i, regRight, &pR, &regFree2);
+ codeCompare(pParse, pL, pR, opx, r1, r2, dest, p5);
+ testcase(op==OP_Lt); VdbeCoverageIf(v,op==OP_Lt);
+ testcase(op==OP_Le); VdbeCoverageIf(v,op==OP_Le);
+ testcase(op==OP_Gt); VdbeCoverageIf(v,op==OP_Gt);
+ testcase(op==OP_Ge); VdbeCoverageIf(v,op==OP_Ge);
+ testcase(op==OP_Eq); VdbeCoverageIf(v,op==OP_Eq);
+ testcase(op==OP_Ne); VdbeCoverageIf(v,op==OP_Ne);
+ sqlite3ReleaseTempReg(pParse, regFree1);
+ sqlite3ReleaseTempReg(pParse, regFree2);
+ if( i>0 ) sqlite3ExprCachePop(pParse);
+ if( i==nLeft-1 ){
+ break;
+ }
+ if( opx==TK_EQ ){
+ sqlite3VdbeAddOp2(v, OP_IfNot, dest, addrDone); VdbeCoverage(v);
+ p5 |= SQLITE_KEEPNULL;
+ }else if( opx==TK_NE ){
+ sqlite3VdbeAddOp2(v, OP_If, dest, addrDone); VdbeCoverage(v);
+ p5 |= SQLITE_KEEPNULL;
+ }else{
+ assert( op==TK_LT || op==TK_GT || op==TK_LE || op==TK_GE );
+ sqlite3VdbeAddOp2(v, OP_ElseNotEq, 0, addrDone);
+ VdbeCoverageIf(v, op==TK_LT);
+ VdbeCoverageIf(v, op==TK_GT);
+ VdbeCoverageIf(v, op==TK_LE);
+ VdbeCoverageIf(v, op==TK_GE);
+ if( i==nLeft-2 ) opx = op;
+ }
+ }
+ sqlite3VdbeResolveLabel(v, addrDone);
+}
+
#if SQLITE_MAX_EXPR_DEPTH>0
/*
** Check that argument nHeight is less than or equal to the maximum
@@ -89913,7 +90436,7 @@ SQLITE_PRIVATE void sqlite3ExprSetHeightAndFlags(Parse *pParse, Expr *p){
** is allocated to hold the integer text and the dequote flag is ignored.
*/
SQLITE_PRIVATE Expr *sqlite3ExprAlloc(
- sqlite3 *db, /* Handle for sqlite3DbMallocZero() (may be null) */
+ sqlite3 *db, /* Handle for sqlite3DbMallocRawNN() */
int op, /* Expression opcode */
const Token *pToken, /* Token argument. Might be NULL */
int dequote /* True to dequote */
@@ -90131,7 +90654,7 @@ SQLITE_PRIVATE Expr *sqlite3ExprFunction(Parse *pParse, ExprList *pList, Token *
** instance of the wildcard, the next sequential variable number is
** assigned.
*/
-SQLITE_PRIVATE void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr){
+SQLITE_PRIVATE void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr, u32 n){
sqlite3 *db = pParse->db;
const char *z;
@@ -90140,19 +90663,19 @@ SQLITE_PRIVATE void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr){
z = pExpr->u.zToken;
assert( z!=0 );
assert( z[0]!=0 );
+ assert( n==sqlite3Strlen30(z) );
if( z[1]==0 ){
/* Wildcard of the form "?". Assign the next variable number */
assert( z[0]=='?' );
pExpr->iColumn = (ynVar)(++pParse->nVar);
}else{
- ynVar x = 0;
- u32 n = sqlite3Strlen30(z);
+ ynVar x;
if( z[0]=='?' ){
/* Wildcard of the form "?nnn". Convert "nnn" to an integer and
** use it as the variable number */
i64 i;
int bOk = 0==sqlite3Atoi64(&z[1], &i, n-1, SQLITE_UTF8);
- pExpr->iColumn = x = (ynVar)i;
+ x = (ynVar)i;
testcase( i==0 );
testcase( i==1 );
testcase( i==db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER]-1 );
@@ -90160,7 +90683,7 @@ SQLITE_PRIVATE void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr){
if( bOk==0 || i<1 || i>db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] ){
sqlite3ErrorMsg(pParse, "variable number must be between ?1 and ?%d",
db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER]);
- x = 0;
+ return;
}
if( i>pParse->nVar ){
pParse->nVar = (int)i;
@@ -90171,33 +90694,31 @@ SQLITE_PRIVATE void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr){
** has never appeared before, reuse the same variable number
*/
ynVar i;
- for(i=0; i<pParse->nzVar; i++){
+ for(i=x=0; i<pParse->nzVar; i++){
if( pParse->azVar[i] && strcmp(pParse->azVar[i],z)==0 ){
- pExpr->iColumn = x = (ynVar)i+1;
+ x = (ynVar)i+1;
break;
}
}
- if( x==0 ) x = pExpr->iColumn = (ynVar)(++pParse->nVar);
+ if( x==0 ) x = (ynVar)(++pParse->nVar);
}
- if( x>0 ){
- if( x>pParse->nzVar ){
- char **a;
- a = sqlite3DbRealloc(db, pParse->azVar, x*sizeof(a[0]));
- if( a==0 ){
- assert( db->mallocFailed ); /* Error reported through mallocFailed */
- return;
- }
- pParse->azVar = a;
- memset(&a[pParse->nzVar], 0, (x-pParse->nzVar)*sizeof(a[0]));
- pParse->nzVar = x;
- }
- if( z[0]!='?' || pParse->azVar[x-1]==0 ){
- sqlite3DbFree(db, pParse->azVar[x-1]);
- pParse->azVar[x-1] = sqlite3DbStrNDup(db, z, n);
+ pExpr->iColumn = x;
+ if( x>pParse->nzVar ){
+ char **a;
+ a = sqlite3DbRealloc(db, pParse->azVar, x*sizeof(a[0]));
+ if( a==0 ){
+ assert( db->mallocFailed ); /* Error reported through mallocFailed */
+ return;
}
+ pParse->azVar = a;
+ memset(&a[pParse->nzVar], 0, (x-pParse->nzVar)*sizeof(a[0]));
+ pParse->nzVar = x;
+ }
+ if( pParse->azVar[x-1]==0 ){
+ pParse->azVar[x-1] = sqlite3DbStrNDup(db, z, n);
}
}
- if( !pParse->nErr && pParse->nVar>db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] ){
+ if( pParse->nVar>db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] ){
sqlite3ErrorMsg(pParse, "too many SQL variables");
}
}
@@ -90209,18 +90730,25 @@ static SQLITE_NOINLINE void sqlite3ExprDeleteNN(sqlite3 *db, Expr *p){
assert( p!=0 );
/* Sanity check: Assert that the IntValue is non-negative if it exists */
assert( !ExprHasProperty(p, EP_IntValue) || p->u.iValue>=0 );
- if( !ExprHasProperty(p, EP_TokenOnly) ){
+#ifdef SQLITE_DEBUG
+ if( ExprHasProperty(p, EP_Leaf) && !ExprHasProperty(p, EP_TokenOnly) ){
+ assert( p->pLeft==0 );
+ assert( p->pRight==0 );
+ assert( p->x.pSelect==0 );
+ }
+#endif
+ if( !ExprHasProperty(p, (EP_TokenOnly|EP_Leaf)) ){
/* The Expr.x union is never used at the same time as Expr.pRight */
assert( p->x.pList==0 || p->pRight==0 );
- sqlite3ExprDelete(db, p->pLeft);
+ if( p->pLeft && p->op!=TK_SELECT_COLUMN ) sqlite3ExprDeleteNN(db, p->pLeft);
sqlite3ExprDelete(db, p->pRight);
- if( ExprHasProperty(p, EP_MemToken) ) sqlite3DbFree(db, p->u.zToken);
if( ExprHasProperty(p, EP_xIsSelect) ){
sqlite3SelectDelete(db, p->x.pSelect);
}else{
sqlite3ExprListDelete(db, p->x.pList);
}
}
+ if( ExprHasProperty(p, EP_MemToken) ) sqlite3DbFree(db, p->u.zToken);
if( !ExprHasProperty(p, EP_Static) ){
sqlite3DbFree(db, p);
}
@@ -90397,7 +90925,7 @@ static Expr *exprDup(sqlite3 *db, Expr *p, int dupFlags, u8 **pzBuffer){
memcpy(zToken, p->u.zToken, nToken);
}
- if( 0==((p->flags|pNew->flags) & EP_TokenOnly) ){
+ if( 0==((p->flags|pNew->flags) & (EP_TokenOnly|EP_Leaf)) ){
/* Fill in the pNew->x.pSelect or pNew->x.pList member. */
if( ExprHasProperty(p, EP_xIsSelect) ){
pNew->x.pSelect = sqlite3SelectDup(db, p->x.pSelect, dupFlags);
@@ -90409,7 +90937,7 @@ static Expr *exprDup(sqlite3 *db, Expr *p, int dupFlags, u8 **pzBuffer){
/* Fill in pNew->pLeft and pNew->pRight. */
if( ExprHasProperty(pNew, EP_Reduced|EP_TokenOnly) ){
zAlloc += dupedExprNodeSize(p, dupFlags);
- if( ExprHasProperty(pNew, EP_Reduced) ){
+ if( !ExprHasProperty(pNew, EP_TokenOnly|EP_Leaf) ){
pNew->pLeft = p->pLeft ?
exprDup(db, p->pLeft, EXPRDUP_REDUCE, &zAlloc) : 0;
pNew->pRight = p->pRight ?
@@ -90419,8 +90947,12 @@ static Expr *exprDup(sqlite3 *db, Expr *p, int dupFlags, u8 **pzBuffer){
*pzBuffer = zAlloc;
}
}else{
- if( !ExprHasProperty(p, EP_TokenOnly) ){
- pNew->pLeft = sqlite3ExprDup(db, p->pLeft, 0);
+ if( !ExprHasProperty(p, EP_TokenOnly|EP_Leaf) ){
+ if( pNew->op==TK_SELECT_COLUMN ){
+ pNew->pLeft = p->pLeft;
+ }else{
+ pNew->pLeft = sqlite3ExprDup(db, p->pLeft, 0);
+ }
pNew->pRight = sqlite3ExprDup(db, p->pRight, 0);
}
}
@@ -90662,6 +91194,60 @@ no_mem:
}
/*
+** pColumns and pExpr form a vector assignment which is part of the SET
+** clause of an UPDATE statement. Like this:
+**
+** (a,b,c) = (expr1,expr2,expr3)
+** Or: (a,b,c) = (SELECT x,y,z FROM ....)
+**
+** For each term of the vector assignment, append new entries to the
+** expression list pList. In the case of a subquery on the LHS, append
+** TK_SELECT_COLUMN expressions.
+*/
+SQLITE_PRIVATE ExprList *sqlite3ExprListAppendVector(
+ Parse *pParse, /* Parsing context */
+ ExprList *pList, /* List to which to append. Might be NULL */
+ IdList *pColumns, /* List of names of LHS of the assignment */
+ Expr *pExpr /* Vector expression to be appended. Might be NULL */
+){
+ sqlite3 *db = pParse->db;
+ int n;
+ int i;
+ int iFirst = pList ? pList->nExpr : 0;
+ /* pColumns can only be NULL due to an OOM but an OOM will cause an
+ ** exit prior to this routine being invoked */
+ if( NEVER(pColumns==0) ) goto vector_append_error;
+ if( pExpr==0 ) goto vector_append_error;
+ n = sqlite3ExprVectorSize(pExpr);
+ if( pColumns->nId!=n ){
+ sqlite3ErrorMsg(pParse, "%d columns assigned %d values",
+ pColumns->nId, n);
+ goto vector_append_error;
+ }
+ for(i=0; i<n; i++){
+ Expr *pSubExpr = sqlite3ExprForVectorField(pParse, pExpr, i);
+ pList = sqlite3ExprListAppend(pParse, pList, pSubExpr);
+ if( pList ){
+ assert( pList->nExpr==iFirst+i+1 );
+ pList->a[pList->nExpr-1].zName = pColumns->a[i].zName;
+ pColumns->a[i].zName = 0;
+ }
+ }
+ if( pExpr->op==TK_SELECT ){
+ if( pList && pList->a[iFirst].pExpr ){
+ assert( pList->a[iFirst].pExpr->op==TK_SELECT_COLUMN );
+ pList->a[iFirst].pExpr->pRight = pExpr;
+ pExpr = 0;
+ }
+ }
+
+vector_append_error:
+ sqlite3ExprDelete(db, pExpr);
+ sqlite3IdListDelete(db, pColumns);
+ return pList;
+}
+
+/*
** Set the sort order for the last element on the given ExprList.
*/
SQLITE_PRIVATE void sqlite3ExprListSetSortOrder(ExprList *p, int iSortOrder){
@@ -91068,8 +91654,8 @@ static Select *isCandidateForInOpt(Expr *pX){
Select *p;
SrcList *pSrc;
ExprList *pEList;
- Expr *pRes;
Table *pTab;
+ int i;
if( !ExprHasProperty(pX, EP_xIsSelect) ) return 0; /* Not a subquery */
if( ExprHasProperty(pX, EP_VarSelect) ) return 0; /* Correlated subq */
p = pX->x.pSelect;
@@ -91092,23 +91678,18 @@ static Select *isCandidateForInOpt(Expr *pX){
assert( pTab->pSelect==0 ); /* FROM clause is not a view */
if( IsVirtual(pTab) ) return 0; /* FROM clause not a virtual table */
pEList = p->pEList;
- if( pEList->nExpr!=1 ) return 0; /* One column in the result set */
- pRes = pEList->a[0].pExpr;
- if( pRes->op!=TK_COLUMN ) return 0; /* Result is a column */
- assert( pRes->iTable==pSrc->a[0].iCursor ); /* Not a correlated subquery */
+ assert( pEList!=0 );
+ /* All SELECT results must be columns. */
+ for(i=0; i<pEList->nExpr; i++){
+ Expr *pRes = pEList->a[i].pExpr;
+ if( pRes->op!=TK_COLUMN ) return 0;
+ assert( pRes->iTable==pSrc->a[0].iCursor ); /* Not a correlated subquery */
+ }
return p;
}
#endif /* SQLITE_OMIT_SUBQUERY */
-/*
-** Code an OP_Once instruction and allocate space for its flag. Return the
-** address of the new instruction.
-*/
-SQLITE_PRIVATE int sqlite3CodeOnce(Parse *pParse){
- Vdbe *v = sqlite3GetVdbe(pParse); /* Virtual machine being coded */
- return sqlite3VdbeAddOp1(v, OP_Once, pParse->nOnce++);
-}
-
+#ifndef SQLITE_OMIT_SUBQUERY
/*
** Generate code that checks the left-most column of index table iCur to see if
** it contains any NULL entries. Cause the register at regHasNull to be set
@@ -91124,6 +91705,7 @@ static void sqlite3SetHasNullFlag(Vdbe *v, int iCur, int regHasNull){
VdbeComment((v, "first_entry_in(%d)", iCur));
sqlite3VdbeJumpHere(v, addr1);
}
+#endif
#ifndef SQLITE_OMIT_SUBQUERY
@@ -91168,7 +91750,7 @@ static int sqlite3InRhsIsConstant(Expr *pIn){
** An existing b-tree might be used if the RHS expression pX is a simple
** subquery such as:
**
-** SELECT <column> FROM <table>
+** SELECT <column1>, <column2>... FROM <table>
**
** If the RHS of the IN operator is a list or a more complex subquery, then
** an ephemeral table might need to be generated from the RHS and then
@@ -91184,14 +91766,14 @@ static int sqlite3InRhsIsConstant(Expr *pIn){
**
** When IN_INDEX_LOOP is used (and the b-tree will be used to iterate
** through the set members) then the b-tree must not contain duplicates.
-** An epheremal table must be used unless the selected <column> is guaranteed
-** to be unique - either because it is an INTEGER PRIMARY KEY or it
-** has a UNIQUE constraint or UNIQUE index.
+** An epheremal table must be used unless the selected columns are guaranteed
+** to be unique - either because it is an INTEGER PRIMARY KEY or due to
+** a UNIQUE constraint or index.
**
** When IN_INDEX_MEMBERSHIP is used (and the b-tree will be used
** for fast set membership tests) then an epheremal table must
-** be used unless <column> is an INTEGER PRIMARY KEY or an index can
-** be found with <column> as its left-most column.
+** be used unless <columns> is a single INTEGER PRIMARY KEY column or an
+** index can be found with the specified <columns> as its left-most.
**
** If the IN_INDEX_NOOP_OK and IN_INDEX_MEMBERSHIP are both set and
** if the RHS of the IN operator is a list (not a subquery) then this
@@ -91212,9 +91794,26 @@ static int sqlite3InRhsIsConstant(Expr *pIn){
** the value in that register will be NULL if the b-tree contains one or more
** NULL values, and it will be some non-NULL value if the b-tree contains no
** NULL values.
+**
+** If the aiMap parameter is not NULL, it must point to an array containing
+** one element for each column returned by the SELECT statement on the RHS
+** of the IN(...) operator. The i'th entry of the array is populated with the
+** offset of the index column that matches the i'th column returned by the
+** SELECT. For example, if the expression and selected index are:
+**
+** (?,?,?) IN (SELECT a, b, c FROM t1)
+** CREATE INDEX i1 ON t1(b, c, a);
+**
+** then aiMap[] is populated with {2, 0, 1}.
*/
#ifndef SQLITE_OMIT_SUBQUERY
-SQLITE_PRIVATE int sqlite3FindInIndex(Parse *pParse, Expr *pX, u32 inFlags, int *prRhsHasNull){
+SQLITE_PRIVATE int sqlite3FindInIndex(
+ Parse *pParse, /* Parsing context */
+ Expr *pX, /* The right-hand side (RHS) of the IN operator */
+ u32 inFlags, /* IN_INDEX_LOOP, _MEMBERSHIP, and/or _NOOP_OK */
+ int *prRhsHasNull, /* Register holding NULL status. See notes */
+ int *aiMap /* Mapping from Index fields to RHS fields */
+){
Select *p; /* SELECT to the right of IN operator */
int eType = 0; /* Type of RHS table. IN_INDEX_* */
int iTab = pParse->nTab++; /* Cursor of the RHS table */
@@ -91224,36 +91823,46 @@ SQLITE_PRIVATE int sqlite3FindInIndex(Parse *pParse, Expr *pX, u32 inFlags, int
assert( pX->op==TK_IN );
mustBeUnique = (inFlags & IN_INDEX_LOOP)!=0;
+ /* If the RHS of this IN(...) operator is a SELECT, and if it matters
+ ** whether or not the SELECT result contains NULL values, check whether
+ ** or not NULL is actually possible (it may not be, for example, due
+ ** to NOT NULL constraints in the schema). If no NULL values are possible,
+ ** set prRhsHasNull to 0 before continuing. */
+ if( prRhsHasNull && (pX->flags & EP_xIsSelect) ){
+ int i;
+ ExprList *pEList = pX->x.pSelect->pEList;
+ for(i=0; i<pEList->nExpr; i++){
+ if( sqlite3ExprCanBeNull(pEList->a[i].pExpr) ) break;
+ }
+ if( i==pEList->nExpr ){
+ prRhsHasNull = 0;
+ }
+ }
+
/* Check to see if an existing table or index can be used to
** satisfy the query. This is preferable to generating a new
- ** ephemeral table.
- */
+ ** ephemeral table. */
if( pParse->nErr==0 && (p = isCandidateForInOpt(pX))!=0 ){
sqlite3 *db = pParse->db; /* Database connection */
Table *pTab; /* Table <table>. */
- Expr *pExpr; /* Expression <column> */
- i16 iCol; /* Index of column <column> */
i16 iDb; /* Database idx for pTab */
+ ExprList *pEList = p->pEList;
+ int nExpr = pEList->nExpr;
assert( p->pEList!=0 ); /* Because of isCandidateForInOpt(p) */
assert( p->pEList->a[0].pExpr!=0 ); /* Because of isCandidateForInOpt(p) */
assert( p->pSrc!=0 ); /* Because of isCandidateForInOpt(p) */
pTab = p->pSrc->a[0].pTab;
- pExpr = p->pEList->a[0].pExpr;
- iCol = (i16)pExpr->iColumn;
-
+
/* Code an OP_Transaction and OP_TableLock for <table>. */
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
sqlite3CodeVerifySchema(pParse, iDb);
sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName);
- /* This function is only called from two places. In both cases the vdbe
- ** has already been allocated. So assume sqlite3GetVdbe() is always
- ** successful here.
- */
- assert(v);
- if( iCol<0 ){
- int iAddr = sqlite3CodeOnce(pParse);
+ assert(v); /* sqlite3GetVdbe() has always been previously called */
+ if( nExpr==1 && pEList->a[0].pExpr->iColumn<0 ){
+ /* The "x IN (SELECT rowid FROM table)" case */
+ int iAddr = sqlite3VdbeAddOp0(v, OP_Once);
VdbeCoverage(v);
sqlite3OpenTable(pParse, iTab, iDb, pTab, OP_OpenRead);
@@ -91262,44 +91871,109 @@ SQLITE_PRIVATE int sqlite3FindInIndex(Parse *pParse, Expr *pX, u32 inFlags, int
sqlite3VdbeJumpHere(v, iAddr);
}else{
Index *pIdx; /* Iterator variable */
+ int affinity_ok = 1;
+ int i;
- /* The collation sequence used by the comparison. If an index is to
- ** be used in place of a temp-table, it must be ordered according
- ** to this collation sequence. */
- CollSeq *pReq = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pExpr);
-
- /* Check that the affinity that will be used to perform the
- ** comparison is the same as the affinity of the column. If
- ** it is not, it is not possible to use any index.
- */
- int affinity_ok = sqlite3IndexAffinityOk(pX, pTab->aCol[iCol].affinity);
-
- for(pIdx=pTab->pIndex; pIdx && eType==0 && affinity_ok; pIdx=pIdx->pNext){
- if( (pIdx->aiColumn[0]==iCol)
- && sqlite3FindCollSeq(db, ENC(db), pIdx->azColl[0], 0)==pReq
- && (!mustBeUnique || (pIdx->nKeyCol==1 && IsUniqueIndex(pIdx)))
- ){
- int iAddr = sqlite3CodeOnce(pParse); VdbeCoverage(v);
- sqlite3VdbeAddOp3(v, OP_OpenRead, iTab, pIdx->tnum, iDb);
- sqlite3VdbeSetP4KeyInfo(pParse, pIdx);
- VdbeComment((v, "%s", pIdx->zName));
- assert( IN_INDEX_INDEX_DESC == IN_INDEX_INDEX_ASC+1 );
- eType = IN_INDEX_INDEX_ASC + pIdx->aSortOrder[0];
-
- if( prRhsHasNull && !pTab->aCol[iCol].notNull ){
+ /* Check that the affinity that will be used to perform each
+ ** comparison is the same as the affinity of each column in table
+ ** on the RHS of the IN operator. If it not, it is not possible to
+ ** use any index of the RHS table. */
+ for(i=0; i<nExpr && affinity_ok; i++){
+ Expr *pLhs = sqlite3VectorFieldSubexpr(pX->pLeft, i);
+ int iCol = pEList->a[i].pExpr->iColumn;
+ char idxaff = sqlite3TableColumnAffinity(pTab,iCol); /* RHS table */
+ char cmpaff = sqlite3CompareAffinity(pLhs, idxaff);
+ testcase( cmpaff==SQLITE_AFF_BLOB );
+ testcase( cmpaff==SQLITE_AFF_TEXT );
+ switch( cmpaff ){
+ case SQLITE_AFF_BLOB:
+ break;
+ case SQLITE_AFF_TEXT:
+ /* sqlite3CompareAffinity() only returns TEXT if one side or the
+ ** other has no affinity and the other side is TEXT. Hence,
+ ** the only way for cmpaff to be TEXT is for idxaff to be TEXT
+ ** and for the term on the LHS of the IN to have no affinity. */
+ assert( idxaff==SQLITE_AFF_TEXT );
+ break;
+ default:
+ affinity_ok = sqlite3IsNumericAffinity(idxaff);
+ }
+ }
+
+ if( affinity_ok ){
+ /* Search for an existing index that will work for this IN operator */
+ for(pIdx=pTab->pIndex; pIdx && eType==0; pIdx=pIdx->pNext){
+ Bitmask colUsed; /* Columns of the index used */
+ Bitmask mCol; /* Mask for the current column */
+ if( pIdx->nColumn<nExpr ) continue;
+ /* Maximum nColumn is BMS-2, not BMS-1, so that we can compute
+ ** BITMASK(nExpr) without overflowing */
+ testcase( pIdx->nColumn==BMS-2 );
+ testcase( pIdx->nColumn==BMS-1 );
+ if( pIdx->nColumn>=BMS-1 ) continue;
+ if( mustBeUnique ){
+ if( pIdx->nKeyCol>nExpr
+ ||(pIdx->nColumn>nExpr && !IsUniqueIndex(pIdx))
+ ){
+ continue; /* This index is not unique over the IN RHS columns */
+ }
+ }
+
+ colUsed = 0; /* Columns of index used so far */
+ for(i=0; i<nExpr; i++){
+ Expr *pLhs = sqlite3VectorFieldSubexpr(pX->pLeft, i);
+ Expr *pRhs = pEList->a[i].pExpr;
+ CollSeq *pReq = sqlite3BinaryCompareCollSeq(pParse, pLhs, pRhs);
+ int j;
+
+ assert( pReq!=0 || pRhs->iColumn==XN_ROWID || pParse->nErr );
+ for(j=0; j<nExpr; j++){
+ if( pIdx->aiColumn[j]!=pRhs->iColumn ) continue;
+ assert( pIdx->azColl[j] );
+ if( pReq!=0 && sqlite3StrICmp(pReq->zName, pIdx->azColl[j])!=0 ){
+ continue;
+ }
+ break;
+ }
+ if( j==nExpr ) break;
+ mCol = MASKBIT(j);
+ if( mCol & colUsed ) break; /* Each column used only once */
+ colUsed |= mCol;
+ if( aiMap ) aiMap[i] = j;
+ }
+
+ assert( i==nExpr || colUsed!=(MASKBIT(nExpr)-1) );
+ if( colUsed==(MASKBIT(nExpr)-1) ){
+ /* If we reach this point, that means the index pIdx is usable */
+ int iAddr = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
+#ifndef SQLITE_OMIT_EXPLAIN
+ sqlite3VdbeAddOp4(v, OP_Explain, 0, 0, 0,
+ sqlite3MPrintf(db, "USING INDEX %s FOR IN-OPERATOR",pIdx->zName),
+ P4_DYNAMIC);
+#endif
+ sqlite3VdbeAddOp3(v, OP_OpenRead, iTab, pIdx->tnum, iDb);
+ sqlite3VdbeSetP4KeyInfo(pParse, pIdx);
+ VdbeComment((v, "%s", pIdx->zName));
+ assert( IN_INDEX_INDEX_DESC == IN_INDEX_INDEX_ASC+1 );
+ eType = IN_INDEX_INDEX_ASC + pIdx->aSortOrder[0];
+
+ if( prRhsHasNull ){
#ifdef SQLITE_ENABLE_COLUMN_USED_MASK
- const i64 sOne = 1;
- sqlite3VdbeAddOp4Dup8(v, OP_ColumnsUsed,
- iTab, 0, 0, (u8*)&sOne, P4_INT64);
+ i64 mask = (1<<nExpr)-1;
+ sqlite3VdbeAddOp4Dup8(v, OP_ColumnsUsed,
+ iTab, 0, 0, (u8*)&mask, P4_INT64);
#endif
- *prRhsHasNull = ++pParse->nMem;
- sqlite3SetHasNullFlag(v, iTab, *prRhsHasNull);
+ *prRhsHasNull = ++pParse->nMem;
+ if( nExpr==1 ){
+ sqlite3SetHasNullFlag(v, iTab, *prRhsHasNull);
+ }
+ }
+ sqlite3VdbeJumpHere(v, iAddr);
}
- sqlite3VdbeJumpHere(v, iAddr);
- }
- }
- }
- }
+ } /* End loop over indexes */
+ } /* End if( affinity_ok ) */
+ } /* End if not an rowid index */
+ } /* End attempt to optimize using an index */
/* If no preexisting index is available for the IN clause
** and IN_INDEX_NOOP is an allowed reply
@@ -91315,7 +91989,6 @@ SQLITE_PRIVATE int sqlite3FindInIndex(Parse *pParse, Expr *pX, u32 inFlags, int
){
eType = IN_INDEX_NOOP;
}
-
if( eType==0 ){
/* Could not find an existing table or index to use as the RHS b-tree.
@@ -91337,10 +92010,63 @@ SQLITE_PRIVATE int sqlite3FindInIndex(Parse *pParse, Expr *pX, u32 inFlags, int
}else{
pX->iTable = iTab;
}
+
+ if( aiMap && eType!=IN_INDEX_INDEX_ASC && eType!=IN_INDEX_INDEX_DESC ){
+ int i, n;
+ n = sqlite3ExprVectorSize(pX->pLeft);
+ for(i=0; i<n; i++) aiMap[i] = i;
+ }
return eType;
}
#endif
+#ifndef SQLITE_OMIT_SUBQUERY
+/*
+** Argument pExpr is an (?, ?...) IN(...) expression. This
+** function allocates and returns a nul-terminated string containing
+** the affinities to be used for each column of the comparison.
+**
+** It is the responsibility of the caller to ensure that the returned
+** string is eventually freed using sqlite3DbFree().
+*/
+static char *exprINAffinity(Parse *pParse, Expr *pExpr){
+ Expr *pLeft = pExpr->pLeft;
+ int nVal = sqlite3ExprVectorSize(pLeft);
+ Select *pSelect = (pExpr->flags & EP_xIsSelect) ? pExpr->x.pSelect : 0;
+ char *zRet;
+
+ assert( pExpr->op==TK_IN );
+ zRet = sqlite3DbMallocZero(pParse->db, nVal+1);
+ if( zRet ){
+ int i;
+ for(i=0; i<nVal; i++){
+ Expr *pA = sqlite3VectorFieldSubexpr(pLeft, i);
+ char a = sqlite3ExprAffinity(pA);
+ if( pSelect ){
+ zRet[i] = sqlite3CompareAffinity(pSelect->pEList->a[i].pExpr, a);
+ }else{
+ zRet[i] = a;
+ }
+ }
+ zRet[nVal] = '\0';
+ }
+ return zRet;
+}
+#endif
+
+#ifndef SQLITE_OMIT_SUBQUERY
+/*
+** Load the Parse object passed as the first argument with an error
+** message of the form:
+**
+** "sub-select returns N columns - expected M"
+*/
+SQLITE_PRIVATE void sqlite3SubselectError(Parse *pParse, int nActual, int nExpect){
+ const char *zFmt = "sub-select returns %d columns - expected %d";
+ sqlite3ErrorMsg(pParse, zFmt, nActual, nExpect);
+}
+#endif
+
/*
** Generate code for scalar subqueries used as a subquery expression, EXISTS,
** or IN operators. Examples:
@@ -91366,7 +92092,9 @@ SQLITE_PRIVATE int sqlite3FindInIndex(Parse *pParse, Expr *pX, u32 inFlags, int
** value to non-NULL if the RHS is NULL-free.
**
** For a SELECT or EXISTS operator, return the register that holds the
-** result. For IN operators or if an error occurs, the return value is 0.
+** result. For a multi-column SELECT, the result is stored in a contiguous
+** array of registers and the return value is the register of the left-most
+** result column. Return 0 for IN operators or if an error occurs.
*/
#ifndef SQLITE_OMIT_SUBQUERY
SQLITE_PRIVATE int sqlite3CodeSubselect(
@@ -91381,8 +92109,8 @@ SQLITE_PRIVATE int sqlite3CodeSubselect(
if( NEVER(v==0) ) return 0;
sqlite3ExprCachePush(pParse);
- /* This code must be run in its entirety every time it is encountered
- ** if any of the following is true:
+ /* The evaluation of the IN/EXISTS/SELECT must be repeated every time it
+ ** is encountered if any of the following is true:
**
** * The right-hand side is a correlated subquery
** * The right-hand side is an expression list containing variables
@@ -91392,7 +92120,7 @@ SQLITE_PRIVATE int sqlite3CodeSubselect(
** save the results, and reuse the same result on subsequent invocations.
*/
if( !ExprHasProperty(pExpr, EP_VarSelect) ){
- jmpIfDynamic = sqlite3CodeOnce(pParse); VdbeCoverage(v);
+ jmpIfDynamic = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
}
#ifndef SQLITE_OMIT_EXPLAIN
@@ -91408,17 +92136,18 @@ SQLITE_PRIVATE int sqlite3CodeSubselect(
switch( pExpr->op ){
case TK_IN: {
- char affinity; /* Affinity of the LHS of the IN */
int addr; /* Address of OP_OpenEphemeral instruction */
Expr *pLeft = pExpr->pLeft; /* the LHS of the IN operator */
KeyInfo *pKeyInfo = 0; /* Key information */
-
- affinity = sqlite3ExprAffinity(pLeft);
+ int nVal; /* Size of vector pLeft */
+
+ nVal = sqlite3ExprVectorSize(pLeft);
+ assert( !isRowid || nVal==1 );
/* Whether this is an 'x IN(SELECT...)' or an 'x IN(<exprlist>)'
** expression it is handled the same way. An ephemeral table is
- ** filled with single-field index keys representing the results
- ** from the SELECT or the <exprlist>.
+ ** filled with index keys representing the results from the
+ ** SELECT or the <exprlist>.
**
** If the 'x' expression is a column value, or the SELECT...
** statement returns a column value, then the affinity of that
@@ -91429,8 +92158,9 @@ SQLITE_PRIVATE int sqlite3CodeSubselect(
** is used.
*/
pExpr->iTable = pParse->nTab++;
- addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pExpr->iTable, !isRowid);
- pKeyInfo = isRowid ? 0 : sqlite3KeyInfoAlloc(pParse->db, 1, 1);
+ addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral,
+ pExpr->iTable, (isRowid?0:nVal));
+ pKeyInfo = isRowid ? 0 : sqlite3KeyInfoAlloc(pParse->db, nVal, 1);
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
/* Case 1: expr IN (SELECT ...)
@@ -91439,27 +92169,37 @@ SQLITE_PRIVATE int sqlite3CodeSubselect(
** table allocated and opened above.
*/
Select *pSelect = pExpr->x.pSelect;
- SelectDest dest;
- ExprList *pEList;
+ ExprList *pEList = pSelect->pEList;
assert( !isRowid );
- sqlite3SelectDestInit(&dest, SRT_Set, pExpr->iTable);
- dest.affSdst = (u8)affinity;
- assert( (pExpr->iTable&0x0000FFFF)==pExpr->iTable );
- pSelect->iLimit = 0;
- testcase( pSelect->selFlags & SF_Distinct );
- testcase( pKeyInfo==0 ); /* Caused by OOM in sqlite3KeyInfoAlloc() */
- if( sqlite3Select(pParse, pSelect, &dest) ){
- sqlite3KeyInfoUnref(pKeyInfo);
- return 0;
+ /* If the LHS and RHS of the IN operator do not match, that
+ ** error will have been caught long before we reach this point. */
+ if( ALWAYS(pEList->nExpr==nVal) ){
+ SelectDest dest;
+ int i;
+ sqlite3SelectDestInit(&dest, SRT_Set, pExpr->iTable);
+ dest.zAffSdst = exprINAffinity(pParse, pExpr);
+ assert( (pExpr->iTable&0x0000FFFF)==pExpr->iTable );
+ pSelect->iLimit = 0;
+ testcase( pSelect->selFlags & SF_Distinct );
+ testcase( pKeyInfo==0 ); /* Caused by OOM in sqlite3KeyInfoAlloc() */
+ if( sqlite3Select(pParse, pSelect, &dest) ){
+ sqlite3DbFree(pParse->db, dest.zAffSdst);
+ sqlite3KeyInfoUnref(pKeyInfo);
+ return 0;
+ }
+ sqlite3DbFree(pParse->db, dest.zAffSdst);
+ assert( pKeyInfo!=0 ); /* OOM will cause exit after sqlite3Select() */
+ assert( pEList!=0 );
+ assert( pEList->nExpr>0 );
+ assert( sqlite3KeyInfoIsWriteable(pKeyInfo) );
+ for(i=0; i<nVal; i++){
+ Expr *p = sqlite3VectorFieldSubexpr(pLeft, i);
+ pKeyInfo->aColl[i] = sqlite3BinaryCompareCollSeq(
+ pParse, p, pEList->a[i].pExpr
+ );
+ }
}
- pEList = pSelect->pEList;
- assert( pKeyInfo!=0 ); /* OOM will cause exit after sqlite3Select() */
- assert( pEList!=0 );
- assert( pEList->nExpr>0 );
- assert( sqlite3KeyInfoIsWriteable(pKeyInfo) );
- pKeyInfo->aColl[0] = sqlite3BinaryCompareCollSeq(pParse, pExpr->pLeft,
- pEList->a[0].pExpr);
}else if( ALWAYS(pExpr->x.pList!=0) ){
/* Case 2: expr IN (exprlist)
**
@@ -91468,11 +92208,13 @@ SQLITE_PRIVATE int sqlite3CodeSubselect(
** that columns affinity when building index keys. If <expr> is not
** a column, use numeric affinity.
*/
+ char affinity; /* Affinity of the LHS of the IN */
int i;
ExprList *pList = pExpr->x.pList;
struct ExprList_item *pItem;
int r1, r2, r3;
+ affinity = sqlite3ExprAffinity(pLeft);
if( !affinity ){
affinity = SQLITE_AFF_BLOB;
}
@@ -91528,26 +92270,37 @@ SQLITE_PRIVATE int sqlite3CodeSubselect(
case TK_EXISTS:
case TK_SELECT:
default: {
- /* If this has to be a scalar SELECT. Generate code to put the
- ** value of this select in a memory cell and record the number
- ** of the memory cell in iColumn. If this is an EXISTS, write
- ** an integer 0 (not exists) or 1 (exists) into a memory cell
- ** and record that memory cell in iColumn.
+ /* Case 3: (SELECT ... FROM ...)
+ ** or: EXISTS(SELECT ... FROM ...)
+ **
+ ** For a SELECT, generate code to put the values for all columns of
+ ** the first row into an array of registers and return the index of
+ ** the first register.
+ **
+ ** If this is an EXISTS, write an integer 0 (not exists) or 1 (exists)
+ ** into a register and return that register number.
+ **
+ ** In both cases, the query is augmented with "LIMIT 1". Any
+ ** preexisting limit is discarded in place of the new LIMIT 1.
*/
Select *pSel; /* SELECT statement to encode */
- SelectDest dest; /* How to deal with SELECt result */
+ SelectDest dest; /* How to deal with SELECT result */
+ int nReg; /* Registers to allocate */
testcase( pExpr->op==TK_EXISTS );
testcase( pExpr->op==TK_SELECT );
assert( pExpr->op==TK_EXISTS || pExpr->op==TK_SELECT );
-
assert( ExprHasProperty(pExpr, EP_xIsSelect) );
+
pSel = pExpr->x.pSelect;
- sqlite3SelectDestInit(&dest, 0, ++pParse->nMem);
+ nReg = pExpr->op==TK_SELECT ? pSel->pEList->nExpr : 1;
+ sqlite3SelectDestInit(&dest, 0, pParse->nMem+1);
+ pParse->nMem += nReg;
if( pExpr->op==TK_SELECT ){
dest.eDest = SRT_Mem;
dest.iSdst = dest.iSDParm;
- sqlite3VdbeAddOp2(v, OP_Null, 0, dest.iSDParm);
+ dest.nSdst = nReg;
+ sqlite3VdbeAddOp3(v, OP_Null, 0, dest.iSDParm, dest.iSDParm+nReg-1);
VdbeComment((v, "Init subquery result"));
}else{
dest.eDest = SRT_Exists;
@@ -91555,8 +92308,8 @@ SQLITE_PRIVATE int sqlite3CodeSubselect(
VdbeComment((v, "Init EXISTS result"));
}
sqlite3ExprDelete(pParse->db, pSel->pLimit);
- pSel->pLimit = sqlite3PExpr(pParse, TK_INTEGER, 0, 0,
- &sqlite3IntTokens[1]);
+ pSel->pLimit = sqlite3ExprAlloc(pParse->db, TK_INTEGER,
+ &sqlite3IntTokens[1], 0);
pSel->iLimit = 0;
pSel->selFlags &= ~SF_MultiValue;
if( sqlite3Select(pParse, pSel, &dest) ){
@@ -91583,21 +92336,55 @@ SQLITE_PRIVATE int sqlite3CodeSubselect(
#ifndef SQLITE_OMIT_SUBQUERY
/*
+** Expr pIn is an IN(...) expression. This function checks that the
+** sub-select on the RHS of the IN() operator has the same number of
+** columns as the vector on the LHS. Or, if the RHS of the IN() is not
+** a sub-query, that the LHS is a vector of size 1.
+*/
+SQLITE_PRIVATE int sqlite3ExprCheckIN(Parse *pParse, Expr *pIn){
+ int nVector = sqlite3ExprVectorSize(pIn->pLeft);
+ if( (pIn->flags & EP_xIsSelect) ){
+ if( nVector!=pIn->x.pSelect->pEList->nExpr ){
+ sqlite3SubselectError(pParse, pIn->x.pSelect->pEList->nExpr, nVector);
+ return 1;
+ }
+ }else if( nVector!=1 ){
+ if( (pIn->pLeft->flags & EP_xIsSelect) ){
+ sqlite3SubselectError(pParse, nVector, 1);
+ }else{
+ sqlite3ErrorMsg(pParse, "row value misused");
+ }
+ return 1;
+ }
+ return 0;
+}
+#endif
+
+#ifndef SQLITE_OMIT_SUBQUERY
+/*
** Generate code for an IN expression.
**
** x IN (SELECT ...)
** x IN (value, value, ...)
**
-** The left-hand side (LHS) is a scalar expression. The right-hand side (RHS)
-** is an array of zero or more values. The expression is true if the LHS is
-** contained within the RHS. The value of the expression is unknown (NULL)
-** if the LHS is NULL or if the LHS is not contained within the RHS and the
-** RHS contains one or more NULL values.
+** The left-hand side (LHS) is a scalar or vector expression. The
+** right-hand side (RHS) is an array of zero or more scalar values, or a
+** subquery. If the RHS is a subquery, the number of result columns must
+** match the number of columns in the vector on the LHS. If the RHS is
+** a list of values, the LHS must be a scalar.
+**
+** The IN operator is true if the LHS value is contained within the RHS.
+** The result is false if the LHS is definitely not in the RHS. The
+** result is NULL if the presence of the LHS in the RHS cannot be
+** determined due to NULLs.
**
** This routine generates code that jumps to destIfFalse if the LHS is not
** contained within the RHS. If due to NULLs we cannot determine if the LHS
** is contained in the RHS then jump to destIfNull. If the LHS is contained
** within the RHS then fall through.
+**
+** See the separate in-operator.md documentation file in the canonical
+** SQLite source tree for additional information.
*/
static void sqlite3ExprCodeIN(
Parse *pParse, /* Parsing and code generating context */
@@ -91606,36 +92393,83 @@ static void sqlite3ExprCodeIN(
int destIfNull /* Jump here if the results are unknown due to NULLs */
){
int rRhsHasNull = 0; /* Register that is true if RHS contains NULL values */
- char affinity; /* Comparison affinity to use */
int eType; /* Type of the RHS */
- int r1; /* Temporary use register */
+ int rLhs; /* Register(s) holding the LHS values */
+ int rLhsOrig; /* LHS values prior to reordering by aiMap[] */
Vdbe *v; /* Statement under construction */
+ int *aiMap = 0; /* Map from vector field to index column */
+ char *zAff = 0; /* Affinity string for comparisons */
+ int nVector; /* Size of vectors for this IN operator */
+ int iDummy; /* Dummy parameter to exprCodeVector() */
+ Expr *pLeft; /* The LHS of the IN operator */
+ int i; /* loop counter */
+ int destStep2; /* Where to jump when NULLs seen in step 2 */
+ int destStep6 = 0; /* Start of code for Step 6 */
+ int addrTruthOp; /* Address of opcode that determines the IN is true */
+ int destNotNull; /* Jump here if a comparison is not true in step 6 */
+ int addrTop; /* Top of the step-6 loop */
+
+ pLeft = pExpr->pLeft;
+ if( sqlite3ExprCheckIN(pParse, pExpr) ) return;
+ zAff = exprINAffinity(pParse, pExpr);
+ nVector = sqlite3ExprVectorSize(pExpr->pLeft);
+ aiMap = (int*)sqlite3DbMallocZero(
+ pParse->db, nVector*(sizeof(int) + sizeof(char)) + 1
+ );
+ if( pParse->db->mallocFailed ) goto sqlite3ExprCodeIN_oom_error;
- /* Compute the RHS. After this step, the table with cursor
- ** pExpr->iTable will contains the values that make up the RHS.
- */
+ /* Attempt to compute the RHS. After this step, if anything other than
+ ** IN_INDEX_NOOP is returned, the table opened ith cursor pExpr->iTable
+ ** contains the values that make up the RHS. If IN_INDEX_NOOP is returned,
+ ** the RHS has not yet been coded. */
v = pParse->pVdbe;
assert( v!=0 ); /* OOM detected prior to this routine */
VdbeNoopComment((v, "begin IN expr"));
eType = sqlite3FindInIndex(pParse, pExpr,
IN_INDEX_MEMBERSHIP | IN_INDEX_NOOP_OK,
- destIfFalse==destIfNull ? 0 : &rRhsHasNull);
+ destIfFalse==destIfNull ? 0 : &rRhsHasNull, aiMap);
- /* Figure out the affinity to use to create a key from the results
- ** of the expression. affinityStr stores a static string suitable for
- ** P4 of OP_MakeRecord.
- */
- affinity = comparisonAffinity(pExpr);
+ assert( pParse->nErr || nVector==1 || eType==IN_INDEX_EPH
+ || eType==IN_INDEX_INDEX_ASC || eType==IN_INDEX_INDEX_DESC
+ );
+#ifdef SQLITE_DEBUG
+ /* Confirm that aiMap[] contains nVector integer values between 0 and
+ ** nVector-1. */
+ for(i=0; i<nVector; i++){
+ int j, cnt;
+ for(cnt=j=0; j<nVector; j++) if( aiMap[j]==i ) cnt++;
+ assert( cnt==1 );
+ }
+#endif
- /* Code the LHS, the <expr> from "<expr> IN (...)".
+ /* Code the LHS, the <expr> from "<expr> IN (...)". If the LHS is a
+ ** vector, then it is stored in an array of nVector registers starting
+ ** at r1.
+ **
+ ** sqlite3FindInIndex() might have reordered the fields of the LHS vector
+ ** so that the fields are in the same order as an existing index. The
+ ** aiMap[] array contains a mapping from the original LHS field order to
+ ** the field order that matches the RHS index.
*/
sqlite3ExprCachePush(pParse);
- r1 = sqlite3GetTempReg(pParse);
- sqlite3ExprCode(pParse, pExpr->pLeft, r1);
+ rLhsOrig = exprCodeVector(pParse, pLeft, &iDummy);
+ for(i=0; i<nVector && aiMap[i]==i; i++){} /* Are LHS fields reordered? */
+ if( i==nVector ){
+ /* LHS fields are not reordered */
+ rLhs = rLhsOrig;
+ }else{
+ /* Need to reorder the LHS fields according to aiMap */
+ rLhs = sqlite3GetTempRange(pParse, nVector);
+ for(i=0; i<nVector; i++){
+ sqlite3VdbeAddOp3(v, OP_Copy, rLhsOrig+i, rLhs+aiMap[i], 0);
+ }
+ }
/* If sqlite3FindInIndex() did not find or create an index that is
** suitable for evaluating the IN operator, then evaluate using a
** sequence of comparisons.
+ **
+ ** This is step (1) in the in-operator.md optimized algorithm.
*/
if( eType==IN_INDEX_NOOP ){
ExprList *pList = pExpr->x.pList;
@@ -91647,7 +92481,7 @@ static void sqlite3ExprCodeIN(
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
if( destIfNull!=destIfFalse ){
regCkNull = sqlite3GetTempReg(pParse);
- sqlite3VdbeAddOp3(v, OP_BitAnd, r1, r1, regCkNull);
+ sqlite3VdbeAddOp3(v, OP_BitAnd, rLhs, rLhs, regCkNull);
}
for(ii=0; ii<pList->nExpr; ii++){
r2 = sqlite3ExprCodeTemp(pParse, pList->a[ii].pExpr, &regToFree);
@@ -91655,16 +92489,16 @@ static void sqlite3ExprCodeIN(
sqlite3VdbeAddOp3(v, OP_BitAnd, regCkNull, r2, regCkNull);
}
if( ii<pList->nExpr-1 || destIfNull!=destIfFalse ){
- sqlite3VdbeAddOp4(v, OP_Eq, r1, labelOk, r2,
+ sqlite3VdbeAddOp4(v, OP_Eq, rLhs, labelOk, r2,
(void*)pColl, P4_COLLSEQ);
VdbeCoverageIf(v, ii<pList->nExpr-1);
VdbeCoverageIf(v, ii==pList->nExpr-1);
- sqlite3VdbeChangeP5(v, affinity);
+ sqlite3VdbeChangeP5(v, zAff[0]);
}else{
assert( destIfNull==destIfFalse );
- sqlite3VdbeAddOp4(v, OP_Ne, r1, destIfFalse, r2,
+ sqlite3VdbeAddOp4(v, OP_Ne, rLhs, destIfFalse, r2,
(void*)pColl, P4_COLLSEQ); VdbeCoverage(v);
- sqlite3VdbeChangeP5(v, affinity | SQLITE_JUMPIFNULL);
+ sqlite3VdbeChangeP5(v, zAff[0] | SQLITE_JUMPIFNULL);
}
sqlite3ReleaseTempReg(pParse, regToFree);
}
@@ -91674,77 +92508,113 @@ static void sqlite3ExprCodeIN(
}
sqlite3VdbeResolveLabel(v, labelOk);
sqlite3ReleaseTempReg(pParse, regCkNull);
+ goto sqlite3ExprCodeIN_finished;
+ }
+
+ /* Step 2: Check to see if the LHS contains any NULL columns. If the
+ ** LHS does contain NULLs then the result must be either FALSE or NULL.
+ ** We will then skip the binary search of the RHS.
+ */
+ if( destIfNull==destIfFalse ){
+ destStep2 = destIfFalse;
}else{
-
- /* If the LHS is NULL, then the result is either false or NULL depending
- ** on whether the RHS is empty or not, respectively.
- */
- if( sqlite3ExprCanBeNull(pExpr->pLeft) ){
- if( destIfNull==destIfFalse ){
- /* Shortcut for the common case where the false and NULL outcomes are
- ** the same. */
- sqlite3VdbeAddOp2(v, OP_IsNull, r1, destIfNull); VdbeCoverage(v);
- }else{
- int addr1 = sqlite3VdbeAddOp1(v, OP_NotNull, r1); VdbeCoverage(v);
- sqlite3VdbeAddOp2(v, OP_Rewind, pExpr->iTable, destIfFalse);
- VdbeCoverage(v);
- sqlite3VdbeGoto(v, destIfNull);
- sqlite3VdbeJumpHere(v, addr1);
- }
- }
-
- if( eType==IN_INDEX_ROWID ){
- /* In this case, the RHS is the ROWID of table b-tree
- */
- sqlite3VdbeAddOp3(v, OP_SeekRowid, pExpr->iTable, destIfFalse, r1);
+ destStep2 = destStep6 = sqlite3VdbeMakeLabel(v);
+ }
+ for(i=0; i<nVector; i++){
+ Expr *p = sqlite3VectorFieldSubexpr(pExpr->pLeft, i);
+ if( sqlite3ExprCanBeNull(p) ){
+ sqlite3VdbeAddOp2(v, OP_IsNull, rLhs+i, destStep2);
VdbeCoverage(v);
- }else{
- /* In this case, the RHS is an index b-tree.
- */
- sqlite3VdbeAddOp4(v, OP_Affinity, r1, 1, 0, &affinity, 1);
-
- /* If the set membership test fails, then the result of the
- ** "x IN (...)" expression must be either 0 or NULL. If the set
- ** contains no NULL values, then the result is 0. If the set
- ** contains one or more NULL values, then the result of the
- ** expression is also NULL.
- */
- assert( destIfFalse!=destIfNull || rRhsHasNull==0 );
- if( rRhsHasNull==0 ){
- /* This branch runs if it is known at compile time that the RHS
- ** cannot contain NULL values. This happens as the result
- ** of a "NOT NULL" constraint in the database schema.
- **
- ** Also run this branch if NULL is equivalent to FALSE
- ** for this particular IN operator.
- */
- sqlite3VdbeAddOp4Int(v, OP_NotFound, pExpr->iTable, destIfFalse, r1, 1);
- VdbeCoverage(v);
- }else{
- /* In this branch, the RHS of the IN might contain a NULL and
- ** the presence of a NULL on the RHS makes a difference in the
- ** outcome.
- */
- int addr1;
-
- /* First check to see if the LHS is contained in the RHS. If so,
- ** then the answer is TRUE the presence of NULLs in the RHS does
- ** not matter. If the LHS is not contained in the RHS, then the
- ** answer is NULL if the RHS contains NULLs and the answer is
- ** FALSE if the RHS is NULL-free.
- */
- addr1 = sqlite3VdbeAddOp4Int(v, OP_Found, pExpr->iTable, 0, r1, 1);
- VdbeCoverage(v);
- sqlite3VdbeAddOp2(v, OP_IsNull, rRhsHasNull, destIfNull);
- VdbeCoverage(v);
- sqlite3VdbeGoto(v, destIfFalse);
- sqlite3VdbeJumpHere(v, addr1);
- }
}
}
- sqlite3ReleaseTempReg(pParse, r1);
+
+ /* Step 3. The LHS is now known to be non-NULL. Do the binary search
+ ** of the RHS using the LHS as a probe. If found, the result is
+ ** true.
+ */
+ if( eType==IN_INDEX_ROWID ){
+ /* In this case, the RHS is the ROWID of table b-tree and so we also
+ ** know that the RHS is non-NULL. Hence, we combine steps 3 and 4
+ ** into a single opcode. */
+ sqlite3VdbeAddOp3(v, OP_SeekRowid, pExpr->iTable, destIfFalse, rLhs);
+ VdbeCoverage(v);
+ addrTruthOp = sqlite3VdbeAddOp0(v, OP_Goto); /* Return True */
+ }else{
+ sqlite3VdbeAddOp4(v, OP_Affinity, rLhs, nVector, 0, zAff, nVector);
+ if( destIfFalse==destIfNull ){
+ /* Combine Step 3 and Step 5 into a single opcode */
+ sqlite3VdbeAddOp4Int(v, OP_NotFound, pExpr->iTable, destIfFalse,
+ rLhs, nVector); VdbeCoverage(v);
+ goto sqlite3ExprCodeIN_finished;
+ }
+ /* Ordinary Step 3, for the case where FALSE and NULL are distinct */
+ addrTruthOp = sqlite3VdbeAddOp4Int(v, OP_Found, pExpr->iTable, 0,
+ rLhs, nVector); VdbeCoverage(v);
+ }
+
+ /* Step 4. If the RHS is known to be non-NULL and we did not find
+ ** an match on the search above, then the result must be FALSE.
+ */
+ if( rRhsHasNull && nVector==1 ){
+ sqlite3VdbeAddOp2(v, OP_NotNull, rRhsHasNull, destIfFalse);
+ VdbeCoverage(v);
+ }
+
+ /* Step 5. If we do not care about the difference between NULL and
+ ** FALSE, then just return false.
+ */
+ if( destIfFalse==destIfNull ) sqlite3VdbeGoto(v, destIfFalse);
+
+ /* Step 6: Loop through rows of the RHS. Compare each row to the LHS.
+ ** If any comparison is NULL, then the result is NULL. If all
+ ** comparisons are FALSE then the final result is FALSE.
+ **
+ ** For a scalar LHS, it is sufficient to check just the first row
+ ** of the RHS.
+ */
+ if( destStep6 ) sqlite3VdbeResolveLabel(v, destStep6);
+ addrTop = sqlite3VdbeAddOp2(v, OP_Rewind, pExpr->iTable, destIfFalse);
+ VdbeCoverage(v);
+ if( nVector>1 ){
+ destNotNull = sqlite3VdbeMakeLabel(v);
+ }else{
+ /* For nVector==1, combine steps 6 and 7 by immediately returning
+ ** FALSE if the first comparison is not NULL */
+ destNotNull = destIfFalse;
+ }
+ for(i=0; i<nVector; i++){
+ Expr *p;
+ CollSeq *pColl;
+ int r3 = sqlite3GetTempReg(pParse);
+ p = sqlite3VectorFieldSubexpr(pLeft, i);
+ pColl = sqlite3ExprCollSeq(pParse, p);
+ sqlite3VdbeAddOp3(v, OP_Column, pExpr->iTable, i, r3);
+ sqlite3VdbeAddOp4(v, OP_Ne, rLhs+i, destNotNull, r3,
+ (void*)pColl, P4_COLLSEQ);
+ VdbeCoverage(v);
+ sqlite3ReleaseTempReg(pParse, r3);
+ }
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, destIfNull);
+ if( nVector>1 ){
+ sqlite3VdbeResolveLabel(v, destNotNull);
+ sqlite3VdbeAddOp2(v, OP_Next, pExpr->iTable, addrTop+1);
+ VdbeCoverage(v);
+
+ /* Step 7: If we reach this point, we know that the result must
+ ** be false. */
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, destIfFalse);
+ }
+
+ /* Jumps here in order to return true. */
+ sqlite3VdbeJumpHere(v, addrTruthOp);
+
+sqlite3ExprCodeIN_finished:
+ if( rLhs!=rLhsOrig ) sqlite3ReleaseTempReg(pParse, rLhs);
sqlite3ExprCachePop(pParse);
VdbeComment((v, "end IN expr"));
+sqlite3ExprCodeIN_oom_error:
+ sqlite3DbFree(pParse->db, aiMap);
+ sqlite3DbFree(pParse->db, zAff);
}
#endif /* SQLITE_OMIT_SUBQUERY */
@@ -91808,32 +92678,19 @@ static void codeInteger(Parse *pParse, Expr *pExpr, int negFlag, int iMem){
}
}
-#if defined(SQLITE_DEBUG)
/*
-** Verify the consistency of the column cache
+** Erase column-cache entry number i
*/
-static int cacheIsValid(Parse *pParse){
- int i, n;
- for(i=n=0; i<SQLITE_N_COLCACHE; i++){
- if( pParse->aColCache[i].iReg>0 ) n++;
- }
- return n==pParse->nColCache;
-}
-#endif
-
-/*
-** Clear a cache entry.
-*/
-static void cacheEntryClear(Parse *pParse, struct yColCache *p){
- if( p->tempReg ){
+static void cacheEntryClear(Parse *pParse, int i){
+ if( pParse->aColCache[i].tempReg ){
if( pParse->nTempReg<ArraySize(pParse->aTempReg) ){
- pParse->aTempReg[pParse->nTempReg++] = p->iReg;
+ pParse->aTempReg[pParse->nTempReg++] = pParse->aColCache[i].iReg;
}
- p->tempReg = 0;
}
- p->iReg = 0;
pParse->nColCache--;
- assert( pParse->db->mallocFailed || cacheIsValid(pParse) );
+ if( i<pParse->nColCache ){
+ pParse->aColCache[i] = pParse->aColCache[pParse->nColCache];
+ }
}
@@ -91863,46 +92720,33 @@ SQLITE_PRIVATE void sqlite3ExprCacheStore(Parse *pParse, int iTab, int iCol, int
** that the object will never already be in cache. Verify this guarantee.
*/
#ifndef NDEBUG
- for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){
- assert( p->iReg==0 || p->iTable!=iTab || p->iColumn!=iCol );
+ for(i=0, p=pParse->aColCache; i<pParse->nColCache; i++, p++){
+ assert( p->iTable!=iTab || p->iColumn!=iCol );
}
#endif
- /* Find an empty slot and replace it */
- for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){
- if( p->iReg==0 ){
- p->iLevel = pParse->iCacheLevel;
- p->iTable = iTab;
- p->iColumn = iCol;
- p->iReg = iReg;
- p->tempReg = 0;
- p->lru = pParse->iCacheCnt++;
- pParse->nColCache++;
- assert( pParse->db->mallocFailed || cacheIsValid(pParse) );
- return;
- }
- }
-
- /* Replace the last recently used */
- minLru = 0x7fffffff;
- idxLru = -1;
- for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){
- if( p->lru<minLru ){
- idxLru = i;
- minLru = p->lru;
+ /* If the cache is already full, delete the least recently used entry */
+ if( pParse->nColCache>=SQLITE_N_COLCACHE ){
+ minLru = 0x7fffffff;
+ idxLru = -1;
+ for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){
+ if( p->lru<minLru ){
+ idxLru = i;
+ minLru = p->lru;
+ }
}
- }
- if( ALWAYS(idxLru>=0) ){
p = &pParse->aColCache[idxLru];
- p->iLevel = pParse->iCacheLevel;
- p->iTable = iTab;
- p->iColumn = iCol;
- p->iReg = iReg;
- p->tempReg = 0;
- p->lru = pParse->iCacheCnt++;
- assert( cacheIsValid(pParse) );
- return;
+ }else{
+ p = &pParse->aColCache[pParse->nColCache++];
}
+
+ /* Add the new entry to the end of the cache */
+ p->iLevel = pParse->iCacheLevel;
+ p->iTable = iTab;
+ p->iColumn = iCol;
+ p->iReg = iReg;
+ p->tempReg = 0;
+ p->lru = pParse->iCacheCnt++;
}
/*
@@ -91910,13 +92754,14 @@ SQLITE_PRIVATE void sqlite3ExprCacheStore(Parse *pParse, int iTab, int iCol, int
** Purge the range of registers from the column cache.
*/
SQLITE_PRIVATE void sqlite3ExprCacheRemove(Parse *pParse, int iReg, int nReg){
- struct yColCache *p;
- if( iReg<=0 || pParse->nColCache==0 ) return;
- p = &pParse->aColCache[SQLITE_N_COLCACHE-1];
- while(1){
- if( p->iReg >= iReg && p->iReg < iReg+nReg ) cacheEntryClear(pParse, p);
- if( p==pParse->aColCache ) break;
- p--;
+ int i = 0;
+ while( i<pParse->nColCache ){
+ struct yColCache *p = &pParse->aColCache[i];
+ if( p->iReg >= iReg && p->iReg < iReg+nReg ){
+ cacheEntryClear(pParse, i);
+ }else{
+ i++;
+ }
}
}
@@ -91940,8 +92785,7 @@ SQLITE_PRIVATE void sqlite3ExprCachePush(Parse *pParse){
** the cache to the state it was in prior the most recent Push.
*/
SQLITE_PRIVATE void sqlite3ExprCachePop(Parse *pParse){
- int i;
- struct yColCache *p;
+ int i = 0;
assert( pParse->iCacheLevel>=1 );
pParse->iCacheLevel--;
#ifdef SQLITE_DEBUG
@@ -91949,9 +92793,11 @@ SQLITE_PRIVATE void sqlite3ExprCachePop(Parse *pParse){
printf("POP to %d\n", pParse->iCacheLevel);
}
#endif
- for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){
- if( p->iReg && p->iLevel>pParse->iCacheLevel ){
- cacheEntryClear(pParse, p);
+ while( i<pParse->nColCache ){
+ if( pParse->aColCache[i].iLevel>pParse->iCacheLevel ){
+ cacheEntryClear(pParse, i);
+ }else{
+ i++;
}
}
}
@@ -91965,7 +92811,7 @@ SQLITE_PRIVATE void sqlite3ExprCachePop(Parse *pParse){
static void sqlite3ExprCachePinRegister(Parse *pParse, int iReg){
int i;
struct yColCache *p;
- for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){
+ for(i=0, p=pParse->aColCache; i<pParse->nColCache; i++, p++){
if( p->iReg==iReg ){
p->tempReg = 0;
}
@@ -92043,8 +92889,8 @@ SQLITE_PRIVATE int sqlite3ExprCodeGetColumn(
int i;
struct yColCache *p;
- for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){
- if( p->iReg>0 && p->iTable==iTable && p->iColumn==iColumn ){
+ for(i=0, p=pParse->aColCache; i<pParse->nColCache; i++, p++){
+ if( p->iTable==iTable && p->iColumn==iColumn ){
p->lru = pParse->iCacheCnt++;
sqlite3ExprCachePinRegister(pParse, p->iReg);
return p->iReg;
@@ -92076,18 +92922,20 @@ SQLITE_PRIVATE void sqlite3ExprCodeGetColumnToReg(
*/
SQLITE_PRIVATE void sqlite3ExprCacheClear(Parse *pParse){
int i;
- struct yColCache *p;
#if SQLITE_DEBUG
if( pParse->db->flags & SQLITE_VdbeAddopTrace ){
printf("CLEAR\n");
}
#endif
- for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){
- if( p->iReg ){
- cacheEntryClear(pParse, p);
+ for(i=0; i<pParse->nColCache; i++){
+ if( pParse->aColCache[i].tempReg
+ && pParse->nTempReg<ArraySize(pParse->aTempReg)
+ ){
+ pParse->aTempReg[pParse->nTempReg++] = pParse->aColCache[i].iReg;
}
}
+ pParse->nColCache = 0;
}
/*
@@ -92119,7 +92967,7 @@ SQLITE_PRIVATE void sqlite3ExprCodeMove(Parse *pParse, int iFrom, int iTo, int n
static int usedAsColumnCache(Parse *pParse, int iFrom, int iTo){
int i;
struct yColCache *p;
- for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){
+ for(i=0, p=pParse->aColCache; i<pParse->nColCache; i++, p++){
int r = p->iReg;
if( r>=iFrom && r<=iTo ) return 1; /*NO_TEST*/
}
@@ -92129,7 +92977,9 @@ static int usedAsColumnCache(Parse *pParse, int iFrom, int iTo){
/*
-** Convert an expression node to a TK_REGISTER
+** Convert a scalar expression node to a TK_REGISTER referencing
+** register iReg. The caller must ensure that iReg already contains
+** the correct value for the expression.
*/
static void exprToRegister(Expr *p, int iReg){
p->op2 = p->op;
@@ -92139,6 +92989,38 @@ static void exprToRegister(Expr *p, int iReg){
}
/*
+** Evaluate an expression (either a vector or a scalar expression) and store
+** the result in continguous temporary registers. Return the index of
+** the first register used to store the result.
+**
+** If the returned result register is a temporary scalar, then also write
+** that register number into *piFreeable. If the returned result register
+** is not a temporary or if the expression is a vector set *piFreeable
+** to 0.
+*/
+static int exprCodeVector(Parse *pParse, Expr *p, int *piFreeable){
+ int iResult;
+ int nResult = sqlite3ExprVectorSize(p);
+ if( nResult==1 ){
+ iResult = sqlite3ExprCodeTemp(pParse, p, piFreeable);
+ }else{
+ *piFreeable = 0;
+ if( p->op==TK_SELECT ){
+ iResult = sqlite3CodeSubselect(pParse, p, 0, 0);
+ }else{
+ int i;
+ iResult = pParse->nMem+1;
+ pParse->nMem += nResult;
+ for(i=0; i<nResult; i++){
+ sqlite3ExprCode(pParse, p->x.pList->a[i].pExpr, i+iResult);
+ }
+ }
+ }
+ return iResult;
+}
+
+
+/*
** Generate code into the current Vdbe to evaluate the given
** expression. Attempt to store the results in register "target".
** Return the register where results are stored.
@@ -92155,9 +93037,9 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
int inReg = target; /* Results stored in register inReg */
int regFree1 = 0; /* If non-zero free this temporary register */
int regFree2 = 0; /* If non-zero free this temporary register */
- int r1, r2, r3, r4; /* Various register numbers */
- sqlite3 *db = pParse->db; /* The database connection */
+ int r1, r2; /* Various register numbers */
Expr tempX; /* Temporary expression node */
+ int p5 = 0;
assert( target>0 && target<=pParse->nMem );
if( v==0 ){
@@ -92176,12 +93058,11 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
struct AggInfo_col *pCol = &pAggInfo->aCol[pExpr->iAgg];
if( !pAggInfo->directMode ){
assert( pCol->iMem>0 );
- inReg = pCol->iMem;
- break;
+ return pCol->iMem;
}else if( pAggInfo->useSortingIdx ){
sqlite3VdbeAddOp3(v, OP_Column, pAggInfo->sortingIdxPTab,
pCol->iSorterColumn, target);
- break;
+ return target;
}
/* Otherwise, fall thru into the TK_COLUMN case */
}
@@ -92190,38 +93071,36 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
if( iTab<0 ){
if( pParse->ckBase>0 ){
/* Generating CHECK constraints or inserting into partial index */
- inReg = pExpr->iColumn + pParse->ckBase;
- break;
+ return pExpr->iColumn + pParse->ckBase;
}else{
/* Coding an expression that is part of an index where column names
** in the index refer to the table to which the index belongs */
iTab = pParse->iSelfTab;
}
}
- inReg = sqlite3ExprCodeGetColumn(pParse, pExpr->pTab,
+ return sqlite3ExprCodeGetColumn(pParse, pExpr->pTab,
pExpr->iColumn, iTab, target,
pExpr->op2);
- break;
}
case TK_INTEGER: {
codeInteger(pParse, pExpr, 0, target);
- break;
+ return target;
}
#ifndef SQLITE_OMIT_FLOATING_POINT
case TK_FLOAT: {
assert( !ExprHasProperty(pExpr, EP_IntValue) );
codeReal(v, pExpr->u.zToken, 0, target);
- break;
+ return target;
}
#endif
case TK_STRING: {
assert( !ExprHasProperty(pExpr, EP_IntValue) );
sqlite3VdbeLoadString(v, target, pExpr->u.zToken);
- break;
+ return target;
}
case TK_NULL: {
sqlite3VdbeAddOp2(v, OP_Null, 0, target);
- break;
+ return target;
}
#ifndef SQLITE_OMIT_BLOB_LITERAL
case TK_BLOB: {
@@ -92236,7 +93115,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
assert( z[n]=='\'' );
zBlob = sqlite3HexToBlob(sqlite3VdbeDb(v), z, n);
sqlite3VdbeAddOp4(v, OP_Blob, n/2, target, 0, zBlob, P4_DYNAMIC);
- break;
+ return target;
}
#endif
case TK_VARIABLE: {
@@ -92249,11 +93128,10 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
|| strcmp(pExpr->u.zToken, pParse->azVar[pExpr->iColumn-1])==0 );
sqlite3VdbeChangeP4(v, -1, pParse->azVar[pExpr->iColumn-1], P4_STATIC);
}
- break;
+ return target;
}
case TK_REGISTER: {
- inReg = pExpr->iTable;
- break;
+ return pExpr->iTable;
}
#ifndef SQLITE_OMIT_CAST
case TK_CAST: {
@@ -92267,42 +93145,37 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
sqlite3AffinityType(pExpr->u.zToken, 0));
testcase( usedAsColumnCache(pParse, inReg, inReg) );
sqlite3ExprCacheAffinityChange(pParse, inReg, 1);
- break;
+ return inReg;
}
#endif /* SQLITE_OMIT_CAST */
+ case TK_IS:
+ case TK_ISNOT:
+ op = (op==TK_IS) ? TK_EQ : TK_NE;
+ p5 = SQLITE_NULLEQ;
+ /* fall-through */
case TK_LT:
case TK_LE:
case TK_GT:
case TK_GE:
case TK_NE:
case TK_EQ: {
- r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree1);
- r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, &regFree2);
- codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op,
- r1, r2, inReg, SQLITE_STOREP2);
- assert(TK_LT==OP_Lt); testcase(op==OP_Lt); VdbeCoverageIf(v,op==OP_Lt);
- assert(TK_LE==OP_Le); testcase(op==OP_Le); VdbeCoverageIf(v,op==OP_Le);
- assert(TK_GT==OP_Gt); testcase(op==OP_Gt); VdbeCoverageIf(v,op==OP_Gt);
- assert(TK_GE==OP_Ge); testcase(op==OP_Ge); VdbeCoverageIf(v,op==OP_Ge);
- assert(TK_EQ==OP_Eq); testcase(op==OP_Eq); VdbeCoverageIf(v,op==OP_Eq);
- assert(TK_NE==OP_Ne); testcase(op==OP_Ne); VdbeCoverageIf(v,op==OP_Ne);
- testcase( regFree1==0 );
- testcase( regFree2==0 );
- break;
- }
- case TK_IS:
- case TK_ISNOT: {
- testcase( op==TK_IS );
- testcase( op==TK_ISNOT );
- r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree1);
- r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, &regFree2);
- op = (op==TK_IS) ? TK_EQ : TK_NE;
- codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op,
- r1, r2, inReg, SQLITE_STOREP2 | SQLITE_NULLEQ);
- VdbeCoverageIf(v, op==TK_EQ);
- VdbeCoverageIf(v, op==TK_NE);
- testcase( regFree1==0 );
- testcase( regFree2==0 );
+ Expr *pLeft = pExpr->pLeft;
+ if( sqlite3ExprIsVector(pLeft) ){
+ codeVectorCompare(pParse, pExpr, target, op, p5);
+ }else{
+ r1 = sqlite3ExprCodeTemp(pParse, pLeft, &regFree1);
+ r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, &regFree2);
+ codeCompare(pParse, pLeft, pExpr->pRight, op,
+ r1, r2, inReg, SQLITE_STOREP2 | p5);
+ assert(TK_LT==OP_Lt); testcase(op==OP_Lt); VdbeCoverageIf(v,op==OP_Lt);
+ assert(TK_LE==OP_Le); testcase(op==OP_Le); VdbeCoverageIf(v,op==OP_Le);
+ assert(TK_GT==OP_Gt); testcase(op==OP_Gt); VdbeCoverageIf(v,op==OP_Gt);
+ assert(TK_GE==OP_Ge); testcase(op==OP_Ge); VdbeCoverageIf(v,op==OP_Ge);
+ assert(TK_EQ==OP_Eq); testcase(op==OP_Eq); VdbeCoverageIf(v,op==OP_Eq);
+ assert(TK_NE==OP_Ne); testcase(op==OP_Ne); VdbeCoverageIf(v,op==OP_Ne);
+ testcase( regFree1==0 );
+ testcase( regFree2==0 );
+ }
break;
}
case TK_AND:
@@ -92340,10 +93213,12 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
assert( pLeft );
if( pLeft->op==TK_INTEGER ){
codeInteger(pParse, pLeft, 1, target);
+ return target;
#ifndef SQLITE_OMIT_FLOATING_POINT
}else if( pLeft->op==TK_FLOAT ){
assert( !ExprHasProperty(pExpr, EP_IntValue) );
codeReal(v, pLeft->u.zToken, 1, target);
+ return target;
#endif
}else{
tempX.op = TK_INTEGER;
@@ -92354,7 +93229,6 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
sqlite3VdbeAddOp3(v, OP_Subtract, r2, r1, target);
testcase( regFree2==0 );
}
- inReg = target;
break;
}
case TK_BITNOT:
@@ -92363,7 +93237,6 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
assert( TK_NOT==OP_Not ); testcase( op==TK_NOT );
r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree1);
testcase( regFree1==0 );
- inReg = target;
sqlite3VdbeAddOp2(v, op, r1, inReg);
break;
}
@@ -92388,7 +93261,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
assert( !ExprHasProperty(pExpr, EP_IntValue) );
sqlite3ErrorMsg(pParse, "misuse of aggregate: %s()", pExpr->u.zToken);
}else{
- inReg = pInfo->aFunc[pExpr->iAgg].iMem;
+ return pInfo->aFunc[pExpr->iAgg].iMem;
}
break;
}
@@ -92399,6 +93272,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
const char *zId; /* The function name */
u32 constMask = 0; /* Mask of function arguments that are constant */
int i; /* Loop counter */
+ sqlite3 *db = pParse->db; /* The database connection */
u8 enc = ENC(db); /* The text encoding used by this database */
CollSeq *pColl = 0; /* A collating sequence */
@@ -92447,8 +93321,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
*/
if( pDef->funcFlags & SQLITE_FUNC_UNLIKELY ){
assert( nFarg>=1 );
- inReg = sqlite3ExprCodeTarget(pParse, pFarg->a[0].pExpr, target);
- break;
+ return sqlite3ExprCodeTarget(pParse, pFarg->a[0].pExpr, target);
}
for(i=0; i<nFarg; i++){
@@ -92523,16 +93396,27 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
if( nFarg && constMask==0 ){
sqlite3ReleaseTempRange(pParse, r1, nFarg);
}
- break;
+ return target;
}
#ifndef SQLITE_OMIT_SUBQUERY
case TK_EXISTS:
case TK_SELECT: {
+ int nCol;
testcase( op==TK_EXISTS );
testcase( op==TK_SELECT );
- inReg = sqlite3CodeSubselect(pParse, pExpr, 0, 0);
+ if( op==TK_SELECT && (nCol = pExpr->x.pSelect->pEList->nExpr)!=1 ){
+ sqlite3SubselectError(pParse, nCol, 1);
+ }else{
+ return sqlite3CodeSubselect(pParse, pExpr, 0, 0);
+ }
break;
}
+ case TK_SELECT_COLUMN: {
+ if( pExpr->pLeft->iTable==0 ){
+ pExpr->pLeft->iTable = sqlite3CodeSubselect(pParse, pExpr->pLeft, 0, 0);
+ }
+ return pExpr->pLeft->iTable + pExpr->iColumn;
+ }
case TK_IN: {
int destIfFalse = sqlite3VdbeMakeLabel(v);
int destIfNull = sqlite3VdbeMakeLabel(v);
@@ -92542,7 +93426,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
sqlite3VdbeResolveLabel(v, destIfFalse);
sqlite3VdbeAddOp2(v, OP_AddImm, target, 0);
sqlite3VdbeResolveLabel(v, destIfNull);
- break;
+ return target;
}
#endif /* SQLITE_OMIT_SUBQUERY */
@@ -92559,35 +93443,13 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
** Z is stored in pExpr->pList->a[1].pExpr.
*/
case TK_BETWEEN: {
- Expr *pLeft = pExpr->pLeft;
- struct ExprList_item *pLItem = pExpr->x.pList->a;
- Expr *pRight = pLItem->pExpr;
-
- r1 = sqlite3ExprCodeTemp(pParse, pLeft, &regFree1);
- r2 = sqlite3ExprCodeTemp(pParse, pRight, &regFree2);
- testcase( regFree1==0 );
- testcase( regFree2==0 );
- r3 = sqlite3GetTempReg(pParse);
- r4 = sqlite3GetTempReg(pParse);
- codeCompare(pParse, pLeft, pRight, OP_Ge,
- r1, r2, r3, SQLITE_STOREP2); VdbeCoverage(v);
- pLItem++;
- pRight = pLItem->pExpr;
- sqlite3ReleaseTempReg(pParse, regFree2);
- r2 = sqlite3ExprCodeTemp(pParse, pRight, &regFree2);
- testcase( regFree2==0 );
- codeCompare(pParse, pLeft, pRight, OP_Le, r1, r2, r4, SQLITE_STOREP2);
- VdbeCoverage(v);
- sqlite3VdbeAddOp3(v, OP_And, r3, r4, target);
- sqlite3ReleaseTempReg(pParse, r3);
- sqlite3ReleaseTempReg(pParse, r4);
- break;
+ exprCodeBetween(pParse, pExpr, target, 0, 0);
+ return target;
}
case TK_SPAN:
case TK_COLLATE:
case TK_UPLUS: {
- inReg = sqlite3ExprCodeTarget(pParse, pExpr->pLeft, target);
- break;
+ return sqlite3ExprCodeTarget(pParse, pExpr->pLeft, target);
}
case TK_TRIGGER: {
@@ -92646,6 +93508,10 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
break;
}
+ case TK_VECTOR: {
+ sqlite3ErrorMsg(pParse, "row value misused");
+ break;
+ }
/*
** Form A:
@@ -92689,8 +93555,9 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
if( (pX = pExpr->pLeft)!=0 ){
tempX = *pX;
testcase( pX->op==TK_COLUMN );
- exprToRegister(&tempX, sqlite3ExprCodeTemp(pParse, pX, &regFree1));
+ exprToRegister(&tempX, exprCodeVector(pParse, &tempX, &regFree1));
testcase( regFree1==0 );
+ memset(&opCompare, 0, sizeof(opCompare));
opCompare.op = TK_EQ;
opCompare.pLeft = &tempX;
pTest = &opCompare;
@@ -92724,7 +93591,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
}else{
sqlite3VdbeAddOp2(v, OP_Null, 0, target);
}
- assert( db->mallocFailed || pParse->nErr>0
+ assert( pParse->db->mallocFailed || pParse->nErr>0
|| pParse->iCacheLevel==iCacheLevel );
sqlite3VdbeResolveLabel(v, endLabel);
break;
@@ -92969,20 +93836,33 @@ SQLITE_PRIVATE int sqlite3ExprCodeExprList(
**
** Code it as such, taking care to do the common subexpression
** elimination of x.
+**
+** The xJumpIf parameter determines details:
+**
+** NULL: Store the boolean result in reg[dest]
+** sqlite3ExprIfTrue: Jump to dest if true
+** sqlite3ExprIfFalse: Jump to dest if false
+**
+** The jumpIfNull parameter is ignored if xJumpIf is NULL.
*/
static void exprCodeBetween(
Parse *pParse, /* Parsing and code generating context */
Expr *pExpr, /* The BETWEEN expression */
- int dest, /* Jump here if the jump is taken */
- int jumpIfTrue, /* Take the jump if the BETWEEN is true */
+ int dest, /* Jump destination or storage location */
+ void (*xJump)(Parse*,Expr*,int,int), /* Action to take */
int jumpIfNull /* Take the jump if the BETWEEN is NULL */
){
- Expr exprAnd; /* The AND operator in x>=y AND x<=z */
+ Expr exprAnd; /* The AND operator in x>=y AND x<=z */
Expr compLeft; /* The x>=y term */
Expr compRight; /* The x<=z term */
Expr exprX; /* The x subexpression */
int regFree1 = 0; /* Temporary use register */
+
+ memset(&compLeft, 0, sizeof(Expr));
+ memset(&compRight, 0, sizeof(Expr));
+ memset(&exprAnd, 0, sizeof(Expr));
+
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
exprX = *pExpr->pLeft;
exprAnd.op = TK_AND;
@@ -92994,23 +93874,25 @@ static void exprCodeBetween(
compRight.op = TK_LE;
compRight.pLeft = &exprX;
compRight.pRight = pExpr->x.pList->a[1].pExpr;
- exprToRegister(&exprX, sqlite3ExprCodeTemp(pParse, &exprX, &regFree1));
- if( jumpIfTrue ){
- sqlite3ExprIfTrue(pParse, &exprAnd, dest, jumpIfNull);
+ exprToRegister(&exprX, exprCodeVector(pParse, &exprX, &regFree1));
+ if( xJump ){
+ xJump(pParse, &exprAnd, dest, jumpIfNull);
}else{
- sqlite3ExprIfFalse(pParse, &exprAnd, dest, jumpIfNull);
+ exprX.flags |= EP_FromJoin;
+ sqlite3ExprCodeTarget(pParse, &exprAnd, dest);
}
sqlite3ReleaseTempReg(pParse, regFree1);
/* Ensure adequate test coverage */
- testcase( jumpIfTrue==0 && jumpIfNull==0 && regFree1==0 );
- testcase( jumpIfTrue==0 && jumpIfNull==0 && regFree1!=0 );
- testcase( jumpIfTrue==0 && jumpIfNull!=0 && regFree1==0 );
- testcase( jumpIfTrue==0 && jumpIfNull!=0 && regFree1!=0 );
- testcase( jumpIfTrue!=0 && jumpIfNull==0 && regFree1==0 );
- testcase( jumpIfTrue!=0 && jumpIfNull==0 && regFree1!=0 );
- testcase( jumpIfTrue!=0 && jumpIfNull!=0 && regFree1==0 );
- testcase( jumpIfTrue!=0 && jumpIfNull!=0 && regFree1!=0 );
+ testcase( xJump==sqlite3ExprIfTrue && jumpIfNull==0 && regFree1==0 );
+ testcase( xJump==sqlite3ExprIfTrue && jumpIfNull==0 && regFree1!=0 );
+ testcase( xJump==sqlite3ExprIfTrue && jumpIfNull!=0 && regFree1==0 );
+ testcase( xJump==sqlite3ExprIfTrue && jumpIfNull!=0 && regFree1!=0 );
+ testcase( xJump==sqlite3ExprIfFalse && jumpIfNull==0 && regFree1==0 );
+ testcase( xJump==sqlite3ExprIfFalse && jumpIfNull==0 && regFree1!=0 );
+ testcase( xJump==sqlite3ExprIfFalse && jumpIfNull!=0 && regFree1==0 );
+ testcase( xJump==sqlite3ExprIfFalse && jumpIfNull!=0 && regFree1!=0 );
+ testcase( xJump==0 );
}
/*
@@ -93075,6 +93957,7 @@ SQLITE_PRIVATE void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int
case TK_GE:
case TK_NE:
case TK_EQ: {
+ if( sqlite3ExprIsVector(pExpr->pLeft) ) goto default_expr;
testcase( jumpIfNull==0 );
r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree1);
r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, &regFree2);
@@ -93107,7 +93990,7 @@ SQLITE_PRIVATE void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int
}
case TK_BETWEEN: {
testcase( jumpIfNull==0 );
- exprCodeBetween(pParse, pExpr, dest, 1, jumpIfNull);
+ exprCodeBetween(pParse, pExpr, dest, sqlite3ExprIfTrue, jumpIfNull);
break;
}
#ifndef SQLITE_OMIT_SUBQUERY
@@ -93121,6 +94004,7 @@ SQLITE_PRIVATE void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int
}
#endif
default: {
+ default_expr:
if( exprAlwaysTrue(pExpr) ){
sqlite3VdbeGoto(v, dest);
}else if( exprAlwaysFalse(pExpr) ){
@@ -93227,6 +94111,7 @@ SQLITE_PRIVATE void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int
case TK_GE:
case TK_NE:
case TK_EQ: {
+ if( sqlite3ExprIsVector(pExpr->pLeft) ) goto default_expr;
testcase( jumpIfNull==0 );
r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree1);
r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, &regFree2);
@@ -93257,7 +94142,7 @@ SQLITE_PRIVATE void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int
}
case TK_BETWEEN: {
testcase( jumpIfNull==0 );
- exprCodeBetween(pParse, pExpr, dest, 0, jumpIfNull);
+ exprCodeBetween(pParse, pExpr, dest, sqlite3ExprIfFalse, jumpIfNull);
break;
}
#ifndef SQLITE_OMIT_SUBQUERY
@@ -93273,6 +94158,7 @@ SQLITE_PRIVATE void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int
}
#endif
default: {
+ default_expr:
if( exprAlwaysFalse(pExpr) ){
sqlite3VdbeGoto(v, dest);
}else if( exprAlwaysTrue(pExpr) ){
@@ -93777,7 +94663,7 @@ SQLITE_PRIVATE void sqlite3ReleaseTempReg(Parse *pParse, int iReg){
if( iReg && pParse->nTempReg<ArraySize(pParse->aTempReg) ){
int i;
struct yColCache *p;
- for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){
+ for(i=0, p=pParse->aColCache; i<pParse->nColCache; i++, p++){
if( p->iReg==iReg ){
p->tempReg = 1;
return;
@@ -93788,10 +94674,11 @@ SQLITE_PRIVATE void sqlite3ReleaseTempReg(Parse *pParse, int iReg){
}
/*
-** Allocate or deallocate a block of nReg consecutive registers
+** Allocate or deallocate a block of nReg consecutive registers.
*/
SQLITE_PRIVATE int sqlite3GetTempRange(Parse *pParse, int nReg){
int i, n;
+ if( nReg==1 ) return sqlite3GetTempReg(pParse);
i = pParse->iRangeReg;
n = pParse->nRangeReg;
if( nReg<=n ){
@@ -93805,6 +94692,10 @@ SQLITE_PRIVATE int sqlite3GetTempRange(Parse *pParse, int nReg){
return i;
}
SQLITE_PRIVATE void sqlite3ReleaseTempRange(Parse *pParse, int iReg, int nReg){
+ if( nReg==1 ){
+ sqlite3ReleaseTempReg(pParse, iReg);
+ return;
+ }
sqlite3ExprCacheRemove(pParse, iReg, nReg);
if( nReg>pParse->nRangeReg ){
pParse->nRangeReg = nReg;
@@ -94260,7 +95151,7 @@ SQLITE_PRIVATE void sqlite3AlterRenameTable(
pTab = sqlite3LocateTableItem(pParse, 0, &pSrc->a[0]);
if( !pTab ) goto exit_rename_table;
iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
- zDb = db->aDb[iDb].zName;
+ zDb = db->aDb[iDb].zDbSName;
db->flags |= SQLITE_PreferBuiltin;
/* Get a NULL terminated version of the new table name. */
@@ -94458,7 +95349,7 @@ SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
assert( sqlite3BtreeHoldsAllMutexes(db) );
iDb = sqlite3SchemaToIndex(db, pNew->pSchema);
- zDb = db->aDb[iDb].zName;
+ zDb = db->aDb[iDb].zDbSName;
zTab = &pNew->zName[16]; /* Skip the "sqlite_altertab_" prefix on the name */
pCol = &pNew->aCol[pNew->nCol-1];
pDflt = pCol->pDflt;
@@ -94868,14 +95759,14 @@ static void openStatTable(
for(i=0; i<ArraySize(aTable); i++){
const char *zTab = aTable[i].zName;
Table *pStat;
- if( (pStat = sqlite3FindTable(db, zTab, pDb->zName))==0 ){
+ if( (pStat = sqlite3FindTable(db, zTab, pDb->zDbSName))==0 ){
if( aTable[i].zCols ){
/* The sqlite_statN table does not exist. Create it. Note that a
** side-effect of the CREATE TABLE statement is to leave the rootpage
** of the new table in register pParse->regRoot. This is important
** because the OpenWrite opcode below will be needing it. */
sqlite3NestedParse(pParse,
- "CREATE TABLE %Q.%s(%s)", pDb->zName, zTab, aTable[i].zCols
+ "CREATE TABLE %Q.%s(%s)", pDb->zDbSName, zTab, aTable[i].zCols
);
aRoot[i] = pParse->regRoot;
aCreateTbl[i] = OPFLAG_P2ISREG;
@@ -94890,7 +95781,7 @@ static void openStatTable(
if( zWhere ){
sqlite3NestedParse(pParse,
"DELETE FROM %Q.%s WHERE %s=%Q",
- pDb->zName, zTab, zWhereType, zWhere
+ pDb->zDbSName, zTab, zWhereType, zWhere
);
}else{
/* The sqlite_stat[134] table already exists. Delete all rows. */
@@ -95652,7 +96543,7 @@ static void analyzeOneTable(
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
#ifndef SQLITE_OMIT_AUTHORIZATION
if( sqlite3AuthCheck(pParse, SQLITE_ANALYZE, pTab->zName, 0,
- db->aDb[iDb].zName ) ){
+ db->aDb[iDb].zDbSName ) ){
return;
}
#endif
@@ -96042,7 +96933,7 @@ SQLITE_PRIVATE void sqlite3Analyze(Parse *pParse, Token *pName1, Token *pName2){
/* Form 3: Analyze the fully qualified table name */
iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pTableName);
if( iDb>=0 ){
- zDb = db->aDb[iDb].zName;
+ zDb = db->aDb[iDb].zDbSName;
z = sqlite3NameFromToken(db, pTableName);
if( z ){
if( (pIdx = sqlite3FindIndex(db, z, zDb))!=0 ){
@@ -96502,7 +97393,7 @@ SQLITE_PRIVATE int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
/* Load new statistics out of the sqlite_stat1 table */
sInfo.db = db;
- sInfo.zDatabase = db->aDb[iDb].zName;
+ sInfo.zDatabase = db->aDb[iDb].zDbSName;
if( sqlite3FindTable(db, "sqlite_stat1", sInfo.zDatabase)!=0 ){
zSql = sqlite3MPrintf(db,
"SELECT tbl,idx,stat FROM %Q.sqlite_stat1", sInfo.zDatabase);
@@ -96645,7 +97536,7 @@ static void attachFunc(
goto attach_error;
}
for(i=0; i<db->nDb; i++){
- char *z = db->aDb[i].zName;
+ char *z = db->aDb[i].zDbSName;
assert( z && zName );
if( sqlite3StrICmp(z, zName)==0 ){
zErrDyn = sqlite3MPrintf(db, "database %s is already in use", zName);
@@ -96710,8 +97601,8 @@ static void attachFunc(
sqlite3BtreeLeave(aNew->pBt);
}
aNew->safety_level = SQLITE_DEFAULT_SYNCHRONOUS+1;
- aNew->zName = sqlite3DbStrDup(db, zName);
- if( rc==SQLITE_OK && aNew->zName==0 ){
+ aNew->zDbSName = sqlite3DbStrDup(db, zName);
+ if( rc==SQLITE_OK && aNew->zDbSName==0 ){
rc = SQLITE_NOMEM_BKPT;
}
@@ -96740,7 +97631,7 @@ static void attachFunc(
case SQLITE_NULL:
/* No key specified. Use the key from the main database */
sqlite3CodecGetKey(db, 0, (void**)&zKey, &nKey);
- if( nKey>0 || sqlite3BtreeGetOptimalReserve(db->aDb[0].pBt)>0 ){
+ if( nKey || sqlite3BtreeGetOptimalReserve(db->aDb[0].pBt)>0 ){
rc = sqlite3CodecAttach(db, db->nDb-1, zKey, nKey);
}
break;
@@ -96823,7 +97714,7 @@ static void detachFunc(
for(i=0; i<db->nDb; i++){
pDb = &db->aDb[i];
if( pDb->pBt==0 ) continue;
- if( sqlite3StrICmp(pDb->zName, zName)==0 ) break;
+ if( sqlite3StrICmp(pDb->zDbSName, zName)==0 ) break;
}
if( i>=db->nDb ){
@@ -96981,7 +97872,7 @@ SQLITE_PRIVATE void sqlite3FixInit(
db = pParse->db;
assert( db->nDb>iDb );
pFix->pParse = pParse;
- pFix->zDb = db->aDb[iDb].zName;
+ pFix->zDb = db->aDb[iDb].zDbSName;
pFix->pSchema = db->aDb[iDb].pSchema;
pFix->zType = zType;
pFix->pName = pName;
@@ -97078,7 +97969,7 @@ SQLITE_PRIVATE int sqlite3FixExpr(
return 1;
}
}
- if( ExprHasProperty(pExpr, EP_TokenOnly) ) break;
+ if( ExprHasProperty(pExpr, EP_TokenOnly|EP_Leaf) ) break;
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
if( sqlite3FixSelect(pFix, pExpr->x.pSelect) ) return 1;
}else{
@@ -97239,9 +98130,9 @@ SQLITE_PRIVATE int sqlite3AuthReadCol(
const char *zCol, /* Column name */
int iDb /* Index of containing database. */
){
- sqlite3 *db = pParse->db; /* Database handle */
- char *zDb = db->aDb[iDb].zName; /* Name of attached database */
- int rc; /* Auth callback return code */
+ sqlite3 *db = pParse->db; /* Database handle */
+ char *zDb = db->aDb[iDb].zDbSName; /* Schema name of attached database */
+ int rc; /* Auth callback return code */
if( db->init.busy ) return SQLITE_OK;
rc = db->xAuth(db->pAuthArg, SQLITE_READ, zTab,zCol,zDb,pParse->zAuthContext
@@ -97542,15 +98433,14 @@ SQLITE_PRIVATE void sqlite3FinishCoding(Parse *pParse){
assert( !pParse->isMultiWrite
|| sqlite3VdbeAssertMayAbort(v, pParse->mayAbort));
if( v ){
- while( sqlite3VdbeDeletePriorOpcode(v, OP_Close) ){}
sqlite3VdbeAddOp0(v, OP_Halt);
#if SQLITE_USER_AUTHENTICATION
if( pParse->nTableLock>0 && db->init.busy==0 ){
sqlite3UserAuthInit(db);
if( db->auth.authLevel<UAUTH_User ){
- pParse->rc = SQLITE_AUTH_USER;
sqlite3ErrorMsg(pParse, "user not authenticated");
+ pParse->rc = SQLITE_AUTH_USER;
return;
}
}
@@ -97569,14 +98459,16 @@ SQLITE_PRIVATE void sqlite3FinishCoding(Parse *pParse){
assert( sqlite3VdbeGetOp(v, 0)->opcode==OP_Init );
sqlite3VdbeJumpHere(v, 0);
for(iDb=0; iDb<db->nDb; iDb++){
+ Schema *pSchema;
if( DbMaskTest(pParse->cookieMask, iDb)==0 ) continue;
sqlite3VdbeUsesBtree(v, iDb);
+ pSchema = db->aDb[iDb].pSchema;
sqlite3VdbeAddOp4Int(v,
OP_Transaction, /* Opcode */
iDb, /* P1 */
DbMaskTest(pParse->writeMask,iDb), /* P2 */
- pParse->cookieValue[iDb], /* P3 */
- db->aDb[iDb].pSchema->iGeneration /* P4 */
+ pSchema->schema_cookie, /* P3 */
+ pSchema->iGeneration /* P4 */
);
if( db->init.busy==0 ) sqlite3VdbeChangeP5(v, 1);
VdbeComment((v,
@@ -97627,16 +98519,6 @@ SQLITE_PRIVATE void sqlite3FinishCoding(Parse *pParse){
}else{
pParse->rc = SQLITE_ERROR;
}
-
- /* We are done with this Parse object. There is no need to de-initialize it */
-#if 0
- pParse->colNamesSet = 0;
- pParse->nTab = 0;
- pParse->nMem = 0;
- pParse->nSet = 0;
- pParse->nVar = 0;
- DbMaskZero(pParse->cookieMask);
-#endif
}
/*
@@ -97656,8 +98538,7 @@ SQLITE_PRIVATE void sqlite3NestedParse(Parse *pParse, const char *zFormat, ...){
char *zSql;
char *zErrMsg = 0;
sqlite3 *db = pParse->db;
-# define SAVE_SZ (sizeof(Parse) - offsetof(Parse,nVar))
- char saveBuf[SAVE_SZ];
+ char saveBuf[PARSE_TAIL_SZ];
if( pParse->nErr ) return;
assert( pParse->nested<10 ); /* Nesting should only be of limited depth */
@@ -97668,12 +98549,12 @@ SQLITE_PRIVATE void sqlite3NestedParse(Parse *pParse, const char *zFormat, ...){
return; /* A malloc must have failed */
}
pParse->nested++;
- memcpy(saveBuf, &pParse->nVar, SAVE_SZ);
- memset(&pParse->nVar, 0, SAVE_SZ);
+ memcpy(saveBuf, PARSE_TAIL(pParse), PARSE_TAIL_SZ);
+ memset(PARSE_TAIL(pParse), 0, PARSE_TAIL_SZ);
sqlite3RunParser(pParse, zSql, &zErrMsg);
sqlite3DbFree(db, zErrMsg);
sqlite3DbFree(db, zSql);
- memcpy(&pParse->nVar, saveBuf, SAVE_SZ);
+ memcpy(PARSE_TAIL(pParse), saveBuf, PARSE_TAIL_SZ);
pParse->nested--;
}
@@ -97714,10 +98595,11 @@ SQLITE_PRIVATE Table *sqlite3FindTable(sqlite3 *db, const char *zName, const cha
#endif
for(i=OMIT_TEMPDB; i<db->nDb; i++){
int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */
- if( zDatabase!=0 && sqlite3StrICmp(zDatabase, db->aDb[j].zName) ) continue;
- assert( sqlite3SchemaMutexHeld(db, j, 0) );
- p = sqlite3HashFind(&db->aDb[j].pSchema->tblHash, zName);
- if( p ) break;
+ if( zDatabase==0 || sqlite3StrICmp(zDatabase, db->aDb[j].zDbSName)==0 ){
+ assert( sqlite3SchemaMutexHeld(db, j, 0) );
+ p = sqlite3HashFind(&db->aDb[j].pSchema->tblHash, zName);
+ if( p ) break;
+ }
}
return p;
}
@@ -97791,7 +98673,7 @@ SQLITE_PRIVATE Table *sqlite3LocateTableItem(
assert( p->pSchema==0 || p->zDatabase==0 );
if( p->pSchema ){
int iDb = sqlite3SchemaToIndex(pParse->db, p->pSchema);
- zDb = pParse->db->aDb[iDb].zName;
+ zDb = pParse->db->aDb[iDb].zDbSName;
}else{
zDb = p->zDatabase;
}
@@ -97819,7 +98701,7 @@ SQLITE_PRIVATE Index *sqlite3FindIndex(sqlite3 *db, const char *zName, const cha
int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */
Schema *pSchema = db->aDb[j].pSchema;
assert( pSchema );
- if( zDb && sqlite3StrICmp(zDb, db->aDb[j].zName) ) continue;
+ if( zDb && sqlite3StrICmp(zDb, db->aDb[j].zDbSName) ) continue;
assert( sqlite3SchemaMutexHeld(db, j, 0) );
p = sqlite3HashFind(&pSchema->idxHash, zName);
if( p ) break;
@@ -97888,8 +98770,8 @@ SQLITE_PRIVATE void sqlite3CollapseDatabaseArray(sqlite3 *db){
for(i=j=2; i<db->nDb; i++){
struct Db *pDb = &db->aDb[i];
if( pDb->pBt==0 ){
- sqlite3DbFree(db, pDb->zName);
- pDb->zName = 0;
+ sqlite3DbFree(db, pDb->zDbSName);
+ pDb->zDbSName = 0;
continue;
}
if( j<i ){
@@ -98109,7 +98991,7 @@ SQLITE_PRIVATE int sqlite3FindDbName(sqlite3 *db, const char *zName){
if( zName ){
Db *pDb;
for(i=(db->nDb-1), pDb=&db->aDb[i]; i>=0; i--, pDb--){
- if( 0==sqlite3StrICmp(pDb->zName, zName) ) break;
+ if( 0==sqlite3StrICmp(pDb->zDbSName, zName) ) break;
}
}
return i;
@@ -98168,7 +99050,7 @@ SQLITE_PRIVATE int sqlite3TwoPartName(
return -1;
}
}else{
- assert( db->init.iDb==0 || db->init.busy );
+ assert( db->init.iDb==0 || db->init.busy || (db->flags & SQLITE_Vacuum)!=0);
iDb = db->init.iDb;
*pUnqual = pName1;
}
@@ -98279,7 +99161,7 @@ SQLITE_PRIVATE void sqlite3StartTable(
SQLITE_CREATE_VIEW,
SQLITE_CREATE_TEMP_VIEW
};
- char *zDb = db->aDb[iDb].zName;
+ char *zDb = db->aDb[iDb].zDbSName;
if( sqlite3AuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(isTemp), 0, zDb) ){
goto begin_table_error;
}
@@ -98298,7 +99180,7 @@ SQLITE_PRIVATE void sqlite3StartTable(
** collisions.
*/
if( !IN_DECLARE_VTAB ){
- char *zDb = db->aDb[iDb].zName;
+ char *zDb = db->aDb[iDb].zDbSName;
if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){
goto begin_table_error;
}
@@ -98853,6 +99735,9 @@ SQLITE_PRIVATE CollSeq *sqlite3LocateCollSeq(Parse *pParse, const char *zName){
** set back to prior value. But schema changes are infrequent
** and the probability of hitting the same cookie value is only
** 1 chance in 2^32. So we're safe enough.
+**
+** IMPLEMENTATION-OF: R-34230-56049 SQLite automatically increments
+** the schema-version whenever the schema changes.
*/
SQLITE_PRIVATE void sqlite3ChangeCookie(Parse *pParse, int iDb){
sqlite3 *db = pParse->db;
@@ -99391,7 +100276,7 @@ SQLITE_PRIVATE void sqlite3EndTable(
"UPDATE %Q.%s "
"SET type='%s', name=%Q, tbl_name=%Q, rootpage=#%d, sql=%Q "
"WHERE rowid=#%d",
- db->aDb[iDb].zName, SCHEMA_TABLE(iDb),
+ db->aDb[iDb].zDbSName, SCHEMA_TABLE(iDb),
zType,
p->zName,
p->zName,
@@ -99406,13 +100291,13 @@ SQLITE_PRIVATE void sqlite3EndTable(
/* Check to see if we need to create an sqlite_sequence table for
** keeping track of autoincrement keys.
*/
- if( p->tabFlags & TF_Autoincrement ){
+ if( (p->tabFlags & TF_Autoincrement)!=0 ){
Db *pDb = &db->aDb[iDb];
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
if( pDb->pSchema->pSeqTab==0 ){
sqlite3NestedParse(pParse,
"CREATE TABLE %Q.sqlite_sequence(name,seq)",
- pDb->zName
+ pDb->zDbSName
);
}
}
@@ -99536,7 +100421,9 @@ SQLITE_PRIVATE int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
int nErr = 0; /* Number of errors encountered */
int n; /* Temporarily holds the number of cursors assigned */
sqlite3 *db = pParse->db; /* Database connection for malloc errors */
+#ifndef SQLITE_OMIT_AUTHORIZATION
sqlite3_xauth xAuth; /* Saved xAuth pointer */
+#endif
assert( pTable );
@@ -99726,7 +100613,7 @@ static void destroyRootPage(Parse *pParse, int iTable, int iDb){
*/
sqlite3NestedParse(pParse,
"UPDATE %Q.%s SET rootpage=%d WHERE #%d AND rootpage=#%d",
- pParse->db->aDb[iDb].zName, SCHEMA_TABLE(iDb), iTable, r1, r1);
+ pParse->db->aDb[iDb].zDbSName, SCHEMA_TABLE(iDb), iTable, r1, r1);
#endif
sqlite3ReleaseTempReg(pParse, r1);
}
@@ -99802,7 +100689,7 @@ static void sqlite3ClearStatTables(
const char *zName /* Name of index or table */
){
int i;
- const char *zDbName = pParse->db->aDb[iDb].zName;
+ const char *zDbName = pParse->db->aDb[iDb].zDbSName;
for(i=1; i<=4; i++){
char zTab[24];
sqlite3_snprintf(sizeof(zTab),zTab,"sqlite_stat%d",i);
@@ -99855,7 +100742,7 @@ SQLITE_PRIVATE void sqlite3CodeDropTable(Parse *pParse, Table *pTab, int iDb, in
if( pTab->tabFlags & TF_Autoincrement ){
sqlite3NestedParse(pParse,
"DELETE FROM %Q.sqlite_sequence WHERE name=%Q",
- pDb->zName, pTab->zName
+ pDb->zDbSName, pTab->zName
);
}
#endif
@@ -99869,7 +100756,7 @@ SQLITE_PRIVATE void sqlite3CodeDropTable(Parse *pParse, Table *pTab, int iDb, in
*/
sqlite3NestedParse(pParse,
"DELETE FROM %Q.%s WHERE tbl_name=%Q and type!='trigger'",
- pDb->zName, SCHEMA_TABLE(iDb), pTab->zName);
+ pDb->zDbSName, SCHEMA_TABLE(iDb), pTab->zName);
if( !isView && !IsVirtual(pTab) ){
destroyTable(pParse, pTab);
}
@@ -99923,7 +100810,7 @@ SQLITE_PRIVATE void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView,
{
int code;
const char *zTab = SCHEMA_TABLE(iDb);
- const char *zDb = db->aDb[iDb].zName;
+ const char *zDb = db->aDb[iDb].zDbSName;
const char *zArg2 = 0;
if( sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb)){
goto exit_drop_table;
@@ -100164,7 +101051,7 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
#ifndef SQLITE_OMIT_AUTHORIZATION
if( sqlite3AuthCheck(pParse, SQLITE_REINDEX, pIndex->zName, 0,
- db->aDb[iDb].zName ) ){
+ db->aDb[iDb].zDbSName ) ){
return;
}
#endif
@@ -100416,7 +101303,7 @@ SQLITE_PRIVATE void sqlite3CreateIndex(
goto exit_create_index;
}
}
- if( sqlite3FindIndex(db, zName, pDb->zName)!=0 ){
+ if( sqlite3FindIndex(db, zName, pDb->zDbSName)!=0 ){
if( !ifNotExist ){
sqlite3ErrorMsg(pParse, "index %s already exists", zName);
}else{
@@ -100446,7 +101333,7 @@ SQLITE_PRIVATE void sqlite3CreateIndex(
*/
#ifndef SQLITE_OMIT_AUTHORIZATION
{
- const char *zDb = pDb->zName;
+ const char *zDb = pDb->zDbSName;
if( sqlite3AuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(iDb), 0, zDb) ){
goto exit_create_index;
}
@@ -100761,7 +101648,7 @@ SQLITE_PRIVATE void sqlite3CreateIndex(
*/
sqlite3NestedParse(pParse,
"INSERT INTO %Q.%s VALUES('index',%Q,%Q,#%d,%Q);",
- db->aDb[iDb].zName, SCHEMA_TABLE(iDb),
+ db->aDb[iDb].zDbSName, SCHEMA_TABLE(iDb),
pIndex->zName,
pTab->zName,
iMem,
@@ -100895,7 +101782,7 @@ SQLITE_PRIVATE void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists
{
int code = SQLITE_DROP_INDEX;
Table *pTab = pIndex->pTable;
- const char *zDb = db->aDb[iDb].zName;
+ const char *zDb = db->aDb[iDb].zDbSName;
const char *zTab = SCHEMA_TABLE(iDb);
if( sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb) ){
goto exit_drop_index;
@@ -100913,7 +101800,7 @@ SQLITE_PRIVATE void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists
sqlite3BeginWriteOperation(pParse, 1, iDb);
sqlite3NestedParse(pParse,
"DELETE FROM %Q.%s WHERE name=%Q AND type='index'",
- db->aDb[iDb].zName, SCHEMA_TABLE(iDb), pIndex->zName
+ db->aDb[iDb].zDbSName, SCHEMA_TABLE(iDb), pIndex->zName
);
sqlite3ClearStatTables(pParse, iDb, "idx", pIndex->zName);
sqlite3ChangeCookie(pParse, iDb);
@@ -101434,15 +102321,13 @@ SQLITE_PRIVATE int sqlite3OpenTempDatabase(Parse *pParse){
*/
SQLITE_PRIVATE void sqlite3CodeVerifySchema(Parse *pParse, int iDb){
Parse *pToplevel = sqlite3ParseToplevel(pParse);
- sqlite3 *db = pToplevel->db;
- assert( iDb>=0 && iDb<db->nDb );
- assert( db->aDb[iDb].pBt!=0 || iDb==1 );
+ assert( iDb>=0 && iDb<pParse->db->nDb );
+ assert( pParse->db->aDb[iDb].pBt!=0 || iDb==1 );
assert( iDb<SQLITE_MAX_ATTACHED+2 );
- assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
+ assert( sqlite3SchemaMutexHeld(pParse->db, iDb, 0) );
if( DbMaskTest(pToplevel->cookieMask, iDb)==0 ){
DbMaskSet(pToplevel->cookieMask, iDb);
- pToplevel->cookieValue[iDb] = db->aDb[iDb].pSchema->schema_cookie;
if( !OMIT_TEMPDB && iDb==1 ){
sqlite3OpenTempDatabase(pToplevel);
}
@@ -101458,7 +102343,7 @@ SQLITE_PRIVATE void sqlite3CodeVerifyNamedSchema(Parse *pParse, const char *zDb)
int i;
for(i=0; i<db->nDb; i++){
Db *pDb = &db->aDb[i];
- if( pDb->pBt && (!zDb || 0==sqlite3StrICmp(zDb, pDb->zName)) ){
+ if( pDb->pBt && (!zDb || 0==sqlite3StrICmp(zDb, pDb->zDbSName)) ){
sqlite3CodeVerifySchema(pParse, i);
}
}
@@ -101705,7 +102590,7 @@ SQLITE_PRIVATE void sqlite3Reindex(Parse *pParse, Token *pName1, Token *pName2){
if( iDb<0 ) return;
z = sqlite3NameFromToken(db, pObjName);
if( z==0 ) return;
- zDb = db->aDb[iDb].zName;
+ zDb = db->aDb[iDb].zDbSName;
pTab = sqlite3FindTable(db, z, zDb);
if( pTab ){
reindexTable(pParse, pTab, 0);
@@ -102419,7 +103304,7 @@ SQLITE_PRIVATE void sqlite3MaterializeView(
if( pFrom ){
assert( pFrom->nSrc==1 );
pFrom->a[0].zName = sqlite3DbStrDup(db, pView->zName);
- pFrom->a[0].zDatabase = sqlite3DbStrDup(db, db->aDb[iDb].zName);
+ pFrom->a[0].zDatabase = sqlite3DbStrDup(db, db->aDb[iDb].zDbSName);
assert( pFrom->a[0].pOn==0 );
assert( pFrom->a[0].pUsing==0 );
}
@@ -102529,7 +103414,6 @@ SQLITE_PRIVATE void sqlite3DeleteFrom(
){
Vdbe *v; /* The virtual database engine */
Table *pTab; /* The table from which records will be deleted */
- const char *zDb; /* Name of database holding pTab */
int i; /* Loop counter */
WhereInfo *pWInfo; /* Information about the WHERE clause */
Index *pIdx; /* For looping over indices of the table */
@@ -102606,8 +103490,8 @@ SQLITE_PRIVATE void sqlite3DeleteFrom(
}
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
assert( iDb<db->nDb );
- zDb = db->aDb[iDb].zName;
- rcauth = sqlite3AuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0, zDb);
+ rcauth = sqlite3AuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0,
+ db->aDb[iDb].zDbSName);
assert( rcauth==SQLITE_OK || rcauth==SQLITE_DENY || rcauth==SQLITE_IGNORE );
if( rcauth==SQLITE_DENY ){
goto delete_from_cleanup;
@@ -102791,7 +103675,7 @@ SQLITE_PRIVATE void sqlite3DeleteFrom(
if( !isView ){
int iAddrOnce = 0;
if( eOnePass==ONEPASS_MULTI ){
- iAddrOnce = sqlite3CodeOnce(pParse); VdbeCoverage(v);
+ iAddrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
}
testcase( IsVirtual(pTab) );
sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, OPFLAG_FORDELETE,
@@ -105938,7 +106822,7 @@ SQLITE_PRIVATE void sqlite3FkCheck(
if( (db->flags&SQLITE_ForeignKeys)==0 ) return;
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
- zDb = db->aDb[iDb].zName;
+ zDb = db->aDb[iDb].zDbSName;
/* Loop through all the foreign key constraints for which pTab is the
** child table (the table that the foreign key definition is part of). */
@@ -106309,10 +107193,10 @@ static Trigger *fkActionTrigger(
if( pDflt ){
pNew = sqlite3ExprDup(db, pDflt, 0);
}else{
- pNew = sqlite3PExpr(pParse, TK_NULL, 0, 0, 0);
+ pNew = sqlite3ExprAlloc(db, TK_NULL, 0, 0);
}
}else{
- pNew = sqlite3PExpr(pParse, TK_NULL, 0, 0, 0);
+ pNew = sqlite3ExprAlloc(db, TK_NULL, 0, 0);
}
pList = sqlite3ExprListAppend(pParse, pList, pNew);
sqlite3ExprListSetName(pParse, pList, &tFromCol, 0);
@@ -106678,7 +107562,9 @@ static int readsTable(Parse *p, int iDb, Table *pTab){
/*
** Locate or create an AutoincInfo structure associated with table pTab
** which is in database iDb. Return the register number for the register
-** that holds the maximum rowid.
+** that holds the maximum rowid. Return zero if pTab is not an AUTOINCREMENT
+** table. (Also return zero when doing a VACUUM since we do not want to
+** update the AUTOINCREMENT counters during a VACUUM.)
**
** There is at most one AutoincInfo structure per table even if the
** same table is autoincremented multiple times due to inserts within
@@ -106701,7 +107587,9 @@ static int autoIncBegin(
Table *pTab /* The table we are writing to */
){
int memId = 0; /* Register holding maximum rowid */
- if( pTab->tabFlags & TF_Autoincrement ){
+ if( (pTab->tabFlags & TF_Autoincrement)!=0
+ && (pParse->db->flags & SQLITE_Vacuum)==0
+ ){
Parse *pToplevel = sqlite3ParseToplevel(pParse);
AutoincInfo *pInfo;
@@ -106959,7 +107847,6 @@ SQLITE_PRIVATE void sqlite3Insert(
sqlite3 *db; /* The main database structure */
Table *pTab; /* The table to insert into. aka TABLE */
char *zTab; /* Name of the table into which we are inserting */
- const char *zDb; /* Name of the database holding this table */
int i, j, idx; /* Loop counters */
Vdbe *v; /* Generate code into this virtual machine */
Index *pIdx; /* For looping over indices of the table */
@@ -106974,7 +107861,6 @@ SQLITE_PRIVATE void sqlite3Insert(
int addrCont = 0; /* Top of insert loop. Label "C" in templates 3 and 4 */
SelectDest dest; /* Destination for SELECT on rhs of INSERT */
int iDb; /* Index of database holding TABLE */
- Db *pDb; /* The database containing table being inserted into */
u8 useTempTable = 0; /* Store SELECT results in intermediate table */
u8 appendFlag = 0; /* True if the insert is likely to be an append */
u8 withoutRowid; /* 0 for normal table. 1 for WITHOUT ROWID table */
@@ -107024,9 +107910,8 @@ SQLITE_PRIVATE void sqlite3Insert(
}
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
assert( iDb<db->nDb );
- pDb = &db->aDb[iDb];
- zDb = pDb->zName;
- if( sqlite3AuthCheck(pParse, SQLITE_INSERT, pTab->zName, 0, zDb) ){
+ if( sqlite3AuthCheck(pParse, SQLITE_INSERT, pTab->zName, 0,
+ db->aDb[iDb].zDbSName) ){
goto insert_cleanup;
}
withoutRowid = !HasRowid(pTab);
@@ -108654,6 +109539,7 @@ static int xferOptimization(
sqlite3ReleaseTempReg(pParse, regRowid);
sqlite3ReleaseTempReg(pParse, regData);
if( emptyDestTest ){
+ sqlite3AutoincrementEnd(pParse);
sqlite3VdbeAddOp2(v, OP_Halt, SQLITE_OK, 0);
sqlite3VdbeJumpHere(v, emptyDestTest);
sqlite3VdbeAddOp2(v, OP_Close, iDest, 0);
@@ -109999,18 +110885,7 @@ SQLITE_API int sqlite3_enable_load_extension(sqlite3 *db, int onoff){
return SQLITE_OK;
}
-#endif /* SQLITE_OMIT_LOAD_EXTENSION */
-
-/*
-** The auto-extension code added regardless of whether or not extension
-** loading is supported. We need a dummy sqlite3Apis pointer for that
-** code if regular extension loading is not available. This is that
-** dummy pointer.
-*/
-#ifdef SQLITE_OMIT_LOAD_EXTENSION
-static const sqlite3_api_routines sqlite3Apis = { 0 };
-#endif
-
+#endif /* !defined(SQLITE_OMIT_LOAD_EXTENSION) */
/*
** The following object holds the list of automatically loaded
@@ -110155,6 +111030,11 @@ SQLITE_PRIVATE void sqlite3AutoLoadExtensions(sqlite3 *db){
#if SQLITE_THREADSAFE
sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
#endif
+#ifdef SQLITE_OMIT_LOAD_EXTENSION
+ const sqlite3_api_routines *pThunk = 0;
+#else
+ const sqlite3_api_routines *pThunk = &sqlite3Apis;
+#endif
sqlite3_mutex_enter(mutex);
if( i>=wsdAutoext.nExt ){
xInit = 0;
@@ -110164,7 +111044,7 @@ SQLITE_PRIVATE void sqlite3AutoLoadExtensions(sqlite3 *db){
}
sqlite3_mutex_leave(mutex);
zErrmsg = 0;
- if( xInit && (rc = xInit(db, &zErrmsg, &sqlite3Apis))!=0 ){
+ if( xInit && (rc = xInit(db, &zErrmsg, pThunk))!=0 ){
sqlite3ErrorWithMsg(db, rc,
"automatic extension loading failed: %s", zErrmsg);
go = 0;
@@ -110983,7 +111863,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
}
assert( pId2 );
- zDb = pId2->n>0 ? pDb->zName : 0;
+ zDb = pId2->n>0 ? pDb->zDbSName : 0;
if( sqlite3AuthCheck(pParse, SQLITE_PRAGMA, zLeft, zRight, zDb) ){
goto pragma_out;
}
@@ -111836,10 +112716,10 @@ SQLITE_PRIVATE void sqlite3Pragma(
setAllColumnNames(v, 3, azCol); assert( 3==ArraySize(azCol) );
for(i=0; i<db->nDb; i++){
if( db->aDb[i].pBt==0 ) continue;
- assert( db->aDb[i].zName!=0 );
+ assert( db->aDb[i].zDbSName!=0 );
sqlite3VdbeMultiLoad(v, 1, "iss",
i,
- db->aDb[i].zName,
+ db->aDb[i].zDbSName,
sqlite3BtreeGetFilename(db->aDb[i].pBt));
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 3);
}
@@ -112128,7 +113008,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
sqlite3VdbeChangeP5(v, (u8)i);
addr = sqlite3VdbeAddOp1(v, OP_IsNull, 2); VdbeCoverage(v);
sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0,
- sqlite3MPrintf(db, "*** in database %s ***\n", db->aDb[i].zName),
+ sqlite3MPrintf(db, "*** in database %s ***\n", db->aDb[i].zDbSName),
P4_DYNAMIC);
sqlite3VdbeAddOp3(v, OP_Move, 2, 4, 1);
sqlite3VdbeAddOp3(v, OP_Concat, 4, 3, 2);
@@ -112567,15 +113447,15 @@ SQLITE_PRIVATE void sqlite3Pragma(
Btree *pBt;
const char *zState = "unknown";
int j;
- if( db->aDb[i].zName==0 ) continue;
+ if( db->aDb[i].zDbSName==0 ) continue;
pBt = db->aDb[i].pBt;
if( pBt==0 || sqlite3BtreePager(pBt)==0 ){
zState = "closed";
- }else if( sqlite3_file_control(db, i ? db->aDb[i].zName : 0,
+ }else if( sqlite3_file_control(db, i ? db->aDb[i].zDbSName : 0,
SQLITE_FCNTL_LOCKSTATE, &j)==SQLITE_OK ){
zState = azLockName[j];
}
- sqlite3VdbeMultiLoad(v, 1, "ss", db->aDb[i].zName, zState);
+ sqlite3VdbeMultiLoad(v, 1, "ss", db->aDb[i].zDbSName, zState);
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 2);
}
break;
@@ -112711,6 +113591,7 @@ SQLITE_PRIVATE int sqlite3InitCallback(void *pInit, int argc, char **argv, char
** structures that describe the table, index, or view.
*/
int rc;
+ u8 saved_iDb = db->init.iDb;
sqlite3_stmt *pStmt;
TESTONLY(int rcp); /* Return code from sqlite3_prepare() */
@@ -112721,7 +113602,8 @@ SQLITE_PRIVATE int sqlite3InitCallback(void *pInit, int argc, char **argv, char
TESTONLY(rcp = ) sqlite3_prepare(db, argv[2], -1, &pStmt, 0);
rc = db->errCode;
assert( (rc&0xFF)==(rcp&0xFF) );
- db->init.iDb = 0;
+ db->init.iDb = saved_iDb;
+ assert( saved_iDb==0 || (db->flags & SQLITE_Vacuum)!=0 );
if( SQLITE_OK!=rc ){
if( db->init.orphanTrigger ){
assert( iDb==1 );
@@ -112745,7 +113627,7 @@ SQLITE_PRIVATE int sqlite3InitCallback(void *pInit, int argc, char **argv, char
** to do here is record the root page number for that index.
*/
Index *pIndex;
- pIndex = sqlite3FindIndex(db, argv[0], db->aDb[iDb].zName);
+ pIndex = sqlite3FindIndex(db, argv[0], db->aDb[iDb].zDbSName);
if( pIndex==0 ){
/* This can occur if there exists an index on a TEMP table which
** has the same name as another index on a permanent index. Since
@@ -112924,7 +113806,7 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
char *zSql;
zSql = sqlite3MPrintf(db,
"SELECT name, rootpage, sql FROM \"%w\".%s ORDER BY rowid",
- db->aDb[iDb].zName, zMasterName);
+ db->aDb[iDb].zDbSName, zMasterName);
#ifndef SQLITE_OMIT_AUTHORIZATION
{
sqlite3_xauth xAuth;
@@ -113154,18 +114036,14 @@ static int sqlite3Prepare(
sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */
const char **pzTail /* OUT: End of parsed string */
){
- Parse *pParse; /* Parsing context */
char *zErrMsg = 0; /* Error message */
int rc = SQLITE_OK; /* Result code */
int i; /* Loop counter */
+ Parse sParse; /* Parsing context */
- /* Allocate the parsing context */
- pParse = sqlite3StackAllocZero(db, sizeof(*pParse));
- if( pParse==0 ){
- rc = SQLITE_NOMEM_BKPT;
- goto end_prepare;
- }
- pParse->pReprepare = pReprepare;
+ memset(&sParse, 0, PARSE_HDR_SZ);
+ memset(PARSE_TAIL(&sParse), 0, PARSE_TAIL_SZ);
+ sParse.pReprepare = pReprepare;
assert( ppStmt && *ppStmt==0 );
/* assert( !db->mallocFailed ); // not true with SQLITE_USE_ALLOCA */
assert( sqlite3_mutex_held(db->mutex) );
@@ -113199,7 +114077,7 @@ static int sqlite3Prepare(
assert( sqlite3BtreeHoldsMutex(pBt) );
rc = sqlite3BtreeSchemaLocked(pBt);
if( rc ){
- const char *zDb = db->aDb[i].zName;
+ const char *zDb = db->aDb[i].zDbSName;
sqlite3ErrorWithMsg(db, rc, "database schema is locked: %s", zDb);
testcase( db->flags & SQLITE_ReadUncommitted );
goto end_prepare;
@@ -113209,8 +114087,7 @@ static int sqlite3Prepare(
sqlite3VtabUnlockList(db);
- pParse->db = db;
- pParse->nQueryLoop = 0; /* Logarithmic, so 0 really means 1 */
+ sParse.db = db;
if( nBytes>=0 && (nBytes==0 || zSql[nBytes-1]!=0) ){
char *zSqlCopy;
int mxLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH];
@@ -113223,61 +114100,61 @@ static int sqlite3Prepare(
}
zSqlCopy = sqlite3DbStrNDup(db, zSql, nBytes);
if( zSqlCopy ){
- sqlite3RunParser(pParse, zSqlCopy, &zErrMsg);
- pParse->zTail = &zSql[pParse->zTail-zSqlCopy];
+ sqlite3RunParser(&sParse, zSqlCopy, &zErrMsg);
+ sParse.zTail = &zSql[sParse.zTail-zSqlCopy];
sqlite3DbFree(db, zSqlCopy);
}else{
- pParse->zTail = &zSql[nBytes];
+ sParse.zTail = &zSql[nBytes];
}
}else{
- sqlite3RunParser(pParse, zSql, &zErrMsg);
+ sqlite3RunParser(&sParse, zSql, &zErrMsg);
}
- assert( 0==pParse->nQueryLoop );
+ assert( 0==sParse.nQueryLoop );
- if( pParse->rc==SQLITE_DONE ) pParse->rc = SQLITE_OK;
- if( pParse->checkSchema ){
- schemaIsValid(pParse);
+ if( sParse.rc==SQLITE_DONE ) sParse.rc = SQLITE_OK;
+ if( sParse.checkSchema ){
+ schemaIsValid(&sParse);
}
if( db->mallocFailed ){
- pParse->rc = SQLITE_NOMEM_BKPT;
+ sParse.rc = SQLITE_NOMEM_BKPT;
}
if( pzTail ){
- *pzTail = pParse->zTail;
+ *pzTail = sParse.zTail;
}
- rc = pParse->rc;
+ rc = sParse.rc;
#ifndef SQLITE_OMIT_EXPLAIN
- if( rc==SQLITE_OK && pParse->pVdbe && pParse->explain ){
+ if( rc==SQLITE_OK && sParse.pVdbe && sParse.explain ){
static const char * const azColName[] = {
"addr", "opcode", "p1", "p2", "p3", "p4", "p5", "comment",
"selectid", "order", "from", "detail"
};
int iFirst, mx;
- if( pParse->explain==2 ){
- sqlite3VdbeSetNumCols(pParse->pVdbe, 4);
+ if( sParse.explain==2 ){
+ sqlite3VdbeSetNumCols(sParse.pVdbe, 4);
iFirst = 8;
mx = 12;
}else{
- sqlite3VdbeSetNumCols(pParse->pVdbe, 8);
+ sqlite3VdbeSetNumCols(sParse.pVdbe, 8);
iFirst = 0;
mx = 8;
}
for(i=iFirst; i<mx; i++){
- sqlite3VdbeSetColName(pParse->pVdbe, i-iFirst, COLNAME_NAME,
+ sqlite3VdbeSetColName(sParse.pVdbe, i-iFirst, COLNAME_NAME,
azColName[i], SQLITE_STATIC);
}
}
#endif
if( db->init.busy==0 ){
- Vdbe *pVdbe = pParse->pVdbe;
- sqlite3VdbeSetSql(pVdbe, zSql, (int)(pParse->zTail-zSql), saveSqlFlag);
+ Vdbe *pVdbe = sParse.pVdbe;
+ sqlite3VdbeSetSql(pVdbe, zSql, (int)(sParse.zTail-zSql), saveSqlFlag);
}
- if( pParse->pVdbe && (rc!=SQLITE_OK || db->mallocFailed) ){
- sqlite3VdbeFinalize(pParse->pVdbe);
+ if( sParse.pVdbe && (rc!=SQLITE_OK || db->mallocFailed) ){
+ sqlite3VdbeFinalize(sParse.pVdbe);
assert(!(*ppStmt));
}else{
- *ppStmt = (sqlite3_stmt*)pParse->pVdbe;
+ *ppStmt = (sqlite3_stmt*)sParse.pVdbe;
}
if( zErrMsg ){
@@ -113288,16 +114165,15 @@ static int sqlite3Prepare(
}
/* Delete any TriggerPrg structures allocated while parsing this statement. */
- while( pParse->pTriggerPrg ){
- TriggerPrg *pT = pParse->pTriggerPrg;
- pParse->pTriggerPrg = pT->pNext;
+ while( sParse.pTriggerPrg ){
+ TriggerPrg *pT = sParse.pTriggerPrg;
+ sParse.pTriggerPrg = pT->pNext;
sqlite3DbFree(db, pT);
}
end_prepare:
- sqlite3ParserReset(pParse);
- sqlite3StackFree(db, pParse);
+ sqlite3ParserReset(&sParse);
rc = sqlite3ApiExit(db, rc);
assert( (rc&db->errMask)==rc );
return rc;
@@ -113585,7 +114461,7 @@ static void clearSelect(sqlite3 *db, Select *p, int bFree){
SQLITE_PRIVATE void sqlite3SelectDestInit(SelectDest *pDest, int eDest, int iParm){
pDest->eDest = (u8)eDest;
pDest->iSDParm = iParm;
- pDest->affSdst = 0;
+ pDest->zAffSdst = 0;
pDest->iSdst = 0;
pDest->nSdst = 0;
}
@@ -114156,30 +115032,6 @@ static void codeDistinct(
sqlite3ReleaseTempReg(pParse, r1);
}
-#ifndef SQLITE_OMIT_SUBQUERY
-/*
-** Generate an error message when a SELECT is used within a subexpression
-** (example: "a IN (SELECT * FROM table)") but it has more than 1 result
-** column. We do this in a subroutine because the error used to occur
-** in multiple places. (The error only occurs in one place now, but we
-** retain the subroutine to minimize code disruption.)
-*/
-static int checkForMultiColumnSelectError(
- Parse *pParse, /* Parse context. */
- SelectDest *pDest, /* Destination of SELECT results */
- int nExpr /* Number of result columns returned by SELECT */
-){
- int eDest = pDest->eDest;
- if( nExpr>1 && (eDest==SRT_Mem || eDest==SRT_Set) ){
- sqlite3ErrorMsg(pParse, "only a single result allowed for "
- "a SELECT that is part of an expression");
- return 1;
- }else{
- return 0;
- }
-}
-#endif
-
/*
** This routine generates the code for the inside of the inner loop
** of a SELECT.
@@ -114389,19 +115241,19 @@ static void selectInnerLoop(
** item into the set table with bogus data.
*/
case SRT_Set: {
- assert( nResultCol==1 );
- pDest->affSdst =
- sqlite3CompareAffinity(pEList->a[0].pExpr, pDest->affSdst);
if( pSort ){
/* At first glance you would think we could optimize out the
** ORDER BY in this case since the order of entries in the set
** does not matter. But there might be a LIMIT clause, in which
** case the order does matter */
- pushOntoSorter(pParse, pSort, p, regResult, regResult, 1, nPrefixReg);
+ pushOntoSorter(
+ pParse, pSort, p, regResult, regResult, nResultCol, nPrefixReg);
}else{
int r1 = sqlite3GetTempReg(pParse);
- sqlite3VdbeAddOp4(v, OP_MakeRecord, regResult,1,r1, &pDest->affSdst, 1);
- sqlite3ExprCacheAffinityChange(pParse, regResult, 1);
+ assert( sqlite3Strlen30(pDest->zAffSdst)==nResultCol );
+ sqlite3VdbeAddOp4(v, OP_MakeRecord, regResult, nResultCol,
+ r1, pDest->zAffSdst, nResultCol);
+ sqlite3ExprCacheAffinityChange(pParse, regResult, nResultCol);
sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, r1);
sqlite3ReleaseTempReg(pParse, r1);
}
@@ -114417,13 +115269,14 @@ static void selectInnerLoop(
}
/* If this is a scalar select that is part of an expression, then
- ** store the results in the appropriate memory cell and break out
- ** of the scan loop.
+ ** store the results in the appropriate memory cell or array of
+ ** memory cells and break out of the scan loop.
*/
case SRT_Mem: {
- assert( nResultCol==1 );
+ assert( nResultCol==pDest->nSdst );
if( pSort ){
- pushOntoSorter(pParse, pSort, p, regResult, regResult, 1, nPrefixReg);
+ pushOntoSorter(
+ pParse, pSort, p, regResult, regResult, nResultCol, nPrefixReg);
}else{
assert( regResult==iParm );
/* The LIMIT clause will jump out of the loop for us */
@@ -114525,7 +115378,7 @@ static void selectInnerLoop(
*/
SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N, int X){
int nExtra = (N+X)*(sizeof(CollSeq*)+1);
- KeyInfo *p = sqlite3DbMallocRaw(db, sizeof(KeyInfo) + nExtra);
+ KeyInfo *p = sqlite3DbMallocRawNN(db, sizeof(KeyInfo) + nExtra);
if( p ){
p->aSortOrder = (u8*)&p->aColl[N+X];
p->nField = (u16)N;
@@ -114738,21 +115591,21 @@ static void generateSortTail(
sqlite3VdbeResolveLabel(v, pSort->labelBkOut);
}
iTab = pSort->iECursor;
- if( eDest==SRT_Output || eDest==SRT_Coroutine ){
+ if( eDest==SRT_Output || eDest==SRT_Coroutine || eDest==SRT_Mem ){
regRowid = 0;
regRow = pDest->iSdst;
nSortData = nColumn;
}else{
regRowid = sqlite3GetTempReg(pParse);
- regRow = sqlite3GetTempReg(pParse);
- nSortData = 1;
+ regRow = sqlite3GetTempRange(pParse, nColumn);
+ nSortData = nColumn;
}
nKey = pOrderBy->nExpr - pSort->nOBSat;
if( pSort->sortFlags & SORTFLAG_UseSorter ){
int regSortOut = ++pParse->nMem;
iSortTab = pParse->nTab++;
if( pSort->labelBkOut ){
- addrOnce = sqlite3CodeOnce(pParse); VdbeCoverage(v);
+ addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
}
sqlite3VdbeAddOp3(v, OP_OpenPseudo, iSortTab, regSortOut, nKey+1+nSortData);
if( addrOnce ) sqlite3VdbeJumpHere(v, addrOnce);
@@ -114780,16 +115633,14 @@ static void generateSortTail(
}
#ifndef SQLITE_OMIT_SUBQUERY
case SRT_Set: {
- assert( nColumn==1 );
- sqlite3VdbeAddOp4(v, OP_MakeRecord, regRow, 1, regRowid,
- &pDest->affSdst, 1);
- sqlite3ExprCacheAffinityChange(pParse, regRow, 1);
+ assert( nColumn==sqlite3Strlen30(pDest->zAffSdst) );
+ sqlite3VdbeAddOp4(v, OP_MakeRecord, regRow, nColumn, regRowid,
+ pDest->zAffSdst, nColumn);
+ sqlite3ExprCacheAffinityChange(pParse, regRow, nColumn);
sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, regRowid);
break;
}
case SRT_Mem: {
- assert( nColumn==1 );
- sqlite3ExprCodeMove(pParse, regRow, iParm, 1);
/* The LIMIT clause will terminate the loop for us */
break;
}
@@ -114808,7 +115659,11 @@ static void generateSortTail(
}
}
if( regRowid ){
- sqlite3ReleaseTempReg(pParse, regRow);
+ if( eDest==SRT_Set ){
+ sqlite3ReleaseTempRange(pParse, regRow, nColumn);
+ }else{
+ sqlite3ReleaseTempReg(pParse, regRow);
+ }
sqlite3ReleaseTempReg(pParse, regRowid);
}
/* The bottom of the loop
@@ -114955,7 +115810,7 @@ static const char *columnTypeImpl(
zOrigTab = pTab->zName;
if( pNC->pParse ){
int iDb = sqlite3SchemaToIndex(pNC->pParse->db, pTab->pSchema);
- zOrigDb = pNC->pParse->db->aDb[iDb].zName;
+ zOrigDb = pNC->pParse->db->aDb[iDb].zDbSName;
}
#else
if( iCol<0 ){
@@ -115310,7 +116165,7 @@ SQLITE_PRIVATE Table *sqlite3ResultSetOfSelect(Parse *pParse, Select *pSelect){
*/
static SQLITE_NOINLINE Vdbe *allocVdbe(Parse *pParse){
Vdbe *v = pParse->pVdbe = sqlite3VdbeCreate(pParse);
- if( v ) sqlite3VdbeAddOp0(v, OP_Init);
+ if( v ) sqlite3VdbeAddOp2(v, OP_Init, 0, 1);
if( pParse->pToplevel==0
&& OptimizationEnabled(pParse->db,SQLITE_FactorOutConst)
){
@@ -116149,18 +117004,15 @@ static int generateOutputSubroutine(
}
#ifndef SQLITE_OMIT_SUBQUERY
- /* If we are creating a set for an "expr IN (SELECT ...)" construct,
- ** then there should be a single item on the stack. Write this
- ** item into the set table with bogus data.
+ /* If we are creating a set for an "expr IN (SELECT ...)".
*/
case SRT_Set: {
int r1;
- assert( pIn->nSdst==1 || pParse->nErr>0 );
- pDest->affSdst =
- sqlite3CompareAffinity(p->pEList->a[0].pExpr, pDest->affSdst);
+ testcase( pIn->nSdst>1 );
r1 = sqlite3GetTempReg(pParse);
- sqlite3VdbeAddOp4(v, OP_MakeRecord, pIn->iSdst, 1, r1, &pDest->affSdst,1);
- sqlite3ExprCacheAffinityChange(pParse, pIn->iSdst, 1);
+ sqlite3VdbeAddOp4(v, OP_MakeRecord, pIn->iSdst, pIn->nSdst,
+ r1, pDest->zAffSdst, pIn->nSdst);
+ sqlite3ExprCacheAffinityChange(pParse, pIn->iSdst, pIn->nSdst);
sqlite3VdbeAddOp2(v, OP_IdxInsert, pDest->iSDParm, r1);
sqlite3ReleaseTempReg(pParse, r1);
break;
@@ -117216,12 +118068,13 @@ static int flattenSubquery(
assert( pParent->pHaving==0 );
pParent->pHaving = pParent->pWhere;
pParent->pWhere = pWhere;
- pParent->pHaving = sqlite3ExprAnd(db, pParent->pHaving,
- sqlite3ExprDup(db, pSub->pHaving, 0));
+ pParent->pHaving = sqlite3ExprAnd(db,
+ sqlite3ExprDup(db, pSub->pHaving, 0), pParent->pHaving
+ );
assert( pParent->pGroupBy==0 );
pParent->pGroupBy = sqlite3ExprListDup(db, pSub->pGroupBy, 0);
}else{
- pParent->pWhere = sqlite3ExprAnd(db, pParent->pWhere, pWhere);
+ pParent->pWhere = sqlite3ExprAnd(db, pWhere, pParent->pWhere);
}
substSelect(db, pParent, iParent, pSub->pEList, 0);
@@ -117911,7 +118764,7 @@ static int selectExpander(Walker *pWalker, Select *p){
continue;
}
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
- zSchemaName = iDb>=0 ? db->aDb[iDb].zName : "*";
+ zSchemaName = iDb>=0 ? db->aDb[iDb].zDbSName : "*";
}
for(j=0; j<pTab->nCol; j++){
char *zName = pTab->aCol[j].zName;
@@ -118394,16 +119247,6 @@ SQLITE_PRIVATE int sqlite3Select(
}
#endif
-
- /* If writing to memory or generating a set
- ** only a single column may be output.
- */
-#ifndef SQLITE_OMIT_SUBQUERY
- if( checkForMultiColumnSelectError(pParse, pDest, p->pEList->nExpr) ){
- goto select_end;
- }
-#endif
-
/* Try to flatten subqueries in the FROM clause up into the main query
*/
#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
@@ -118558,7 +119401,7 @@ SQLITE_PRIVATE int sqlite3Select(
/* If the subquery is not correlated and if we are not inside of
** a trigger, then we only need to compute the value of the subquery
** once. */
- onceAddr = sqlite3CodeOnce(pParse); VdbeCoverage(v);
+ onceAddr = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
VdbeComment((v, "materialize \"%s\"", pItem->pTab->zName));
}else{
VdbeNoopComment((v, "materialize \"%s\"", pItem->pTab->zName));
@@ -119505,7 +120348,6 @@ SQLITE_PRIVATE void sqlite3BeginTrigger(
int iDb; /* The database to store the trigger in */
Token *pName; /* The unqualified db name */
DbFixer sFix; /* State vector for the DB fixer */
- int iTabDb; /* Index of the database holding pTab */
assert( pName1!=0 ); /* pName1->z might be NULL, but not pName1 itself */
assert( pName2!=0 );
@@ -119618,13 +120460,13 @@ SQLITE_PRIVATE void sqlite3BeginTrigger(
" trigger on table: %S", pTableName, 0);
goto trigger_cleanup;
}
- iTabDb = sqlite3SchemaToIndex(db, pTab->pSchema);
#ifndef SQLITE_OMIT_AUTHORIZATION
{
+ int iTabDb = sqlite3SchemaToIndex(db, pTab->pSchema);
int code = SQLITE_CREATE_TRIGGER;
- const char *zDb = db->aDb[iTabDb].zName;
- const char *zDbTrig = isTemp ? db->aDb[1].zName : zDb;
+ const char *zDb = db->aDb[iTabDb].zDbSName;
+ const char *zDbTrig = isTemp ? db->aDb[1].zDbSName : zDb;
if( iTabDb==1 || isTemp ) code = SQLITE_CREATE_TEMP_TRIGGER;
if( sqlite3AuthCheck(pParse, code, zName, pTab->zName, zDbTrig) ){
goto trigger_cleanup;
@@ -119718,7 +120560,7 @@ SQLITE_PRIVATE void sqlite3FinishTrigger(
z = sqlite3DbStrNDup(db, (char*)pAll->z, pAll->n);
sqlite3NestedParse(pParse,
"INSERT INTO %Q.%s VALUES('trigger',%Q,%Q,0,'CREATE TRIGGER %q')",
- db->aDb[iDb].zName, SCHEMA_TABLE(iDb), zName,
+ db->aDb[iDb].zDbSName, SCHEMA_TABLE(iDb), zName,
pTrig->table, z);
sqlite3DbFree(db, z);
sqlite3ChangeCookie(pParse, iDb);
@@ -119907,7 +120749,7 @@ SQLITE_PRIVATE void sqlite3DropTrigger(Parse *pParse, SrcList *pName, int noErr)
assert( zDb!=0 || sqlite3BtreeHoldsAllMutexes(db) );
for(i=OMIT_TEMPDB; i<db->nDb; i++){
int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */
- if( zDb && sqlite3StrICmp(db->aDb[j].zName, zDb) ) continue;
+ if( zDb && sqlite3StrICmp(db->aDb[j].zDbSName, zDb) ) continue;
assert( sqlite3SchemaMutexHeld(db, j, 0) );
pTrigger = sqlite3HashFind(&(db->aDb[j].pSchema->trigHash), zName);
if( pTrigger ) break;
@@ -119953,7 +120795,7 @@ SQLITE_PRIVATE void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger){
#ifndef SQLITE_OMIT_AUTHORIZATION
{
int code = SQLITE_DROP_TRIGGER;
- const char *zDb = db->aDb[iDb].zName;
+ const char *zDb = db->aDb[iDb].zDbSName;
const char *zTab = SCHEMA_TABLE(iDb);
if( iDb==1 ) code = SQLITE_DROP_TEMP_TRIGGER;
if( sqlite3AuthCheck(pParse, code, pTrigger->zName, pTable->zName, zDb) ||
@@ -119969,7 +120811,7 @@ SQLITE_PRIVATE void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger){
if( (v = sqlite3GetVdbe(pParse))!=0 ){
sqlite3NestedParse(pParse,
"DELETE FROM %Q.%s WHERE name=%Q AND type='trigger'",
- db->aDb[iDb].zName, SCHEMA_TABLE(iDb), pTrigger->zName
+ db->aDb[iDb].zDbSName, SCHEMA_TABLE(iDb), pTrigger->zName
);
sqlite3ChangeCookie(pParse, iDb);
sqlite3VdbeAddOp4(v, OP_DropTrigger, iDb, 0, 0, pTrigger->zName, 0);
@@ -120072,8 +120914,10 @@ static SrcList *targetSrcList(
pSrc->a[pSrc->nSrc-1].zName = sqlite3DbStrDup(db, pStep->zTarget);
iDb = sqlite3SchemaToIndex(db, pStep->pTrig->pSchema);
if( iDb==0 || iDb>=2 ){
+ const char *zDb;
assert( iDb<db->nDb );
- pSrc->a[pSrc->nSrc-1].zDatabase = sqlite3DbStrDup(db, db->aDb[iDb].zName);
+ zDb = db->aDb[iDb].zDbSName;
+ pSrc->a[pSrc->nSrc-1].zDatabase = sqlite3DbStrDup(db, zDb);
}
}
return pSrc;
@@ -120287,7 +121131,6 @@ static TriggerPrg *codeRowTrigger(
}
pProgram->nMem = pSubParse->nMem;
pProgram->nCsr = pSubParse->nTab;
- pProgram->nOnce = pSubParse->nOnce;
pProgram->token = (void *)pTrigger;
pPrg->aColmask[0] = pSubParse->oldmask;
pPrg->aColmask[1] = pSubParse->newmask;
@@ -120760,7 +121603,7 @@ SQLITE_PRIVATE void sqlite3Update(
int rc;
rc = sqlite3AuthCheck(pParse, SQLITE_UPDATE, pTab->zName,
j<0 ? "ROWID" : pTab->aCol[j].zName,
- db->aDb[iDb].zName);
+ db->aDb[iDb].zDbSName);
if( rc==SQLITE_DENY ){
goto update_cleanup;
}else if( rc==SQLITE_IGNORE ){
@@ -121362,57 +122205,52 @@ static void updateVirtualTable(
/* #include "vdbeInt.h" */
#if !defined(SQLITE_OMIT_VACUUM) && !defined(SQLITE_OMIT_ATTACH)
-/*
-** Finalize a prepared statement. If there was an error, store the
-** text of the error message in *pzErrMsg. Return the result code.
-*/
-static int vacuumFinalize(sqlite3 *db, sqlite3_stmt *pStmt, char **pzErrMsg){
- int rc;
- rc = sqlite3VdbeFinalize((Vdbe*)pStmt);
- if( rc ){
- sqlite3SetString(pzErrMsg, db, sqlite3_errmsg(db));
- }
- return rc;
-}
/*
-** Execute zSql on database db. Return an error code.
+** Execute zSql on database db.
+**
+** If zSql returns rows, then each row will have exactly one
+** column. (This will only happen if zSql begins with "SELECT".)
+** Take each row of result and call execSql() again recursively.
+**
+** The execSqlF() routine does the same thing, except it accepts
+** a format string as its third argument
*/
static int execSql(sqlite3 *db, char **pzErrMsg, const char *zSql){
sqlite3_stmt *pStmt;
- VVA_ONLY( int rc; )
- if( !zSql ){
- return SQLITE_NOMEM_BKPT;
- }
- if( SQLITE_OK!=sqlite3_prepare(db, zSql, -1, &pStmt, 0) ){
- sqlite3SetString(pzErrMsg, db, sqlite3_errmsg(db));
- return sqlite3_errcode(db);
- }
- VVA_ONLY( rc = ) sqlite3_step(pStmt);
- assert( rc!=SQLITE_ROW || (db->flags&SQLITE_CountRows) );
- return vacuumFinalize(db, pStmt, pzErrMsg);
-}
-
-/*
-** Execute zSql on database db. The statement returns exactly
-** one column. Execute this as SQL on the same database.
-*/
-static int execExecSql(sqlite3 *db, char **pzErrMsg, const char *zSql){
- sqlite3_stmt *pStmt;
int rc;
- rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0);
+ /* printf("SQL: [%s]\n", zSql); fflush(stdout); */
+ rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
if( rc!=SQLITE_OK ) return rc;
-
- while( SQLITE_ROW==sqlite3_step(pStmt) ){
- rc = execSql(db, pzErrMsg, (char*)sqlite3_column_text(pStmt, 0));
- if( rc!=SQLITE_OK ){
- vacuumFinalize(db, pStmt, pzErrMsg);
- return rc;
+ while( SQLITE_ROW==(rc = sqlite3_step(pStmt)) ){
+ const char *zSubSql = (const char*)sqlite3_column_text(pStmt,0);
+ assert( sqlite3_strnicmp(zSql,"SELECT",6)==0 );
+ if( zSubSql ){
+ assert( zSubSql[0]!='S' );
+ rc = execSql(db, pzErrMsg, zSubSql);
+ if( rc!=SQLITE_OK ) break;
}
}
-
- return vacuumFinalize(db, pStmt, pzErrMsg);
+ assert( rc!=SQLITE_ROW );
+ if( rc==SQLITE_DONE ) rc = SQLITE_OK;
+ if( rc ){
+ sqlite3SetString(pzErrMsg, db, sqlite3_errmsg(db));
+ }
+ (void)sqlite3_finalize(pStmt);
+ return rc;
+}
+static int execSqlF(sqlite3 *db, char **pzErrMsg, const char *zSql, ...){
+ char *z;
+ va_list ap;
+ int rc;
+ va_start(ap, zSql);
+ z = sqlite3VMPrintf(db, zSql, ap);
+ va_end(ap);
+ if( z==0 ) return SQLITE_NOMEM;
+ rc = execSql(db, pzErrMsg, z);
+ sqlite3DbFree(db, z);
+ return rc;
}
/*
@@ -121445,11 +122283,12 @@ static int execExecSql(sqlite3 *db, char **pzErrMsg, const char *zSql){
** transient would cause the database file to appear to be deleted
** following reboot.
*/
-SQLITE_PRIVATE void sqlite3Vacuum(Parse *pParse){
+SQLITE_PRIVATE void sqlite3Vacuum(Parse *pParse, Token *pNm){
Vdbe *v = sqlite3GetVdbe(pParse);
- if( v ){
- sqlite3VdbeAddOp2(v, OP_Vacuum, 0, 0);
- sqlite3VdbeUsesBtree(v, 0);
+ int iDb = pNm ? sqlite3TwoPartName(pParse, pNm, pNm, &pNm) : 0;
+ if( v && (iDb>=2 || iDb==0) ){
+ sqlite3VdbeAddOp1(v, OP_Vacuum, iDb);
+ sqlite3VdbeUsesBtree(v, iDb);
}
return;
}
@@ -121457,11 +122296,10 @@ SQLITE_PRIVATE void sqlite3Vacuum(Parse *pParse){
/*
** This routine implements the OP_Vacuum opcode of the VDBE.
*/
-SQLITE_PRIVATE int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
+SQLITE_PRIVATE int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db, int iDb){
int rc = SQLITE_OK; /* Return code from service routines */
Btree *pMain; /* The database being vacuumed */
Btree *pTemp; /* The temporary database we vacuum into */
- char *zSql = 0; /* SQL statements */
int saved_flags; /* Saved value of the db->flags */
int saved_nChange; /* Saved value of db->nChange */
int saved_nTotalChange; /* Saved value of db->nTotalChange */
@@ -121470,6 +122308,7 @@ SQLITE_PRIVATE int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
int isMemDb; /* True if vacuuming a :memory: database */
int nRes; /* Bytes of reserved space at the end of each page */
int nDb; /* Number of attached databases */
+ const char *zDbMain; /* Schema name of database to vacuum */
if( !db->autoCommit ){
sqlite3SetString(pzErrMsg, db, "cannot VACUUM from within a transaction");
@@ -121487,11 +122326,13 @@ SQLITE_PRIVATE int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
saved_nChange = db->nChange;
saved_nTotalChange = db->nTotalChange;
saved_mTrace = db->mTrace;
- db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks | SQLITE_PreferBuiltin;
- db->flags &= ~(SQLITE_ForeignKeys | SQLITE_ReverseOrder);
+ db->flags |= (SQLITE_WriteSchema | SQLITE_IgnoreChecks
+ | SQLITE_PreferBuiltin | SQLITE_Vacuum);
+ db->flags &= ~(SQLITE_ForeignKeys | SQLITE_ReverseOrder | SQLITE_CountRows);
db->mTrace = 0;
- pMain = db->aDb[0].pBt;
+ zDbMain = db->aDb[iDb].zDbSName;
+ pMain = db->aDb[iDb].pBt;
isMemDb = sqlite3PagerIsMemdb(sqlite3BtreePager(pMain));
/* Attach the temporary database as 'vacuum_db'. The synchronous pragma
@@ -121509,18 +122350,12 @@ SQLITE_PRIVATE int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
** to write the journal header file.
*/
nDb = db->nDb;
- if( sqlite3TempInMemory(db) ){
- zSql = "ATTACH ':memory:' AS vacuum_db;";
- }else{
- zSql = "ATTACH '' AS vacuum_db;";
- }
- rc = execSql(db, pzErrMsg, zSql);
- if( db->nDb>nDb ){
- pDb = &db->aDb[db->nDb-1];
- assert( strcmp(pDb->zName,"vacuum_db")==0 );
- }
+ rc = execSql(db, pzErrMsg, "ATTACH''AS vacuum_db");
if( rc!=SQLITE_OK ) goto end_of_vacuum;
- pTemp = db->aDb[db->nDb-1].pBt;
+ assert( (db->nDb-1)==nDb );
+ pDb = &db->aDb[nDb];
+ assert( strcmp(pDb->zDbSName,"vacuum_db")==0 );
+ pTemp = pDb->pBt;
/* The call to execSql() to attach the temp database has left the file
** locked (as there was more than one active statement when the transaction
@@ -121541,16 +122376,15 @@ SQLITE_PRIVATE int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
}
#endif
- sqlite3BtreeSetCacheSize(pTemp, db->aDb[0].pSchema->cache_size);
+ sqlite3BtreeSetCacheSize(pTemp, db->aDb[iDb].pSchema->cache_size);
sqlite3BtreeSetSpillSize(pTemp, sqlite3BtreeSetSpillSize(pMain,0));
- rc = execSql(db, pzErrMsg, "PRAGMA vacuum_db.synchronous=OFF");
- if( rc!=SQLITE_OK ) goto end_of_vacuum;
+ sqlite3BtreeSetPagerFlags(pTemp, PAGER_SYNCHRONOUS_OFF|PAGER_CACHESPILL);
/* Begin a transaction and take an exclusive lock on the main database
** file. This is done before the sqlite3BtreeGetPageSize(pMain) call below,
** to ensure that we do not try to change the page-size on a WAL database.
*/
- rc = execSql(db, pzErrMsg, "BEGIN;");
+ rc = execSql(db, pzErrMsg, "BEGIN");
if( rc!=SQLITE_OK ) goto end_of_vacuum;
rc = sqlite3BtreeBeginTrans(pMain, 2);
if( rc!=SQLITE_OK ) goto end_of_vacuum;
@@ -121577,64 +122411,48 @@ SQLITE_PRIVATE int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
/* Query the schema of the main database. Create a mirror schema
** in the temporary database.
*/
- rc = execExecSql(db, pzErrMsg,
- "SELECT 'CREATE TABLE vacuum_db.' || substr(sql,14) "
- " FROM sqlite_master WHERE type='table' AND name!='sqlite_sequence'"
- " AND coalesce(rootpage,1)>0"
+ db->init.iDb = nDb; /* force new CREATE statements into vacuum_db */
+ rc = execSqlF(db, pzErrMsg,
+ "SELECT sql FROM \"%w\".sqlite_master"
+ " WHERE type='table'AND name<>'sqlite_sequence'"
+ " AND coalesce(rootpage,1)>0",
+ zDbMain
);
if( rc!=SQLITE_OK ) goto end_of_vacuum;
- rc = execExecSql(db, pzErrMsg,
- "SELECT 'CREATE INDEX vacuum_db.' || substr(sql,14)"
- " FROM sqlite_master WHERE sql LIKE 'CREATE INDEX %' ");
- if( rc!=SQLITE_OK ) goto end_of_vacuum;
- rc = execExecSql(db, pzErrMsg,
- "SELECT 'CREATE UNIQUE INDEX vacuum_db.' || substr(sql,21) "
- " FROM sqlite_master WHERE sql LIKE 'CREATE UNIQUE INDEX %'");
+ rc = execSqlF(db, pzErrMsg,
+ "SELECT sql FROM \"%w\".sqlite_master"
+ " WHERE type='index' AND length(sql)>10",
+ zDbMain
+ );
if( rc!=SQLITE_OK ) goto end_of_vacuum;
+ db->init.iDb = 0;
/* Loop through the tables in the main database. For each, do
** an "INSERT INTO vacuum_db.xxx SELECT * FROM main.xxx;" to copy
** the contents to the temporary database.
*/
- assert( (db->flags & SQLITE_Vacuum)==0 );
- db->flags |= SQLITE_Vacuum;
- rc = execExecSql(db, pzErrMsg,
- "SELECT 'INSERT INTO vacuum_db.' || quote(name) "
- "|| ' SELECT * FROM main.' || quote(name) || ';'"
- "FROM main.sqlite_master "
- "WHERE type = 'table' AND name!='sqlite_sequence' "
- " AND coalesce(rootpage,1)>0"
+ rc = execSqlF(db, pzErrMsg,
+ "SELECT'INSERT INTO vacuum_db.'||quote(name)"
+ "||' SELECT*FROM\"%w\".'||quote(name)"
+ "FROM vacuum_db.sqlite_master "
+ "WHERE type='table'AND coalesce(rootpage,1)>0",
+ zDbMain
);
assert( (db->flags & SQLITE_Vacuum)!=0 );
db->flags &= ~SQLITE_Vacuum;
if( rc!=SQLITE_OK ) goto end_of_vacuum;
- /* Copy over the sequence table
- */
- rc = execExecSql(db, pzErrMsg,
- "SELECT 'DELETE FROM vacuum_db.' || quote(name) || ';' "
- "FROM vacuum_db.sqlite_master WHERE name='sqlite_sequence' "
- );
- if( rc!=SQLITE_OK ) goto end_of_vacuum;
- rc = execExecSql(db, pzErrMsg,
- "SELECT 'INSERT INTO vacuum_db.' || quote(name) "
- "|| ' SELECT * FROM main.' || quote(name) || ';' "
- "FROM vacuum_db.sqlite_master WHERE name=='sqlite_sequence';"
- );
- if( rc!=SQLITE_OK ) goto end_of_vacuum;
-
-
/* Copy the triggers, views, and virtual tables from the main database
** over to the temporary database. None of these objects has any
** associated storage, so all we have to do is copy their entries
** from the SQLITE_MASTER table.
*/
- rc = execSql(db, pzErrMsg,
- "INSERT INTO vacuum_db.sqlite_master "
- " SELECT type, name, tbl_name, rootpage, sql"
- " FROM main.sqlite_master"
- " WHERE type='view' OR type='trigger'"
- " OR (type='table' AND rootpage=0)"
+ rc = execSqlF(db, pzErrMsg,
+ "INSERT INTO vacuum_db.sqlite_master"
+ " SELECT*FROM \"%w\".sqlite_master"
+ " WHERE type IN('view','trigger')"
+ " OR(type='table'AND rootpage=0)",
+ zDbMain
);
if( rc ) goto end_of_vacuum;
@@ -121688,6 +122506,7 @@ SQLITE_PRIVATE int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
end_of_vacuum:
/* Restore the original value of db->flags */
+ db->init.iDb = 0;
db->flags = saved_flags;
db->nChange = saved_nChange;
db->nTotalChange = saved_nTotalChange;
@@ -122066,7 +122885,7 @@ SQLITE_PRIVATE void sqlite3VtabBeginParse(
*/
if( pTable->azModuleArg ){
sqlite3AuthCheck(pParse, SQLITE_CREATE_VTABLE, pTable->zName,
- pTable->azModuleArg[0], pParse->db->aDb[iDb].zName);
+ pTable->azModuleArg[0], pParse->db->aDb[iDb].zDbSName);
}
#endif
}
@@ -122130,7 +122949,7 @@ SQLITE_PRIVATE void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){
"UPDATE %Q.%s "
"SET type='table', name=%Q, tbl_name=%Q, rootpage=0, sql=%Q "
"WHERE rowid=#%d",
- db->aDb[iDb].zName, SCHEMA_TABLE(iDb),
+ db->aDb[iDb].zDbSName, SCHEMA_TABLE(iDb),
pTab->zName,
pTab->zName,
zStmt,
@@ -122240,7 +123059,7 @@ static int vtabCallConstructor(
pVTable->pMod = pMod;
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
- pTab->azModuleArg[1] = db->aDb[iDb].zName;
+ pTab->azModuleArg[1] = db->aDb[iDb].zDbSName;
/* Invoke the virtual table constructor */
assert( &db->pVtabCtx );
@@ -122394,7 +123213,7 @@ static void addToVTrans(sqlite3 *db, VTable *pVTab){
** This function is invoked by the vdbe to call the xCreate method
** of the virtual table named zTab in database iDb.
**
-** If an error occurs, *pzErr is set to point an an English language
+** If an error occurs, *pzErr is set to point to an English language
** description of the error and an SQLITE_XXX error code is returned.
** In this case the caller must call sqlite3DbFree(db, ) on *pzErr.
*/
@@ -122404,7 +123223,7 @@ SQLITE_PRIVATE int sqlite3VtabCallCreate(sqlite3 *db, int iDb, const char *zTab,
Module *pMod;
const char *zMod;
- pTab = sqlite3FindTable(db, zTab, db->aDb[iDb].zName);
+ pTab = sqlite3FindTable(db, zTab, db->aDb[iDb].zDbSName);
assert( pTab && (pTab->tabFlags & TF_Virtual)!=0 && !pTab->pVTable );
/* Locate the required virtual table module */
@@ -122528,7 +123347,7 @@ SQLITE_PRIVATE int sqlite3VtabCallDestroy(sqlite3 *db, int iDb, const char *zTab
int rc = SQLITE_OK;
Table *pTab;
- pTab = sqlite3FindTable(db, zTab, db->aDb[iDb].zName);
+ pTab = sqlite3FindTable(db, zTab, db->aDb[iDb].zDbSName);
if( pTab!=0 && ALWAYS(pTab->pVTable!=0) ){
VTable *p;
int (*xDestroy)(sqlite3_vtab *);
@@ -123095,6 +123914,8 @@ struct WhereLoop {
union {
struct { /* Information for internal btree tables */
u16 nEq; /* Number of equality constraints */
+ u16 nBtm; /* Size of BTM vector */
+ u16 nTop; /* Size of TOP vector */
Index *pIndex; /* Index used, or NULL */
} btree;
struct { /* Information for virtual tables */
@@ -123217,19 +124038,20 @@ struct WherePath {
*/
struct WhereTerm {
Expr *pExpr; /* Pointer to the subexpression that is this term */
+ WhereClause *pWC; /* The clause this term is part of */
+ LogEst truthProb; /* Probability of truth for this expression */
+ u16 wtFlags; /* TERM_xxx bit flags. See below */
+ u16 eOperator; /* A WO_xx value describing <op> */
+ u8 nChild; /* Number of children that must disable us */
+ u8 eMatchOp; /* Op for vtab MATCH/LIKE/GLOB/REGEXP terms */
int iParent; /* Disable pWC->a[iParent] when this term disabled */
int leftCursor; /* Cursor number of X in "X <op> <expr>" */
+ int iField; /* Field in (?,?,?) IN (SELECT...) vector */
union {
int leftColumn; /* Column number of X in "X <op> <expr>" */
WhereOrInfo *pOrInfo; /* Extra information if (eOperator & WO_OR)!=0 */
WhereAndInfo *pAndInfo; /* Extra information if (eOperator& WO_AND)!=0 */
} u;
- LogEst truthProb; /* Probability of truth for this expression */
- u16 eOperator; /* A WO_xx value describing <op> */
- u16 wtFlags; /* TERM_xxx bit flags. See below */
- u8 nChild; /* Number of children that must disable us */
- u8 eMatchOp; /* Op for vtab MATCH/LIKE/GLOB/REGEXP terms */
- WhereClause *pWC; /* The clause this term is part of */
Bitmask prereqRight; /* Bitmask of tables used by pExpr->pRight */
Bitmask prereqAll; /* Bitmask of tables referenced by pExpr */
};
@@ -123382,25 +124204,25 @@ struct WhereInfo {
SrcList *pTabList; /* List of tables in the join */
ExprList *pOrderBy; /* The ORDER BY clause or NULL */
ExprList *pDistinctSet; /* DISTINCT over all these values */
- WhereLoop *pLoops; /* List of all WhereLoop objects */
- Bitmask revMask; /* Mask of ORDER BY terms that need reversing */
- LogEst nRowOut; /* Estimated number of output rows */
LogEst iLimit; /* LIMIT if wctrlFlags has WHERE_USE_LIMIT */
+ int aiCurOnePass[2]; /* OP_OpenWrite cursors for the ONEPASS opt */
+ int iContinue; /* Jump here to continue with next record */
+ int iBreak; /* Jump here to break out of the loop */
+ int savedNQueryLoop; /* pParse->nQueryLoop outside the WHERE loop */
u16 wctrlFlags; /* Flags originally passed to sqlite3WhereBegin() */
+ u8 nLevel; /* Number of nested loop */
i8 nOBSat; /* Number of ORDER BY terms satisfied by indices */
u8 sorted; /* True if really sorted (not just grouped) */
u8 eOnePass; /* ONEPASS_OFF, or _SINGLE, or _MULTI */
u8 untestedTerms; /* Not all WHERE terms resolved by outer loop */
u8 eDistinct; /* One of the WHERE_DISTINCT_* values */
- u8 nLevel; /* Number of nested loop */
u8 bOrderedInnerLoop; /* True if only the inner-most loop is ordered */
int iTop; /* The very beginning of the WHERE loop */
- int iContinue; /* Jump here to continue with next record */
- int iBreak; /* Jump here to break out of the loop */
- int savedNQueryLoop; /* pParse->nQueryLoop outside the WHERE loop */
- int aiCurOnePass[2]; /* OP_OpenWrite cursors for the ONEPASS opt */
- WhereMaskSet sMaskSet; /* Map cursor numbers to bitmasks */
+ WhereLoop *pLoops; /* List of all WhereLoop objects */
+ Bitmask revMask; /* Mask of ORDER BY terms that need reversing */
+ LogEst nRowOut; /* Estimated number of output rows */
WhereClause sWC; /* Decomposition of the WHERE clause */
+ WhereMaskSet sMaskSet; /* Map cursor numbers to bitmasks */
WhereLevel a[1]; /* Information about each nest loop in WHERE */
};
@@ -123524,6 +124346,17 @@ SQLITE_PRIVATE void sqlite3WhereTabFuncArgs(Parse*, struct SrcList_item*, WhereC
/************** Continuing where we left off in wherecode.c ******************/
#ifndef SQLITE_OMIT_EXPLAIN
+
+/*
+** Return the name of the i-th column of the pIdx index.
+*/
+static const char *explainIndexColumnName(Index *pIdx, int i){
+ i = pIdx->aiColumn[i];
+ if( i==XN_EXPR ) return "<expr>";
+ if( i==XN_ROWID ) return "rowid";
+ return pIdx->pTable->aCol[i].zName;
+}
+
/*
** This routine is a helper for explainIndexRange() below
**
@@ -123534,24 +124367,32 @@ SQLITE_PRIVATE void sqlite3WhereTabFuncArgs(Parse*, struct SrcList_item*, WhereC
*/
static void explainAppendTerm(
StrAccum *pStr, /* The text expression being built */
- int iTerm, /* Index of this term. First is zero */
- const char *zColumn, /* Name of the column */
+ Index *pIdx, /* Index to read column names from */
+ int nTerm, /* Number of terms */
+ int iTerm, /* Zero-based index of first term. */
+ int bAnd, /* Non-zero to append " AND " */
const char *zOp /* Name of the operator */
){
- if( iTerm ) sqlite3StrAccumAppend(pStr, " AND ", 5);
- sqlite3StrAccumAppendAll(pStr, zColumn);
+ int i;
+
+ assert( nTerm>=1 );
+ if( bAnd ) sqlite3StrAccumAppend(pStr, " AND ", 5);
+
+ if( nTerm>1 ) sqlite3StrAccumAppend(pStr, "(", 1);
+ for(i=0; i<nTerm; i++){
+ if( i ) sqlite3StrAccumAppend(pStr, ",", 1);
+ sqlite3StrAccumAppendAll(pStr, explainIndexColumnName(pIdx, iTerm+i));
+ }
+ if( nTerm>1 ) sqlite3StrAccumAppend(pStr, ")", 1);
+
sqlite3StrAccumAppend(pStr, zOp, 1);
- sqlite3StrAccumAppend(pStr, "?", 1);
-}
-/*
-** Return the name of the i-th column of the pIdx index.
-*/
-static const char *explainIndexColumnName(Index *pIdx, int i){
- i = pIdx->aiColumn[i];
- if( i==XN_EXPR ) return "<expr>";
- if( i==XN_ROWID ) return "rowid";
- return pIdx->pTable->aCol[i].zName;
+ if( nTerm>1 ) sqlite3StrAccumAppend(pStr, "(", 1);
+ for(i=0; i<nTerm; i++){
+ if( i ) sqlite3StrAccumAppend(pStr, ",", 1);
+ sqlite3StrAccumAppend(pStr, "?", 1);
+ }
+ if( nTerm>1 ) sqlite3StrAccumAppend(pStr, ")", 1);
}
/*
@@ -123584,12 +124425,11 @@ static void explainIndexRange(StrAccum *pStr, WhereLoop *pLoop){
j = i;
if( pLoop->wsFlags&WHERE_BTM_LIMIT ){
- const char *z = explainIndexColumnName(pIndex, i);
- explainAppendTerm(pStr, i++, z, ">");
+ explainAppendTerm(pStr, pIndex, pLoop->u.btree.nBtm, j, i, ">");
+ i = 1;
}
if( pLoop->wsFlags&WHERE_TOP_LIMIT ){
- const char *z = explainIndexColumnName(pIndex, j);
- explainAppendTerm(pStr, i, z, "<");
+ explainAppendTerm(pStr, pIndex, pLoop->u.btree.nTop, j, i, "<");
}
sqlite3StrAccumAppend(pStr, ")", 1);
}
@@ -123779,7 +124619,7 @@ SQLITE_PRIVATE void sqlite3WhereAddScanStatus(
*/
static void disableTerm(WhereLevel *pLevel, WhereTerm *pTerm){
int nLoop = 0;
- while( pTerm
+ while( ALWAYS(pTerm!=0)
&& (pTerm->wtFlags & TERM_CODED)==0
&& (pLevel->iLeftJoin==0 || ExprHasProperty(pTerm->pExpr, EP_FromJoin))
&& (pLevel->notReady & pTerm->prereqAll)==0
@@ -123835,16 +124675,45 @@ static void codeApplyAffinity(Parse *pParse, int base, int n, char *zAff){
}
}
+/*
+** Expression pRight, which is the RHS of a comparison operation, is
+** either a vector of n elements or, if n==1, a scalar expression.
+** Before the comparison operation, affinity zAff is to be applied
+** to the pRight values. This function modifies characters within the
+** affinity string to SQLITE_AFF_BLOB if either:
+**
+** * the comparison will be performed with no affinity, or
+** * the affinity change in zAff is guaranteed not to change the value.
+*/
+static void updateRangeAffinityStr(
+ Expr *pRight, /* RHS of comparison */
+ int n, /* Number of vector elements in comparison */
+ char *zAff /* Affinity string to modify */
+){
+ int i;
+ for(i=0; i<n; i++){
+ Expr *p = sqlite3VectorFieldSubexpr(pRight, i);
+ if( sqlite3CompareAffinity(p, zAff[i])==SQLITE_AFF_BLOB
+ || sqlite3ExprNeedsNoAffinityChange(p, zAff[i])
+ ){
+ zAff[i] = SQLITE_AFF_BLOB;
+ }
+ }
+}
/*
** Generate code for a single equality term of the WHERE clause. An equality
** term can be either X=expr or X IN (...). pTerm is the term to be
** coded.
**
-** The current value for the constraint is left in register iReg.
+** The current value for the constraint is left in a register, the index
+** of which is returned. An attempt is made store the result in iTarget but
+** this is only guaranteed for TK_ISNULL and TK_IN constraints. If the
+** constraint is a TK_EQ or TK_IS, then the current value might be left in
+** some other register and it is the caller's responsibility to compensate.
**
-** For a constraint of the form X=expr, the expression is evaluated and its
-** result is left on the stack. For constraints of the form X IN (...)
+** For a constraint of the form X=expr, the expression is evaluated in
+** straight-line code. For constraints of the form X IN (...)
** this routine sets up a loop that will iterate over all values of X.
*/
static int codeEqualityTerm(
@@ -123859,6 +124728,7 @@ static int codeEqualityTerm(
Vdbe *v = pParse->pVdbe;
int iReg; /* Register holding results */
+ assert( pLevel->pWLoop->aLTerm[iEq]==pTerm );
assert( iTarget>0 );
if( pX->op==TK_EQ || pX->op==TK_IS ){
iReg = sqlite3ExprCodeTarget(pParse, pX->pRight, iTarget);
@@ -123867,10 +124737,13 @@ static int codeEqualityTerm(
sqlite3VdbeAddOp2(v, OP_Null, 0, iReg);
#ifndef SQLITE_OMIT_SUBQUERY
}else{
- int eType;
+ int eType = IN_INDEX_NOOP;
int iTab;
struct InLoop *pIn;
WhereLoop *pLoop = pLevel->pWLoop;
+ int i;
+ int nEq = 0;
+ int *aiMap = 0;
if( (pLoop->wsFlags & WHERE_VIRTUALTABLE)==0
&& pLoop->u.btree.pIndex!=0
@@ -123882,7 +124755,75 @@ static int codeEqualityTerm(
}
assert( pX->op==TK_IN );
iReg = iTarget;
- eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0);
+
+ for(i=0; i<iEq; i++){
+ if( pLoop->aLTerm[i] && pLoop->aLTerm[i]->pExpr==pX ){
+ disableTerm(pLevel, pTerm);
+ return iTarget;
+ }
+ }
+ for(i=iEq;i<pLoop->nLTerm; i++){
+ if( ALWAYS(pLoop->aLTerm[i]) && pLoop->aLTerm[i]->pExpr==pX ) nEq++;
+ }
+
+ if( (pX->flags & EP_xIsSelect)==0 || pX->x.pSelect->pEList->nExpr==1 ){
+ eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, 0);
+ }else{
+ Select *pSelect = pX->x.pSelect;
+ sqlite3 *db = pParse->db;
+ ExprList *pOrigRhs = pSelect->pEList;
+ ExprList *pOrigLhs = pX->pLeft->x.pList;
+ ExprList *pRhs = 0; /* New Select.pEList for RHS */
+ ExprList *pLhs = 0; /* New pX->pLeft vector */
+
+ for(i=iEq;i<pLoop->nLTerm; i++){
+ if( pLoop->aLTerm[i]->pExpr==pX ){
+ int iField = pLoop->aLTerm[i]->iField - 1;
+ Expr *pNewRhs = sqlite3ExprDup(db, pOrigRhs->a[iField].pExpr, 0);
+ Expr *pNewLhs = sqlite3ExprDup(db, pOrigLhs->a[iField].pExpr, 0);
+
+ pRhs = sqlite3ExprListAppend(pParse, pRhs, pNewRhs);
+ pLhs = sqlite3ExprListAppend(pParse, pLhs, pNewLhs);
+ }
+ }
+ if( !db->mallocFailed ){
+ Expr *pLeft = pX->pLeft;
+
+ if( pSelect->pOrderBy ){
+ /* If the SELECT statement has an ORDER BY clause, zero the
+ ** iOrderByCol variables. These are set to non-zero when an
+ ** ORDER BY term exactly matches one of the terms of the
+ ** result-set. Since the result-set of the SELECT statement may
+ ** have been modified or reordered, these variables are no longer
+ ** set correctly. Since setting them is just an optimization,
+ ** it's easiest just to zero them here. */
+ ExprList *pOrderBy = pSelect->pOrderBy;
+ for(i=0; i<pOrderBy->nExpr; i++){
+ pOrderBy->a[i].u.x.iOrderByCol = 0;
+ }
+ }
+
+ /* Take care here not to generate a TK_VECTOR containing only a
+ ** single value. Since the parser never creates such a vector, some
+ ** of the subroutines do not handle this case. */
+ if( pLhs->nExpr==1 ){
+ pX->pLeft = pLhs->a[0].pExpr;
+ }else{
+ pLeft->x.pList = pLhs;
+ aiMap = (int*)sqlite3DbMallocZero(pParse->db, sizeof(int) * nEq);
+ testcase( aiMap==0 );
+ }
+ pSelect->pEList = pRhs;
+ eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, aiMap);
+ testcase( aiMap!=0 && aiMap[0]!=0 );
+ pSelect->pEList = pOrigRhs;
+ pLeft->x.pList = pOrigLhs;
+ pX->pLeft = pLeft;
+ }
+ sqlite3ExprListDelete(pParse->db, pLhs);
+ sqlite3ExprListDelete(pParse->db, pRhs);
+ }
+
if( eType==IN_INDEX_INDEX_DESC ){
testcase( bRev );
bRev = !bRev;
@@ -123892,28 +124833,45 @@ static int codeEqualityTerm(
VdbeCoverageIf(v, bRev);
VdbeCoverageIf(v, !bRev);
assert( (pLoop->wsFlags & WHERE_MULTI_OR)==0 );
+
pLoop->wsFlags |= WHERE_IN_ABLE;
if( pLevel->u.in.nIn==0 ){
pLevel->addrNxt = sqlite3VdbeMakeLabel(v);
}
- pLevel->u.in.nIn++;
+
+ i = pLevel->u.in.nIn;
+ pLevel->u.in.nIn += nEq;
pLevel->u.in.aInLoop =
sqlite3DbReallocOrFree(pParse->db, pLevel->u.in.aInLoop,
sizeof(pLevel->u.in.aInLoop[0])*pLevel->u.in.nIn);
pIn = pLevel->u.in.aInLoop;
if( pIn ){
- pIn += pLevel->u.in.nIn - 1;
- pIn->iCur = iTab;
- if( eType==IN_INDEX_ROWID ){
- pIn->addrInTop = sqlite3VdbeAddOp2(v, OP_Rowid, iTab, iReg);
- }else{
- pIn->addrInTop = sqlite3VdbeAddOp3(v, OP_Column, iTab, 0, iReg);
+ int iMap = 0; /* Index in aiMap[] */
+ pIn += i;
+ for(i=iEq;i<pLoop->nLTerm; i++){
+ if( pLoop->aLTerm[i]->pExpr==pX ){
+ int iOut = iReg + i - iEq;
+ if( eType==IN_INDEX_ROWID ){
+ testcase( nEq>1 ); /* Happens with a UNIQUE index on ROWID */
+ pIn->addrInTop = sqlite3VdbeAddOp2(v, OP_Rowid, iTab, iOut);
+ }else{
+ int iCol = aiMap ? aiMap[iMap++] : 0;
+ pIn->addrInTop = sqlite3VdbeAddOp3(v,OP_Column,iTab, iCol, iOut);
+ }
+ sqlite3VdbeAddOp1(v, OP_IsNull, iOut); VdbeCoverage(v);
+ if( i==iEq ){
+ pIn->iCur = iTab;
+ pIn->eEndLoopOp = bRev ? OP_PrevIfOpen : OP_NextIfOpen;
+ }else{
+ pIn->eEndLoopOp = OP_Noop;
+ }
+ pIn++;
+ }
}
- pIn->eEndLoopOp = bRev ? OP_PrevIfOpen : OP_NextIfOpen;
- sqlite3VdbeAddOp1(v, OP_IsNull, iReg); VdbeCoverage(v);
}else{
pLevel->u.in.nIn = 0;
}
+ sqlite3DbFree(pParse->db, aiMap);
#endif
}
disableTerm(pLevel, pTerm);
@@ -124039,7 +124997,7 @@ static int codeAllEqualityTerms(
sqlite3VdbeAddOp2(v, OP_SCopy, r1, regBase+j);
}
}
- if( (pTerm->eOperator & WO_IN)!=0 ){
+ if( pTerm->eOperator & WO_IN ){
if( pTerm->pExpr->flags & EP_xIsSelect ){
/* No affinity ever needs to be (or should be) applied to a value
** from the RHS of an "? IN (SELECT ...)" expression. The
@@ -124371,6 +125329,39 @@ static void codeDeferredSeek(
}
/*
+** If the expression passed as the second argument is a vector, generate
+** code to write the first nReg elements of the vector into an array
+** of registers starting with iReg.
+**
+** If the expression is not a vector, then nReg must be passed 1. In
+** this case, generate code to evaluate the expression and leave the
+** result in register iReg.
+*/
+static void codeExprOrVector(Parse *pParse, Expr *p, int iReg, int nReg){
+ assert( nReg>0 );
+ if( sqlite3ExprIsVector(p) ){
+#ifndef SQLITE_OMIT_SUBQUERY
+ if( (p->flags & EP_xIsSelect) ){
+ Vdbe *v = pParse->pVdbe;
+ int iSelect = sqlite3CodeSubselect(pParse, p, 0, 0);
+ sqlite3VdbeAddOp3(v, OP_Copy, iSelect, iReg, nReg-1);
+ }else
+#endif
+ {
+ int i;
+ ExprList *pList = p->x.pList;
+ assert( nReg<=pList->nExpr );
+ for(i=0; i<nReg; i++){
+ sqlite3ExprCode(pParse, pList->a[i].pExpr, iReg+i);
+ }
+ }
+ }else{
+ assert( nReg==1 );
+ sqlite3ExprCode(pParse, p, iReg);
+ }
+}
+
+/*
** Generate code for the start of the iLevel-th loop in the WHERE clause
** implementation described by pWInfo.
*/
@@ -124465,7 +125456,8 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
codeEqualityTerm(pParse, pTerm, pLevel, j, bRev, iTarget);
addrNotFound = pLevel->addrNxt;
}else{
- sqlite3ExprCode(pParse, pTerm->pExpr->pRight, iTarget);
+ Expr *pRight = pTerm->pExpr->pRight;
+ codeExprOrVector(pParse, pRight, iTarget, 1);
}
}
sqlite3VdbeAddOp2(v, OP_Integer, pLoop->u.vtab.idxNum, iReg);
@@ -124579,6 +125571,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
if( pStart ){
Expr *pX; /* The expression that defines the start bound */
int r1, rTemp; /* Registers for holding the start boundary */
+ int op; /* Cursor seek operation */
/* The following constant maps TK_xx codes into corresponding
** seek opcodes. It depends on a particular ordering of TK_xx
@@ -124598,8 +125591,16 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
pX = pStart->pExpr;
assert( pX!=0 );
testcase( pStart->leftCursor!=iCur ); /* transitive constraints */
- r1 = sqlite3ExprCodeTemp(pParse, pX->pRight, &rTemp);
- sqlite3VdbeAddOp3(v, aMoveOp[pX->op-TK_GT], iCur, addrBrk, r1);
+ if( sqlite3ExprIsVector(pX->pRight) ){
+ r1 = rTemp = sqlite3GetTempReg(pParse);
+ codeExprOrVector(pParse, pX->pRight, r1, 1);
+ op = aMoveOp[(pX->op - TK_GT) | 0x0001];
+ }else{
+ r1 = sqlite3ExprCodeTemp(pParse, pX->pRight, &rTemp);
+ disableTerm(pLevel, pStart);
+ op = aMoveOp[(pX->op - TK_GT)];
+ }
+ sqlite3VdbeAddOp3(v, op, iCur, addrBrk, r1);
VdbeComment((v, "pk"));
VdbeCoverageIf(v, pX->op==TK_GT);
VdbeCoverageIf(v, pX->op==TK_LE);
@@ -124607,7 +125608,6 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
VdbeCoverageIf(v, pX->op==TK_GE);
sqlite3ExprCacheAffinityChange(pParse, r1, 1);
sqlite3ReleaseTempReg(pParse, rTemp);
- disableTerm(pLevel, pStart);
}else{
sqlite3VdbeAddOp2(v, bRev ? OP_Last : OP_Rewind, iCur, addrBrk);
VdbeCoverageIf(v, bRev==0);
@@ -124621,13 +125621,17 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
testcase( pEnd->leftCursor!=iCur ); /* Transitive constraints */
testcase( pEnd->wtFlags & TERM_VIRTUAL );
memEndValue = ++pParse->nMem;
- sqlite3ExprCode(pParse, pX->pRight, memEndValue);
- if( pX->op==TK_LT || pX->op==TK_GT ){
+ codeExprOrVector(pParse, pX->pRight, memEndValue, 1);
+ if( 0==sqlite3ExprIsVector(pX->pRight)
+ && (pX->op==TK_LT || pX->op==TK_GT)
+ ){
testOp = bRev ? OP_Le : OP_Ge;
}else{
testOp = bRev ? OP_Lt : OP_Gt;
}
- disableTerm(pLevel, pEnd);
+ if( 0==sqlite3ExprIsVector(pX->pRight) ){
+ disableTerm(pLevel, pEnd);
+ }
}
start = sqlite3VdbeCurrentAddr(v);
pLevel->op = bRev ? OP_Prev : OP_Next;
@@ -124694,6 +125698,8 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
OP_IdxLT, /* 3: (end_constraints && bRev && endEq) */
};
u16 nEq = pLoop->u.btree.nEq; /* Number of == or IN terms */
+ u16 nBtm = pLoop->u.btree.nBtm; /* Length of BTM vector */
+ u16 nTop = pLoop->u.btree.nTop; /* Length of TOP vector */
int regBase; /* Base register holding constraint values */
WhereTerm *pRangeStart = 0; /* Inequality constraint at range start */
WhereTerm *pRangeEnd = 0; /* Inequality constraint at range end */
@@ -124706,7 +125712,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
int nExtraReg = 0; /* Number of extra registers needed */
int op; /* Instruction opcode */
char *zStartAff; /* Affinity for start of range constraint */
- char cEndAff = 0; /* Affinity for end of range constraint */
+ char *zEndAff = 0; /* Affinity for end of range constraint */
u8 bSeekPastNull = 0; /* True to seek past initial nulls */
u8 bStopAtNull = 0; /* Add condition to terminate at NULLs */
@@ -124740,14 +125746,14 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
j = nEq;
if( pLoop->wsFlags & WHERE_BTM_LIMIT ){
pRangeStart = pLoop->aLTerm[j++];
- nExtraReg = 1;
+ nExtraReg = MAX(nExtraReg, pLoop->u.btree.nBtm);
/* Like optimization range constraints always occur in pairs */
assert( (pRangeStart->wtFlags & TERM_LIKEOPT)==0 ||
(pLoop->wsFlags & WHERE_TOP_LIMIT)!=0 );
}
if( pLoop->wsFlags & WHERE_TOP_LIMIT ){
pRangeEnd = pLoop->aLTerm[j++];
- nExtraReg = 1;
+ nExtraReg = MAX(nExtraReg, pLoop->u.btree.nTop);
#ifndef SQLITE_LIKE_DOESNT_MATCH_BLOBS
if( (pRangeEnd->wtFlags & TERM_LIKEOPT)!=0 ){
assert( pRangeStart!=0 ); /* LIKE opt constraints */
@@ -124765,11 +125771,11 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
pLevel->iLikeRepCntr |= bRev ^ (pIdx->aSortOrder[nEq]==SQLITE_SO_DESC);
}
#endif
- if( pRangeStart==0
- && (j = pIdx->aiColumn[nEq])>=0
- && pIdx->pTable->aCol[j].notNull==0
- ){
- bSeekPastNull = 1;
+ if( pRangeStart==0 ){
+ j = pIdx->aiColumn[nEq];
+ if( (j>=0 && pIdx->pTable->aCol[j].notNull==0) || j==XN_EXPR ){
+ bSeekPastNull = 1;
+ }
}
}
assert( pRangeEnd==0 || (pRangeEnd->wtFlags & TERM_VNULL)==0 );
@@ -124783,6 +125789,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
){
SWAP(WhereTerm *, pRangeEnd, pRangeStart);
SWAP(u8, bSeekPastNull, bStopAtNull);
+ SWAP(u8, nBtm, nTop);
}
/* Generate code to evaluate all constraint terms using == or IN
@@ -124792,7 +125799,9 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
codeCursorHint(pTabItem, pWInfo, pLevel, pRangeEnd);
regBase = codeAllEqualityTerms(pParse,pLevel,bRev,nExtraReg,&zStartAff);
assert( zStartAff==0 || sqlite3Strlen30(zStartAff)>=nEq );
- if( zStartAff ) cEndAff = zStartAff[nEq];
+ if( zStartAff && nTop ){
+ zEndAff = sqlite3DbStrDup(db, &zStartAff[nEq]);
+ }
addrNxt = pLevel->addrNxt;
testcase( pRangeStart && (pRangeStart->eOperator & WO_LE)!=0 );
@@ -124807,7 +125816,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
nConstraint = nEq;
if( pRangeStart ){
Expr *pRight = pRangeStart->pExpr->pRight;
- sqlite3ExprCode(pParse, pRight, regBase+nEq);
+ codeExprOrVector(pParse, pRight, regBase+nEq, nBtm);
whereLikeOptimizationStringFixup(v, pLevel, pRangeStart);
if( (pRangeStart->wtFlags & TERM_VNULL)==0
&& sqlite3ExprCanBeNull(pRight)
@@ -124816,18 +125825,15 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
VdbeCoverage(v);
}
if( zStartAff ){
- if( sqlite3CompareAffinity(pRight, zStartAff[nEq])==SQLITE_AFF_BLOB){
- /* Since the comparison is to be performed with no conversions
- ** applied to the operands, set the affinity to apply to pRight to
- ** SQLITE_AFF_BLOB. */
- zStartAff[nEq] = SQLITE_AFF_BLOB;
- }
- if( sqlite3ExprNeedsNoAffinityChange(pRight, zStartAff[nEq]) ){
- zStartAff[nEq] = SQLITE_AFF_BLOB;
- }
+ updateRangeAffinityStr(pRight, nBtm, &zStartAff[nEq]);
}
- nConstraint++;
+ nConstraint += nBtm;
testcase( pRangeStart->wtFlags & TERM_VIRTUAL );
+ if( sqlite3ExprIsVector(pRight)==0 ){
+ disableTerm(pLevel, pRangeStart);
+ }else{
+ startEq = 1;
+ }
bSeekPastNull = 0;
}else if( bSeekPastNull ){
sqlite3VdbeAddOp2(v, OP_Null, 0, regBase+nEq);
@@ -124860,7 +125866,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
if( pRangeEnd ){
Expr *pRight = pRangeEnd->pExpr->pRight;
sqlite3ExprCacheRemove(pParse, regBase+nEq, 1);
- sqlite3ExprCode(pParse, pRight, regBase+nEq);
+ codeExprOrVector(pParse, pRight, regBase+nEq, nTop);
whereLikeOptimizationStringFixup(v, pLevel, pRangeEnd);
if( (pRangeEnd->wtFlags & TERM_VNULL)==0
&& sqlite3ExprCanBeNull(pRight)
@@ -124868,19 +125874,27 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
sqlite3VdbeAddOp2(v, OP_IsNull, regBase+nEq, addrNxt);
VdbeCoverage(v);
}
- if( sqlite3CompareAffinity(pRight, cEndAff)!=SQLITE_AFF_BLOB
- && !sqlite3ExprNeedsNoAffinityChange(pRight, cEndAff)
- ){
- codeApplyAffinity(pParse, regBase+nEq, 1, &cEndAff);
+ if( zEndAff ){
+ updateRangeAffinityStr(pRight, nTop, zEndAff);
+ codeApplyAffinity(pParse, regBase+nEq, nTop, zEndAff);
+ }else{
+ assert( pParse->db->mallocFailed );
}
- nConstraint++;
+ nConstraint += nTop;
testcase( pRangeEnd->wtFlags & TERM_VIRTUAL );
+
+ if( sqlite3ExprIsVector(pRight)==0 ){
+ disableTerm(pLevel, pRangeEnd);
+ }else{
+ endEq = 1;
+ }
}else if( bStopAtNull ){
sqlite3VdbeAddOp2(v, OP_Null, 0, regBase+nEq);
endEq = 0;
nConstraint++;
}
sqlite3DbFree(db, zStartAff);
+ sqlite3DbFree(db, zEndAff);
/* Top of the loop body */
pLevel->p2 = sqlite3VdbeCurrentAddr(v);
@@ -124896,8 +125910,6 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
}
/* Seek the table cursor, if required */
- disableTerm(pLevel, pRangeStart);
- disableTerm(pLevel, pRangeEnd);
if( omitTable ){
/* pIdx is a covering index. No need to access the main table. */
}else if( HasRowid(pIdx->pTable) ){
@@ -124921,9 +125933,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
iRowidReg, pPk->nKeyCol); VdbeCoverage(v);
}
- /* Record the instruction used to terminate the loop. Disable
- ** WHERE clause terms made redundant by the index range scan.
- */
+ /* Record the instruction used to terminate the loop. */
if( pLoop->wsFlags & WHERE_ONEROW ){
pLevel->op = OP_Noop;
}else if( bRev ){
@@ -125000,7 +126010,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
u16 wctrlFlags; /* Flags for sub-WHERE clause */
Expr *pAndExpr = 0; /* An ".. AND (...)" expression */
Table *pTab = pTabItem->pTab;
-
+
pTerm = pLoop->aLTerm[0];
assert( pTerm!=0 );
assert( pTerm->eOperator & WO_OR );
@@ -125301,7 +126311,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
** the implied "t1.a=123" constraint.
*/
for(pTerm=pWC->a, j=pWC->nTerm; j>0; j--, pTerm++){
- Expr *pE, *pEAlt;
+ Expr *pE, sEAlt;
WhereTerm *pAlt;
if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue;
if( (pTerm->eOperator & (WO_EQ|WO_IS))==0 ) continue;
@@ -125319,13 +126329,9 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
testcase( pAlt->eOperator & WO_IS );
testcase( pAlt->eOperator & WO_IN );
VdbeModuleComment((v, "begin transitive constraint"));
- pEAlt = sqlite3StackAllocRaw(db, sizeof(*pEAlt));
- if( pEAlt ){
- *pEAlt = *pAlt->pExpr;
- pEAlt->pLeft = pE->pLeft;
- sqlite3ExprIfFalse(pParse, pEAlt, addrCont, SQLITE_JUMPIFNULL);
- sqlite3StackFree(db, pEAlt);
- }
+ sEAlt = *pAlt->pExpr;
+ sEAlt.pLeft = pE->pLeft;
+ sqlite3ExprIfFalse(pParse, &sEAlt, addrCont, SQLITE_JUMPIFNULL);
}
/* For a LEFT OUTER JOIN, generate code that will record the fact that
@@ -125434,7 +126440,6 @@ static int whereClauseInsert(WhereClause *pWC, Expr *p, u16 wtFlags){
sqlite3DbFree(db, pOld);
}
pWC->nSlot = sqlite3DbMallocSize(db, pWC->a)/sizeof(pWC->a[0]);
- memset(&pWC->a[pWC->nTerm], 0, sizeof(pWC->a[0])*(pWC->nSlot-pWC->nTerm));
}
pTerm = &pWC->a[idx = pWC->nTerm++];
if( p && ExprHasProperty(p, EP_Unlikely) ){
@@ -125446,13 +126451,15 @@ static int whereClauseInsert(WhereClause *pWC, Expr *p, u16 wtFlags){
pTerm->wtFlags = wtFlags;
pTerm->pWC = pWC;
pTerm->iParent = -1;
+ memset(&pTerm->eOperator, 0,
+ sizeof(WhereTerm) - offsetof(WhereTerm,eOperator));
return idx;
}
/*
** Return TRUE if the given operator is one of the operators that is
** allowed for an indexable WHERE clause term. The allowed operators are
-** "=", "<", ">", "<=", ">=", "IN", and "IS NULL"
+** "=", "<", ">", "<=", ">=", "IN", "IS", and "IS NULL"
*/
static int allowedOp(int op){
assert( TK_GT>TK_EQ && TK_GT<TK_GE );
@@ -125647,7 +126654,7 @@ static int isMatchOfColumn(
Expr *pExpr, /* Test this expression */
unsigned char *peOp2 /* OUT: 0 for MATCH, or else an op2 value */
){
- struct Op2 {
+ static const struct Op2 {
const char *zOp;
unsigned char eOp2;
} aOp[] = {
@@ -126180,7 +127187,8 @@ static Bitmask exprSelectUsage(WhereMaskSet *pMaskSet, Select *pS){
** in any index. Return TRUE (1) if pExpr is an indexed term and return
** FALSE (0) if not. If TRUE is returned, also set *piCur to the cursor
** number of the table that is indexed and *piColumn to the column number
-** of the column that is indexed, or -2 if an expression is being indexed.
+** of the column that is indexed, or XN_EXPR (-2) if an expression is being
+** indexed.
**
** If pExpr is a TK_COLUMN column reference, then this routine always returns
** true even if that particular column is not indexed, because the column
@@ -126188,6 +127196,7 @@ static Bitmask exprSelectUsage(WhereMaskSet *pMaskSet, Select *pS){
*/
static int exprMightBeIndexed(
SrcList *pFrom, /* The FROM clause */
+ int op, /* The specific comparison operator */
Bitmask mPrereq, /* Bitmask of FROM clause terms referenced by pExpr */
Expr *pExpr, /* An operand of a comparison operator */
int *piCur, /* Write the referenced table cursor number here */
@@ -126196,6 +127205,17 @@ static int exprMightBeIndexed(
Index *pIdx;
int i;
int iCur;
+
+ /* If this expression is a vector to the left or right of a
+ ** inequality constraint (>, <, >= or <=), perform the processing
+ ** on the first element of the vector. */
+ assert( TK_GT+1==TK_LE && TK_GT+2==TK_LT && TK_GT+3==TK_GE );
+ assert( TK_IS<TK_GE && TK_ISNULL<TK_GE && TK_IN<TK_GE );
+ assert( op<=TK_GE );
+ if( pExpr->op==TK_VECTOR && (op>=TK_GT && ALWAYS(op<=TK_GE)) ){
+ pExpr = pExpr->x.pList->a[0].pExpr;
+ }
+
if( pExpr->op==TK_COLUMN ){
*piCur = pExpr->iTable;
*piColumn = pExpr->iColumn;
@@ -126208,10 +127228,10 @@ static int exprMightBeIndexed(
for(pIdx=pFrom->a[i].pTab->pIndex; pIdx; pIdx=pIdx->pNext){
if( pIdx->aColExpr==0 ) continue;
for(i=0; i<pIdx->nKeyCol; i++){
- if( pIdx->aiColumn[i]!=(-2) ) continue;
+ if( pIdx->aiColumn[i]!=XN_EXPR ) continue;
if( sqlite3ExprCompare(pExpr, pIdx->aColExpr->a[i].pExpr, iCur)==0 ){
*piCur = iCur;
- *piColumn = -2;
+ *piColumn = XN_EXPR;
return 1;
}
}
@@ -126268,6 +127288,7 @@ static void exprAnalyze(
op = pExpr->op;
if( op==TK_IN ){
assert( pExpr->pRight==0 );
+ if( sqlite3ExprCheckIN(pParse, pExpr) ) return;
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
pTerm->prereqRight = exprSelectUsage(pMaskSet, pExpr->x.pSelect);
}else{
@@ -126294,18 +127315,26 @@ static void exprAnalyze(
Expr *pLeft = sqlite3ExprSkipCollate(pExpr->pLeft);
Expr *pRight = sqlite3ExprSkipCollate(pExpr->pRight);
u16 opMask = (pTerm->prereqRight & prereqLeft)==0 ? WO_ALL : WO_EQUIV;
- if( exprMightBeIndexed(pSrc, prereqLeft, pLeft, &iCur, &iColumn) ){
+
+ if( pTerm->iField>0 ){
+ assert( op==TK_IN );
+ assert( pLeft->op==TK_VECTOR );
+ pLeft = pLeft->x.pList->a[pTerm->iField-1].pExpr;
+ }
+
+ if( exprMightBeIndexed(pSrc, op, prereqLeft, pLeft, &iCur, &iColumn) ){
pTerm->leftCursor = iCur;
pTerm->u.leftColumn = iColumn;
pTerm->eOperator = operatorMask(op) & opMask;
}
if( op==TK_IS ) pTerm->wtFlags |= TERM_IS;
if( pRight
- && exprMightBeIndexed(pSrc, pTerm->prereqRight, pRight, &iCur, &iColumn)
+ && exprMightBeIndexed(pSrc, op, pTerm->prereqRight, pRight, &iCur,&iColumn)
){
WhereTerm *pNew;
Expr *pDup;
u16 eExtraOp = 0; /* Extra bits for pNew->eOperator */
+ assert( pTerm->iField==0 );
if( pTerm->leftCursor>=0 ){
int idxNew;
pDup = sqlite3ExprDup(db, pExpr, 0);
@@ -126509,6 +127538,60 @@ static void exprAnalyze(
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
+ /* If there is a vector == or IS term - e.g. "(a, b) == (?, ?)" - create
+ ** new terms for each component comparison - "a = ?" and "b = ?". The
+ ** new terms completely replace the original vector comparison, which is
+ ** no longer used.
+ **
+ ** This is only required if at least one side of the comparison operation
+ ** is not a sub-select. */
+ if( pWC->op==TK_AND
+ && (pExpr->op==TK_EQ || pExpr->op==TK_IS)
+ && sqlite3ExprIsVector(pExpr->pLeft)
+ && ( (pExpr->pLeft->flags & EP_xIsSelect)==0
+ || (pExpr->pRight->flags & EP_xIsSelect)==0
+ )){
+ int nLeft = sqlite3ExprVectorSize(pExpr->pLeft);
+ int i;
+ assert( nLeft==sqlite3ExprVectorSize(pExpr->pRight) );
+ for(i=0; i<nLeft; i++){
+ int idxNew;
+ Expr *pNew;
+ Expr *pLeft = sqlite3ExprForVectorField(pParse, pExpr->pLeft, i);
+ Expr *pRight = sqlite3ExprForVectorField(pParse, pExpr->pRight, i);
+
+ pNew = sqlite3PExpr(pParse, pExpr->op, pLeft, pRight, 0);
+ transferJoinMarkings(pNew, pExpr);
+ idxNew = whereClauseInsert(pWC, pNew, TERM_DYNAMIC);
+ exprAnalyze(pSrc, pWC, idxNew);
+ }
+ pTerm = &pWC->a[idxTerm];
+ pTerm->wtFlags = TERM_CODED|TERM_VIRTUAL; /* Disable the original */
+ pTerm->eOperator = 0;
+ }
+
+ /* If there is a vector IN term - e.g. "(a, b) IN (SELECT ...)" - create
+ ** a virtual term for each vector component. The expression object
+ ** used by each such virtual term is pExpr (the full vector IN(...)
+ ** expression). The WhereTerm.iField variable identifies the index within
+ ** the vector on the LHS that the virtual term represents.
+ **
+ ** This only works if the RHS is a simple SELECT, not a compound
+ */
+ if( pWC->op==TK_AND && pExpr->op==TK_IN && pTerm->iField==0
+ && pExpr->pLeft->op==TK_VECTOR
+ && pExpr->x.pSelect->pPrior==0
+ ){
+ int i;
+ for(i=0; i<sqlite3ExprVectorSize(pExpr->pLeft); i++){
+ int idxNew;
+ idxNew = whereClauseInsert(pWC, pExpr, TERM_VIRTUAL);
+ pWC->a[idxNew].iField = i+1;
+ exprAnalyze(pSrc, pWC, idxNew);
+ markTermAsChild(pWC, idxNew, idxTerm);
+ }
+ }
+
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
/* When sqlite_stat3 histogram data is available an operator of the
** form "x IS NOT NULL" can sometimes be evaluated more efficiently
@@ -126529,7 +127612,7 @@ static void exprAnalyze(
pNewExpr = sqlite3PExpr(pParse, TK_GT,
sqlite3ExprDup(db, pLeft, 0),
- sqlite3PExpr(pParse, TK_NULL, 0, 0, 0), 0);
+ sqlite3ExprAlloc(db, TK_NULL, 0, 0), 0);
idxNew = whereClauseInsert(pWC, pNewExpr,
TERM_VIRTUAL|TERM_DYNAMIC|TERM_VNULL);
@@ -126632,13 +127715,14 @@ SQLITE_PRIVATE void sqlite3WhereClauseClear(WhereClause *pWC){
** tree.
*/
SQLITE_PRIVATE Bitmask sqlite3WhereExprUsage(WhereMaskSet *pMaskSet, Expr *p){
- Bitmask mask = 0;
+ Bitmask mask;
if( p==0 ) return 0;
if( p->op==TK_COLUMN ){
mask = sqlite3WhereGetMask(pMaskSet, p->iTable);
return mask;
}
- mask = sqlite3WhereExprUsage(pMaskSet, p->pRight);
+ assert( !ExprHasProperty(p, EP_TokenOnly) );
+ mask = p->pRight ? sqlite3WhereExprUsage(pMaskSet, p->pRight) : 0;
if( p->pLeft ) mask |= sqlite3WhereExprUsage(pMaskSet, p->pLeft);
if( ExprHasProperty(p, EP_xIsSelect) ){
mask |= exprSelectUsage(pMaskSet, p->x.pSelect);
@@ -126706,7 +127790,7 @@ SQLITE_PRIVATE void sqlite3WhereTabFuncArgs(
pTab->zName, j);
return;
}
- pColRef = sqlite3PExpr(pParse, TK_COLUMN, 0, 0, 0);
+ pColRef = sqlite3ExprAlloc(pParse->db, TK_COLUMN, 0, 0);
if( pColRef==0 ) return;
pColRef->iTable = pItem->iCursor;
pColRef->iColumn = k++;
@@ -127372,7 +128456,7 @@ static void constructAutomaticIndex(
** transient index on 2nd and subsequent iterations of the loop. */
v = pParse->pVdbe;
assert( v!=0 );
- addrInit = sqlite3CodeOnce(pParse); VdbeCoverage(v);
+ addrInit = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
/* Count the number of columns that will be added to the index
** and used to match WHERE clause constraints */
@@ -127547,7 +128631,8 @@ static sqlite3_index_info *allocateIndexInfo(
WhereClause *pWC,
Bitmask mUnusable, /* Ignore terms with these prereqs */
struct SrcList_item *pSrc,
- ExprList *pOrderBy
+ ExprList *pOrderBy,
+ u16 *pmNoOmit /* Mask of terms not to omit */
){
int i, j;
int nTerm;
@@ -127557,6 +128642,7 @@ static sqlite3_index_info *allocateIndexInfo(
WhereTerm *pTerm;
int nOrderBy;
sqlite3_index_info *pIdxInfo;
+ u16 mNoOmit = 0;
/* Count the number of possible WHERE clause constraints referring
** to this virtual table */
@@ -127645,6 +128731,15 @@ static sqlite3_index_info *allocateIndexInfo(
assert( WO_GE==SQLITE_INDEX_CONSTRAINT_GE );
assert( WO_MATCH==SQLITE_INDEX_CONSTRAINT_MATCH );
assert( pTerm->eOperator & (WO_IN|WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE|WO_MATCH) );
+
+ if( op & (WO_LT|WO_LE|WO_GT|WO_GE)
+ && sqlite3ExprIsVector(pTerm->pExpr->pRight)
+ ){
+ if( i<16 ) mNoOmit |= (1 << i);
+ if( op==WO_LT ) pIdxCons[j].op = WO_LE;
+ if( op==WO_GT ) pIdxCons[j].op = WO_GE;
+ }
+
j++;
}
for(i=0; i<nOrderBy; i++){
@@ -127653,6 +128748,7 @@ static sqlite3_index_info *allocateIndexInfo(
pIdxOrderBy[i].desc = pOrderBy->a[i].sortOrder;
}
+ *pmNoOmit = mNoOmit;
return pIdxInfo;
}
@@ -127928,7 +129024,7 @@ static LogEst whereRangeAdjust(WhereTerm *pTerm, LogEst nNew){
/*
** Return the affinity for a single column of an index.
*/
-static char sqlite3IndexColumnAffinity(sqlite3 *db, Index *pIdx, int iCol){
+SQLITE_PRIVATE char sqlite3IndexColumnAffinity(sqlite3 *db, Index *pIdx, int iCol){
assert( iCol>=0 && iCol<pIdx->nColumn );
if( !pIdx->zColAff ){
if( sqlite3IndexAffinityStr(db, pIdx)==0 ) return SQLITE_AFF_BLOB;
@@ -128105,7 +129201,8 @@ static int whereRangeScanEst(
if( nEq==pBuilder->nRecValid ){
UnpackedRecord *pRec = pBuilder->pRec;
tRowcnt a[2];
- u8 aff;
+ int nBtm = pLoop->u.btree.nBtm;
+ int nTop = pLoop->u.btree.nTop;
/* Variable iLower will be set to the estimate of the number of rows in
** the index that are less than the lower bound of the range query. The
@@ -128135,8 +129232,6 @@ static int whereRangeScanEst(
testcase( pRec->nField!=pBuilder->nRecValid );
pRec->nField = pBuilder->nRecValid;
}
- aff = sqlite3IndexColumnAffinity(pParse->db, p, nEq);
- assert( nEq!=p->nKeyCol || aff==SQLITE_AFF_INTEGER );
/* Determine iLower and iUpper using ($P) only. */
if( nEq==0 ){
iLower = 0;
@@ -128155,17 +129250,20 @@ static int whereRangeScanEst(
if( p->aSortOrder[nEq] ){
/* The roles of pLower and pUpper are swapped for a DESC index */
SWAP(WhereTerm*, pLower, pUpper);
+ SWAP(int, nBtm, nTop);
}
/* If possible, improve on the iLower estimate using ($P:$L). */
if( pLower ){
- int bOk; /* True if value is extracted from pExpr */
+ int n; /* Values extracted from pExpr */
Expr *pExpr = pLower->pExpr->pRight;
- rc = sqlite3Stat4ProbeSetValue(pParse, p, &pRec, pExpr, aff, nEq, &bOk);
- if( rc==SQLITE_OK && bOk ){
+ rc = sqlite3Stat4ProbeSetValue(pParse, p, &pRec, pExpr, nBtm, nEq, &n);
+ if( rc==SQLITE_OK && n ){
tRowcnt iNew;
+ u16 mask = WO_GT|WO_LE;
+ if( sqlite3ExprVectorSize(pExpr)>n ) mask = (WO_LE|WO_LT);
iLwrIdx = whereKeyStats(pParse, p, pRec, 0, a);
- iNew = a[0] + ((pLower->eOperator & (WO_GT|WO_LE)) ? a[1] : 0);
+ iNew = a[0] + ((pLower->eOperator & mask) ? a[1] : 0);
if( iNew>iLower ) iLower = iNew;
nOut--;
pLower = 0;
@@ -128174,13 +129272,15 @@ static int whereRangeScanEst(
/* If possible, improve on the iUpper estimate using ($P:$U). */
if( pUpper ){
- int bOk; /* True if value is extracted from pExpr */
+ int n; /* Values extracted from pExpr */
Expr *pExpr = pUpper->pExpr->pRight;
- rc = sqlite3Stat4ProbeSetValue(pParse, p, &pRec, pExpr, aff, nEq, &bOk);
- if( rc==SQLITE_OK && bOk ){
+ rc = sqlite3Stat4ProbeSetValue(pParse, p, &pRec, pExpr, nTop, nEq, &n);
+ if( rc==SQLITE_OK && n ){
tRowcnt iNew;
+ u16 mask = WO_GT|WO_LE;
+ if( sqlite3ExprVectorSize(pExpr)>n ) mask = (WO_LE|WO_LT);
iUprIdx = whereKeyStats(pParse, p, pRec, 1, a);
- iNew = a[0] + ((pUpper->eOperator & (WO_GT|WO_LE)) ? a[1] : 0);
+ iNew = a[0] + ((pUpper->eOperator & mask) ? a[1] : 0);
if( iNew<iUpper ) iUpper = iNew;
nOut--;
pUpper = 0;
@@ -128270,7 +129370,6 @@ static int whereEqualScanEst(
Index *p = pBuilder->pNew->u.btree.pIndex;
int nEq = pBuilder->pNew->u.btree.nEq;
UnpackedRecord *pRec = pBuilder->pRec;
- u8 aff; /* Column affinity */
int rc; /* Subfunction return code */
tRowcnt a[2]; /* Statistics */
int bOk;
@@ -128294,8 +129393,7 @@ static int whereEqualScanEst(
return SQLITE_OK;
}
- aff = sqlite3IndexColumnAffinity(pParse->db, p, nEq-1);
- rc = sqlite3Stat4ProbeSetValue(pParse, p, &pRec, pExpr, aff, nEq-1, &bOk);
+ rc = sqlite3Stat4ProbeSetValue(pParse, p, &pRec, pExpr, 1, nEq-1, &bOk);
pBuilder->pRec = pRec;
if( rc!=SQLITE_OK ) return rc;
if( bOk==0 ) return SQLITE_NOTFOUND;
@@ -128384,9 +129482,14 @@ static void whereTermPrint(WhereTerm *pTerm, int iTerm){
sqlite3_snprintf(sizeof(zLeft),zLeft,"left=%d", pTerm->leftCursor);
}
sqlite3DebugPrintf(
- "TERM-%-3d %p %s %-12s prob=%-3d op=0x%03x wtFlags=0x%04x\n",
+ "TERM-%-3d %p %s %-12s prob=%-3d op=0x%03x wtFlags=0x%04x",
iTerm, pTerm, zType, zLeft, pTerm->truthProb,
pTerm->eOperator, pTerm->wtFlags);
+ if( pTerm->iField ){
+ sqlite3DebugPrintf(" iField=%d\n", pTerm->iField);
+ }else{
+ sqlite3DebugPrintf("\n");
+ }
sqlite3TreeViewExpr(0, pTerm->pExpr, 0);
}
}
@@ -128908,6 +130011,72 @@ static void whereLoopOutputAdjust(
if( pLoop->nOut > nRow-iReduce ) pLoop->nOut = nRow - iReduce;
}
+/*
+** Term pTerm is a vector range comparison operation. The first comparison
+** in the vector can be optimized using column nEq of the index. This
+** function returns the total number of vector elements that can be used
+** as part of the range comparison.
+**
+** For example, if the query is:
+**
+** WHERE a = ? AND (b, c, d) > (?, ?, ?)
+**
+** and the index:
+**
+** CREATE INDEX ... ON (a, b, c, d, e)
+**
+** then this function would be invoked with nEq=1. The value returned in
+** this case is 3.
+*/
+static int whereRangeVectorLen(
+ Parse *pParse, /* Parsing context */
+ int iCur, /* Cursor open on pIdx */
+ Index *pIdx, /* The index to be used for a inequality constraint */
+ int nEq, /* Number of prior equality constraints on same index */
+ WhereTerm *pTerm /* The vector inequality constraint */
+){
+ int nCmp = sqlite3ExprVectorSize(pTerm->pExpr->pLeft);
+ int i;
+
+ nCmp = MIN(nCmp, (pIdx->nColumn - nEq));
+ for(i=1; i<nCmp; i++){
+ /* Test if comparison i of pTerm is compatible with column (i+nEq)
+ ** of the index. If not, exit the loop. */
+ char aff; /* Comparison affinity */
+ char idxaff = 0; /* Indexed columns affinity */
+ CollSeq *pColl; /* Comparison collation sequence */
+ Expr *pLhs = pTerm->pExpr->pLeft->x.pList->a[i].pExpr;
+ Expr *pRhs = pTerm->pExpr->pRight;
+ if( pRhs->flags & EP_xIsSelect ){
+ pRhs = pRhs->x.pSelect->pEList->a[i].pExpr;
+ }else{
+ pRhs = pRhs->x.pList->a[i].pExpr;
+ }
+
+ /* Check that the LHS of the comparison is a column reference to
+ ** the right column of the right source table. And that the sort
+ ** order of the index column is the same as the sort order of the
+ ** leftmost index column. */
+ if( pLhs->op!=TK_COLUMN
+ || pLhs->iTable!=iCur
+ || pLhs->iColumn!=pIdx->aiColumn[i+nEq]
+ || pIdx->aSortOrder[i+nEq]!=pIdx->aSortOrder[nEq]
+ ){
+ break;
+ }
+
+ testcase( pLhs->iColumn==XN_ROWID );
+ aff = sqlite3CompareAffinity(pRhs, sqlite3ExprAffinity(pLhs));
+ idxaff = sqlite3TableColumnAffinity(pIdx->pTable, pLhs->iColumn);
+ if( aff!=idxaff ) break;
+
+ pColl = sqlite3BinaryCompareCollSeq(pParse, pLhs, pRhs);
+ if( pColl==0 ) break;
+ if( sqlite3StrICmp(pColl->zName, pIdx->azColl[i+nEq]) ) break;
+ }
+ return i;
+}
+
/*
** Adjust the cost C by the costMult facter T. This only occurs if
** compiled with -DSQLITE_ENABLE_COSTMULT
@@ -128946,6 +130115,8 @@ static int whereLoopAddBtreeIndex(
Bitmask saved_prereq; /* Original value of pNew->prereq */
u16 saved_nLTerm; /* Original value of pNew->nLTerm */
u16 saved_nEq; /* Original value of pNew->u.btree.nEq */
+ u16 saved_nBtm; /* Original value of pNew->u.btree.nBtm */
+ u16 saved_nTop; /* Original value of pNew->u.btree.nTop */
u16 saved_nSkip; /* Original value of pNew->nSkip */
u32 saved_wsFlags; /* Original value of pNew->wsFlags */
LogEst saved_nOut; /* Original value of pNew->nOut */
@@ -128956,12 +130127,15 @@ static int whereLoopAddBtreeIndex(
pNew = pBuilder->pNew;
if( db->mallocFailed ) return SQLITE_NOMEM_BKPT;
+ WHERETRACE(0x800, ("BEGIN addBtreeIdx(%s), nEq=%d\n",
+ pProbe->zName, pNew->u.btree.nEq));
assert( (pNew->wsFlags & WHERE_VIRTUALTABLE)==0 );
assert( (pNew->wsFlags & WHERE_TOP_LIMIT)==0 );
if( pNew->wsFlags & WHERE_BTM_LIMIT ){
opMask = WO_LT|WO_LE;
}else{
+ assert( pNew->u.btree.nBtm==0 );
opMask = WO_EQ|WO_IN|WO_GT|WO_GE|WO_LT|WO_LE|WO_ISNULL|WO_IS;
}
if( pProbe->bUnordered ) opMask &= ~(WO_GT|WO_GE|WO_LT|WO_LE);
@@ -128969,6 +130143,8 @@ static int whereLoopAddBtreeIndex(
assert( pNew->u.btree.nEq<pProbe->nColumn );
saved_nEq = pNew->u.btree.nEq;
+ saved_nBtm = pNew->u.btree.nBtm;
+ saved_nTop = pNew->u.btree.nTop;
saved_nSkip = pNew->nSkip;
saved_nLTerm = pNew->nLTerm;
saved_wsFlags = pNew->wsFlags;
@@ -129012,6 +130188,8 @@ static int whereLoopAddBtreeIndex(
pNew->wsFlags = saved_wsFlags;
pNew->u.btree.nEq = saved_nEq;
+ pNew->u.btree.nBtm = saved_nBtm;
+ pNew->u.btree.nTop = saved_nTop;
pNew->nLTerm = saved_nLTerm;
if( whereLoopResize(db, pNew, pNew->nLTerm+1) ) break; /* OOM */
pNew->aLTerm[pNew->nLTerm++] = pTerm;
@@ -129028,14 +130206,23 @@ static int whereLoopAddBtreeIndex(
pNew->wsFlags |= WHERE_COLUMN_IN;
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
/* "x IN (SELECT ...)": TUNING: the SELECT returns 25 rows */
+ int i;
nIn = 46; assert( 46==sqlite3LogEst(25) );
+
+ /* The expression may actually be of the form (x, y) IN (SELECT...).
+ ** In this case there is a separate term for each of (x) and (y).
+ ** However, the nIn multiplier should only be applied once, not once
+ ** for each such term. The following loop checks that pTerm is the
+ ** first such term in use, and sets nIn back to 0 if it is not. */
+ for(i=0; i<pNew->nLTerm-1; i++){
+ if( pNew->aLTerm[i] && pNew->aLTerm[i]->pExpr==pExpr ) nIn = 0;
+ }
}else if( ALWAYS(pExpr->x.pList && pExpr->x.pList->nExpr) ){
/* "x IN (value, value, ...)" */
nIn = sqlite3LogEst(pExpr->x.pList->nExpr);
+ assert( nIn>0 ); /* RHS always has 2 or more terms... The parser
+ ** changes "x IN (?)" into "x=?". */
}
- assert( nIn>0 ); /* RHS always has 2 or more terms... The parser
- ** changes "x IN (?)" into "x=?". */
-
}else if( eOp & (WO_EQ|WO_IS) ){
int iCol = pProbe->aiColumn[saved_nEq];
pNew->wsFlags |= WHERE_COLUMN_EQ;
@@ -129055,6 +130242,9 @@ static int whereLoopAddBtreeIndex(
testcase( eOp & WO_GT );
testcase( eOp & WO_GE );
pNew->wsFlags |= WHERE_COLUMN_RANGE|WHERE_BTM_LIMIT;
+ pNew->u.btree.nBtm = whereRangeVectorLen(
+ pParse, pSrc->iCursor, pProbe, saved_nEq, pTerm
+ );
pBtm = pTerm;
pTop = 0;
if( pTerm->wtFlags & TERM_LIKEOPT ){
@@ -129067,12 +130257,16 @@ static int whereLoopAddBtreeIndex(
if( whereLoopResize(db, pNew, pNew->nLTerm+1) ) break; /* OOM */
pNew->aLTerm[pNew->nLTerm++] = pTop;
pNew->wsFlags |= WHERE_TOP_LIMIT;
+ pNew->u.btree.nTop = 1;
}
}else{
assert( eOp & (WO_LT|WO_LE) );
testcase( eOp & WO_LT );
testcase( eOp & WO_LE );
pNew->wsFlags |= WHERE_COLUMN_RANGE|WHERE_TOP_LIMIT;
+ pNew->u.btree.nTop = whereRangeVectorLen(
+ pParse, pSrc->iCursor, pProbe, saved_nEq, pTerm
+ );
pTop = pTerm;
pBtm = (pNew->wsFlags & WHERE_BTM_LIMIT)!=0 ?
pNew->aLTerm[pNew->nLTerm-2] : 0;
@@ -129172,6 +130366,8 @@ static int whereLoopAddBtreeIndex(
}
pNew->prereq = saved_prereq;
pNew->u.btree.nEq = saved_nEq;
+ pNew->u.btree.nBtm = saved_nBtm;
+ pNew->u.btree.nTop = saved_nTop;
pNew->nSkip = saved_nSkip;
pNew->wsFlags = saved_wsFlags;
pNew->nOut = saved_nOut;
@@ -129211,6 +130407,8 @@ static int whereLoopAddBtreeIndex(
pNew->wsFlags = saved_wsFlags;
}
+ WHERETRACE(0x800, ("END addBtreeIdx(%s), nEq=%d, rc=%d\n",
+ pProbe->zName, saved_nEq, rc));
return rc;
}
@@ -129293,7 +130491,7 @@ static int whereUsablePartialIndex(int iTab, WhereClause *pWC, Expr *pWhere){
/*
** Add all WhereLoop objects for a single table of the join where the table
-** is idenfied by pBuilder->pNew->iTab. That table is guaranteed to be
+** is identified by pBuilder->pNew->iTab. That table is guaranteed to be
** a b-tree table, not a virtual table.
**
** The costs (WhereLoop.rRun) of the b-tree loops added by this function
@@ -129447,6 +130645,8 @@ static int whereLoopAddBtree(
}
rSize = pProbe->aiRowLogEst[0];
pNew->u.btree.nEq = 0;
+ pNew->u.btree.nBtm = 0;
+ pNew->u.btree.nTop = 0;
pNew->nSkip = 0;
pNew->nLTerm = 0;
pNew->iSortIdx = 0;
@@ -129575,6 +130775,7 @@ static int whereLoopAddVirtualOne(
Bitmask mUsable, /* Mask of usable tables */
u16 mExclude, /* Exclude terms using these operators */
sqlite3_index_info *pIdxInfo, /* Populated object for xBestIndex */
+ u16 mNoOmit, /* Do not omit these constraints */
int *pbIn /* OUT: True if plan uses an IN(...) op */
){
WhereClause *pWC = pBuilder->pWC;
@@ -129663,6 +130864,7 @@ static int whereLoopAddVirtualOne(
}
}
}
+ pNew->u.vtab.omitMask &= ~mNoOmit;
pNew->nLTerm = mxTerm+1;
assert( pNew->nLTerm<=pNew->nLSlot );
@@ -129736,6 +130938,7 @@ static int whereLoopAddVirtual(
int bIn; /* True if plan uses IN(...) operator */
WhereLoop *pNew;
Bitmask mBest; /* Tables used by best possible plan */
+ u16 mNoOmit;
assert( (mPrereq & mUnusable)==0 );
pWInfo = pBuilder->pWInfo;
@@ -129744,7 +130947,8 @@ static int whereLoopAddVirtual(
pNew = pBuilder->pNew;
pSrc = &pWInfo->pTabList->a[pNew->iTab];
assert( IsVirtual(pSrc->pTab) );
- p = allocateIndexInfo(pParse, pWC, mUnusable, pSrc, pBuilder->pOrderBy);
+ p = allocateIndexInfo(pParse, pWC, mUnusable, pSrc, pBuilder->pOrderBy,
+ &mNoOmit);
if( p==0 ) return SQLITE_NOMEM_BKPT;
pNew->rSetup = 0;
pNew->wsFlags = WHERE_VIRTUALTABLE;
@@ -129758,7 +130962,7 @@ static int whereLoopAddVirtual(
/* First call xBestIndex() with all constraints usable. */
WHERETRACE(0x40, (" VirtualOne: all usable\n"));
- rc = whereLoopAddVirtualOne(pBuilder, mPrereq, ALLBITS, 0, p, &bIn);
+ rc = whereLoopAddVirtualOne(pBuilder, mPrereq, ALLBITS, 0, p, mNoOmit, &bIn);
/* If the call to xBestIndex() with all terms enabled produced a plan
** that does not require any source tables (IOW: a plan with mBest==0),
@@ -129775,7 +130979,8 @@ static int whereLoopAddVirtual(
** xBestIndex again, this time with IN(...) terms disabled. */
if( bIn ){
WHERETRACE(0x40, (" VirtualOne: all usable w/o IN\n"));
- rc = whereLoopAddVirtualOne(pBuilder, mPrereq, ALLBITS, WO_IN, p, &bIn);
+ rc = whereLoopAddVirtualOne(
+ pBuilder, mPrereq, ALLBITS, WO_IN, p, mNoOmit, &bIn);
assert( bIn==0 );
mBestNoIn = pNew->prereq & ~mPrereq;
if( mBestNoIn==0 ){
@@ -129801,7 +131006,8 @@ static int whereLoopAddVirtual(
if( mNext==mBest || mNext==mBestNoIn ) continue;
WHERETRACE(0x40, (" VirtualOne: mPrev=%04llx mNext=%04llx\n",
(sqlite3_uint64)mPrev, (sqlite3_uint64)mNext));
- rc = whereLoopAddVirtualOne(pBuilder, mPrereq, mNext|mPrereq, 0, p, &bIn);
+ rc = whereLoopAddVirtualOne(
+ pBuilder, mPrereq, mNext|mPrereq, 0, p, mNoOmit, &bIn);
if( pNew->prereq==mPrereq ){
seenZero = 1;
if( bIn==0 ) seenZeroNoIN = 1;
@@ -129813,7 +131019,8 @@ static int whereLoopAddVirtual(
** usable), make a call here with all source tables disabled */
if( rc==SQLITE_OK && seenZero==0 ){
WHERETRACE(0x40, (" VirtualOne: all disabled\n"));
- rc = whereLoopAddVirtualOne(pBuilder, mPrereq, mPrereq, 0, p, &bIn);
+ rc = whereLoopAddVirtualOne(
+ pBuilder, mPrereq, mPrereq, 0, p, mNoOmit, &bIn);
if( bIn==0 ) seenZeroNoIN = 1;
}
@@ -129822,7 +131029,8 @@ static int whereLoopAddVirtual(
** operator, make a final call to obtain one here. */
if( rc==SQLITE_OK && seenZeroNoIN==0 ){
WHERETRACE(0x40, (" VirtualOne: all disabled and w/o IN\n"));
- rc = whereLoopAddVirtualOne(pBuilder, mPrereq, mPrereq, WO_IN, p, &bIn);
+ rc = whereLoopAddVirtualOne(
+ pBuilder, mPrereq, mPrereq, WO_IN, p, mNoOmit, &bIn);
}
}
@@ -130166,20 +131374,42 @@ static i8 wherePathSatisfiesOrderBy(
rev = revSet = 0;
distinctColumns = 0;
for(j=0; j<nColumn; j++){
- u8 bOnce; /* True to run the ORDER BY search loop */
+ u8 bOnce = 1; /* True to run the ORDER BY search loop */
- /* Skip over == and IS and ISNULL terms.
- ** (Also skip IN terms when doing WHERE_ORDERBY_LIMIT processing)
- */
- if( j<pLoop->u.btree.nEq
- && pLoop->nSkip==0
- && ((i = pLoop->aLTerm[j]->eOperator) & eqOpMask)!=0
- ){
- if( i & WO_ISNULL ){
- testcase( isOrderDistinct );
- isOrderDistinct = 0;
+ assert( j>=pLoop->u.btree.nEq
+ || (pLoop->aLTerm[j]==0)==(j<pLoop->nSkip)
+ );
+ if( j<pLoop->u.btree.nEq && j>=pLoop->nSkip ){
+ u16 eOp = pLoop->aLTerm[j]->eOperator;
+
+ /* Skip over == and IS and ISNULL terms. (Also skip IN terms when
+ ** doing WHERE_ORDERBY_LIMIT processing).
+ **
+ ** If the current term is a column of an ((?,?) IN (SELECT...))
+ ** expression for which the SELECT returns more than one column,
+ ** check that it is the only column used by this loop. Otherwise,
+ ** if it is one of two or more, none of the columns can be
+ ** considered to match an ORDER BY term. */
+ if( (eOp & eqOpMask)!=0 ){
+ if( eOp & WO_ISNULL ){
+ testcase( isOrderDistinct );
+ isOrderDistinct = 0;
+ }
+ continue;
+ }else if( ALWAYS(eOp & WO_IN) ){
+ /* ALWAYS() justification: eOp is an equality operator due to the
+ ** j<pLoop->u.btree.nEq constraint above. Any equality other
+ ** than WO_IN is captured by the previous "if". So this one
+ ** always has to be WO_IN. */
+ Expr *pX = pLoop->aLTerm[j]->pExpr;
+ for(i=j+1; i<pLoop->u.btree.nEq; i++){
+ if( pLoop->aLTerm[i]->pExpr==pX ){
+ assert( (pLoop->aLTerm[i]->eOperator & WO_IN) );
+ bOnce = 0;
+ break;
+ }
+ }
}
- continue;
}
/* Get the column number in the table (iColumn) and sort order
@@ -130208,7 +131438,6 @@ static i8 wherePathSatisfiesOrderBy(
/* Find the ORDER BY term that corresponds to the j-th column
** of the index and mark that ORDER BY term off
*/
- bOnce = 1;
isMatch = 0;
for(i=0; bOnce && i<nOrderBy; i++){
if( MASKBIT(i) & obSat ) continue;
@@ -130245,7 +131474,7 @@ static i8 wherePathSatisfiesOrderBy(
}
}
if( isMatch ){
- if( iColumn<0 ){
+ if( iColumn==XN_ROWID ){
testcase( distinctColumns==0 );
distinctColumns = 1;
}
@@ -130700,13 +131929,20 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
pWInfo->revMask = pFrom->revLoop;
if( pWInfo->nOBSat<=0 ){
pWInfo->nOBSat = 0;
- if( nLoop>0 && (pFrom->aLoop[nLoop-1]->wsFlags & WHERE_ONEROW)==0 ){
- Bitmask m = 0;
- int rc = wherePathSatisfiesOrderBy(pWInfo, pWInfo->pOrderBy, pFrom,
+ if( nLoop>0 ){
+ u32 wsFlags = pFrom->aLoop[nLoop-1]->wsFlags;
+ if( (wsFlags & WHERE_ONEROW)==0
+ && (wsFlags&(WHERE_IPK|WHERE_COLUMN_IN))!=(WHERE_IPK|WHERE_COLUMN_IN)
+ ){
+ Bitmask m = 0;
+ int rc = wherePathSatisfiesOrderBy(pWInfo, pWInfo->pOrderBy, pFrom,
WHERE_ORDERBY_LIMIT, nLoop-1, pFrom->aLoop[nLoop-1], &m);
- if( rc==pWInfo->pOrderBy->nExpr ){
- pWInfo->bOrderedInnerLoop = 1;
- pWInfo->revMask = m;
+ testcase( wsFlags & WHERE_IPK );
+ testcase( wsFlags & WHERE_COLUMN_IN );
+ if( rc==pWInfo->pOrderBy->nExpr ){
+ pWInfo->bOrderedInnerLoop = 1;
+ pWInfo->revMask = m;
+ }
}
}
}
@@ -130983,22 +132219,25 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
** some architectures. Hence the ROUND8() below.
*/
nByteWInfo = ROUND8(sizeof(WhereInfo)+(nTabList-1)*sizeof(WhereLevel));
- pWInfo = sqlite3DbMallocZero(db, nByteWInfo + sizeof(WhereLoop));
+ pWInfo = sqlite3DbMallocRawNN(db, nByteWInfo + sizeof(WhereLoop));
if( db->mallocFailed ){
sqlite3DbFree(db, pWInfo);
pWInfo = 0;
goto whereBeginError;
}
- pWInfo->aiCurOnePass[0] = pWInfo->aiCurOnePass[1] = -1;
- pWInfo->nLevel = nTabList;
pWInfo->pParse = pParse;
pWInfo->pTabList = pTabList;
pWInfo->pOrderBy = pOrderBy;
pWInfo->pDistinctSet = pDistinctSet;
+ pWInfo->aiCurOnePass[0] = pWInfo->aiCurOnePass[1] = -1;
+ pWInfo->nLevel = nTabList;
pWInfo->iBreak = pWInfo->iContinue = sqlite3VdbeMakeLabel(v);
pWInfo->wctrlFlags = wctrlFlags;
pWInfo->iLimit = iAuxArg;
pWInfo->savedNQueryLoop = pParse->nQueryLoop;
+ memset(&pWInfo->nOBSat, 0,
+ offsetof(WhereInfo,sWC) - offsetof(WhereInfo,nOBSat));
+ memset(&pWInfo->a[0], 0, sizeof(WhereLoop)+nTabList*sizeof(WhereLevel));
assert( pWInfo->eOnePass==ONEPASS_OFF ); /* ONEPASS defaults to OFF */
pMaskSet = &pWInfo->sMaskSet;
sWLB.pWInfo = pWInfo;
@@ -131402,10 +132641,12 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){
sqlite3VdbeResolveLabel(v, pLevel->addrNxt);
for(j=pLevel->u.in.nIn, pIn=&pLevel->u.in.aInLoop[j-1]; j>0; j--, pIn--){
sqlite3VdbeJumpHere(v, pIn->addrInTop+1);
- sqlite3VdbeAddOp2(v, pIn->eEndLoopOp, pIn->iCur, pIn->addrInTop);
- VdbeCoverage(v);
- VdbeCoverageIf(v, pIn->eEndLoopOp==OP_PrevIfOpen);
- VdbeCoverageIf(v, pIn->eEndLoopOp==OP_NextIfOpen);
+ if( pIn->eEndLoopOp!=OP_Noop ){
+ sqlite3VdbeAddOp2(v, pIn->eEndLoopOp, pIn->iCur, pIn->addrInTop);
+ VdbeCoverage(v);
+ VdbeCoverageIf(v, pIn->eEndLoopOp==OP_PrevIfOpen);
+ VdbeCoverageIf(v, pIn->eEndLoopOp==OP_NextIfOpen);
+ }
sqlite3VdbeJumpHere(v, pIn->addrInTop-1);
}
}
@@ -131424,13 +132665,15 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){
}
#endif
if( pLevel->iLeftJoin ){
+ int ws = pLoop->wsFlags;
addr = sqlite3VdbeAddOp1(v, OP_IfPos, pLevel->iLeftJoin); VdbeCoverage(v);
- assert( (pLoop->wsFlags & WHERE_IDX_ONLY)==0
- || (pLoop->wsFlags & WHERE_INDEXED)!=0 );
- if( (pLoop->wsFlags & WHERE_IDX_ONLY)==0 ){
+ assert( (ws & WHERE_IDX_ONLY)==0 || (ws & WHERE_INDEXED)!=0 );
+ if( (ws & WHERE_IDX_ONLY)==0 ){
sqlite3VdbeAddOp1(v, OP_NullRow, pTabList->a[i].iCursor);
}
- if( pLoop->wsFlags & WHERE_INDEXED ){
+ if( (ws & WHERE_INDEXED)
+ || ((ws & WHERE_MULTI_OR) && pLevel->u.pCovidx)
+ ){
sqlite3VdbeAddOp1(v, OP_NullRow, pLevel->iIdxCur);
}
if( pLevel->op==OP_Return ){
@@ -131608,15 +132851,6 @@ struct LimitVal {
};
/*
-** An instance of this structure is used to store the LIKE,
-** GLOB, NOT LIKE, and NOT GLOB operators.
-*/
-struct LikeOp {
- Token eOperator; /* "like" or "glob" or "regexp" */
- int bNot; /* True if the NOT keyword is present */
-};
-
-/*
** An instance of the following structure describes the event of a
** TRIGGER. "a" is the event type, one of TK_UPDATE, TK_INSERT,
** TK_DELETE, or TK_INSTEAD. If the event is of the form
@@ -131628,11 +132862,6 @@ struct LikeOp {
struct TrigEvent { int a; IdList * b; };
/*
-** An instance of this structure holds the ATTACH key and the key type.
-*/
-struct AttachKey { int type; Token key; };
-
-/*
** Disable lookaside memory allocation for objects that might be
** shared across database connections.
*/
@@ -131678,7 +132907,24 @@ static void disableLookaside(Parse *pParse){
** that created the expression.
*/
static void spanExpr(ExprSpan *pOut, Parse *pParse, int op, Token t){
- pOut->pExpr = sqlite3PExpr(pParse, op, 0, 0, &t);
+ Expr *p = sqlite3DbMallocRawNN(pParse->db, sizeof(Expr)+t.n+1);
+ if( p ){
+ memset(p, 0, sizeof(Expr));
+ p->op = (u8)op;
+ p->flags = EP_Leaf;
+ p->iAgg = -1;
+ p->u.zToken = (char*)&p[1];
+ memcpy(p->u.zToken, t.z, t.n);
+ p->u.zToken[t.n] = 0;
+ if( sqlite3Isquote(p->u.zToken[0]) ){
+ if( p->u.zToken[0]=='"' ) p->flags |= EP_DblQuoted;
+ sqlite3Dequote(p->u.zToken);
+ }
+#if SQLITE_MAX_EXPR_DEPTH>0
+ p->nHeight = 1;
+#endif
+ }
+ pOut->pExpr = p;
pOut->zStart = t.z;
pOut->zEnd = &t.z[t.n];
}
@@ -131841,7 +133087,6 @@ typedef union {
With* yy285;
struct TrigEvent yy332;
struct LimitVal yy354;
- struct LikeOp yy392;
struct {int value; int mask;} yy497;
} YYMINORTYPE;
#ifndef YYSTACKDEPTH
@@ -131852,16 +133097,16 @@ typedef union {
#define sqlite3ParserARG_FETCH Parse *pParse = yypParser->pParse
#define sqlite3ParserARG_STORE yypParser->pParse = pParse
#define YYFALLBACK 1
-#define YYNSTATE 443
-#define YYNRULE 328
-#define YY_MAX_SHIFT 442
-#define YY_MIN_SHIFTREDUCE 653
-#define YY_MAX_SHIFTREDUCE 980
-#define YY_MIN_REDUCE 981
-#define YY_MAX_REDUCE 1308
-#define YY_ERROR_ACTION 1309
-#define YY_ACCEPT_ACTION 1310
-#define YY_NO_ACTION 1311
+#define YYNSTATE 456
+#define YYNRULE 332
+#define YY_MAX_SHIFT 455
+#define YY_MIN_SHIFTREDUCE 668
+#define YY_MAX_SHIFTREDUCE 999
+#define YY_MIN_REDUCE 1000
+#define YY_MAX_REDUCE 1331
+#define YY_ERROR_ACTION 1332
+#define YY_ACCEPT_ACTION 1333
+#define YY_NO_ACTION 1334
/************* End control #defines *******************************************/
/* Define the yytestcase() macro to be a no-op if is not already defined
@@ -131893,7 +133138,7 @@ typedef union {
**
** N between YY_MIN_REDUCE Reduce by rule N-YY_MIN_REDUCE
** and YY_MAX_REDUCE
-
+**
** N == YY_ERROR_ACTION A syntax error has occurred.
**
** N == YY_ACCEPT_ACTION The parser accepts its input.
@@ -131902,16 +133147,20 @@ typedef union {
** slots in the yy_action[] table.
**
** The action table is constructed as a single large table named yy_action[].
-** Given state S and lookahead X, the action is computed as
+** Given state S and lookahead X, the action is computed as either:
**
-** yy_action[ yy_shift_ofst[S] + X ]
+** (A) N = yy_action[ yy_shift_ofst[S] + X ]
+** (B) N = yy_default[S]
**
-** If the index value yy_shift_ofst[S]+X is out of range or if the value
-** yy_lookahead[yy_shift_ofst[S]+X] is not equal to X or if yy_shift_ofst[S]
-** is equal to YY_SHIFT_USE_DFLT, it means that the action is not in the table
-** and that yy_default[S] should be used instead.
+** The (A) formula is preferred. The B formula is used instead if:
+** (1) The yy_shift_ofst[S]+X value is out of range, or
+** (2) yy_lookahead[yy_shift_ofst[S]+X] is not equal to X, or
+** (3) yy_shift_ofst[S] equal YY_SHIFT_USE_DFLT.
+** (Implementation note: YY_SHIFT_USE_DFLT is chosen so that
+** YY_SHIFT_USE_DFLT+X will be out of range for all possible lookaheads X.
+** Hence only tests (1) and (2) need to be evaluated.)
**
-** The formula above is for computing the action when the lookahead is
+** The formulas above are for computing the action when the lookahead is
** a terminal symbol. If the lookahead is a non-terminal (as occurs after
** a reduce action) then the yy_reduce_ofst[] array is used in place of
** the yy_shift_ofst[] array and YY_REDUCE_USE_DFLT is used in place of
@@ -131929,159 +133178,165 @@ typedef union {
** yy_default[] Default action for each state.
**
*********** Begin parsing tables **********************************************/
-#define YY_ACTTAB_COUNT (1507)
+#define YY_ACTTAB_COUNT (1567)
static const YYACTIONTYPE yy_action[] = {
- /* 0 */ 317, 814, 341, 808, 5, 195, 195, 802, 93, 94,
- /* 10 */ 84, 823, 823, 835, 838, 827, 827, 91, 91, 92,
- /* 20 */ 92, 92, 92, 293, 90, 90, 90, 90, 89, 89,
- /* 30 */ 88, 88, 88, 87, 341, 317, 958, 958, 807, 807,
- /* 40 */ 807, 928, 344, 93, 94, 84, 823, 823, 835, 838,
- /* 50 */ 827, 827, 91, 91, 92, 92, 92, 92, 328, 90,
- /* 60 */ 90, 90, 90, 89, 89, 88, 88, 88, 87, 341,
- /* 70 */ 89, 89, 88, 88, 88, 87, 341, 776, 958, 958,
- /* 80 */ 317, 88, 88, 88, 87, 341, 777, 69, 93, 94,
- /* 90 */ 84, 823, 823, 835, 838, 827, 827, 91, 91, 92,
- /* 100 */ 92, 92, 92, 437, 90, 90, 90, 90, 89, 89,
- /* 110 */ 88, 88, 88, 87, 341, 1310, 147, 147, 2, 317,
- /* 120 */ 76, 25, 74, 49, 49, 87, 341, 93, 94, 84,
- /* 130 */ 823, 823, 835, 838, 827, 827, 91, 91, 92, 92,
- /* 140 */ 92, 92, 95, 90, 90, 90, 90, 89, 89, 88,
- /* 150 */ 88, 88, 87, 341, 939, 939, 317, 260, 415, 400,
- /* 160 */ 398, 58, 737, 737, 93, 94, 84, 823, 823, 835,
- /* 170 */ 838, 827, 827, 91, 91, 92, 92, 92, 92, 57,
- /* 180 */ 90, 90, 90, 90, 89, 89, 88, 88, 88, 87,
- /* 190 */ 341, 317, 1253, 928, 344, 269, 940, 941, 242, 93,
- /* 200 */ 94, 84, 823, 823, 835, 838, 827, 827, 91, 91,
- /* 210 */ 92, 92, 92, 92, 293, 90, 90, 90, 90, 89,
- /* 220 */ 89, 88, 88, 88, 87, 341, 317, 919, 1303, 793,
- /* 230 */ 691, 1303, 724, 724, 93, 94, 84, 823, 823, 835,
- /* 240 */ 838, 827, 827, 91, 91, 92, 92, 92, 92, 337,
- /* 250 */ 90, 90, 90, 90, 89, 89, 88, 88, 88, 87,
- /* 260 */ 341, 317, 114, 919, 1304, 684, 395, 1304, 124, 93,
- /* 270 */ 94, 84, 823, 823, 835, 838, 827, 827, 91, 91,
- /* 280 */ 92, 92, 92, 92, 683, 90, 90, 90, 90, 89,
- /* 290 */ 89, 88, 88, 88, 87, 341, 317, 86, 83, 169,
- /* 300 */ 801, 917, 234, 399, 93, 94, 84, 823, 823, 835,
- /* 310 */ 838, 827, 827, 91, 91, 92, 92, 92, 92, 686,
- /* 320 */ 90, 90, 90, 90, 89, 89, 88, 88, 88, 87,
- /* 330 */ 341, 317, 436, 742, 86, 83, 169, 917, 741, 93,
- /* 340 */ 94, 84, 823, 823, 835, 838, 827, 827, 91, 91,
- /* 350 */ 92, 92, 92, 92, 902, 90, 90, 90, 90, 89,
- /* 360 */ 89, 88, 88, 88, 87, 341, 317, 321, 434, 434,
- /* 370 */ 434, 1, 722, 722, 93, 94, 84, 823, 823, 835,
- /* 380 */ 838, 827, 827, 91, 91, 92, 92, 92, 92, 190,
- /* 390 */ 90, 90, 90, 90, 89, 89, 88, 88, 88, 87,
- /* 400 */ 341, 317, 685, 292, 939, 939, 150, 977, 310, 93,
- /* 410 */ 94, 84, 823, 823, 835, 838, 827, 827, 91, 91,
- /* 420 */ 92, 92, 92, 92, 437, 90, 90, 90, 90, 89,
- /* 430 */ 89, 88, 88, 88, 87, 341, 926, 2, 372, 719,
- /* 440 */ 698, 369, 950, 317, 49, 49, 940, 941, 719, 177,
- /* 450 */ 72, 93, 94, 84, 823, 823, 835, 838, 827, 827,
- /* 460 */ 91, 91, 92, 92, 92, 92, 322, 90, 90, 90,
- /* 470 */ 90, 89, 89, 88, 88, 88, 87, 341, 317, 415,
- /* 480 */ 405, 824, 824, 836, 839, 75, 93, 82, 84, 823,
- /* 490 */ 823, 835, 838, 827, 827, 91, 91, 92, 92, 92,
- /* 500 */ 92, 430, 90, 90, 90, 90, 89, 89, 88, 88,
- /* 510 */ 88, 87, 341, 317, 340, 340, 340, 658, 659, 660,
- /* 520 */ 333, 288, 94, 84, 823, 823, 835, 838, 827, 827,
- /* 530 */ 91, 91, 92, 92, 92, 92, 437, 90, 90, 90,
- /* 540 */ 90, 89, 89, 88, 88, 88, 87, 341, 317, 882,
- /* 550 */ 882, 375, 828, 66, 330, 409, 49, 49, 84, 823,
- /* 560 */ 823, 835, 838, 827, 827, 91, 91, 92, 92, 92,
- /* 570 */ 92, 351, 90, 90, 90, 90, 89, 89, 88, 88,
- /* 580 */ 88, 87, 341, 80, 432, 742, 3, 1180, 351, 350,
- /* 590 */ 741, 334, 796, 939, 939, 761, 80, 432, 278, 3,
- /* 600 */ 204, 161, 279, 393, 274, 392, 191, 362, 437, 277,
- /* 610 */ 745, 77, 78, 272, 800, 254, 355, 243, 79, 342,
- /* 620 */ 342, 86, 83, 169, 77, 78, 234, 399, 49, 49,
- /* 630 */ 435, 79, 342, 342, 437, 940, 941, 186, 442, 655,
- /* 640 */ 390, 387, 386, 435, 235, 213, 108, 421, 761, 351,
- /* 650 */ 437, 385, 167, 732, 10, 10, 124, 124, 671, 814,
- /* 660 */ 421, 439, 438, 415, 414, 802, 362, 168, 327, 124,
- /* 670 */ 49, 49, 814, 219, 439, 438, 800, 186, 802, 326,
- /* 680 */ 390, 387, 386, 437, 1248, 1248, 23, 939, 939, 80,
- /* 690 */ 432, 385, 3, 761, 416, 876, 807, 807, 807, 809,
- /* 700 */ 19, 290, 149, 49, 49, 415, 396, 260, 910, 807,
- /* 710 */ 807, 807, 809, 19, 312, 237, 145, 77, 78, 746,
- /* 720 */ 168, 702, 437, 149, 79, 342, 342, 114, 358, 940,
- /* 730 */ 941, 302, 223, 397, 345, 313, 435, 260, 415, 417,
- /* 740 */ 858, 374, 31, 31, 80, 432, 761, 3, 348, 92,
- /* 750 */ 92, 92, 92, 421, 90, 90, 90, 90, 89, 89,
- /* 760 */ 88, 88, 88, 87, 341, 814, 114, 439, 438, 796,
- /* 770 */ 367, 802, 77, 78, 701, 796, 124, 1187, 220, 79,
- /* 780 */ 342, 342, 124, 747, 734, 939, 939, 775, 404, 939,
- /* 790 */ 939, 435, 254, 360, 253, 402, 895, 346, 254, 360,
- /* 800 */ 253, 774, 807, 807, 807, 809, 19, 800, 421, 90,
- /* 810 */ 90, 90, 90, 89, 89, 88, 88, 88, 87, 341,
- /* 820 */ 814, 114, 439, 438, 939, 939, 802, 940, 941, 114,
- /* 830 */ 437, 940, 941, 86, 83, 169, 192, 166, 309, 979,
- /* 840 */ 70, 432, 700, 3, 382, 870, 238, 86, 83, 169,
- /* 850 */ 10, 10, 361, 406, 763, 190, 222, 807, 807, 807,
- /* 860 */ 809, 19, 870, 872, 329, 24, 940, 941, 77, 78,
- /* 870 */ 359, 437, 335, 260, 218, 79, 342, 342, 437, 307,
- /* 880 */ 306, 305, 207, 303, 339, 338, 668, 435, 339, 338,
- /* 890 */ 407, 10, 10, 762, 216, 216, 939, 939, 49, 49,
- /* 900 */ 437, 260, 97, 241, 421, 225, 402, 189, 188, 187,
- /* 910 */ 309, 918, 980, 149, 221, 898, 814, 868, 439, 438,
- /* 920 */ 10, 10, 802, 870, 915, 316, 898, 163, 162, 171,
- /* 930 */ 249, 240, 322, 410, 412, 687, 687, 272, 940, 941,
- /* 940 */ 239, 965, 901, 437, 226, 403, 226, 437, 963, 367,
- /* 950 */ 964, 173, 248, 807, 807, 807, 809, 19, 174, 367,
- /* 960 */ 899, 124, 172, 48, 48, 9, 9, 35, 35, 966,
- /* 970 */ 966, 899, 363, 966, 966, 814, 900, 808, 725, 939,
- /* 980 */ 939, 802, 895, 318, 980, 324, 125, 900, 726, 420,
- /* 990 */ 92, 92, 92, 92, 85, 90, 90, 90, 90, 89,
- /* 1000 */ 89, 88, 88, 88, 87, 341, 216, 216, 437, 946,
- /* 1010 */ 349, 292, 807, 807, 807, 114, 291, 693, 402, 705,
- /* 1020 */ 890, 940, 941, 437, 245, 889, 247, 437, 36, 36,
- /* 1030 */ 437, 353, 391, 437, 260, 252, 260, 437, 361, 437,
- /* 1040 */ 706, 437, 370, 12, 12, 224, 437, 27, 27, 437,
- /* 1050 */ 37, 37, 437, 38, 38, 752, 368, 39, 39, 28,
- /* 1060 */ 28, 29, 29, 215, 166, 331, 40, 40, 437, 41,
- /* 1070 */ 41, 437, 42, 42, 437, 866, 246, 731, 437, 879,
- /* 1080 */ 437, 256, 437, 878, 437, 267, 437, 261, 11, 11,
- /* 1090 */ 437, 43, 43, 437, 99, 99, 437, 373, 44, 44,
- /* 1100 */ 45, 45, 32, 32, 46, 46, 47, 47, 437, 426,
- /* 1110 */ 33, 33, 776, 116, 116, 437, 117, 117, 437, 124,
- /* 1120 */ 437, 777, 437, 260, 437, 957, 437, 352, 118, 118,
- /* 1130 */ 437, 195, 437, 111, 437, 53, 53, 264, 34, 34,
- /* 1140 */ 100, 100, 50, 50, 101, 101, 102, 102, 437, 260,
- /* 1150 */ 98, 98, 115, 115, 113, 113, 437, 262, 437, 265,
- /* 1160 */ 437, 943, 958, 437, 727, 437, 681, 437, 106, 106,
- /* 1170 */ 68, 437, 893, 730, 437, 365, 105, 105, 103, 103,
- /* 1180 */ 104, 104, 217, 52, 52, 54, 54, 51, 51, 694,
- /* 1190 */ 259, 26, 26, 266, 30, 30, 677, 323, 433, 323,
- /* 1200 */ 674, 423, 427, 943, 958, 114, 114, 431, 681, 865,
- /* 1210 */ 1277, 233, 366, 714, 112, 20, 154, 704, 703, 810,
- /* 1220 */ 914, 55, 159, 311, 798, 255, 383, 194, 68, 200,
- /* 1230 */ 21, 694, 268, 114, 114, 114, 270, 711, 712, 68,
- /* 1240 */ 114, 739, 770, 715, 71, 194, 861, 875, 875, 200,
- /* 1250 */ 696, 865, 874, 874, 679, 699, 273, 110, 229, 419,
- /* 1260 */ 768, 810, 799, 378, 748, 759, 418, 210, 294, 281,
- /* 1270 */ 295, 806, 283, 682, 676, 665, 664, 666, 933, 151,
- /* 1280 */ 285, 7, 1267, 308, 251, 790, 354, 244, 892, 364,
- /* 1290 */ 287, 422, 300, 164, 160, 936, 974, 127, 197, 137,
- /* 1300 */ 909, 907, 971, 388, 276, 863, 862, 56, 698, 325,
- /* 1310 */ 148, 59, 122, 66, 356, 381, 357, 176, 152, 62,
- /* 1320 */ 371, 130, 877, 181, 377, 760, 211, 182, 132, 133,
- /* 1330 */ 134, 135, 258, 146, 140, 795, 787, 263, 183, 379,
- /* 1340 */ 667, 394, 184, 332, 894, 314, 718, 717, 857, 716,
- /* 1350 */ 696, 315, 709, 690, 65, 196, 6, 408, 289, 708,
- /* 1360 */ 275, 689, 688, 948, 756, 757, 280, 282, 425, 755,
- /* 1370 */ 284, 336, 73, 67, 754, 429, 411, 96, 286, 413,
- /* 1380 */ 205, 934, 673, 22, 209, 440, 119, 120, 109, 206,
- /* 1390 */ 208, 441, 662, 661, 656, 843, 654, 343, 158, 236,
- /* 1400 */ 170, 347, 107, 227, 121, 738, 873, 298, 296, 297,
- /* 1410 */ 299, 871, 794, 128, 129, 728, 230, 131, 175, 250,
- /* 1420 */ 888, 136, 138, 231, 232, 139, 60, 61, 891, 178,
- /* 1430 */ 179, 887, 8, 13, 180, 257, 880, 968, 194, 141,
- /* 1440 */ 142, 376, 153, 670, 380, 185, 143, 277, 63, 384,
- /* 1450 */ 14, 707, 271, 15, 389, 64, 319, 320, 126, 228,
- /* 1460 */ 813, 812, 841, 736, 123, 16, 401, 740, 4, 769,
- /* 1470 */ 165, 212, 214, 193, 144, 764, 71, 68, 17, 18,
- /* 1480 */ 856, 842, 840, 897, 845, 896, 199, 198, 923, 155,
- /* 1490 */ 424, 929, 924, 156, 201, 202, 428, 844, 157, 203,
- /* 1500 */ 811, 680, 81, 1269, 1268, 301, 304,
+ /* 0 */ 325, 832, 351, 825, 5, 203, 203, 819, 99, 100,
+ /* 10 */ 90, 842, 842, 854, 857, 846, 846, 97, 97, 98,
+ /* 20 */ 98, 98, 98, 301, 96, 96, 96, 96, 95, 95,
+ /* 30 */ 94, 94, 94, 93, 351, 325, 977, 977, 824, 824,
+ /* 40 */ 826, 947, 354, 99, 100, 90, 842, 842, 854, 857,
+ /* 50 */ 846, 846, 97, 97, 98, 98, 98, 98, 338, 96,
+ /* 60 */ 96, 96, 96, 95, 95, 94, 94, 94, 93, 351,
+ /* 70 */ 95, 95, 94, 94, 94, 93, 351, 791, 977, 977,
+ /* 80 */ 325, 94, 94, 94, 93, 351, 792, 75, 99, 100,
+ /* 90 */ 90, 842, 842, 854, 857, 846, 846, 97, 97, 98,
+ /* 100 */ 98, 98, 98, 450, 96, 96, 96, 96, 95, 95,
+ /* 110 */ 94, 94, 94, 93, 351, 1333, 155, 155, 2, 325,
+ /* 120 */ 275, 146, 132, 52, 52, 93, 351, 99, 100, 90,
+ /* 130 */ 842, 842, 854, 857, 846, 846, 97, 97, 98, 98,
+ /* 140 */ 98, 98, 101, 96, 96, 96, 96, 95, 95, 94,
+ /* 150 */ 94, 94, 93, 351, 958, 958, 325, 268, 428, 413,
+ /* 160 */ 411, 61, 752, 752, 99, 100, 90, 842, 842, 854,
+ /* 170 */ 857, 846, 846, 97, 97, 98, 98, 98, 98, 60,
+ /* 180 */ 96, 96, 96, 96, 95, 95, 94, 94, 94, 93,
+ /* 190 */ 351, 325, 270, 329, 273, 277, 959, 960, 250, 99,
+ /* 200 */ 100, 90, 842, 842, 854, 857, 846, 846, 97, 97,
+ /* 210 */ 98, 98, 98, 98, 301, 96, 96, 96, 96, 95,
+ /* 220 */ 95, 94, 94, 94, 93, 351, 325, 938, 1326, 698,
+ /* 230 */ 706, 1326, 242, 412, 99, 100, 90, 842, 842, 854,
+ /* 240 */ 857, 846, 846, 97, 97, 98, 98, 98, 98, 347,
+ /* 250 */ 96, 96, 96, 96, 95, 95, 94, 94, 94, 93,
+ /* 260 */ 351, 325, 938, 1327, 384, 699, 1327, 381, 379, 99,
+ /* 270 */ 100, 90, 842, 842, 854, 857, 846, 846, 97, 97,
+ /* 280 */ 98, 98, 98, 98, 701, 96, 96, 96, 96, 95,
+ /* 290 */ 95, 94, 94, 94, 93, 351, 325, 92, 89, 178,
+ /* 300 */ 833, 936, 373, 700, 99, 100, 90, 842, 842, 854,
+ /* 310 */ 857, 846, 846, 97, 97, 98, 98, 98, 98, 375,
+ /* 320 */ 96, 96, 96, 96, 95, 95, 94, 94, 94, 93,
+ /* 330 */ 351, 325, 1276, 947, 354, 818, 936, 739, 739, 99,
+ /* 340 */ 100, 90, 842, 842, 854, 857, 846, 846, 97, 97,
+ /* 350 */ 98, 98, 98, 98, 230, 96, 96, 96, 96, 95,
+ /* 360 */ 95, 94, 94, 94, 93, 351, 325, 969, 227, 92,
+ /* 370 */ 89, 178, 373, 300, 99, 100, 90, 842, 842, 854,
+ /* 380 */ 857, 846, 846, 97, 97, 98, 98, 98, 98, 921,
+ /* 390 */ 96, 96, 96, 96, 95, 95, 94, 94, 94, 93,
+ /* 400 */ 351, 325, 449, 447, 447, 447, 147, 737, 737, 99,
+ /* 410 */ 100, 90, 842, 842, 854, 857, 846, 846, 97, 97,
+ /* 420 */ 98, 98, 98, 98, 296, 96, 96, 96, 96, 95,
+ /* 430 */ 95, 94, 94, 94, 93, 351, 325, 419, 231, 958,
+ /* 440 */ 958, 158, 25, 422, 99, 100, 90, 842, 842, 854,
+ /* 450 */ 857, 846, 846, 97, 97, 98, 98, 98, 98, 450,
+ /* 460 */ 96, 96, 96, 96, 95, 95, 94, 94, 94, 93,
+ /* 470 */ 351, 443, 224, 224, 420, 958, 958, 962, 325, 52,
+ /* 480 */ 52, 959, 960, 176, 415, 78, 99, 100, 90, 842,
+ /* 490 */ 842, 854, 857, 846, 846, 97, 97, 98, 98, 98,
+ /* 500 */ 98, 379, 96, 96, 96, 96, 95, 95, 94, 94,
+ /* 510 */ 94, 93, 351, 325, 428, 418, 298, 959, 960, 962,
+ /* 520 */ 81, 99, 88, 90, 842, 842, 854, 857, 846, 846,
+ /* 530 */ 97, 97, 98, 98, 98, 98, 717, 96, 96, 96,
+ /* 540 */ 96, 95, 95, 94, 94, 94, 93, 351, 325, 843,
+ /* 550 */ 843, 855, 858, 996, 318, 343, 379, 100, 90, 842,
+ /* 560 */ 842, 854, 857, 846, 846, 97, 97, 98, 98, 98,
+ /* 570 */ 98, 450, 96, 96, 96, 96, 95, 95, 94, 94,
+ /* 580 */ 94, 93, 351, 325, 350, 350, 350, 260, 377, 340,
+ /* 590 */ 929, 52, 52, 90, 842, 842, 854, 857, 846, 846,
+ /* 600 */ 97, 97, 98, 98, 98, 98, 361, 96, 96, 96,
+ /* 610 */ 96, 95, 95, 94, 94, 94, 93, 351, 86, 445,
+ /* 620 */ 847, 3, 1203, 361, 360, 378, 344, 813, 958, 958,
+ /* 630 */ 1300, 86, 445, 729, 3, 212, 169, 287, 405, 282,
+ /* 640 */ 404, 199, 232, 450, 300, 760, 83, 84, 280, 245,
+ /* 650 */ 262, 365, 251, 85, 352, 352, 92, 89, 178, 83,
+ /* 660 */ 84, 242, 412, 52, 52, 448, 85, 352, 352, 246,
+ /* 670 */ 959, 960, 194, 455, 670, 402, 399, 398, 448, 243,
+ /* 680 */ 221, 114, 434, 776, 361, 450, 397, 268, 747, 224,
+ /* 690 */ 224, 132, 132, 198, 832, 434, 452, 451, 428, 427,
+ /* 700 */ 819, 415, 734, 713, 132, 52, 52, 832, 268, 452,
+ /* 710 */ 451, 734, 194, 819, 363, 402, 399, 398, 450, 1271,
+ /* 720 */ 1271, 23, 958, 958, 86, 445, 397, 3, 228, 429,
+ /* 730 */ 895, 824, 824, 826, 827, 19, 203, 720, 52, 52,
+ /* 740 */ 428, 408, 439, 249, 824, 824, 826, 827, 19, 229,
+ /* 750 */ 403, 153, 83, 84, 761, 177, 241, 450, 721, 85,
+ /* 760 */ 352, 352, 120, 157, 959, 960, 58, 977, 409, 355,
+ /* 770 */ 330, 448, 268, 428, 430, 320, 790, 32, 32, 86,
+ /* 780 */ 445, 776, 3, 341, 98, 98, 98, 98, 434, 96,
+ /* 790 */ 96, 96, 96, 95, 95, 94, 94, 94, 93, 351,
+ /* 800 */ 832, 120, 452, 451, 813, 887, 819, 83, 84, 977,
+ /* 810 */ 813, 132, 410, 920, 85, 352, 352, 132, 407, 789,
+ /* 820 */ 958, 958, 92, 89, 178, 917, 448, 262, 370, 261,
+ /* 830 */ 82, 914, 80, 262, 370, 261, 776, 824, 824, 826,
+ /* 840 */ 827, 19, 934, 434, 96, 96, 96, 96, 95, 95,
+ /* 850 */ 94, 94, 94, 93, 351, 832, 74, 452, 451, 958,
+ /* 860 */ 958, 819, 959, 960, 120, 92, 89, 178, 945, 2,
+ /* 870 */ 918, 965, 268, 1, 976, 76, 445, 762, 3, 708,
+ /* 880 */ 901, 901, 387, 958, 958, 757, 919, 371, 740, 778,
+ /* 890 */ 756, 257, 824, 824, 826, 827, 19, 417, 741, 450,
+ /* 900 */ 24, 959, 960, 83, 84, 369, 958, 958, 177, 226,
+ /* 910 */ 85, 352, 352, 885, 315, 314, 313, 215, 311, 10,
+ /* 920 */ 10, 683, 448, 349, 348, 959, 960, 909, 777, 157,
+ /* 930 */ 120, 958, 958, 337, 776, 416, 711, 310, 450, 434,
+ /* 940 */ 450, 321, 450, 791, 103, 200, 175, 450, 959, 960,
+ /* 950 */ 908, 832, 792, 452, 451, 9, 9, 819, 10, 10,
+ /* 960 */ 52, 52, 51, 51, 180, 716, 248, 10, 10, 171,
+ /* 970 */ 170, 167, 339, 959, 960, 247, 984, 702, 702, 450,
+ /* 980 */ 715, 233, 686, 982, 889, 983, 182, 914, 824, 824,
+ /* 990 */ 826, 827, 19, 183, 256, 423, 132, 181, 394, 10,
+ /* 1000 */ 10, 889, 891, 749, 958, 958, 917, 268, 985, 198,
+ /* 1010 */ 985, 349, 348, 425, 415, 299, 817, 832, 326, 825,
+ /* 1020 */ 120, 332, 133, 819, 268, 98, 98, 98, 98, 91,
+ /* 1030 */ 96, 96, 96, 96, 95, 95, 94, 94, 94, 93,
+ /* 1040 */ 351, 157, 810, 371, 382, 359, 959, 960, 358, 268,
+ /* 1050 */ 450, 918, 368, 324, 824, 824, 826, 450, 709, 450,
+ /* 1060 */ 264, 380, 889, 450, 877, 746, 253, 919, 255, 433,
+ /* 1070 */ 36, 36, 234, 450, 234, 120, 269, 37, 37, 12,
+ /* 1080 */ 12, 334, 272, 27, 27, 450, 330, 118, 450, 162,
+ /* 1090 */ 742, 280, 450, 38, 38, 450, 985, 356, 985, 450,
+ /* 1100 */ 709, 1210, 450, 132, 450, 39, 39, 450, 40, 40,
+ /* 1110 */ 450, 362, 41, 41, 450, 42, 42, 450, 254, 28,
+ /* 1120 */ 28, 450, 29, 29, 31, 31, 450, 43, 43, 450,
+ /* 1130 */ 44, 44, 450, 714, 45, 45, 450, 11, 11, 767,
+ /* 1140 */ 450, 46, 46, 450, 268, 450, 105, 105, 450, 47,
+ /* 1150 */ 47, 450, 48, 48, 450, 237, 33, 33, 450, 172,
+ /* 1160 */ 49, 49, 450, 50, 50, 34, 34, 274, 122, 122,
+ /* 1170 */ 450, 123, 123, 450, 124, 124, 450, 898, 56, 56,
+ /* 1180 */ 450, 897, 35, 35, 450, 267, 450, 817, 450, 817,
+ /* 1190 */ 106, 106, 450, 53, 53, 385, 107, 107, 450, 817,
+ /* 1200 */ 108, 108, 817, 450, 104, 104, 121, 121, 119, 119,
+ /* 1210 */ 450, 117, 112, 112, 450, 276, 450, 225, 111, 111,
+ /* 1220 */ 450, 730, 450, 109, 109, 450, 673, 674, 675, 912,
+ /* 1230 */ 110, 110, 317, 998, 55, 55, 57, 57, 692, 331,
+ /* 1240 */ 54, 54, 26, 26, 696, 30, 30, 317, 937, 197,
+ /* 1250 */ 196, 195, 335, 281, 336, 446, 331, 745, 689, 436,
+ /* 1260 */ 440, 444, 120, 72, 386, 223, 175, 345, 757, 933,
+ /* 1270 */ 20, 286, 319, 756, 815, 372, 374, 202, 202, 202,
+ /* 1280 */ 263, 395, 285, 74, 208, 21, 696, 719, 718, 884,
+ /* 1290 */ 120, 120, 120, 120, 120, 754, 278, 828, 77, 74,
+ /* 1300 */ 726, 727, 785, 783, 880, 202, 999, 208, 894, 893,
+ /* 1310 */ 894, 893, 694, 816, 763, 116, 774, 1290, 431, 432,
+ /* 1320 */ 302, 999, 390, 303, 823, 697, 691, 680, 159, 289,
+ /* 1330 */ 679, 884, 681, 952, 291, 218, 293, 7, 316, 828,
+ /* 1340 */ 173, 805, 259, 364, 252, 911, 376, 713, 295, 435,
+ /* 1350 */ 308, 168, 955, 993, 135, 400, 990, 284, 882, 881,
+ /* 1360 */ 205, 928, 926, 59, 333, 62, 144, 156, 130, 72,
+ /* 1370 */ 802, 366, 367, 393, 137, 185, 189, 160, 139, 383,
+ /* 1380 */ 67, 896, 140, 141, 142, 148, 389, 812, 775, 266,
+ /* 1390 */ 219, 190, 154, 391, 913, 876, 271, 406, 191, 322,
+ /* 1400 */ 682, 733, 192, 342, 732, 724, 731, 711, 723, 421,
+ /* 1410 */ 705, 71, 323, 6, 204, 771, 288, 79, 297, 346,
+ /* 1420 */ 772, 704, 290, 283, 703, 770, 292, 294, 967, 239,
+ /* 1430 */ 769, 102, 862, 438, 426, 240, 424, 442, 73, 213,
+ /* 1440 */ 688, 238, 22, 453, 953, 214, 217, 216, 454, 677,
+ /* 1450 */ 676, 671, 753, 125, 115, 235, 126, 669, 353, 166,
+ /* 1460 */ 127, 244, 179, 357, 306, 304, 305, 307, 113, 892,
+ /* 1470 */ 327, 890, 811, 328, 134, 128, 136, 138, 743, 258,
+ /* 1480 */ 907, 184, 143, 129, 910, 186, 63, 64, 145, 187,
+ /* 1490 */ 906, 65, 8, 66, 13, 188, 202, 899, 265, 149,
+ /* 1500 */ 987, 388, 150, 685, 161, 392, 285, 193, 279, 396,
+ /* 1510 */ 151, 401, 68, 14, 15, 722, 69, 236, 831, 131,
+ /* 1520 */ 830, 860, 70, 751, 16, 414, 755, 4, 174, 220,
+ /* 1530 */ 222, 784, 201, 152, 779, 77, 74, 17, 18, 875,
+ /* 1540 */ 861, 859, 916, 864, 915, 207, 206, 942, 163, 437,
+ /* 1550 */ 948, 943, 164, 209, 1002, 441, 863, 165, 210, 829,
+ /* 1560 */ 695, 87, 312, 211, 1292, 1291, 309,
};
static const YYCODETYPE yy_lookahead[] = {
/* 0 */ 19, 95, 53, 97, 22, 24, 24, 101, 27, 28,
@@ -132096,281 +133351,290 @@ static const YYCODETYPE yy_lookahead[] = {
/* 90 */ 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
/* 100 */ 39, 40, 41, 152, 43, 44, 45, 46, 47, 48,
/* 110 */ 49, 50, 51, 52, 53, 144, 145, 146, 147, 19,
- /* 120 */ 137, 22, 139, 172, 173, 52, 53, 27, 28, 29,
+ /* 120 */ 16, 22, 92, 172, 173, 52, 53, 27, 28, 29,
/* 130 */ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
/* 140 */ 40, 41, 81, 43, 44, 45, 46, 47, 48, 49,
/* 150 */ 50, 51, 52, 53, 55, 56, 19, 152, 207, 208,
/* 160 */ 115, 24, 117, 118, 27, 28, 29, 30, 31, 32,
/* 170 */ 33, 34, 35, 36, 37, 38, 39, 40, 41, 79,
/* 180 */ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
- /* 190 */ 53, 19, 0, 1, 2, 23, 97, 98, 193, 27,
+ /* 190 */ 53, 19, 88, 157, 90, 23, 97, 98, 193, 27,
/* 200 */ 28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
/* 210 */ 38, 39, 40, 41, 152, 43, 44, 45, 46, 47,
- /* 220 */ 48, 49, 50, 51, 52, 53, 19, 22, 23, 163,
- /* 230 */ 23, 26, 190, 191, 27, 28, 29, 30, 31, 32,
+ /* 220 */ 48, 49, 50, 51, 52, 53, 19, 22, 23, 172,
+ /* 230 */ 23, 26, 119, 120, 27, 28, 29, 30, 31, 32,
/* 240 */ 33, 34, 35, 36, 37, 38, 39, 40, 41, 187,
/* 250 */ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
- /* 260 */ 53, 19, 196, 22, 23, 23, 49, 26, 92, 27,
+ /* 260 */ 53, 19, 22, 23, 228, 23, 26, 231, 152, 27,
/* 270 */ 28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
/* 280 */ 38, 39, 40, 41, 172, 43, 44, 45, 46, 47,
/* 290 */ 48, 49, 50, 51, 52, 53, 19, 221, 222, 223,
- /* 300 */ 23, 96, 119, 120, 27, 28, 29, 30, 31, 32,
- /* 310 */ 33, 34, 35, 36, 37, 38, 39, 40, 41, 172,
+ /* 300 */ 23, 96, 152, 172, 27, 28, 29, 30, 31, 32,
+ /* 310 */ 33, 34, 35, 36, 37, 38, 39, 40, 41, 152,
/* 320 */ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
- /* 330 */ 53, 19, 152, 116, 221, 222, 223, 96, 121, 27,
+ /* 330 */ 53, 19, 0, 1, 2, 23, 96, 190, 191, 27,
/* 340 */ 28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
- /* 350 */ 38, 39, 40, 41, 241, 43, 44, 45, 46, 47,
- /* 360 */ 48, 49, 50, 51, 52, 53, 19, 157, 168, 169,
- /* 370 */ 170, 22, 190, 191, 27, 28, 29, 30, 31, 32,
- /* 380 */ 33, 34, 35, 36, 37, 38, 39, 40, 41, 30,
+ /* 350 */ 38, 39, 40, 41, 238, 43, 44, 45, 46, 47,
+ /* 360 */ 48, 49, 50, 51, 52, 53, 19, 185, 218, 221,
+ /* 370 */ 222, 223, 152, 152, 27, 28, 29, 30, 31, 32,
+ /* 380 */ 33, 34, 35, 36, 37, 38, 39, 40, 41, 241,
/* 390 */ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
- /* 400 */ 53, 19, 172, 152, 55, 56, 24, 247, 248, 27,
+ /* 400 */ 53, 19, 152, 168, 169, 170, 22, 190, 191, 27,
/* 410 */ 28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
/* 420 */ 38, 39, 40, 41, 152, 43, 44, 45, 46, 47,
- /* 430 */ 48, 49, 50, 51, 52, 53, 146, 147, 228, 179,
- /* 440 */ 180, 231, 185, 19, 172, 173, 97, 98, 188, 26,
- /* 450 */ 138, 27, 28, 29, 30, 31, 32, 33, 34, 35,
- /* 460 */ 36, 37, 38, 39, 40, 41, 107, 43, 44, 45,
- /* 470 */ 46, 47, 48, 49, 50, 51, 52, 53, 19, 207,
- /* 480 */ 208, 30, 31, 32, 33, 138, 27, 28, 29, 30,
+ /* 430 */ 48, 49, 50, 51, 52, 53, 19, 19, 218, 55,
+ /* 440 */ 56, 24, 22, 152, 27, 28, 29, 30, 31, 32,
+ /* 450 */ 33, 34, 35, 36, 37, 38, 39, 40, 41, 152,
+ /* 460 */ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
+ /* 470 */ 53, 250, 194, 195, 56, 55, 56, 55, 19, 172,
+ /* 480 */ 173, 97, 98, 152, 206, 138, 27, 28, 29, 30,
/* 490 */ 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
- /* 500 */ 41, 250, 43, 44, 45, 46, 47, 48, 49, 50,
- /* 510 */ 51, 52, 53, 19, 168, 169, 170, 7, 8, 9,
- /* 520 */ 19, 152, 28, 29, 30, 31, 32, 33, 34, 35,
- /* 530 */ 36, 37, 38, 39, 40, 41, 152, 43, 44, 45,
- /* 540 */ 46, 47, 48, 49, 50, 51, 52, 53, 19, 108,
- /* 550 */ 109, 110, 101, 130, 53, 152, 172, 173, 29, 30,
+ /* 500 */ 41, 152, 43, 44, 45, 46, 47, 48, 49, 50,
+ /* 510 */ 51, 52, 53, 19, 207, 208, 152, 97, 98, 97,
+ /* 520 */ 138, 27, 28, 29, 30, 31, 32, 33, 34, 35,
+ /* 530 */ 36, 37, 38, 39, 40, 41, 181, 43, 44, 45,
+ /* 540 */ 46, 47, 48, 49, 50, 51, 52, 53, 19, 30,
+ /* 550 */ 31, 32, 33, 247, 248, 19, 152, 28, 29, 30,
/* 560 */ 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
/* 570 */ 41, 152, 43, 44, 45, 46, 47, 48, 49, 50,
- /* 580 */ 51, 52, 53, 19, 20, 116, 22, 23, 169, 170,
- /* 590 */ 121, 207, 85, 55, 56, 26, 19, 20, 101, 22,
- /* 600 */ 99, 100, 101, 102, 103, 104, 105, 152, 152, 112,
- /* 610 */ 210, 47, 48, 112, 152, 108, 109, 110, 54, 55,
- /* 620 */ 56, 221, 222, 223, 47, 48, 119, 120, 172, 173,
- /* 630 */ 66, 54, 55, 56, 152, 97, 98, 99, 148, 149,
- /* 640 */ 102, 103, 104, 66, 154, 23, 156, 83, 26, 230,
- /* 650 */ 152, 113, 152, 163, 172, 173, 92, 92, 21, 95,
- /* 660 */ 83, 97, 98, 207, 208, 101, 152, 98, 186, 92,
- /* 670 */ 172, 173, 95, 218, 97, 98, 152, 99, 101, 217,
- /* 680 */ 102, 103, 104, 152, 119, 120, 196, 55, 56, 19,
- /* 690 */ 20, 113, 22, 124, 163, 11, 132, 133, 134, 135,
- /* 700 */ 136, 152, 152, 172, 173, 207, 208, 152, 152, 132,
- /* 710 */ 133, 134, 135, 136, 164, 152, 84, 47, 48, 49,
- /* 720 */ 98, 181, 152, 152, 54, 55, 56, 196, 91, 97,
- /* 730 */ 98, 160, 218, 163, 244, 164, 66, 152, 207, 208,
- /* 740 */ 103, 217, 172, 173, 19, 20, 124, 22, 193, 38,
- /* 750 */ 39, 40, 41, 83, 43, 44, 45, 46, 47, 48,
- /* 760 */ 49, 50, 51, 52, 53, 95, 196, 97, 98, 85,
- /* 770 */ 152, 101, 47, 48, 181, 85, 92, 140, 193, 54,
- /* 780 */ 55, 56, 92, 49, 195, 55, 56, 175, 163, 55,
- /* 790 */ 56, 66, 108, 109, 110, 206, 163, 242, 108, 109,
- /* 800 */ 110, 175, 132, 133, 134, 135, 136, 152, 83, 43,
- /* 810 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
- /* 820 */ 95, 196, 97, 98, 55, 56, 101, 97, 98, 196,
- /* 830 */ 152, 97, 98, 221, 222, 223, 211, 212, 22, 23,
- /* 840 */ 19, 20, 181, 22, 19, 152, 152, 221, 222, 223,
- /* 850 */ 172, 173, 219, 19, 124, 30, 238, 132, 133, 134,
- /* 860 */ 135, 136, 169, 170, 186, 232, 97, 98, 47, 48,
- /* 870 */ 237, 152, 217, 152, 5, 54, 55, 56, 152, 10,
- /* 880 */ 11, 12, 13, 14, 47, 48, 17, 66, 47, 48,
- /* 890 */ 56, 172, 173, 124, 194, 195, 55, 56, 172, 173,
- /* 900 */ 152, 152, 22, 152, 83, 186, 206, 108, 109, 110,
- /* 910 */ 22, 23, 96, 152, 193, 12, 95, 152, 97, 98,
- /* 920 */ 172, 173, 101, 230, 152, 164, 12, 47, 48, 60,
- /* 930 */ 152, 62, 107, 207, 186, 55, 56, 112, 97, 98,
- /* 940 */ 71, 100, 193, 152, 183, 152, 185, 152, 107, 152,
- /* 950 */ 109, 82, 16, 132, 133, 134, 135, 136, 89, 152,
- /* 960 */ 57, 92, 93, 172, 173, 172, 173, 172, 173, 132,
- /* 970 */ 133, 57, 152, 132, 133, 95, 73, 97, 75, 55,
- /* 980 */ 56, 101, 163, 114, 96, 245, 246, 73, 85, 75,
- /* 990 */ 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
- /* 1000 */ 48, 49, 50, 51, 52, 53, 194, 195, 152, 171,
- /* 1010 */ 141, 152, 132, 133, 134, 196, 225, 179, 206, 65,
- /* 1020 */ 152, 97, 98, 152, 88, 152, 90, 152, 172, 173,
- /* 1030 */ 152, 219, 78, 152, 152, 238, 152, 152, 219, 152,
- /* 1040 */ 86, 152, 152, 172, 173, 238, 152, 172, 173, 152,
- /* 1050 */ 172, 173, 152, 172, 173, 213, 237, 172, 173, 172,
- /* 1060 */ 173, 172, 173, 211, 212, 111, 172, 173, 152, 172,
- /* 1070 */ 173, 152, 172, 173, 152, 193, 140, 193, 152, 59,
- /* 1080 */ 152, 152, 152, 63, 152, 16, 152, 152, 172, 173,
- /* 1090 */ 152, 172, 173, 152, 172, 173, 152, 77, 172, 173,
- /* 1100 */ 172, 173, 172, 173, 172, 173, 172, 173, 152, 250,
- /* 1110 */ 172, 173, 61, 172, 173, 152, 172, 173, 152, 92,
- /* 1120 */ 152, 70, 152, 152, 152, 26, 152, 100, 172, 173,
- /* 1130 */ 152, 24, 152, 22, 152, 172, 173, 152, 172, 173,
- /* 1140 */ 172, 173, 172, 173, 172, 173, 172, 173, 152, 152,
- /* 1150 */ 172, 173, 172, 173, 172, 173, 152, 88, 152, 90,
- /* 1160 */ 152, 55, 55, 152, 193, 152, 55, 152, 172, 173,
- /* 1170 */ 26, 152, 163, 163, 152, 19, 172, 173, 172, 173,
- /* 1180 */ 172, 173, 22, 172, 173, 172, 173, 172, 173, 55,
- /* 1190 */ 193, 172, 173, 152, 172, 173, 166, 167, 166, 167,
- /* 1200 */ 163, 163, 163, 97, 97, 196, 196, 163, 97, 55,
- /* 1210 */ 23, 199, 56, 26, 22, 22, 24, 100, 101, 55,
- /* 1220 */ 23, 209, 123, 26, 23, 23, 23, 26, 26, 26,
- /* 1230 */ 37, 97, 152, 196, 196, 196, 23, 7, 8, 26,
- /* 1240 */ 196, 23, 23, 152, 26, 26, 23, 132, 133, 26,
- /* 1250 */ 106, 97, 132, 133, 23, 152, 152, 26, 210, 191,
- /* 1260 */ 152, 97, 152, 234, 152, 152, 152, 233, 152, 210,
- /* 1270 */ 152, 152, 210, 152, 152, 152, 152, 152, 152, 197,
- /* 1280 */ 210, 198, 122, 150, 239, 201, 214, 214, 201, 239,
- /* 1290 */ 214, 227, 200, 184, 198, 155, 67, 243, 122, 22,
- /* 1300 */ 159, 159, 69, 176, 175, 175, 175, 240, 180, 159,
- /* 1310 */ 220, 240, 27, 130, 18, 18, 159, 158, 220, 137,
- /* 1320 */ 159, 189, 236, 158, 74, 159, 159, 158, 192, 192,
- /* 1330 */ 192, 192, 235, 22, 189, 189, 201, 159, 158, 177,
- /* 1340 */ 159, 107, 158, 76, 201, 177, 174, 174, 201, 174,
- /* 1350 */ 106, 177, 182, 174, 107, 159, 22, 125, 159, 182,
- /* 1360 */ 174, 176, 174, 174, 216, 216, 215, 215, 177, 216,
- /* 1370 */ 215, 53, 137, 128, 216, 177, 127, 129, 215, 126,
- /* 1380 */ 25, 13, 162, 26, 6, 161, 165, 165, 178, 153,
- /* 1390 */ 153, 151, 151, 151, 151, 224, 4, 3, 22, 142,
- /* 1400 */ 15, 94, 16, 178, 165, 205, 23, 202, 204, 203,
- /* 1410 */ 201, 23, 120, 131, 111, 20, 226, 123, 125, 16,
- /* 1420 */ 1, 123, 131, 229, 229, 111, 37, 37, 56, 64,
- /* 1430 */ 122, 1, 5, 22, 107, 140, 80, 87, 26, 80,
- /* 1440 */ 107, 72, 24, 20, 19, 105, 22, 112, 22, 79,
- /* 1450 */ 22, 58, 23, 22, 79, 22, 249, 249, 246, 79,
- /* 1460 */ 23, 23, 23, 116, 68, 22, 26, 23, 22, 56,
- /* 1470 */ 122, 23, 23, 64, 22, 124, 26, 26, 64, 64,
- /* 1480 */ 23, 23, 23, 23, 11, 23, 22, 26, 23, 22,
- /* 1490 */ 24, 1, 23, 22, 26, 122, 24, 23, 22, 122,
- /* 1500 */ 23, 23, 22, 122, 122, 23, 15,
+ /* 580 */ 51, 52, 53, 19, 168, 169, 170, 238, 19, 53,
+ /* 590 */ 152, 172, 173, 29, 30, 31, 32, 33, 34, 35,
+ /* 600 */ 36, 37, 38, 39, 40, 41, 152, 43, 44, 45,
+ /* 610 */ 46, 47, 48, 49, 50, 51, 52, 53, 19, 20,
+ /* 620 */ 101, 22, 23, 169, 170, 56, 207, 85, 55, 56,
+ /* 630 */ 23, 19, 20, 26, 22, 99, 100, 101, 102, 103,
+ /* 640 */ 104, 105, 238, 152, 152, 210, 47, 48, 112, 152,
+ /* 650 */ 108, 109, 110, 54, 55, 56, 221, 222, 223, 47,
+ /* 660 */ 48, 119, 120, 172, 173, 66, 54, 55, 56, 152,
+ /* 670 */ 97, 98, 99, 148, 149, 102, 103, 104, 66, 154,
+ /* 680 */ 23, 156, 83, 26, 230, 152, 113, 152, 163, 194,
+ /* 690 */ 195, 92, 92, 30, 95, 83, 97, 98, 207, 208,
+ /* 700 */ 101, 206, 179, 180, 92, 172, 173, 95, 152, 97,
+ /* 710 */ 98, 188, 99, 101, 219, 102, 103, 104, 152, 119,
+ /* 720 */ 120, 196, 55, 56, 19, 20, 113, 22, 193, 163,
+ /* 730 */ 11, 132, 133, 134, 135, 136, 24, 65, 172, 173,
+ /* 740 */ 207, 208, 250, 152, 132, 133, 134, 135, 136, 193,
+ /* 750 */ 78, 84, 47, 48, 49, 98, 199, 152, 86, 54,
+ /* 760 */ 55, 56, 196, 152, 97, 98, 209, 55, 163, 244,
+ /* 770 */ 107, 66, 152, 207, 208, 164, 175, 172, 173, 19,
+ /* 780 */ 20, 124, 22, 111, 38, 39, 40, 41, 83, 43,
+ /* 790 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
+ /* 800 */ 95, 196, 97, 98, 85, 152, 101, 47, 48, 97,
+ /* 810 */ 85, 92, 207, 193, 54, 55, 56, 92, 49, 175,
+ /* 820 */ 55, 56, 221, 222, 223, 12, 66, 108, 109, 110,
+ /* 830 */ 137, 163, 139, 108, 109, 110, 26, 132, 133, 134,
+ /* 840 */ 135, 136, 152, 83, 43, 44, 45, 46, 47, 48,
+ /* 850 */ 49, 50, 51, 52, 53, 95, 26, 97, 98, 55,
+ /* 860 */ 56, 101, 97, 98, 196, 221, 222, 223, 146, 147,
+ /* 870 */ 57, 171, 152, 22, 26, 19, 20, 49, 22, 179,
+ /* 880 */ 108, 109, 110, 55, 56, 116, 73, 219, 75, 124,
+ /* 890 */ 121, 152, 132, 133, 134, 135, 136, 163, 85, 152,
+ /* 900 */ 232, 97, 98, 47, 48, 237, 55, 56, 98, 5,
+ /* 910 */ 54, 55, 56, 193, 10, 11, 12, 13, 14, 172,
+ /* 920 */ 173, 17, 66, 47, 48, 97, 98, 152, 124, 152,
+ /* 930 */ 196, 55, 56, 186, 124, 152, 106, 160, 152, 83,
+ /* 940 */ 152, 164, 152, 61, 22, 211, 212, 152, 97, 98,
+ /* 950 */ 152, 95, 70, 97, 98, 172, 173, 101, 172, 173,
+ /* 960 */ 172, 173, 172, 173, 60, 181, 62, 172, 173, 47,
+ /* 970 */ 48, 123, 186, 97, 98, 71, 100, 55, 56, 152,
+ /* 980 */ 181, 186, 21, 107, 152, 109, 82, 163, 132, 133,
+ /* 990 */ 134, 135, 136, 89, 16, 207, 92, 93, 19, 172,
+ /* 1000 */ 173, 169, 170, 195, 55, 56, 12, 152, 132, 30,
+ /* 1010 */ 134, 47, 48, 186, 206, 225, 152, 95, 114, 97,
+ /* 1020 */ 196, 245, 246, 101, 152, 38, 39, 40, 41, 42,
+ /* 1030 */ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
+ /* 1040 */ 53, 152, 163, 219, 152, 141, 97, 98, 193, 152,
+ /* 1050 */ 152, 57, 91, 164, 132, 133, 134, 152, 55, 152,
+ /* 1060 */ 152, 237, 230, 152, 103, 193, 88, 73, 90, 75,
+ /* 1070 */ 172, 173, 183, 152, 185, 196, 152, 172, 173, 172,
+ /* 1080 */ 173, 217, 152, 172, 173, 152, 107, 22, 152, 24,
+ /* 1090 */ 193, 112, 152, 172, 173, 152, 132, 242, 134, 152,
+ /* 1100 */ 97, 140, 152, 92, 152, 172, 173, 152, 172, 173,
+ /* 1110 */ 152, 100, 172, 173, 152, 172, 173, 152, 140, 172,
+ /* 1120 */ 173, 152, 172, 173, 172, 173, 152, 172, 173, 152,
+ /* 1130 */ 172, 173, 152, 152, 172, 173, 152, 172, 173, 213,
+ /* 1140 */ 152, 172, 173, 152, 152, 152, 172, 173, 152, 172,
+ /* 1150 */ 173, 152, 172, 173, 152, 210, 172, 173, 152, 26,
+ /* 1160 */ 172, 173, 152, 172, 173, 172, 173, 152, 172, 173,
+ /* 1170 */ 152, 172, 173, 152, 172, 173, 152, 59, 172, 173,
+ /* 1180 */ 152, 63, 172, 173, 152, 193, 152, 152, 152, 152,
+ /* 1190 */ 172, 173, 152, 172, 173, 77, 172, 173, 152, 152,
+ /* 1200 */ 172, 173, 152, 152, 172, 173, 172, 173, 172, 173,
+ /* 1210 */ 152, 22, 172, 173, 152, 152, 152, 22, 172, 173,
+ /* 1220 */ 152, 152, 152, 172, 173, 152, 7, 8, 9, 163,
+ /* 1230 */ 172, 173, 22, 23, 172, 173, 172, 173, 166, 167,
+ /* 1240 */ 172, 173, 172, 173, 55, 172, 173, 22, 23, 108,
+ /* 1250 */ 109, 110, 217, 152, 217, 166, 167, 163, 163, 163,
+ /* 1260 */ 163, 163, 196, 130, 217, 211, 212, 217, 116, 23,
+ /* 1270 */ 22, 101, 26, 121, 23, 23, 23, 26, 26, 26,
+ /* 1280 */ 23, 23, 112, 26, 26, 37, 97, 100, 101, 55,
+ /* 1290 */ 196, 196, 196, 196, 196, 23, 23, 55, 26, 26,
+ /* 1300 */ 7, 8, 23, 152, 23, 26, 96, 26, 132, 132,
+ /* 1310 */ 134, 134, 23, 152, 152, 26, 152, 122, 152, 191,
+ /* 1320 */ 152, 96, 234, 152, 152, 152, 152, 152, 197, 210,
+ /* 1330 */ 152, 97, 152, 152, 210, 233, 210, 198, 150, 97,
+ /* 1340 */ 184, 201, 239, 214, 214, 201, 239, 180, 214, 227,
+ /* 1350 */ 200, 198, 155, 67, 243, 176, 69, 175, 175, 175,
+ /* 1360 */ 122, 159, 159, 240, 159, 240, 22, 220, 27, 130,
+ /* 1370 */ 201, 18, 159, 18, 189, 158, 158, 220, 192, 159,
+ /* 1380 */ 137, 236, 192, 192, 192, 189, 74, 189, 159, 235,
+ /* 1390 */ 159, 158, 22, 177, 201, 201, 159, 107, 158, 177,
+ /* 1400 */ 159, 174, 158, 76, 174, 182, 174, 106, 182, 125,
+ /* 1410 */ 174, 107, 177, 22, 159, 216, 215, 137, 159, 53,
+ /* 1420 */ 216, 176, 215, 174, 174, 216, 215, 215, 174, 229,
+ /* 1430 */ 216, 129, 224, 177, 126, 229, 127, 177, 128, 25,
+ /* 1440 */ 162, 226, 26, 161, 13, 153, 6, 153, 151, 151,
+ /* 1450 */ 151, 151, 205, 165, 178, 178, 165, 4, 3, 22,
+ /* 1460 */ 165, 142, 15, 94, 202, 204, 203, 201, 16, 23,
+ /* 1470 */ 249, 23, 120, 249, 246, 111, 131, 123, 20, 16,
+ /* 1480 */ 1, 125, 123, 111, 56, 64, 37, 37, 131, 122,
+ /* 1490 */ 1, 37, 5, 37, 22, 107, 26, 80, 140, 80,
+ /* 1500 */ 87, 72, 107, 20, 24, 19, 112, 105, 23, 79,
+ /* 1510 */ 22, 79, 22, 22, 22, 58, 22, 79, 23, 68,
+ /* 1520 */ 23, 23, 26, 116, 22, 26, 23, 22, 122, 23,
+ /* 1530 */ 23, 56, 64, 22, 124, 26, 26, 64, 64, 23,
+ /* 1540 */ 23, 23, 23, 11, 23, 22, 26, 23, 22, 24,
+ /* 1550 */ 1, 23, 22, 26, 251, 24, 23, 22, 122, 23,
+ /* 1560 */ 23, 22, 15, 122, 122, 122, 23,
};
-#define YY_SHIFT_USE_DFLT (-95)
-#define YY_SHIFT_COUNT (442)
-#define YY_SHIFT_MIN (-94)
-#define YY_SHIFT_MAX (1491)
+#define YY_SHIFT_USE_DFLT (1567)
+#define YY_SHIFT_COUNT (455)
+#define YY_SHIFT_MIN (-94)
+#define YY_SHIFT_MAX (1549)
static const short yy_shift_ofst[] = {
- /* 0 */ 40, 564, 869, 577, 725, 725, 725, 725, 690, -19,
- /* 10 */ 16, 16, 100, 725, 725, 725, 725, 725, 725, 725,
- /* 20 */ 841, 841, 538, 507, 684, 565, 61, 137, 172, 207,
- /* 30 */ 242, 277, 312, 347, 382, 424, 424, 424, 424, 424,
- /* 40 */ 424, 424, 424, 424, 424, 424, 424, 424, 424, 424,
- /* 50 */ 459, 424, 494, 529, 529, 670, 725, 725, 725, 725,
- /* 60 */ 725, 725, 725, 725, 725, 725, 725, 725, 725, 725,
- /* 70 */ 725, 725, 725, 725, 725, 725, 725, 725, 725, 725,
- /* 80 */ 725, 725, 725, 725, 821, 725, 725, 725, 725, 725,
- /* 90 */ 725, 725, 725, 725, 725, 725, 725, 725, 952, 711,
- /* 100 */ 711, 711, 711, 711, 766, 23, 32, 924, 637, 825,
- /* 110 */ 837, 837, 924, 73, 183, -51, -95, -95, -95, 501,
- /* 120 */ 501, 501, 903, 903, 632, 205, 241, 924, 924, 924,
- /* 130 */ 924, 924, 924, 924, 924, 924, 924, 924, 924, 924,
- /* 140 */ 924, 924, 924, 924, 924, 924, 924, 192, 1027, 1106,
- /* 150 */ 1106, 183, 176, 176, 176, 176, 176, 176, -95, -95,
- /* 160 */ -95, 880, -94, -94, 578, 734, 99, 730, 769, 349,
- /* 170 */ 924, 924, 924, 924, 924, 924, 924, 924, 924, 924,
- /* 180 */ 924, 924, 924, 924, 924, 924, 924, 954, 954, 954,
- /* 190 */ 924, 924, 622, 924, 924, 924, -18, 924, 924, 914,
- /* 200 */ 924, 924, 924, 924, 924, 924, 924, 924, 924, 924,
- /* 210 */ 441, 1020, 1107, 1107, 1107, 569, 45, 217, 510, 423,
- /* 220 */ 834, 834, 1156, 423, 1156, 1144, 1187, 359, 1051, 834,
- /* 230 */ -17, 1051, 1051, 1099, 469, 1192, 1229, 1176, 1176, 1233,
- /* 240 */ 1233, 1176, 1277, 1285, 1183, 1296, 1296, 1296, 1296, 1176,
- /* 250 */ 1297, 1183, 1277, 1285, 1285, 1183, 1176, 1297, 1182, 1250,
- /* 260 */ 1176, 1176, 1297, 1311, 1176, 1297, 1176, 1297, 1311, 1234,
- /* 270 */ 1234, 1234, 1267, 1311, 1234, 1244, 1234, 1267, 1234, 1234,
- /* 280 */ 1232, 1247, 1232, 1247, 1232, 1247, 1232, 1247, 1176, 1334,
- /* 290 */ 1176, 1235, 1311, 1318, 1318, 1311, 1248, 1253, 1245, 1249,
- /* 300 */ 1183, 1355, 1357, 1368, 1368, 1378, 1378, 1378, 1378, -95,
- /* 310 */ -95, -95, -95, -95, -95, -95, -95, 451, 936, 816,
- /* 320 */ 888, 1069, 799, 1111, 1197, 1193, 1201, 1202, 1203, 1213,
- /* 330 */ 1134, 1117, 1230, 497, 1218, 1219, 1154, 1223, 1115, 1120,
- /* 340 */ 1231, 1164, 1160, 1392, 1394, 1376, 1257, 1385, 1307, 1386,
- /* 350 */ 1383, 1388, 1292, 1282, 1303, 1294, 1395, 1293, 1403, 1419,
- /* 360 */ 1298, 1291, 1389, 1390, 1314, 1372, 1365, 1308, 1430, 1427,
- /* 370 */ 1411, 1327, 1295, 1356, 1412, 1359, 1350, 1369, 1333, 1418,
- /* 380 */ 1423, 1425, 1335, 1340, 1424, 1370, 1426, 1428, 1429, 1431,
- /* 390 */ 1375, 1393, 1433, 1380, 1396, 1437, 1438, 1439, 1347, 1443,
- /* 400 */ 1444, 1446, 1440, 1348, 1448, 1449, 1413, 1409, 1452, 1351,
- /* 410 */ 1450, 1414, 1451, 1415, 1457, 1450, 1458, 1459, 1460, 1461,
- /* 420 */ 1462, 1464, 1473, 1465, 1467, 1466, 1468, 1469, 1471, 1472,
- /* 430 */ 1468, 1474, 1476, 1477, 1478, 1480, 1373, 1377, 1381, 1382,
- /* 440 */ 1482, 1491, 1490,
+ /* 0 */ 40, 599, 904, 612, 760, 760, 760, 760, 725, -19,
+ /* 10 */ 16, 16, 100, 760, 760, 760, 760, 760, 760, 760,
+ /* 20 */ 876, 876, 573, 542, 719, 600, 61, 137, 172, 207,
+ /* 30 */ 242, 277, 312, 347, 382, 417, 459, 459, 459, 459,
+ /* 40 */ 459, 459, 459, 459, 459, 459, 459, 459, 459, 459,
+ /* 50 */ 459, 459, 459, 494, 459, 529, 564, 564, 705, 760,
+ /* 60 */ 760, 760, 760, 760, 760, 760, 760, 760, 760, 760,
+ /* 70 */ 760, 760, 760, 760, 760, 760, 760, 760, 760, 760,
+ /* 80 */ 760, 760, 760, 760, 760, 760, 760, 760, 760, 760,
+ /* 90 */ 856, 760, 760, 760, 760, 760, 760, 760, 760, 760,
+ /* 100 */ 760, 760, 760, 760, 987, 746, 746, 746, 746, 746,
+ /* 110 */ 801, 23, 32, 949, 961, 979, 964, 964, 949, 73,
+ /* 120 */ 113, -51, 1567, 1567, 1567, 536, 536, 536, 99, 99,
+ /* 130 */ 813, 813, 667, 205, 240, 949, 949, 949, 949, 949,
+ /* 140 */ 949, 949, 949, 949, 949, 949, 949, 949, 949, 949,
+ /* 150 */ 949, 949, 949, 949, 949, 332, 1011, 422, 422, 113,
+ /* 160 */ 30, 30, 30, 30, 30, 30, 1567, 1567, 1567, 922,
+ /* 170 */ -94, -94, 384, 613, 828, 420, 765, 804, 851, 949,
+ /* 180 */ 949, 949, 949, 949, 949, 949, 949, 949, 949, 949,
+ /* 190 */ 949, 949, 949, 949, 949, 672, 672, 672, 949, 949,
+ /* 200 */ 657, 949, 949, 949, -18, 949, 949, 994, 949, 949,
+ /* 210 */ 949, 949, 949, 949, 949, 949, 949, 949, 772, 1118,
+ /* 220 */ 712, 712, 712, 810, 45, 769, 1219, 1133, 418, 418,
+ /* 230 */ 569, 1133, 569, 830, 607, 663, 882, 418, 693, 882,
+ /* 240 */ 882, 848, 1152, 1065, 1286, 1238, 1238, 1287, 1287, 1238,
+ /* 250 */ 1344, 1341, 1239, 1353, 1353, 1353, 1353, 1238, 1355, 1239,
+ /* 260 */ 1344, 1341, 1341, 1239, 1238, 1355, 1243, 1312, 1238, 1238,
+ /* 270 */ 1355, 1370, 1238, 1355, 1238, 1355, 1370, 1290, 1290, 1290,
+ /* 280 */ 1327, 1370, 1290, 1301, 1290, 1327, 1290, 1290, 1284, 1304,
+ /* 290 */ 1284, 1304, 1284, 1304, 1284, 1304, 1238, 1391, 1238, 1280,
+ /* 300 */ 1370, 1366, 1366, 1370, 1302, 1308, 1310, 1309, 1239, 1414,
+ /* 310 */ 1416, 1431, 1431, 1440, 1440, 1440, 1440, 1567, 1567, 1567,
+ /* 320 */ 1567, 1567, 1567, 1567, 1567, 519, 978, 1210, 1225, 104,
+ /* 330 */ 1141, 1189, 1246, 1248, 1251, 1252, 1253, 1257, 1258, 1273,
+ /* 340 */ 1003, 1187, 1293, 1170, 1272, 1279, 1234, 1281, 1176, 1177,
+ /* 350 */ 1289, 1242, 1195, 1453, 1455, 1437, 1319, 1447, 1369, 1452,
+ /* 360 */ 1446, 1448, 1352, 1345, 1364, 1354, 1458, 1356, 1463, 1479,
+ /* 370 */ 1359, 1357, 1449, 1450, 1454, 1456, 1372, 1428, 1421, 1367,
+ /* 380 */ 1489, 1487, 1472, 1388, 1358, 1417, 1470, 1419, 1413, 1429,
+ /* 390 */ 1395, 1480, 1483, 1486, 1394, 1402, 1488, 1430, 1490, 1491,
+ /* 400 */ 1485, 1492, 1432, 1457, 1494, 1438, 1451, 1495, 1497, 1498,
+ /* 410 */ 1496, 1407, 1502, 1503, 1505, 1499, 1406, 1506, 1507, 1475,
+ /* 420 */ 1468, 1511, 1410, 1509, 1473, 1510, 1474, 1516, 1509, 1517,
+ /* 430 */ 1518, 1519, 1520, 1521, 1523, 1532, 1524, 1526, 1525, 1527,
+ /* 440 */ 1528, 1530, 1531, 1527, 1533, 1535, 1536, 1537, 1539, 1436,
+ /* 450 */ 1441, 1442, 1443, 1543, 1547, 1549,
};
#define YY_REDUCE_USE_DFLT (-130)
-#define YY_REDUCE_COUNT (316)
+#define YY_REDUCE_COUNT (324)
#define YY_REDUCE_MIN (-129)
-#define YY_REDUCE_MAX (1243)
+#define YY_REDUCE_MAX (1300)
static const short yy_reduce_ofst[] = {
- /* 0 */ -29, 531, 490, 570, -49, 272, 456, 498, 633, 400,
- /* 10 */ 612, 626, 113, 482, 678, 719, 384, 726, 748, 791,
- /* 20 */ 419, 693, 761, 812, 819, 625, 76, 76, 76, 76,
+ /* 0 */ -29, 566, 525, 605, -49, 307, 491, 533, 668, 435,
+ /* 10 */ 601, 644, 148, 747, 786, 795, 419, 788, 827, 790,
+ /* 20 */ 454, 832, 889, 495, 824, 734, 76, 76, 76, 76,
/* 30 */ 76, 76, 76, 76, 76, 76, 76, 76, 76, 76,
/* 40 */ 76, 76, 76, 76, 76, 76, 76, 76, 76, 76,
- /* 50 */ 76, 76, 76, 76, 76, 793, 795, 856, 871, 875,
- /* 60 */ 878, 881, 885, 887, 889, 894, 897, 900, 916, 919,
- /* 70 */ 922, 926, 928, 930, 932, 934, 938, 941, 944, 956,
- /* 80 */ 963, 966, 968, 970, 972, 974, 978, 980, 982, 996,
- /* 90 */ 1004, 1006, 1008, 1011, 1013, 1015, 1019, 1022, 76, 76,
- /* 100 */ 76, 76, 76, 76, 76, 76, 76, 555, 210, 260,
- /* 110 */ 200, 346, 571, 76, 700, 76, 76, 76, 76, 838,
- /* 120 */ 838, 838, 42, 182, 251, 160, 160, 550, 5, 455,
- /* 130 */ 585, 721, 749, 882, 884, 971, 618, 462, 797, 514,
- /* 140 */ 807, 524, 997, -129, 655, 859, 62, 290, 66, 1030,
- /* 150 */ 1032, 589, 1009, 1010, 1037, 1038, 1039, 1044, 740, 852,
- /* 160 */ 1012, 112, 147, 230, 257, 180, 369, 403, 500, 549,
- /* 170 */ 556, 563, 694, 751, 765, 772, 778, 820, 868, 873,
- /* 180 */ 890, 929, 935, 985, 1041, 1080, 1091, 540, 593, 661,
- /* 190 */ 1103, 1104, 842, 1108, 1110, 1112, 1048, 1113, 1114, 1068,
- /* 200 */ 1116, 1118, 1119, 180, 1121, 1122, 1123, 1124, 1125, 1126,
- /* 210 */ 1029, 1034, 1059, 1062, 1070, 842, 1082, 1083, 1133, 1084,
- /* 220 */ 1072, 1073, 1045, 1087, 1050, 1127, 1109, 1128, 1129, 1076,
- /* 230 */ 1064, 1130, 1131, 1092, 1096, 1140, 1054, 1141, 1142, 1067,
- /* 240 */ 1071, 1150, 1090, 1132, 1135, 1136, 1137, 1138, 1139, 1157,
- /* 250 */ 1159, 1143, 1098, 1145, 1146, 1147, 1161, 1165, 1086, 1097,
- /* 260 */ 1166, 1167, 1169, 1162, 1178, 1180, 1181, 1184, 1168, 1172,
- /* 270 */ 1173, 1175, 1170, 1174, 1179, 1185, 1186, 1177, 1188, 1189,
- /* 280 */ 1148, 1151, 1149, 1152, 1153, 1155, 1158, 1163, 1196, 1171,
- /* 290 */ 1199, 1190, 1191, 1194, 1195, 1198, 1200, 1204, 1206, 1205,
- /* 300 */ 1209, 1220, 1224, 1236, 1237, 1240, 1241, 1242, 1243, 1207,
- /* 310 */ 1208, 1212, 1221, 1222, 1210, 1225, 1239,
+ /* 50 */ 76, 76, 76, 76, 76, 76, 76, 76, 783, 898,
+ /* 60 */ 905, 907, 911, 921, 933, 936, 940, 943, 947, 950,
+ /* 70 */ 952, 955, 958, 962, 965, 969, 974, 977, 980, 984,
+ /* 80 */ 988, 991, 993, 996, 999, 1002, 1006, 1010, 1018, 1021,
+ /* 90 */ 1024, 1028, 1032, 1034, 1036, 1040, 1046, 1051, 1058, 1062,
+ /* 100 */ 1064, 1068, 1070, 1073, 76, 76, 76, 76, 76, 76,
+ /* 110 */ 76, 76, 76, 855, 36, 523, 235, 416, 777, 76,
+ /* 120 */ 278, 76, 76, 76, 76, 700, 700, 700, 150, 220,
+ /* 130 */ 147, 217, 221, 306, 306, 611, 5, 535, 556, 620,
+ /* 140 */ 720, 872, 897, 116, 864, 349, 1035, 1037, 404, 1047,
+ /* 150 */ 992, -129, 1050, 492, 62, 722, 879, 1072, 1089, 808,
+ /* 160 */ 1066, 1094, 1095, 1096, 1097, 1098, 776, 1054, 557, 57,
+ /* 170 */ 112, 131, 167, 182, 250, 272, 291, 331, 364, 438,
+ /* 180 */ 497, 517, 591, 653, 690, 739, 775, 798, 892, 908,
+ /* 190 */ 924, 930, 1015, 1063, 1069, 355, 784, 799, 981, 1101,
+ /* 200 */ 926, 1151, 1161, 1162, 945, 1164, 1166, 1128, 1168, 1171,
+ /* 210 */ 1172, 250, 1173, 1174, 1175, 1178, 1180, 1181, 1088, 1102,
+ /* 220 */ 1119, 1124, 1126, 926, 1131, 1139, 1188, 1140, 1129, 1130,
+ /* 230 */ 1103, 1144, 1107, 1179, 1156, 1167, 1182, 1134, 1122, 1183,
+ /* 240 */ 1184, 1150, 1153, 1197, 1111, 1202, 1203, 1123, 1125, 1205,
+ /* 250 */ 1147, 1185, 1169, 1186, 1190, 1191, 1192, 1213, 1217, 1193,
+ /* 260 */ 1157, 1196, 1198, 1194, 1220, 1218, 1145, 1154, 1229, 1231,
+ /* 270 */ 1233, 1216, 1237, 1240, 1241, 1244, 1222, 1227, 1230, 1232,
+ /* 280 */ 1223, 1235, 1236, 1245, 1249, 1226, 1250, 1254, 1199, 1201,
+ /* 290 */ 1204, 1207, 1209, 1211, 1214, 1212, 1255, 1208, 1259, 1215,
+ /* 300 */ 1256, 1200, 1206, 1260, 1247, 1261, 1263, 1262, 1266, 1278,
+ /* 310 */ 1282, 1292, 1294, 1297, 1298, 1299, 1300, 1221, 1224, 1228,
+ /* 320 */ 1288, 1291, 1276, 1277, 1295,
};
static const YYACTIONTYPE yy_default[] = {
- /* 0 */ 1258, 1248, 1248, 1248, 1180, 1180, 1180, 1180, 1248, 1077,
- /* 10 */ 1106, 1106, 1232, 1309, 1309, 1309, 1309, 1309, 1309, 1179,
- /* 20 */ 1309, 1309, 1309, 1309, 1248, 1081, 1112, 1309, 1309, 1309,
- /* 30 */ 1309, 1309, 1309, 1309, 1309, 1231, 1233, 1120, 1119, 1214,
- /* 40 */ 1093, 1117, 1110, 1114, 1181, 1175, 1176, 1174, 1178, 1182,
- /* 50 */ 1309, 1113, 1144, 1159, 1143, 1309, 1309, 1309, 1309, 1309,
- /* 60 */ 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309,
- /* 70 */ 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309,
- /* 80 */ 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309,
- /* 90 */ 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1153, 1158,
- /* 100 */ 1165, 1157, 1154, 1146, 1145, 1147, 1148, 1309, 1000, 1048,
- /* 110 */ 1309, 1309, 1309, 1149, 1309, 1150, 1162, 1161, 1160, 1239,
- /* 120 */ 1266, 1265, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309,
- /* 130 */ 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309,
- /* 140 */ 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1258, 1248, 1006,
- /* 150 */ 1006, 1309, 1248, 1248, 1248, 1248, 1248, 1248, 1244, 1081,
- /* 160 */ 1072, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309,
- /* 170 */ 1309, 1236, 1234, 1309, 1195, 1309, 1309, 1309, 1309, 1309,
- /* 180 */ 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309,
- /* 190 */ 1309, 1309, 1309, 1309, 1309, 1309, 1077, 1309, 1309, 1309,
- /* 200 */ 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1260,
- /* 210 */ 1309, 1209, 1077, 1077, 1077, 1079, 1061, 1071, 985, 1116,
- /* 220 */ 1095, 1095, 1298, 1116, 1298, 1023, 1280, 1020, 1106, 1095,
- /* 230 */ 1177, 1106, 1106, 1078, 1071, 1309, 1301, 1086, 1086, 1300,
- /* 240 */ 1300, 1086, 1125, 1051, 1116, 1057, 1057, 1057, 1057, 1086,
- /* 250 */ 997, 1116, 1125, 1051, 1051, 1116, 1086, 997, 1213, 1295,
- /* 260 */ 1086, 1086, 997, 1188, 1086, 997, 1086, 997, 1188, 1049,
- /* 270 */ 1049, 1049, 1038, 1188, 1049, 1023, 1049, 1038, 1049, 1049,
- /* 280 */ 1099, 1094, 1099, 1094, 1099, 1094, 1099, 1094, 1086, 1183,
- /* 290 */ 1086, 1309, 1188, 1192, 1192, 1188, 1111, 1100, 1109, 1107,
- /* 300 */ 1116, 1003, 1041, 1263, 1263, 1259, 1259, 1259, 1259, 1306,
- /* 310 */ 1306, 1244, 1275, 1275, 1025, 1025, 1275, 1309, 1309, 1309,
- /* 320 */ 1309, 1309, 1309, 1270, 1309, 1197, 1309, 1309, 1309, 1309,
- /* 330 */ 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309,
- /* 340 */ 1309, 1309, 1131, 1309, 981, 1241, 1309, 1309, 1240, 1309,
- /* 350 */ 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309,
- /* 360 */ 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1297, 1309, 1309,
- /* 370 */ 1309, 1309, 1309, 1309, 1212, 1211, 1309, 1309, 1309, 1309,
- /* 380 */ 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309,
- /* 390 */ 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1063, 1309,
- /* 400 */ 1309, 1309, 1284, 1309, 1309, 1309, 1309, 1309, 1309, 1309,
- /* 410 */ 1108, 1309, 1101, 1309, 1309, 1288, 1309, 1309, 1309, 1309,
- /* 420 */ 1309, 1309, 1309, 1309, 1309, 1309, 1250, 1309, 1309, 1309,
- /* 430 */ 1249, 1309, 1309, 1309, 1309, 1309, 1133, 1309, 1132, 1136,
- /* 440 */ 1309, 991, 1309,
+ /* 0 */ 1281, 1271, 1271, 1271, 1203, 1203, 1203, 1203, 1271, 1096,
+ /* 10 */ 1125, 1125, 1255, 1332, 1332, 1332, 1332, 1332, 1332, 1202,
+ /* 20 */ 1332, 1332, 1332, 1332, 1271, 1100, 1131, 1332, 1332, 1332,
+ /* 30 */ 1332, 1204, 1205, 1332, 1332, 1332, 1254, 1256, 1141, 1140,
+ /* 40 */ 1139, 1138, 1237, 1112, 1136, 1129, 1133, 1204, 1198, 1199,
+ /* 50 */ 1197, 1201, 1205, 1332, 1132, 1167, 1182, 1166, 1332, 1332,
+ /* 60 */ 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332,
+ /* 70 */ 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332,
+ /* 80 */ 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332,
+ /* 90 */ 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332,
+ /* 100 */ 1332, 1332, 1332, 1332, 1176, 1181, 1188, 1180, 1177, 1169,
+ /* 110 */ 1168, 1170, 1171, 1332, 1019, 1067, 1332, 1332, 1332, 1172,
+ /* 120 */ 1332, 1173, 1185, 1184, 1183, 1262, 1289, 1288, 1332, 1332,
+ /* 130 */ 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332,
+ /* 140 */ 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332,
+ /* 150 */ 1332, 1332, 1332, 1332, 1332, 1281, 1271, 1025, 1025, 1332,
+ /* 160 */ 1271, 1271, 1271, 1271, 1271, 1271, 1267, 1100, 1091, 1332,
+ /* 170 */ 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332,
+ /* 180 */ 1259, 1257, 1332, 1218, 1332, 1332, 1332, 1332, 1332, 1332,
+ /* 190 */ 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332,
+ /* 200 */ 1332, 1332, 1332, 1332, 1096, 1332, 1332, 1332, 1332, 1332,
+ /* 210 */ 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1283, 1332, 1232,
+ /* 220 */ 1096, 1096, 1096, 1098, 1080, 1090, 1004, 1135, 1114, 1114,
+ /* 230 */ 1321, 1135, 1321, 1042, 1303, 1039, 1125, 1114, 1200, 1125,
+ /* 240 */ 1125, 1097, 1090, 1332, 1324, 1105, 1105, 1323, 1323, 1105,
+ /* 250 */ 1146, 1070, 1135, 1076, 1076, 1076, 1076, 1105, 1016, 1135,
+ /* 260 */ 1146, 1070, 1070, 1135, 1105, 1016, 1236, 1318, 1105, 1105,
+ /* 270 */ 1016, 1211, 1105, 1016, 1105, 1016, 1211, 1068, 1068, 1068,
+ /* 280 */ 1057, 1211, 1068, 1042, 1068, 1057, 1068, 1068, 1118, 1113,
+ /* 290 */ 1118, 1113, 1118, 1113, 1118, 1113, 1105, 1206, 1105, 1332,
+ /* 300 */ 1211, 1215, 1215, 1211, 1130, 1119, 1128, 1126, 1135, 1022,
+ /* 310 */ 1060, 1286, 1286, 1282, 1282, 1282, 1282, 1329, 1329, 1267,
+ /* 320 */ 1298, 1298, 1044, 1044, 1298, 1332, 1332, 1332, 1332, 1332,
+ /* 330 */ 1332, 1293, 1332, 1220, 1332, 1332, 1332, 1332, 1332, 1332,
+ /* 340 */ 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332,
+ /* 350 */ 1332, 1332, 1152, 1332, 1000, 1264, 1332, 1332, 1263, 1332,
+ /* 360 */ 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332,
+ /* 370 */ 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1320,
+ /* 380 */ 1332, 1332, 1332, 1332, 1332, 1332, 1235, 1234, 1332, 1332,
+ /* 390 */ 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332,
+ /* 400 */ 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332,
+ /* 410 */ 1332, 1082, 1332, 1332, 1332, 1307, 1332, 1332, 1332, 1332,
+ /* 420 */ 1332, 1332, 1332, 1127, 1332, 1120, 1332, 1332, 1311, 1332,
+ /* 430 */ 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1273,
+ /* 440 */ 1332, 1332, 1332, 1272, 1332, 1332, 1332, 1332, 1332, 1154,
+ /* 450 */ 1332, 1153, 1157, 1332, 1010, 1332,
};
/********** End of lemon-generated parsing tables *****************************/
@@ -132604,7 +133868,7 @@ static const char *const yyTokenName[] = {
"VALUES", "DISTINCT", "DOT", "FROM",
"JOIN", "USING", "ORDER", "GROUP",
"HAVING", "LIMIT", "WHERE", "INTO",
- "INTEGER", "FLOAT", "BLOB", "VARIABLE",
+ "FLOAT", "BLOB", "INTEGER", "VARIABLE",
"CASE", "WHEN", "THEN", "ELSE",
"INDEX", "ALTER", "ADD", "error",
"input", "cmdlist", "ecmd", "explain",
@@ -132780,195 +134044,199 @@ static const char *const yyRuleName[] = {
/* 136 */ "where_opt ::= WHERE expr",
/* 137 */ "cmd ::= with UPDATE orconf fullname indexed_opt SET setlist where_opt",
/* 138 */ "setlist ::= setlist COMMA nm EQ expr",
- /* 139 */ "setlist ::= nm EQ expr",
- /* 140 */ "cmd ::= with insert_cmd INTO fullname idlist_opt select",
- /* 141 */ "cmd ::= with insert_cmd INTO fullname idlist_opt DEFAULT VALUES",
- /* 142 */ "insert_cmd ::= INSERT orconf",
- /* 143 */ "insert_cmd ::= REPLACE",
- /* 144 */ "idlist_opt ::=",
- /* 145 */ "idlist_opt ::= LP idlist RP",
- /* 146 */ "idlist ::= idlist COMMA nm",
- /* 147 */ "idlist ::= nm",
- /* 148 */ "expr ::= LP expr RP",
- /* 149 */ "term ::= NULL",
- /* 150 */ "expr ::= ID|INDEXED",
- /* 151 */ "expr ::= JOIN_KW",
- /* 152 */ "expr ::= nm DOT nm",
- /* 153 */ "expr ::= nm DOT nm DOT nm",
- /* 154 */ "term ::= INTEGER|FLOAT|BLOB",
- /* 155 */ "term ::= STRING",
- /* 156 */ "expr ::= VARIABLE",
- /* 157 */ "expr ::= expr COLLATE ID|STRING",
- /* 158 */ "expr ::= CAST LP expr AS typetoken RP",
- /* 159 */ "expr ::= ID|INDEXED LP distinct exprlist RP",
- /* 160 */ "expr ::= ID|INDEXED LP STAR RP",
- /* 161 */ "term ::= CTIME_KW",
- /* 162 */ "expr ::= expr AND expr",
- /* 163 */ "expr ::= expr OR expr",
- /* 164 */ "expr ::= expr LT|GT|GE|LE expr",
- /* 165 */ "expr ::= expr EQ|NE expr",
- /* 166 */ "expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr",
- /* 167 */ "expr ::= expr PLUS|MINUS expr",
- /* 168 */ "expr ::= expr STAR|SLASH|REM expr",
- /* 169 */ "expr ::= expr CONCAT expr",
- /* 170 */ "likeop ::= LIKE_KW|MATCH",
- /* 171 */ "likeop ::= NOT LIKE_KW|MATCH",
- /* 172 */ "expr ::= expr likeop expr",
- /* 173 */ "expr ::= expr likeop expr ESCAPE expr",
- /* 174 */ "expr ::= expr ISNULL|NOTNULL",
- /* 175 */ "expr ::= expr NOT NULL",
- /* 176 */ "expr ::= expr IS expr",
- /* 177 */ "expr ::= expr IS NOT expr",
- /* 178 */ "expr ::= NOT expr",
- /* 179 */ "expr ::= BITNOT expr",
- /* 180 */ "expr ::= MINUS expr",
- /* 181 */ "expr ::= PLUS expr",
- /* 182 */ "between_op ::= BETWEEN",
- /* 183 */ "between_op ::= NOT BETWEEN",
- /* 184 */ "expr ::= expr between_op expr AND expr",
- /* 185 */ "in_op ::= IN",
- /* 186 */ "in_op ::= NOT IN",
- /* 187 */ "expr ::= expr in_op LP exprlist RP",
- /* 188 */ "expr ::= LP select RP",
- /* 189 */ "expr ::= expr in_op LP select RP",
- /* 190 */ "expr ::= expr in_op nm dbnm paren_exprlist",
- /* 191 */ "expr ::= EXISTS LP select RP",
- /* 192 */ "expr ::= CASE case_operand case_exprlist case_else END",
- /* 193 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr",
- /* 194 */ "case_exprlist ::= WHEN expr THEN expr",
- /* 195 */ "case_else ::= ELSE expr",
- /* 196 */ "case_else ::=",
- /* 197 */ "case_operand ::= expr",
- /* 198 */ "case_operand ::=",
- /* 199 */ "exprlist ::=",
- /* 200 */ "nexprlist ::= nexprlist COMMA expr",
- /* 201 */ "nexprlist ::= expr",
- /* 202 */ "paren_exprlist ::=",
- /* 203 */ "paren_exprlist ::= LP exprlist RP",
- /* 204 */ "cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt",
- /* 205 */ "uniqueflag ::= UNIQUE",
- /* 206 */ "uniqueflag ::=",
- /* 207 */ "eidlist_opt ::=",
- /* 208 */ "eidlist_opt ::= LP eidlist RP",
- /* 209 */ "eidlist ::= eidlist COMMA nm collate sortorder",
- /* 210 */ "eidlist ::= nm collate sortorder",
- /* 211 */ "collate ::=",
- /* 212 */ "collate ::= COLLATE ID|STRING",
- /* 213 */ "cmd ::= DROP INDEX ifexists fullname",
- /* 214 */ "cmd ::= VACUUM",
- /* 215 */ "cmd ::= VACUUM nm",
- /* 216 */ "cmd ::= PRAGMA nm dbnm",
- /* 217 */ "cmd ::= PRAGMA nm dbnm EQ nmnum",
- /* 218 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP",
- /* 219 */ "cmd ::= PRAGMA nm dbnm EQ minus_num",
- /* 220 */ "cmd ::= PRAGMA nm dbnm LP minus_num RP",
- /* 221 */ "plus_num ::= PLUS INTEGER|FLOAT",
- /* 222 */ "minus_num ::= MINUS INTEGER|FLOAT",
- /* 223 */ "cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END",
- /* 224 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause",
- /* 225 */ "trigger_time ::= BEFORE",
- /* 226 */ "trigger_time ::= AFTER",
- /* 227 */ "trigger_time ::= INSTEAD OF",
- /* 228 */ "trigger_time ::=",
- /* 229 */ "trigger_event ::= DELETE|INSERT",
- /* 230 */ "trigger_event ::= UPDATE",
- /* 231 */ "trigger_event ::= UPDATE OF idlist",
- /* 232 */ "when_clause ::=",
- /* 233 */ "when_clause ::= WHEN expr",
- /* 234 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI",
- /* 235 */ "trigger_cmd_list ::= trigger_cmd SEMI",
- /* 236 */ "trnm ::= nm DOT nm",
- /* 237 */ "tridxby ::= INDEXED BY nm",
- /* 238 */ "tridxby ::= NOT INDEXED",
- /* 239 */ "trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt",
- /* 240 */ "trigger_cmd ::= insert_cmd INTO trnm idlist_opt select",
- /* 241 */ "trigger_cmd ::= DELETE FROM trnm tridxby where_opt",
- /* 242 */ "trigger_cmd ::= select",
- /* 243 */ "expr ::= RAISE LP IGNORE RP",
- /* 244 */ "expr ::= RAISE LP raisetype COMMA nm RP",
- /* 245 */ "raisetype ::= ROLLBACK",
- /* 246 */ "raisetype ::= ABORT",
- /* 247 */ "raisetype ::= FAIL",
- /* 248 */ "cmd ::= DROP TRIGGER ifexists fullname",
- /* 249 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt",
- /* 250 */ "cmd ::= DETACH database_kw_opt expr",
- /* 251 */ "key_opt ::=",
- /* 252 */ "key_opt ::= KEY expr",
- /* 253 */ "cmd ::= REINDEX",
- /* 254 */ "cmd ::= REINDEX nm dbnm",
- /* 255 */ "cmd ::= ANALYZE",
- /* 256 */ "cmd ::= ANALYZE nm dbnm",
- /* 257 */ "cmd ::= ALTER TABLE fullname RENAME TO nm",
- /* 258 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist",
- /* 259 */ "add_column_fullname ::= fullname",
- /* 260 */ "cmd ::= create_vtab",
- /* 261 */ "cmd ::= create_vtab LP vtabarglist RP",
- /* 262 */ "create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm",
- /* 263 */ "vtabarg ::=",
- /* 264 */ "vtabargtoken ::= ANY",
- /* 265 */ "vtabargtoken ::= lp anylist RP",
- /* 266 */ "lp ::= LP",
- /* 267 */ "with ::=",
- /* 268 */ "with ::= WITH wqlist",
- /* 269 */ "with ::= WITH RECURSIVE wqlist",
- /* 270 */ "wqlist ::= nm eidlist_opt AS LP select RP",
- /* 271 */ "wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP",
- /* 272 */ "input ::= cmdlist",
- /* 273 */ "cmdlist ::= cmdlist ecmd",
- /* 274 */ "cmdlist ::= ecmd",
- /* 275 */ "ecmd ::= SEMI",
- /* 276 */ "ecmd ::= explain cmdx SEMI",
- /* 277 */ "explain ::=",
- /* 278 */ "trans_opt ::=",
- /* 279 */ "trans_opt ::= TRANSACTION",
- /* 280 */ "trans_opt ::= TRANSACTION nm",
- /* 281 */ "savepoint_opt ::= SAVEPOINT",
- /* 282 */ "savepoint_opt ::=",
- /* 283 */ "cmd ::= create_table create_table_args",
- /* 284 */ "columnlist ::= columnlist COMMA columnname carglist",
- /* 285 */ "columnlist ::= columnname carglist",
- /* 286 */ "nm ::= ID|INDEXED",
- /* 287 */ "nm ::= STRING",
- /* 288 */ "nm ::= JOIN_KW",
- /* 289 */ "typetoken ::= typename",
- /* 290 */ "typename ::= ID|STRING",
- /* 291 */ "signed ::= plus_num",
- /* 292 */ "signed ::= minus_num",
- /* 293 */ "carglist ::= carglist ccons",
- /* 294 */ "carglist ::=",
- /* 295 */ "ccons ::= NULL onconf",
- /* 296 */ "conslist_opt ::= COMMA conslist",
- /* 297 */ "conslist ::= conslist tconscomma tcons",
- /* 298 */ "conslist ::= tcons",
- /* 299 */ "tconscomma ::=",
- /* 300 */ "defer_subclause_opt ::= defer_subclause",
- /* 301 */ "resolvetype ::= raisetype",
- /* 302 */ "selectnowith ::= oneselect",
- /* 303 */ "oneselect ::= values",
- /* 304 */ "sclp ::= selcollist COMMA",
- /* 305 */ "as ::= ID|STRING",
- /* 306 */ "expr ::= term",
- /* 307 */ "exprlist ::= nexprlist",
- /* 308 */ "nmnum ::= plus_num",
- /* 309 */ "nmnum ::= nm",
- /* 310 */ "nmnum ::= ON",
- /* 311 */ "nmnum ::= DELETE",
- /* 312 */ "nmnum ::= DEFAULT",
- /* 313 */ "plus_num ::= INTEGER|FLOAT",
- /* 314 */ "foreach_clause ::=",
- /* 315 */ "foreach_clause ::= FOR EACH ROW",
- /* 316 */ "trnm ::= nm",
- /* 317 */ "tridxby ::=",
- /* 318 */ "database_kw_opt ::= DATABASE",
- /* 319 */ "database_kw_opt ::=",
- /* 320 */ "kwcolumn_opt ::=",
- /* 321 */ "kwcolumn_opt ::= COLUMNKW",
- /* 322 */ "vtabarglist ::= vtabarg",
- /* 323 */ "vtabarglist ::= vtabarglist COMMA vtabarg",
- /* 324 */ "vtabarg ::= vtabarg vtabargtoken",
- /* 325 */ "anylist ::=",
- /* 326 */ "anylist ::= anylist LP anylist RP",
- /* 327 */ "anylist ::= anylist ANY",
+ /* 139 */ "setlist ::= setlist COMMA LP idlist RP EQ expr",
+ /* 140 */ "setlist ::= nm EQ expr",
+ /* 141 */ "setlist ::= LP idlist RP EQ expr",
+ /* 142 */ "cmd ::= with insert_cmd INTO fullname idlist_opt select",
+ /* 143 */ "cmd ::= with insert_cmd INTO fullname idlist_opt DEFAULT VALUES",
+ /* 144 */ "insert_cmd ::= INSERT orconf",
+ /* 145 */ "insert_cmd ::= REPLACE",
+ /* 146 */ "idlist_opt ::=",
+ /* 147 */ "idlist_opt ::= LP idlist RP",
+ /* 148 */ "idlist ::= idlist COMMA nm",
+ /* 149 */ "idlist ::= nm",
+ /* 150 */ "expr ::= LP expr RP",
+ /* 151 */ "term ::= NULL",
+ /* 152 */ "expr ::= ID|INDEXED",
+ /* 153 */ "expr ::= JOIN_KW",
+ /* 154 */ "expr ::= nm DOT nm",
+ /* 155 */ "expr ::= nm DOT nm DOT nm",
+ /* 156 */ "term ::= FLOAT|BLOB",
+ /* 157 */ "term ::= STRING",
+ /* 158 */ "term ::= INTEGER",
+ /* 159 */ "expr ::= VARIABLE",
+ /* 160 */ "expr ::= expr COLLATE ID|STRING",
+ /* 161 */ "expr ::= CAST LP expr AS typetoken RP",
+ /* 162 */ "expr ::= ID|INDEXED LP distinct exprlist RP",
+ /* 163 */ "expr ::= ID|INDEXED LP STAR RP",
+ /* 164 */ "term ::= CTIME_KW",
+ /* 165 */ "expr ::= LP nexprlist COMMA expr RP",
+ /* 166 */ "expr ::= expr AND expr",
+ /* 167 */ "expr ::= expr OR expr",
+ /* 168 */ "expr ::= expr LT|GT|GE|LE expr",
+ /* 169 */ "expr ::= expr EQ|NE expr",
+ /* 170 */ "expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr",
+ /* 171 */ "expr ::= expr PLUS|MINUS expr",
+ /* 172 */ "expr ::= expr STAR|SLASH|REM expr",
+ /* 173 */ "expr ::= expr CONCAT expr",
+ /* 174 */ "likeop ::= LIKE_KW|MATCH",
+ /* 175 */ "likeop ::= NOT LIKE_KW|MATCH",
+ /* 176 */ "expr ::= expr likeop expr",
+ /* 177 */ "expr ::= expr likeop expr ESCAPE expr",
+ /* 178 */ "expr ::= expr ISNULL|NOTNULL",
+ /* 179 */ "expr ::= expr NOT NULL",
+ /* 180 */ "expr ::= expr IS expr",
+ /* 181 */ "expr ::= expr IS NOT expr",
+ /* 182 */ "expr ::= NOT expr",
+ /* 183 */ "expr ::= BITNOT expr",
+ /* 184 */ "expr ::= MINUS expr",
+ /* 185 */ "expr ::= PLUS expr",
+ /* 186 */ "between_op ::= BETWEEN",
+ /* 187 */ "between_op ::= NOT BETWEEN",
+ /* 188 */ "expr ::= expr between_op expr AND expr",
+ /* 189 */ "in_op ::= IN",
+ /* 190 */ "in_op ::= NOT IN",
+ /* 191 */ "expr ::= expr in_op LP exprlist RP",
+ /* 192 */ "expr ::= LP select RP",
+ /* 193 */ "expr ::= expr in_op LP select RP",
+ /* 194 */ "expr ::= expr in_op nm dbnm paren_exprlist",
+ /* 195 */ "expr ::= EXISTS LP select RP",
+ /* 196 */ "expr ::= CASE case_operand case_exprlist case_else END",
+ /* 197 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr",
+ /* 198 */ "case_exprlist ::= WHEN expr THEN expr",
+ /* 199 */ "case_else ::= ELSE expr",
+ /* 200 */ "case_else ::=",
+ /* 201 */ "case_operand ::= expr",
+ /* 202 */ "case_operand ::=",
+ /* 203 */ "exprlist ::=",
+ /* 204 */ "nexprlist ::= nexprlist COMMA expr",
+ /* 205 */ "nexprlist ::= expr",
+ /* 206 */ "paren_exprlist ::=",
+ /* 207 */ "paren_exprlist ::= LP exprlist RP",
+ /* 208 */ "cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt",
+ /* 209 */ "uniqueflag ::= UNIQUE",
+ /* 210 */ "uniqueflag ::=",
+ /* 211 */ "eidlist_opt ::=",
+ /* 212 */ "eidlist_opt ::= LP eidlist RP",
+ /* 213 */ "eidlist ::= eidlist COMMA nm collate sortorder",
+ /* 214 */ "eidlist ::= nm collate sortorder",
+ /* 215 */ "collate ::=",
+ /* 216 */ "collate ::= COLLATE ID|STRING",
+ /* 217 */ "cmd ::= DROP INDEX ifexists fullname",
+ /* 218 */ "cmd ::= VACUUM",
+ /* 219 */ "cmd ::= VACUUM nm",
+ /* 220 */ "cmd ::= PRAGMA nm dbnm",
+ /* 221 */ "cmd ::= PRAGMA nm dbnm EQ nmnum",
+ /* 222 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP",
+ /* 223 */ "cmd ::= PRAGMA nm dbnm EQ minus_num",
+ /* 224 */ "cmd ::= PRAGMA nm dbnm LP minus_num RP",
+ /* 225 */ "plus_num ::= PLUS INTEGER|FLOAT",
+ /* 226 */ "minus_num ::= MINUS INTEGER|FLOAT",
+ /* 227 */ "cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END",
+ /* 228 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause",
+ /* 229 */ "trigger_time ::= BEFORE",
+ /* 230 */ "trigger_time ::= AFTER",
+ /* 231 */ "trigger_time ::= INSTEAD OF",
+ /* 232 */ "trigger_time ::=",
+ /* 233 */ "trigger_event ::= DELETE|INSERT",
+ /* 234 */ "trigger_event ::= UPDATE",
+ /* 235 */ "trigger_event ::= UPDATE OF idlist",
+ /* 236 */ "when_clause ::=",
+ /* 237 */ "when_clause ::= WHEN expr",
+ /* 238 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI",
+ /* 239 */ "trigger_cmd_list ::= trigger_cmd SEMI",
+ /* 240 */ "trnm ::= nm DOT nm",
+ /* 241 */ "tridxby ::= INDEXED BY nm",
+ /* 242 */ "tridxby ::= NOT INDEXED",
+ /* 243 */ "trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt",
+ /* 244 */ "trigger_cmd ::= insert_cmd INTO trnm idlist_opt select",
+ /* 245 */ "trigger_cmd ::= DELETE FROM trnm tridxby where_opt",
+ /* 246 */ "trigger_cmd ::= select",
+ /* 247 */ "expr ::= RAISE LP IGNORE RP",
+ /* 248 */ "expr ::= RAISE LP raisetype COMMA nm RP",
+ /* 249 */ "raisetype ::= ROLLBACK",
+ /* 250 */ "raisetype ::= ABORT",
+ /* 251 */ "raisetype ::= FAIL",
+ /* 252 */ "cmd ::= DROP TRIGGER ifexists fullname",
+ /* 253 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt",
+ /* 254 */ "cmd ::= DETACH database_kw_opt expr",
+ /* 255 */ "key_opt ::=",
+ /* 256 */ "key_opt ::= KEY expr",
+ /* 257 */ "cmd ::= REINDEX",
+ /* 258 */ "cmd ::= REINDEX nm dbnm",
+ /* 259 */ "cmd ::= ANALYZE",
+ /* 260 */ "cmd ::= ANALYZE nm dbnm",
+ /* 261 */ "cmd ::= ALTER TABLE fullname RENAME TO nm",
+ /* 262 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist",
+ /* 263 */ "add_column_fullname ::= fullname",
+ /* 264 */ "cmd ::= create_vtab",
+ /* 265 */ "cmd ::= create_vtab LP vtabarglist RP",
+ /* 266 */ "create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm",
+ /* 267 */ "vtabarg ::=",
+ /* 268 */ "vtabargtoken ::= ANY",
+ /* 269 */ "vtabargtoken ::= lp anylist RP",
+ /* 270 */ "lp ::= LP",
+ /* 271 */ "with ::=",
+ /* 272 */ "with ::= WITH wqlist",
+ /* 273 */ "with ::= WITH RECURSIVE wqlist",
+ /* 274 */ "wqlist ::= nm eidlist_opt AS LP select RP",
+ /* 275 */ "wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP",
+ /* 276 */ "input ::= cmdlist",
+ /* 277 */ "cmdlist ::= cmdlist ecmd",
+ /* 278 */ "cmdlist ::= ecmd",
+ /* 279 */ "ecmd ::= SEMI",
+ /* 280 */ "ecmd ::= explain cmdx SEMI",
+ /* 281 */ "explain ::=",
+ /* 282 */ "trans_opt ::=",
+ /* 283 */ "trans_opt ::= TRANSACTION",
+ /* 284 */ "trans_opt ::= TRANSACTION nm",
+ /* 285 */ "savepoint_opt ::= SAVEPOINT",
+ /* 286 */ "savepoint_opt ::=",
+ /* 287 */ "cmd ::= create_table create_table_args",
+ /* 288 */ "columnlist ::= columnlist COMMA columnname carglist",
+ /* 289 */ "columnlist ::= columnname carglist",
+ /* 290 */ "nm ::= ID|INDEXED",
+ /* 291 */ "nm ::= STRING",
+ /* 292 */ "nm ::= JOIN_KW",
+ /* 293 */ "typetoken ::= typename",
+ /* 294 */ "typename ::= ID|STRING",
+ /* 295 */ "signed ::= plus_num",
+ /* 296 */ "signed ::= minus_num",
+ /* 297 */ "carglist ::= carglist ccons",
+ /* 298 */ "carglist ::=",
+ /* 299 */ "ccons ::= NULL onconf",
+ /* 300 */ "conslist_opt ::= COMMA conslist",
+ /* 301 */ "conslist ::= conslist tconscomma tcons",
+ /* 302 */ "conslist ::= tcons",
+ /* 303 */ "tconscomma ::=",
+ /* 304 */ "defer_subclause_opt ::= defer_subclause",
+ /* 305 */ "resolvetype ::= raisetype",
+ /* 306 */ "selectnowith ::= oneselect",
+ /* 307 */ "oneselect ::= values",
+ /* 308 */ "sclp ::= selcollist COMMA",
+ /* 309 */ "as ::= ID|STRING",
+ /* 310 */ "expr ::= term",
+ /* 311 */ "exprlist ::= nexprlist",
+ /* 312 */ "nmnum ::= plus_num",
+ /* 313 */ "nmnum ::= nm",
+ /* 314 */ "nmnum ::= ON",
+ /* 315 */ "nmnum ::= DELETE",
+ /* 316 */ "nmnum ::= DEFAULT",
+ /* 317 */ "plus_num ::= INTEGER|FLOAT",
+ /* 318 */ "foreach_clause ::=",
+ /* 319 */ "foreach_clause ::= FOR EACH ROW",
+ /* 320 */ "trnm ::= nm",
+ /* 321 */ "tridxby ::=",
+ /* 322 */ "database_kw_opt ::= DATABASE",
+ /* 323 */ "database_kw_opt ::=",
+ /* 324 */ "kwcolumn_opt ::=",
+ /* 325 */ "kwcolumn_opt ::= COLUMNKW",
+ /* 326 */ "vtabarglist ::= vtabarg",
+ /* 327 */ "vtabarglist ::= vtabarglist COMMA vtabarg",
+ /* 328 */ "vtabarg ::= vtabarg vtabargtoken",
+ /* 329 */ "anylist ::=",
+ /* 330 */ "anylist ::= anylist LP anylist RP",
+ /* 331 */ "anylist ::= anylist ANY",
};
#endif /* NDEBUG */
@@ -133225,50 +134493,47 @@ static unsigned int yy_find_shift_action(
assert( stateno <= YY_SHIFT_COUNT );
do{
i = yy_shift_ofst[stateno];
- if( i==YY_SHIFT_USE_DFLT ) return yy_default[stateno];
assert( iLookAhead!=YYNOCODE );
i += iLookAhead;
if( i<0 || i>=YY_ACTTAB_COUNT || yy_lookahead[i]!=iLookAhead ){
- if( iLookAhead>0 ){
#ifdef YYFALLBACK
- YYCODETYPE iFallback; /* Fallback token */
- if( iLookAhead<sizeof(yyFallback)/sizeof(yyFallback[0])
- && (iFallback = yyFallback[iLookAhead])!=0 ){
+ YYCODETYPE iFallback; /* Fallback token */
+ if( iLookAhead<sizeof(yyFallback)/sizeof(yyFallback[0])
+ && (iFallback = yyFallback[iLookAhead])!=0 ){
#ifndef NDEBUG
- if( yyTraceFILE ){
- fprintf(yyTraceFILE, "%sFALLBACK %s => %s\n",
- yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[iFallback]);
- }
-#endif
- assert( yyFallback[iFallback]==0 ); /* Fallback loop must terminate */
- iLookAhead = iFallback;
- continue;
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE, "%sFALLBACK %s => %s\n",
+ yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[iFallback]);
}
#endif
+ assert( yyFallback[iFallback]==0 ); /* Fallback loop must terminate */
+ iLookAhead = iFallback;
+ continue;
+ }
+#endif
#ifdef YYWILDCARD
- {
- int j = i - iLookAhead + YYWILDCARD;
- if(
+ {
+ int j = i - iLookAhead + YYWILDCARD;
+ if(
#if YY_SHIFT_MIN+YYWILDCARD<0
- j>=0 &&
+ j>=0 &&
#endif
#if YY_SHIFT_MAX+YYWILDCARD>=YY_ACTTAB_COUNT
- j<YY_ACTTAB_COUNT &&
+ j<YY_ACTTAB_COUNT &&
#endif
- yy_lookahead[j]==YYWILDCARD
- ){
+ yy_lookahead[j]==YYWILDCARD && iLookAhead>0
+ ){
#ifndef NDEBUG
- if( yyTraceFILE ){
- fprintf(yyTraceFILE, "%sWILDCARD %s => %s\n",
- yyTracePrompt, yyTokenName[iLookAhead],
- yyTokenName[YYWILDCARD]);
- }
-#endif /* NDEBUG */
- return yy_action[j];
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE, "%sWILDCARD %s => %s\n",
+ yyTracePrompt, yyTokenName[iLookAhead],
+ yyTokenName[YYWILDCARD]);
}
+#endif /* NDEBUG */
+ return yy_action[j];
}
-#endif /* YYWILDCARD */
}
+#endif /* YYWILDCARD */
return yy_default[stateno];
}else{
return yy_action[i];
@@ -133534,7 +134799,9 @@ static const struct {
{ 201, 2 },
{ 149, 8 },
{ 218, 5 },
+ { 218, 7 },
{ 218, 3 },
+ { 218, 5 },
{ 149, 6 },
{ 149, 7 },
{ 219, 2 },
@@ -133551,12 +134818,14 @@ static const struct {
{ 173, 5 },
{ 172, 1 },
{ 172, 1 },
+ { 172, 1 },
{ 173, 1 },
{ 173, 3 },
{ 173, 6 },
{ 173, 5 },
{ 173, 4 },
{ 172, 1 },
+ { 173, 5 },
{ 173, 3 },
{ 173, 3 },
{ 173, 3 },
@@ -133844,7 +135113,7 @@ static void yy_reduce(
case 67: /* defer_subclause_opt ::= */ yytestcase(yyruleno==67);
case 76: /* ifexists ::= */ yytestcase(yyruleno==76);
case 90: /* distinct ::= */ yytestcase(yyruleno==90);
- case 211: /* collate ::= */ yytestcase(yyruleno==211);
+ case 215: /* collate ::= */ yytestcase(yyruleno==215);
{yymsp[1].minor.yy194 = 0;}
break;
case 17: /* ifnotexists ::= IF NOT EXISTS */
@@ -133983,14 +135252,14 @@ static void yy_reduce(
break;
case 56: /* defer_subclause ::= DEFERRABLE init_deferred_pred_opt */
case 71: /* orconf ::= OR resolvetype */ yytestcase(yyruleno==71);
- case 142: /* insert_cmd ::= INSERT orconf */ yytestcase(yyruleno==142);
+ case 144: /* insert_cmd ::= INSERT orconf */ yytestcase(yyruleno==144);
{yymsp[-1].minor.yy194 = yymsp[0].minor.yy194;}
break;
case 58: /* init_deferred_pred_opt ::= INITIALLY DEFERRED */
case 75: /* ifexists ::= IF EXISTS */ yytestcase(yyruleno==75);
- case 183: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==183);
- case 186: /* in_op ::= NOT IN */ yytestcase(yyruleno==186);
- case 212: /* collate ::= COLLATE ID|STRING */ yytestcase(yyruleno==212);
+ case 187: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==187);
+ case 190: /* in_op ::= NOT IN */ yytestcase(yyruleno==190);
+ case 216: /* collate ::= COLLATE ID|STRING */ yytestcase(yyruleno==216);
{yymsp[-1].minor.yy194 = 1;}
break;
case 59: /* init_deferred_pred_opt ::= INITIALLY IMMEDIATE */
@@ -134026,7 +135295,7 @@ static void yy_reduce(
{yymsp[0].minor.yy194 = OE_Ignore;}
break;
case 73: /* resolvetype ::= REPLACE */
- case 143: /* insert_cmd ::= REPLACE */ yytestcase(yyruleno==143);
+ case 145: /* insert_cmd ::= REPLACE */ yytestcase(yyruleno==145);
{yymsp[0].minor.yy194 = OE_Replace;}
break;
case 74: /* cmd ::= DROP TABLE ifexists fullname */
@@ -134154,9 +135423,9 @@ static void yy_reduce(
case 91: /* sclp ::= */
case 119: /* orderby_opt ::= */ yytestcase(yyruleno==119);
case 126: /* groupby_opt ::= */ yytestcase(yyruleno==126);
- case 199: /* exprlist ::= */ yytestcase(yyruleno==199);
- case 202: /* paren_exprlist ::= */ yytestcase(yyruleno==202);
- case 207: /* eidlist_opt ::= */ yytestcase(yyruleno==207);
+ case 203: /* exprlist ::= */ yytestcase(yyruleno==203);
+ case 206: /* paren_exprlist ::= */ yytestcase(yyruleno==206);
+ case 211: /* eidlist_opt ::= */ yytestcase(yyruleno==211);
{yymsp[1].minor.yy148 = 0;}
break;
case 92: /* selcollist ::= sclp expr as */
@@ -134174,7 +135443,7 @@ static void yy_reduce(
break;
case 94: /* selcollist ::= sclp nm DOT STAR */
{
- Expr *pRight = sqlite3PExpr(pParse, TK_ASTERISK, 0, 0, &yymsp[0].minor.yy0);
+ Expr *pRight = sqlite3PExpr(pParse, TK_ASTERISK, 0, 0, 0);
Expr *pLeft = sqlite3PExpr(pParse, TK_ID, 0, 0, &yymsp[-2].minor.yy0);
Expr *pDot = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight, 0);
yymsp[-3].minor.yy148 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy148, pDot);
@@ -134182,8 +135451,8 @@ static void yy_reduce(
break;
case 95: /* as ::= AS nm */
case 106: /* dbnm ::= DOT nm */ yytestcase(yyruleno==106);
- case 221: /* plus_num ::= PLUS INTEGER|FLOAT */ yytestcase(yyruleno==221);
- case 222: /* minus_num ::= MINUS INTEGER|FLOAT */ yytestcase(yyruleno==222);
+ case 225: /* plus_num ::= PLUS INTEGER|FLOAT */ yytestcase(yyruleno==225);
+ case 226: /* minus_num ::= MINUS INTEGER|FLOAT */ yytestcase(yyruleno==226);
{yymsp[-1].minor.yy0 = yymsp[0].minor.yy0;}
break;
case 97: /* from ::= */
@@ -134266,14 +135535,14 @@ static void yy_reduce(
case 112: /* on_opt ::= ON expr */
case 129: /* having_opt ::= HAVING expr */ yytestcase(yyruleno==129);
case 136: /* where_opt ::= WHERE expr */ yytestcase(yyruleno==136);
- case 195: /* case_else ::= ELSE expr */ yytestcase(yyruleno==195);
+ case 199: /* case_else ::= ELSE expr */ yytestcase(yyruleno==199);
{yymsp[-1].minor.yy72 = yymsp[0].minor.yy190.pExpr;}
break;
case 113: /* on_opt ::= */
case 128: /* having_opt ::= */ yytestcase(yyruleno==128);
case 135: /* where_opt ::= */ yytestcase(yyruleno==135);
- case 196: /* case_else ::= */ yytestcase(yyruleno==196);
- case 198: /* case_operand ::= */ yytestcase(yyruleno==198);
+ case 200: /* case_else ::= */ yytestcase(yyruleno==200);
+ case 202: /* case_operand ::= */ yytestcase(yyruleno==202);
{yymsp[1].minor.yy72 = 0;}
break;
case 115: /* indexed_opt ::= INDEXED BY nm */
@@ -134286,7 +135555,7 @@ static void yy_reduce(
{yymsp[-3].minor.yy254 = yymsp[-1].minor.yy254;}
break;
case 118: /* using_opt ::= */
- case 144: /* idlist_opt ::= */ yytestcase(yyruleno==144);
+ case 146: /* idlist_opt ::= */ yytestcase(yyruleno==146);
{yymsp[1].minor.yy254 = 0;}
break;
case 120: /* orderby_opt ::= ORDER BY sortlist */
@@ -134347,69 +135616,89 @@ static void yy_reduce(
sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy148, &yymsp[-2].minor.yy0, 1);
}
break;
- case 139: /* setlist ::= nm EQ expr */
+ case 139: /* setlist ::= setlist COMMA LP idlist RP EQ expr */
+{
+ yymsp[-6].minor.yy148 = sqlite3ExprListAppendVector(pParse, yymsp[-6].minor.yy148, yymsp[-3].minor.yy254, yymsp[0].minor.yy190.pExpr);
+}
+ break;
+ case 140: /* setlist ::= nm EQ expr */
{
yylhsminor.yy148 = sqlite3ExprListAppend(pParse, 0, yymsp[0].minor.yy190.pExpr);
sqlite3ExprListSetName(pParse, yylhsminor.yy148, &yymsp[-2].minor.yy0, 1);
}
yymsp[-2].minor.yy148 = yylhsminor.yy148;
break;
- case 140: /* cmd ::= with insert_cmd INTO fullname idlist_opt select */
+ case 141: /* setlist ::= LP idlist RP EQ expr */
+{
+ yymsp[-4].minor.yy148 = sqlite3ExprListAppendVector(pParse, 0, yymsp[-3].minor.yy254, yymsp[0].minor.yy190.pExpr);
+}
+ break;
+ case 142: /* cmd ::= with insert_cmd INTO fullname idlist_opt select */
{
sqlite3WithPush(pParse, yymsp[-5].minor.yy285, 1);
sqlite3Insert(pParse, yymsp[-2].minor.yy185, yymsp[0].minor.yy243, yymsp[-1].minor.yy254, yymsp[-4].minor.yy194);
}
break;
- case 141: /* cmd ::= with insert_cmd INTO fullname idlist_opt DEFAULT VALUES */
+ case 143: /* cmd ::= with insert_cmd INTO fullname idlist_opt DEFAULT VALUES */
{
sqlite3WithPush(pParse, yymsp[-6].minor.yy285, 1);
sqlite3Insert(pParse, yymsp[-3].minor.yy185, 0, yymsp[-2].minor.yy254, yymsp[-5].minor.yy194);
}
break;
- case 145: /* idlist_opt ::= LP idlist RP */
+ case 147: /* idlist_opt ::= LP idlist RP */
{yymsp[-2].minor.yy254 = yymsp[-1].minor.yy254;}
break;
- case 146: /* idlist ::= idlist COMMA nm */
+ case 148: /* idlist ::= idlist COMMA nm */
{yymsp[-2].minor.yy254 = sqlite3IdListAppend(pParse->db,yymsp[-2].minor.yy254,&yymsp[0].minor.yy0);}
break;
- case 147: /* idlist ::= nm */
+ case 149: /* idlist ::= nm */
{yymsp[0].minor.yy254 = sqlite3IdListAppend(pParse->db,0,&yymsp[0].minor.yy0); /*A-overwrites-Y*/}
break;
- case 148: /* expr ::= LP expr RP */
+ case 150: /* expr ::= LP expr RP */
{spanSet(&yymsp[-2].minor.yy190,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-B*/ yymsp[-2].minor.yy190.pExpr = yymsp[-1].minor.yy190.pExpr;}
break;
- case 149: /* term ::= NULL */
- case 154: /* term ::= INTEGER|FLOAT|BLOB */ yytestcase(yyruleno==154);
- case 155: /* term ::= STRING */ yytestcase(yyruleno==155);
+ case 151: /* term ::= NULL */
+ case 156: /* term ::= FLOAT|BLOB */ yytestcase(yyruleno==156);
+ case 157: /* term ::= STRING */ yytestcase(yyruleno==157);
{spanExpr(&yymsp[0].minor.yy190,pParse,yymsp[0].major,yymsp[0].minor.yy0);/*A-overwrites-X*/}
break;
- case 150: /* expr ::= ID|INDEXED */
- case 151: /* expr ::= JOIN_KW */ yytestcase(yyruleno==151);
+ case 152: /* expr ::= ID|INDEXED */
+ case 153: /* expr ::= JOIN_KW */ yytestcase(yyruleno==153);
{spanExpr(&yymsp[0].minor.yy190,pParse,TK_ID,yymsp[0].minor.yy0); /*A-overwrites-X*/}
break;
- case 152: /* expr ::= nm DOT nm */
+ case 154: /* expr ::= nm DOT nm */
{
- Expr *temp1 = sqlite3PExpr(pParse, TK_ID, 0, 0, &yymsp[-2].minor.yy0);
- Expr *temp2 = sqlite3PExpr(pParse, TK_ID, 0, 0, &yymsp[0].minor.yy0);
+ Expr *temp1 = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-2].minor.yy0, 1);
+ Expr *temp2 = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[0].minor.yy0, 1);
spanSet(&yymsp[-2].minor.yy190,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/
yymsp[-2].minor.yy190.pExpr = sqlite3PExpr(pParse, TK_DOT, temp1, temp2, 0);
}
break;
- case 153: /* expr ::= nm DOT nm DOT nm */
+ case 155: /* expr ::= nm DOT nm DOT nm */
{
- Expr *temp1 = sqlite3PExpr(pParse, TK_ID, 0, 0, &yymsp[-4].minor.yy0);
- Expr *temp2 = sqlite3PExpr(pParse, TK_ID, 0, 0, &yymsp[-2].minor.yy0);
- Expr *temp3 = sqlite3PExpr(pParse, TK_ID, 0, 0, &yymsp[0].minor.yy0);
+ Expr *temp1 = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-4].minor.yy0, 1);
+ Expr *temp2 = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-2].minor.yy0, 1);
+ Expr *temp3 = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[0].minor.yy0, 1);
Expr *temp4 = sqlite3PExpr(pParse, TK_DOT, temp2, temp3, 0);
spanSet(&yymsp[-4].minor.yy190,&yymsp[-4].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/
yymsp[-4].minor.yy190.pExpr = sqlite3PExpr(pParse, TK_DOT, temp1, temp4, 0);
}
break;
- case 156: /* expr ::= VARIABLE */
+ case 158: /* term ::= INTEGER */
+{
+ yylhsminor.yy190.pExpr = sqlite3ExprAlloc(pParse->db, TK_INTEGER, &yymsp[0].minor.yy0, 1);
+ yylhsminor.yy190.zStart = yymsp[0].minor.yy0.z;
+ yylhsminor.yy190.zEnd = yymsp[0].minor.yy0.z + yymsp[0].minor.yy0.n;
+ if( yylhsminor.yy190.pExpr ) yylhsminor.yy190.pExpr->flags |= EP_Leaf;
+}
+ yymsp[0].minor.yy190 = yylhsminor.yy190;
+ break;
+ case 159: /* expr ::= VARIABLE */
{
if( !(yymsp[0].minor.yy0.z[0]=='#' && sqlite3Isdigit(yymsp[0].minor.yy0.z[1])) ){
+ u32 n = yymsp[0].minor.yy0.n;
spanExpr(&yymsp[0].minor.yy190, pParse, TK_VARIABLE, yymsp[0].minor.yy0);
- sqlite3ExprAssignVarNumber(pParse, yymsp[0].minor.yy190.pExpr);
+ sqlite3ExprAssignVarNumber(pParse, yymsp[0].minor.yy190.pExpr, n);
}else{
/* When doing a nested parse, one can include terms in an expression
** that look like this: #1 #2 ... These terms refer to registers
@@ -134421,25 +135710,25 @@ static void yy_reduce(
sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &t);
yymsp[0].minor.yy190.pExpr = 0;
}else{
- yymsp[0].minor.yy190.pExpr = sqlite3PExpr(pParse, TK_REGISTER, 0, 0, &t);
+ yymsp[0].minor.yy190.pExpr = sqlite3PExpr(pParse, TK_REGISTER, 0, 0, 0);
if( yymsp[0].minor.yy190.pExpr ) sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy190.pExpr->iTable);
}
}
}
break;
- case 157: /* expr ::= expr COLLATE ID|STRING */
+ case 160: /* expr ::= expr COLLATE ID|STRING */
{
yymsp[-2].minor.yy190.pExpr = sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy190.pExpr, &yymsp[0].minor.yy0, 1);
yymsp[-2].minor.yy190.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n];
}
break;
- case 158: /* expr ::= CAST LP expr AS typetoken RP */
+ case 161: /* expr ::= CAST LP expr AS typetoken RP */
{
spanSet(&yymsp[-5].minor.yy190,&yymsp[-5].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/
yymsp[-5].minor.yy190.pExpr = sqlite3PExpr(pParse, TK_CAST, yymsp[-3].minor.yy190.pExpr, 0, &yymsp[-1].minor.yy0);
}
break;
- case 159: /* expr ::= ID|INDEXED LP distinct exprlist RP */
+ case 162: /* expr ::= ID|INDEXED LP distinct exprlist RP */
{
if( yymsp[-1].minor.yy148 && yymsp[-1].minor.yy148->nExpr>pParse->db->aLimit[SQLITE_LIMIT_FUNCTION_ARG] ){
sqlite3ErrorMsg(pParse, "too many arguments on function %T", &yymsp[-4].minor.yy0);
@@ -134452,92 +135741,109 @@ static void yy_reduce(
}
yymsp[-4].minor.yy190 = yylhsminor.yy190;
break;
- case 160: /* expr ::= ID|INDEXED LP STAR RP */
+ case 163: /* expr ::= ID|INDEXED LP STAR RP */
{
yylhsminor.yy190.pExpr = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0);
spanSet(&yylhsminor.yy190,&yymsp[-3].minor.yy0,&yymsp[0].minor.yy0);
}
yymsp[-3].minor.yy190 = yylhsminor.yy190;
break;
- case 161: /* term ::= CTIME_KW */
+ case 164: /* term ::= CTIME_KW */
{
yylhsminor.yy190.pExpr = sqlite3ExprFunction(pParse, 0, &yymsp[0].minor.yy0);
spanSet(&yylhsminor.yy190, &yymsp[0].minor.yy0, &yymsp[0].minor.yy0);
}
yymsp[0].minor.yy190 = yylhsminor.yy190;
break;
- case 162: /* expr ::= expr AND expr */
- case 163: /* expr ::= expr OR expr */ yytestcase(yyruleno==163);
- case 164: /* expr ::= expr LT|GT|GE|LE expr */ yytestcase(yyruleno==164);
- case 165: /* expr ::= expr EQ|NE expr */ yytestcase(yyruleno==165);
- case 166: /* expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ yytestcase(yyruleno==166);
- case 167: /* expr ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==167);
- case 168: /* expr ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==168);
- case 169: /* expr ::= expr CONCAT expr */ yytestcase(yyruleno==169);
+ case 165: /* expr ::= LP nexprlist COMMA expr RP */
+{
+ ExprList *pList = sqlite3ExprListAppend(pParse, yymsp[-3].minor.yy148, yymsp[-1].minor.yy190.pExpr);
+ yylhsminor.yy190.pExpr = sqlite3PExpr(pParse, TK_VECTOR, 0, 0, 0);
+ if( yylhsminor.yy190.pExpr ){
+ yylhsminor.yy190.pExpr->x.pList = pList;
+ spanSet(&yylhsminor.yy190, &yymsp[-4].minor.yy0, &yymsp[0].minor.yy0);
+ }else{
+ sqlite3ExprListDelete(pParse->db, pList);
+ }
+}
+ yymsp[-4].minor.yy190 = yylhsminor.yy190;
+ break;
+ case 166: /* expr ::= expr AND expr */
+ case 167: /* expr ::= expr OR expr */ yytestcase(yyruleno==167);
+ case 168: /* expr ::= expr LT|GT|GE|LE expr */ yytestcase(yyruleno==168);
+ case 169: /* expr ::= expr EQ|NE expr */ yytestcase(yyruleno==169);
+ case 170: /* expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ yytestcase(yyruleno==170);
+ case 171: /* expr ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==171);
+ case 172: /* expr ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==172);
+ case 173: /* expr ::= expr CONCAT expr */ yytestcase(yyruleno==173);
{spanBinaryExpr(pParse,yymsp[-1].major,&yymsp[-2].minor.yy190,&yymsp[0].minor.yy190);}
break;
- case 170: /* likeop ::= LIKE_KW|MATCH */
-{yymsp[0].minor.yy392.eOperator = yymsp[0].minor.yy0; yymsp[0].minor.yy392.bNot = 0;/*A-overwrites-X*/}
+ case 174: /* likeop ::= LIKE_KW|MATCH */
+{yymsp[0].minor.yy0=yymsp[0].minor.yy0;/*A-overwrites-X*/}
break;
- case 171: /* likeop ::= NOT LIKE_KW|MATCH */
-{yymsp[-1].minor.yy392.eOperator = yymsp[0].minor.yy0; yymsp[-1].minor.yy392.bNot = 1;}
+ case 175: /* likeop ::= NOT LIKE_KW|MATCH */
+{yymsp[-1].minor.yy0=yymsp[0].minor.yy0; yymsp[-1].minor.yy0.n|=0x80000000; /*yymsp[-1].minor.yy0-overwrite-yymsp[0].minor.yy0*/}
break;
- case 172: /* expr ::= expr likeop expr */
+ case 176: /* expr ::= expr likeop expr */
{
ExprList *pList;
+ int bNot = yymsp[-1].minor.yy0.n & 0x80000000;
+ yymsp[-1].minor.yy0.n &= 0x7fffffff;
pList = sqlite3ExprListAppend(pParse,0, yymsp[0].minor.yy190.pExpr);
pList = sqlite3ExprListAppend(pParse,pList, yymsp[-2].minor.yy190.pExpr);
- yymsp[-2].minor.yy190.pExpr = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy392.eOperator);
- exprNot(pParse, yymsp[-1].minor.yy392.bNot, &yymsp[-2].minor.yy190);
+ yymsp[-2].minor.yy190.pExpr = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0);
+ exprNot(pParse, bNot, &yymsp[-2].minor.yy190);
yymsp[-2].minor.yy190.zEnd = yymsp[0].minor.yy190.zEnd;
if( yymsp[-2].minor.yy190.pExpr ) yymsp[-2].minor.yy190.pExpr->flags |= EP_InfixFunc;
}
break;
- case 173: /* expr ::= expr likeop expr ESCAPE expr */
+ case 177: /* expr ::= expr likeop expr ESCAPE expr */
{
ExprList *pList;
+ int bNot = yymsp[-3].minor.yy0.n & 0x80000000;
+ yymsp[-3].minor.yy0.n &= 0x7fffffff;
pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy190.pExpr);
pList = sqlite3ExprListAppend(pParse,pList, yymsp[-4].minor.yy190.pExpr);
pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy190.pExpr);
- yymsp[-4].minor.yy190.pExpr = sqlite3ExprFunction(pParse, pList, &yymsp[-3].minor.yy392.eOperator);
- exprNot(pParse, yymsp[-3].minor.yy392.bNot, &yymsp[-4].minor.yy190);
+ yymsp[-4].minor.yy190.pExpr = sqlite3ExprFunction(pParse, pList, &yymsp[-3].minor.yy0);
+ exprNot(pParse, bNot, &yymsp[-4].minor.yy190);
yymsp[-4].minor.yy190.zEnd = yymsp[0].minor.yy190.zEnd;
if( yymsp[-4].minor.yy190.pExpr ) yymsp[-4].minor.yy190.pExpr->flags |= EP_InfixFunc;
}
break;
- case 174: /* expr ::= expr ISNULL|NOTNULL */
+ case 178: /* expr ::= expr ISNULL|NOTNULL */
{spanUnaryPostfix(pParse,yymsp[0].major,&yymsp[-1].minor.yy190,&yymsp[0].minor.yy0);}
break;
- case 175: /* expr ::= expr NOT NULL */
+ case 179: /* expr ::= expr NOT NULL */
{spanUnaryPostfix(pParse,TK_NOTNULL,&yymsp[-2].minor.yy190,&yymsp[0].minor.yy0);}
break;
- case 176: /* expr ::= expr IS expr */
+ case 180: /* expr ::= expr IS expr */
{
spanBinaryExpr(pParse,TK_IS,&yymsp[-2].minor.yy190,&yymsp[0].minor.yy190);
binaryToUnaryIfNull(pParse, yymsp[0].minor.yy190.pExpr, yymsp[-2].minor.yy190.pExpr, TK_ISNULL);
}
break;
- case 177: /* expr ::= expr IS NOT expr */
+ case 181: /* expr ::= expr IS NOT expr */
{
spanBinaryExpr(pParse,TK_ISNOT,&yymsp[-3].minor.yy190,&yymsp[0].minor.yy190);
binaryToUnaryIfNull(pParse, yymsp[0].minor.yy190.pExpr, yymsp[-3].minor.yy190.pExpr, TK_NOTNULL);
}
break;
- case 178: /* expr ::= NOT expr */
- case 179: /* expr ::= BITNOT expr */ yytestcase(yyruleno==179);
+ case 182: /* expr ::= NOT expr */
+ case 183: /* expr ::= BITNOT expr */ yytestcase(yyruleno==183);
{spanUnaryPrefix(&yymsp[-1].minor.yy190,pParse,yymsp[-1].major,&yymsp[0].minor.yy190,&yymsp[-1].minor.yy0);/*A-overwrites-B*/}
break;
- case 180: /* expr ::= MINUS expr */
+ case 184: /* expr ::= MINUS expr */
{spanUnaryPrefix(&yymsp[-1].minor.yy190,pParse,TK_UMINUS,&yymsp[0].minor.yy190,&yymsp[-1].minor.yy0);/*A-overwrites-B*/}
break;
- case 181: /* expr ::= PLUS expr */
+ case 185: /* expr ::= PLUS expr */
{spanUnaryPrefix(&yymsp[-1].minor.yy190,pParse,TK_UPLUS,&yymsp[0].minor.yy190,&yymsp[-1].minor.yy0);/*A-overwrites-B*/}
break;
- case 182: /* between_op ::= BETWEEN */
- case 185: /* in_op ::= IN */ yytestcase(yyruleno==185);
+ case 186: /* between_op ::= BETWEEN */
+ case 189: /* in_op ::= IN */ yytestcase(yyruleno==189);
{yymsp[0].minor.yy194 = 0;}
break;
- case 184: /* expr ::= expr between_op expr AND expr */
+ case 188: /* expr ::= expr between_op expr AND expr */
{
ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy190.pExpr);
pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy190.pExpr);
@@ -134551,7 +135857,7 @@ static void yy_reduce(
yymsp[-4].minor.yy190.zEnd = yymsp[0].minor.yy190.zEnd;
}
break;
- case 187: /* expr ::= expr in_op LP exprlist RP */
+ case 191: /* expr ::= expr in_op LP exprlist RP */
{
if( yymsp[-1].minor.yy148==0 ){
/* Expressions of the form
@@ -134604,14 +135910,14 @@ static void yy_reduce(
yymsp[-4].minor.yy190.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n];
}
break;
- case 188: /* expr ::= LP select RP */
+ case 192: /* expr ::= LP select RP */
{
spanSet(&yymsp[-2].minor.yy190,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-B*/
yymsp[-2].minor.yy190.pExpr = sqlite3PExpr(pParse, TK_SELECT, 0, 0, 0);
sqlite3PExprAddSelect(pParse, yymsp[-2].minor.yy190.pExpr, yymsp[-1].minor.yy243);
}
break;
- case 189: /* expr ::= expr in_op LP select RP */
+ case 193: /* expr ::= expr in_op LP select RP */
{
yymsp[-4].minor.yy190.pExpr = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy190.pExpr, 0, 0);
sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy190.pExpr, yymsp[-1].minor.yy243);
@@ -134619,7 +135925,7 @@ static void yy_reduce(
yymsp[-4].minor.yy190.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n];
}
break;
- case 190: /* expr ::= expr in_op nm dbnm paren_exprlist */
+ case 194: /* expr ::= expr in_op nm dbnm paren_exprlist */
{
SrcList *pSrc = sqlite3SrcListAppend(pParse->db, 0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);
Select *pSelect = sqlite3SelectNew(pParse, 0,pSrc,0,0,0,0,0,0,0);
@@ -134630,7 +135936,7 @@ static void yy_reduce(
yymsp[-4].minor.yy190.zEnd = yymsp[-1].minor.yy0.z ? &yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n] : &yymsp[-2].minor.yy0.z[yymsp[-2].minor.yy0.n];
}
break;
- case 191: /* expr ::= EXISTS LP select RP */
+ case 195: /* expr ::= EXISTS LP select RP */
{
Expr *p;
spanSet(&yymsp[-3].minor.yy190,&yymsp[-3].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-B*/
@@ -134638,7 +135944,7 @@ static void yy_reduce(
sqlite3PExprAddSelect(pParse, p, yymsp[-1].minor.yy243);
}
break;
- case 192: /* expr ::= CASE case_operand case_exprlist case_else END */
+ case 196: /* expr ::= CASE case_operand case_exprlist case_else END */
{
spanSet(&yymsp[-4].minor.yy190,&yymsp[-4].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-C*/
yymsp[-4].minor.yy190.pExpr = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy72, 0, 0);
@@ -134651,78 +135957,80 @@ static void yy_reduce(
}
}
break;
- case 193: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */
+ case 197: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */
{
yymsp[-4].minor.yy148 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy148, yymsp[-2].minor.yy190.pExpr);
yymsp[-4].minor.yy148 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy148, yymsp[0].minor.yy190.pExpr);
}
break;
- case 194: /* case_exprlist ::= WHEN expr THEN expr */
+ case 198: /* case_exprlist ::= WHEN expr THEN expr */
{
yymsp[-3].minor.yy148 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy190.pExpr);
yymsp[-3].minor.yy148 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy148, yymsp[0].minor.yy190.pExpr);
}
break;
- case 197: /* case_operand ::= expr */
+ case 201: /* case_operand ::= expr */
{yymsp[0].minor.yy72 = yymsp[0].minor.yy190.pExpr; /*A-overwrites-X*/}
break;
- case 200: /* nexprlist ::= nexprlist COMMA expr */
+ case 204: /* nexprlist ::= nexprlist COMMA expr */
{yymsp[-2].minor.yy148 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy148,yymsp[0].minor.yy190.pExpr);}
break;
- case 201: /* nexprlist ::= expr */
+ case 205: /* nexprlist ::= expr */
{yymsp[0].minor.yy148 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy190.pExpr); /*A-overwrites-Y*/}
break;
- case 203: /* paren_exprlist ::= LP exprlist RP */
- case 208: /* eidlist_opt ::= LP eidlist RP */ yytestcase(yyruleno==208);
+ case 207: /* paren_exprlist ::= LP exprlist RP */
+ case 212: /* eidlist_opt ::= LP eidlist RP */ yytestcase(yyruleno==212);
{yymsp[-2].minor.yy148 = yymsp[-1].minor.yy148;}
break;
- case 204: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
+ case 208: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
{
sqlite3CreateIndex(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0,
sqlite3SrcListAppend(pParse->db,0,&yymsp[-4].minor.yy0,0), yymsp[-2].minor.yy148, yymsp[-10].minor.yy194,
&yymsp[-11].minor.yy0, yymsp[0].minor.yy72, SQLITE_SO_ASC, yymsp[-8].minor.yy194, SQLITE_IDXTYPE_APPDEF);
}
break;
- case 205: /* uniqueflag ::= UNIQUE */
- case 246: /* raisetype ::= ABORT */ yytestcase(yyruleno==246);
+ case 209: /* uniqueflag ::= UNIQUE */
+ case 250: /* raisetype ::= ABORT */ yytestcase(yyruleno==250);
{yymsp[0].minor.yy194 = OE_Abort;}
break;
- case 206: /* uniqueflag ::= */
+ case 210: /* uniqueflag ::= */
{yymsp[1].minor.yy194 = OE_None;}
break;
- case 209: /* eidlist ::= eidlist COMMA nm collate sortorder */
+ case 213: /* eidlist ::= eidlist COMMA nm collate sortorder */
{
yymsp[-4].minor.yy148 = parserAddExprIdListTerm(pParse, yymsp[-4].minor.yy148, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy194, yymsp[0].minor.yy194);
}
break;
- case 210: /* eidlist ::= nm collate sortorder */
+ case 214: /* eidlist ::= nm collate sortorder */
{
yymsp[-2].minor.yy148 = parserAddExprIdListTerm(pParse, 0, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy194, yymsp[0].minor.yy194); /*A-overwrites-Y*/
}
break;
- case 213: /* cmd ::= DROP INDEX ifexists fullname */
+ case 217: /* cmd ::= DROP INDEX ifexists fullname */
{sqlite3DropIndex(pParse, yymsp[0].minor.yy185, yymsp[-1].minor.yy194);}
break;
- case 214: /* cmd ::= VACUUM */
- case 215: /* cmd ::= VACUUM nm */ yytestcase(yyruleno==215);
-{sqlite3Vacuum(pParse);}
+ case 218: /* cmd ::= VACUUM */
+{sqlite3Vacuum(pParse,0);}
+ break;
+ case 219: /* cmd ::= VACUUM nm */
+{sqlite3Vacuum(pParse,&yymsp[0].minor.yy0);}
break;
- case 216: /* cmd ::= PRAGMA nm dbnm */
+ case 220: /* cmd ::= PRAGMA nm dbnm */
{sqlite3Pragma(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,0,0);}
break;
- case 217: /* cmd ::= PRAGMA nm dbnm EQ nmnum */
+ case 221: /* cmd ::= PRAGMA nm dbnm EQ nmnum */
{sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,0);}
break;
- case 218: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */
+ case 222: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */
{sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,0);}
break;
- case 219: /* cmd ::= PRAGMA nm dbnm EQ minus_num */
+ case 223: /* cmd ::= PRAGMA nm dbnm EQ minus_num */
{sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,1);}
break;
- case 220: /* cmd ::= PRAGMA nm dbnm LP minus_num RP */
+ case 224: /* cmd ::= PRAGMA nm dbnm LP minus_num RP */
{sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,1);}
break;
- case 223: /* cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
+ case 227: /* cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
{
Token all;
all.z = yymsp[-3].minor.yy0.z;
@@ -134730,53 +136038,53 @@ static void yy_reduce(
sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy145, &all);
}
break;
- case 224: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
+ case 228: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
{
sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy194, yymsp[-4].minor.yy332.a, yymsp[-4].minor.yy332.b, yymsp[-2].minor.yy185, yymsp[0].minor.yy72, yymsp[-10].minor.yy194, yymsp[-8].minor.yy194);
yymsp[-10].minor.yy0 = (yymsp[-6].minor.yy0.n==0?yymsp[-7].minor.yy0:yymsp[-6].minor.yy0); /*A-overwrites-T*/
}
break;
- case 225: /* trigger_time ::= BEFORE */
+ case 229: /* trigger_time ::= BEFORE */
{ yymsp[0].minor.yy194 = TK_BEFORE; }
break;
- case 226: /* trigger_time ::= AFTER */
+ case 230: /* trigger_time ::= AFTER */
{ yymsp[0].minor.yy194 = TK_AFTER; }
break;
- case 227: /* trigger_time ::= INSTEAD OF */
+ case 231: /* trigger_time ::= INSTEAD OF */
{ yymsp[-1].minor.yy194 = TK_INSTEAD;}
break;
- case 228: /* trigger_time ::= */
+ case 232: /* trigger_time ::= */
{ yymsp[1].minor.yy194 = TK_BEFORE; }
break;
- case 229: /* trigger_event ::= DELETE|INSERT */
- case 230: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==230);
+ case 233: /* trigger_event ::= DELETE|INSERT */
+ case 234: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==234);
{yymsp[0].minor.yy332.a = yymsp[0].major; /*A-overwrites-X*/ yymsp[0].minor.yy332.b = 0;}
break;
- case 231: /* trigger_event ::= UPDATE OF idlist */
+ case 235: /* trigger_event ::= UPDATE OF idlist */
{yymsp[-2].minor.yy332.a = TK_UPDATE; yymsp[-2].minor.yy332.b = yymsp[0].minor.yy254;}
break;
- case 232: /* when_clause ::= */
- case 251: /* key_opt ::= */ yytestcase(yyruleno==251);
+ case 236: /* when_clause ::= */
+ case 255: /* key_opt ::= */ yytestcase(yyruleno==255);
{ yymsp[1].minor.yy72 = 0; }
break;
- case 233: /* when_clause ::= WHEN expr */
- case 252: /* key_opt ::= KEY expr */ yytestcase(yyruleno==252);
+ case 237: /* when_clause ::= WHEN expr */
+ case 256: /* key_opt ::= KEY expr */ yytestcase(yyruleno==256);
{ yymsp[-1].minor.yy72 = yymsp[0].minor.yy190.pExpr; }
break;
- case 234: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
+ case 238: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
{
assert( yymsp[-2].minor.yy145!=0 );
yymsp[-2].minor.yy145->pLast->pNext = yymsp[-1].minor.yy145;
yymsp[-2].minor.yy145->pLast = yymsp[-1].minor.yy145;
}
break;
- case 235: /* trigger_cmd_list ::= trigger_cmd SEMI */
+ case 239: /* trigger_cmd_list ::= trigger_cmd SEMI */
{
assert( yymsp[-1].minor.yy145!=0 );
yymsp[-1].minor.yy145->pLast = yymsp[-1].minor.yy145;
}
break;
- case 236: /* trnm ::= nm DOT nm */
+ case 240: /* trnm ::= nm DOT nm */
{
yymsp[-2].minor.yy0 = yymsp[0].minor.yy0;
sqlite3ErrorMsg(pParse,
@@ -134784,33 +136092,33 @@ static void yy_reduce(
"statements within triggers");
}
break;
- case 237: /* tridxby ::= INDEXED BY nm */
+ case 241: /* tridxby ::= INDEXED BY nm */
{
sqlite3ErrorMsg(pParse,
"the INDEXED BY clause is not allowed on UPDATE or DELETE statements "
"within triggers");
}
break;
- case 238: /* tridxby ::= NOT INDEXED */
+ case 242: /* tridxby ::= NOT INDEXED */
{
sqlite3ErrorMsg(pParse,
"the NOT INDEXED clause is not allowed on UPDATE or DELETE statements "
"within triggers");
}
break;
- case 239: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt */
+ case 243: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt */
{yymsp[-6].minor.yy145 = sqlite3TriggerUpdateStep(pParse->db, &yymsp[-4].minor.yy0, yymsp[-1].minor.yy148, yymsp[0].minor.yy72, yymsp[-5].minor.yy194);}
break;
- case 240: /* trigger_cmd ::= insert_cmd INTO trnm idlist_opt select */
+ case 244: /* trigger_cmd ::= insert_cmd INTO trnm idlist_opt select */
{yymsp[-4].minor.yy145 = sqlite3TriggerInsertStep(pParse->db, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy254, yymsp[0].minor.yy243, yymsp[-4].minor.yy194);/*A-overwrites-R*/}
break;
- case 241: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt */
+ case 245: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt */
{yymsp[-4].minor.yy145 = sqlite3TriggerDeleteStep(pParse->db, &yymsp[-2].minor.yy0, yymsp[0].minor.yy72);}
break;
- case 242: /* trigger_cmd ::= select */
+ case 246: /* trigger_cmd ::= select */
{yymsp[0].minor.yy145 = sqlite3TriggerSelectStep(pParse->db, yymsp[0].minor.yy243); /*A-overwrites-X*/}
break;
- case 243: /* expr ::= RAISE LP IGNORE RP */
+ case 247: /* expr ::= RAISE LP IGNORE RP */
{
spanSet(&yymsp[-3].minor.yy190,&yymsp[-3].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/
yymsp[-3].minor.yy190.pExpr = sqlite3PExpr(pParse, TK_RAISE, 0, 0, 0);
@@ -134819,7 +136127,7 @@ static void yy_reduce(
}
}
break;
- case 244: /* expr ::= RAISE LP raisetype COMMA nm RP */
+ case 248: /* expr ::= RAISE LP raisetype COMMA nm RP */
{
spanSet(&yymsp[-5].minor.yy190,&yymsp[-5].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/
yymsp[-5].minor.yy190.pExpr = sqlite3PExpr(pParse, TK_RAISE, 0, 0, &yymsp[-1].minor.yy0);
@@ -134828,151 +136136,151 @@ static void yy_reduce(
}
}
break;
- case 245: /* raisetype ::= ROLLBACK */
+ case 249: /* raisetype ::= ROLLBACK */
{yymsp[0].minor.yy194 = OE_Rollback;}
break;
- case 247: /* raisetype ::= FAIL */
+ case 251: /* raisetype ::= FAIL */
{yymsp[0].minor.yy194 = OE_Fail;}
break;
- case 248: /* cmd ::= DROP TRIGGER ifexists fullname */
+ case 252: /* cmd ::= DROP TRIGGER ifexists fullname */
{
sqlite3DropTrigger(pParse,yymsp[0].minor.yy185,yymsp[-1].minor.yy194);
}
break;
- case 249: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
+ case 253: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
{
sqlite3Attach(pParse, yymsp[-3].minor.yy190.pExpr, yymsp[-1].minor.yy190.pExpr, yymsp[0].minor.yy72);
}
break;
- case 250: /* cmd ::= DETACH database_kw_opt expr */
+ case 254: /* cmd ::= DETACH database_kw_opt expr */
{
sqlite3Detach(pParse, yymsp[0].minor.yy190.pExpr);
}
break;
- case 253: /* cmd ::= REINDEX */
+ case 257: /* cmd ::= REINDEX */
{sqlite3Reindex(pParse, 0, 0);}
break;
- case 254: /* cmd ::= REINDEX nm dbnm */
+ case 258: /* cmd ::= REINDEX nm dbnm */
{sqlite3Reindex(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);}
break;
- case 255: /* cmd ::= ANALYZE */
+ case 259: /* cmd ::= ANALYZE */
{sqlite3Analyze(pParse, 0, 0);}
break;
- case 256: /* cmd ::= ANALYZE nm dbnm */
+ case 260: /* cmd ::= ANALYZE nm dbnm */
{sqlite3Analyze(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);}
break;
- case 257: /* cmd ::= ALTER TABLE fullname RENAME TO nm */
+ case 261: /* cmd ::= ALTER TABLE fullname RENAME TO nm */
{
sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy185,&yymsp[0].minor.yy0);
}
break;
- case 258: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
+ case 262: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
{
yymsp[-1].minor.yy0.n = (int)(pParse->sLastToken.z-yymsp[-1].minor.yy0.z) + pParse->sLastToken.n;
sqlite3AlterFinishAddColumn(pParse, &yymsp[-1].minor.yy0);
}
break;
- case 259: /* add_column_fullname ::= fullname */
+ case 263: /* add_column_fullname ::= fullname */
{
disableLookaside(pParse);
sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy185);
}
break;
- case 260: /* cmd ::= create_vtab */
+ case 264: /* cmd ::= create_vtab */
{sqlite3VtabFinishParse(pParse,0);}
break;
- case 261: /* cmd ::= create_vtab LP vtabarglist RP */
+ case 265: /* cmd ::= create_vtab LP vtabarglist RP */
{sqlite3VtabFinishParse(pParse,&yymsp[0].minor.yy0);}
break;
- case 262: /* create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
+ case 266: /* create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
{
sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0, yymsp[-4].minor.yy194);
}
break;
- case 263: /* vtabarg ::= */
+ case 267: /* vtabarg ::= */
{sqlite3VtabArgInit(pParse);}
break;
- case 264: /* vtabargtoken ::= ANY */
- case 265: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==265);
- case 266: /* lp ::= LP */ yytestcase(yyruleno==266);
+ case 268: /* vtabargtoken ::= ANY */
+ case 269: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==269);
+ case 270: /* lp ::= LP */ yytestcase(yyruleno==270);
{sqlite3VtabArgExtend(pParse,&yymsp[0].minor.yy0);}
break;
- case 267: /* with ::= */
+ case 271: /* with ::= */
{yymsp[1].minor.yy285 = 0;}
break;
- case 268: /* with ::= WITH wqlist */
+ case 272: /* with ::= WITH wqlist */
{ yymsp[-1].minor.yy285 = yymsp[0].minor.yy285; }
break;
- case 269: /* with ::= WITH RECURSIVE wqlist */
+ case 273: /* with ::= WITH RECURSIVE wqlist */
{ yymsp[-2].minor.yy285 = yymsp[0].minor.yy285; }
break;
- case 270: /* wqlist ::= nm eidlist_opt AS LP select RP */
+ case 274: /* wqlist ::= nm eidlist_opt AS LP select RP */
{
yymsp[-5].minor.yy285 = sqlite3WithAdd(pParse, 0, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy148, yymsp[-1].minor.yy243); /*A-overwrites-X*/
}
break;
- case 271: /* wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */
+ case 275: /* wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */
{
yymsp[-7].minor.yy285 = sqlite3WithAdd(pParse, yymsp[-7].minor.yy285, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy148, yymsp[-1].minor.yy243);
}
break;
default:
- /* (272) input ::= cmdlist */ yytestcase(yyruleno==272);
- /* (273) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==273);
- /* (274) cmdlist ::= ecmd (OPTIMIZED OUT) */ assert(yyruleno!=274);
- /* (275) ecmd ::= SEMI */ yytestcase(yyruleno==275);
- /* (276) ecmd ::= explain cmdx SEMI */ yytestcase(yyruleno==276);
- /* (277) explain ::= */ yytestcase(yyruleno==277);
- /* (278) trans_opt ::= */ yytestcase(yyruleno==278);
- /* (279) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==279);
- /* (280) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==280);
- /* (281) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==281);
- /* (282) savepoint_opt ::= */ yytestcase(yyruleno==282);
- /* (283) cmd ::= create_table create_table_args */ yytestcase(yyruleno==283);
- /* (284) columnlist ::= columnlist COMMA columnname carglist */ yytestcase(yyruleno==284);
- /* (285) columnlist ::= columnname carglist */ yytestcase(yyruleno==285);
- /* (286) nm ::= ID|INDEXED */ yytestcase(yyruleno==286);
- /* (287) nm ::= STRING */ yytestcase(yyruleno==287);
- /* (288) nm ::= JOIN_KW */ yytestcase(yyruleno==288);
- /* (289) typetoken ::= typename */ yytestcase(yyruleno==289);
- /* (290) typename ::= ID|STRING */ yytestcase(yyruleno==290);
- /* (291) signed ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=291);
- /* (292) signed ::= minus_num (OPTIMIZED OUT) */ assert(yyruleno!=292);
- /* (293) carglist ::= carglist ccons */ yytestcase(yyruleno==293);
- /* (294) carglist ::= */ yytestcase(yyruleno==294);
- /* (295) ccons ::= NULL onconf */ yytestcase(yyruleno==295);
- /* (296) conslist_opt ::= COMMA conslist */ yytestcase(yyruleno==296);
- /* (297) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==297);
- /* (298) conslist ::= tcons (OPTIMIZED OUT) */ assert(yyruleno!=298);
- /* (299) tconscomma ::= */ yytestcase(yyruleno==299);
- /* (300) defer_subclause_opt ::= defer_subclause (OPTIMIZED OUT) */ assert(yyruleno!=300);
- /* (301) resolvetype ::= raisetype (OPTIMIZED OUT) */ assert(yyruleno!=301);
- /* (302) selectnowith ::= oneselect (OPTIMIZED OUT) */ assert(yyruleno!=302);
- /* (303) oneselect ::= values */ yytestcase(yyruleno==303);
- /* (304) sclp ::= selcollist COMMA */ yytestcase(yyruleno==304);
- /* (305) as ::= ID|STRING */ yytestcase(yyruleno==305);
- /* (306) expr ::= term (OPTIMIZED OUT) */ assert(yyruleno!=306);
- /* (307) exprlist ::= nexprlist */ yytestcase(yyruleno==307);
- /* (308) nmnum ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=308);
- /* (309) nmnum ::= nm (OPTIMIZED OUT) */ assert(yyruleno!=309);
- /* (310) nmnum ::= ON */ yytestcase(yyruleno==310);
- /* (311) nmnum ::= DELETE */ yytestcase(yyruleno==311);
- /* (312) nmnum ::= DEFAULT */ yytestcase(yyruleno==312);
- /* (313) plus_num ::= INTEGER|FLOAT */ yytestcase(yyruleno==313);
- /* (314) foreach_clause ::= */ yytestcase(yyruleno==314);
- /* (315) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==315);
- /* (316) trnm ::= nm */ yytestcase(yyruleno==316);
- /* (317) tridxby ::= */ yytestcase(yyruleno==317);
- /* (318) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==318);
- /* (319) database_kw_opt ::= */ yytestcase(yyruleno==319);
- /* (320) kwcolumn_opt ::= */ yytestcase(yyruleno==320);
- /* (321) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==321);
- /* (322) vtabarglist ::= vtabarg */ yytestcase(yyruleno==322);
- /* (323) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==323);
- /* (324) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==324);
- /* (325) anylist ::= */ yytestcase(yyruleno==325);
- /* (326) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==326);
- /* (327) anylist ::= anylist ANY */ yytestcase(yyruleno==327);
+ /* (276) input ::= cmdlist */ yytestcase(yyruleno==276);
+ /* (277) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==277);
+ /* (278) cmdlist ::= ecmd (OPTIMIZED OUT) */ assert(yyruleno!=278);
+ /* (279) ecmd ::= SEMI */ yytestcase(yyruleno==279);
+ /* (280) ecmd ::= explain cmdx SEMI */ yytestcase(yyruleno==280);
+ /* (281) explain ::= */ yytestcase(yyruleno==281);
+ /* (282) trans_opt ::= */ yytestcase(yyruleno==282);
+ /* (283) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==283);
+ /* (284) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==284);
+ /* (285) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==285);
+ /* (286) savepoint_opt ::= */ yytestcase(yyruleno==286);
+ /* (287) cmd ::= create_table create_table_args */ yytestcase(yyruleno==287);
+ /* (288) columnlist ::= columnlist COMMA columnname carglist */ yytestcase(yyruleno==288);
+ /* (289) columnlist ::= columnname carglist */ yytestcase(yyruleno==289);
+ /* (290) nm ::= ID|INDEXED */ yytestcase(yyruleno==290);
+ /* (291) nm ::= STRING */ yytestcase(yyruleno==291);
+ /* (292) nm ::= JOIN_KW */ yytestcase(yyruleno==292);
+ /* (293) typetoken ::= typename */ yytestcase(yyruleno==293);
+ /* (294) typename ::= ID|STRING */ yytestcase(yyruleno==294);
+ /* (295) signed ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=295);
+ /* (296) signed ::= minus_num (OPTIMIZED OUT) */ assert(yyruleno!=296);
+ /* (297) carglist ::= carglist ccons */ yytestcase(yyruleno==297);
+ /* (298) carglist ::= */ yytestcase(yyruleno==298);
+ /* (299) ccons ::= NULL onconf */ yytestcase(yyruleno==299);
+ /* (300) conslist_opt ::= COMMA conslist */ yytestcase(yyruleno==300);
+ /* (301) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==301);
+ /* (302) conslist ::= tcons (OPTIMIZED OUT) */ assert(yyruleno!=302);
+ /* (303) tconscomma ::= */ yytestcase(yyruleno==303);
+ /* (304) defer_subclause_opt ::= defer_subclause (OPTIMIZED OUT) */ assert(yyruleno!=304);
+ /* (305) resolvetype ::= raisetype (OPTIMIZED OUT) */ assert(yyruleno!=305);
+ /* (306) selectnowith ::= oneselect (OPTIMIZED OUT) */ assert(yyruleno!=306);
+ /* (307) oneselect ::= values */ yytestcase(yyruleno==307);
+ /* (308) sclp ::= selcollist COMMA */ yytestcase(yyruleno==308);
+ /* (309) as ::= ID|STRING */ yytestcase(yyruleno==309);
+ /* (310) expr ::= term (OPTIMIZED OUT) */ assert(yyruleno!=310);
+ /* (311) exprlist ::= nexprlist */ yytestcase(yyruleno==311);
+ /* (312) nmnum ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=312);
+ /* (313) nmnum ::= nm (OPTIMIZED OUT) */ assert(yyruleno!=313);
+ /* (314) nmnum ::= ON */ yytestcase(yyruleno==314);
+ /* (315) nmnum ::= DELETE */ yytestcase(yyruleno==315);
+ /* (316) nmnum ::= DEFAULT */ yytestcase(yyruleno==316);
+ /* (317) plus_num ::= INTEGER|FLOAT */ yytestcase(yyruleno==317);
+ /* (318) foreach_clause ::= */ yytestcase(yyruleno==318);
+ /* (319) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==319);
+ /* (320) trnm ::= nm */ yytestcase(yyruleno==320);
+ /* (321) tridxby ::= */ yytestcase(yyruleno==321);
+ /* (322) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==322);
+ /* (323) database_kw_opt ::= */ yytestcase(yyruleno==323);
+ /* (324) kwcolumn_opt ::= */ yytestcase(yyruleno==324);
+ /* (325) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==325);
+ /* (326) vtabarglist ::= vtabarg */ yytestcase(yyruleno==326);
+ /* (327) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==327);
+ /* (328) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==328);
+ /* (329) anylist ::= */ yytestcase(yyruleno==329);
+ /* (330) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==330);
+ /* (331) anylist ::= anylist ANY */ yytestcase(yyruleno==331);
break;
/********** End reduce actions ************************************************/
};
@@ -135163,7 +136471,7 @@ SQLITE_PRIVATE void sqlite3Parser(
yy_destructor(yypParser, (YYCODETYPE)yymajor, &yyminorunion);
yymajor = YYNOCODE;
}else{
- while( yypParser->yytos >= &yypParser->yystack
+ while( yypParser->yytos >= yypParser->yystack
&& yymx != YYERRORSYMBOL
&& (yyact = yy_find_reduce_action(
yypParser->yytos->stateno,
@@ -136031,14 +137339,26 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzEr
assert( pParse->nVar==0 );
assert( pParse->nzVar==0 );
assert( pParse->azVar==0 );
- while( zSql[i]!=0 ){
+ while( 1 ){
assert( i>=0 );
- pParse->sLastToken.z = &zSql[i];
- pParse->sLastToken.n = sqlite3GetToken((unsigned char*)&zSql[i],&tokenType);
- i += pParse->sLastToken.n;
- if( i>mxSqlLen ){
- pParse->rc = SQLITE_TOOBIG;
- break;
+ if( zSql[i]!=0 ){
+ pParse->sLastToken.z = &zSql[i];
+ pParse->sLastToken.n = sqlite3GetToken((u8*)&zSql[i],&tokenType);
+ i += pParse->sLastToken.n;
+ if( i>mxSqlLen ){
+ pParse->rc = SQLITE_TOOBIG;
+ break;
+ }
+ }else{
+ /* Upon reaching the end of input, call the parser two more times
+ ** with tokens TK_SEMI and 0, in that order. */
+ if( lastTokenParsed==TK_SEMI ){
+ tokenType = 0;
+ }else if( lastTokenParsed==0 ){
+ break;
+ }else{
+ tokenType = TK_SEMI;
+ }
}
if( tokenType>=TK_SPACE ){
assert( tokenType==TK_SPACE || tokenType==TK_ILLEGAL );
@@ -136059,15 +137379,6 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzEr
}
assert( nErr==0 );
pParse->zTail = &zSql[i];
- if( pParse->rc==SQLITE_OK && db->mallocFailed==0 ){
- assert( zSql[i]==0 );
- if( lastTokenParsed!=TK_SEMI ){
- sqlite3Parser(pEngine, TK_SEMI, pParse->sLastToken, pParse);
- }
- if( pParse->rc==SQLITE_OK && db->mallocFailed==0 ){
- sqlite3Parser(pEngine, 0, pParse->sLastToken, pParse);
- }
- }
#ifdef YYTRACKMAXSTACKDEPTH
sqlite3_mutex_enter(sqlite3MallocMutex());
sqlite3StatusHighwater(SQLITE_STATUS_PARSER_STACK,
@@ -137307,6 +138618,11 @@ SQLITE_API int sqlite3_db_config(sqlite3 *db, int op, ...){
int rc;
va_start(ap, op);
switch( op ){
+ case SQLITE_DBCONFIG_MAINDBNAME: {
+ db->aDb[0].zDbSName = va_arg(ap,char*);
+ rc = SQLITE_OK;
+ break;
+ }
case SQLITE_DBCONFIG_LOOKASIDE: {
void *pBuf = va_arg(ap, void*); /* IMP: R-26835-10964 */
int sz = va_arg(ap, int); /* IMP: R-47871-25994 */
@@ -139452,9 +140768,9 @@ static int openDatabase(
/* The default safety_level for the main database is FULL; for the temp
** database it is OFF. This matches the pager layer defaults.
*/
- db->aDb[0].zName = "main";
+ db->aDb[0].zDbSName = "main";
db->aDb[0].safety_level = SQLITE_DEFAULT_SYNCHRONOUS+1;
- db->aDb[1].zName = "temp";
+ db->aDb[1].zDbSName = "temp";
db->aDb[1].safety_level = PAGER_SYNCHRONOUS_OFF;
db->magic = SQLITE_MAGIC_OPEN;
@@ -139468,11 +140784,20 @@ static int openDatabase(
*/
sqlite3Error(db, SQLITE_OK);
sqlite3RegisterPerConnectionBuiltinFunctions(db);
+ rc = sqlite3_errcode(db);
+
+#ifdef SQLITE_ENABLE_FTS5
+ /* Register any built-in FTS5 module before loading the automatic
+ ** extensions. This allows automatic extensions to register FTS5
+ ** tokenizers and auxiliary functions. */
+ if( !db->mallocFailed && rc==SQLITE_OK ){
+ rc = sqlite3Fts5Init(db);
+ }
+#endif
/* Load automatic extensions - extensions that have been registered
** using the sqlite3_automatic_extension() API.
*/
- rc = sqlite3_errcode(db);
if( rc==SQLITE_OK ){
sqlite3AutoLoadExtensions(db);
rc = sqlite3_errcode(db);
@@ -139501,12 +140826,6 @@ static int openDatabase(
}
#endif
-#ifdef SQLITE_ENABLE_FTS5
- if( !db->mallocFailed && rc==SQLITE_OK ){
- rc = sqlite3Fts5Init(db);
- }
-#endif
-
#ifdef SQLITE_ENABLE_ICU
if( !db->mallocFailed && rc==SQLITE_OK ){
rc = sqlite3IcuInit(db);
@@ -140294,6 +141613,15 @@ SQLITE_API int sqlite3_test_control(int op, ...){
break;
}
+ /* Set the threshold at which OP_Once counters reset back to zero.
+ ** By default this is 0x7ffffffe (over 2 billion), but that value is
+ ** too big to test in a reasonable amount of time, so this control is
+ ** provided to set a small and easily reachable reset value.
+ */
+ case SQLITE_TESTCTRL_ONCE_RESET_THRESHOLD: {
+ sqlite3GlobalConfig.iOnceResetThreshold = va_arg(ap, int);
+ break;
+ }
/* sqlite3_test_control(SQLITE_TESTCTRL_VDBE_COVERAGE, xCallback, ptr);
**
@@ -140415,7 +141743,7 @@ SQLITE_PRIVATE Btree *sqlite3DbNameToBtree(sqlite3 *db, const char *zDbName){
int i;
for(i=0; i<db->nDb; i++){
if( db->aDb[i].pBt
- && (zDbName==0 || sqlite3StrICmp(zDbName, db->aDb[i].zName)==0)
+ && (zDbName==0 || sqlite3StrICmp(zDbName, db->aDb[i].zDbSName)==0)
){
return db->aDb[i].pBt;
}
@@ -161530,7 +162858,7 @@ static int rtreeFilter(
if( idxNum==1 ){
/* Special case - lookup by rowid. */
RtreeNode *pLeaf; /* Leaf on which the required cell resides */
- RtreeSearchPoint *p; /* Search point for the the leaf */
+ RtreeSearchPoint *p; /* Search point for the leaf */
i64 iRowid = sqlite3_value_int64(argv[0]);
i64 iNode = 0;
rc = findLeafNode(pRtree, iRowid, &pLeaf, &iNode);
@@ -163000,10 +164328,12 @@ static int rtreeQueryStat1(sqlite3 *db, Rtree *pRtree){
int rc;
i64 nRow = 0;
- if( sqlite3_table_column_metadata(db,pRtree->zDb,"sqlite_stat1",
- 0,0,0,0,0,0)==SQLITE_ERROR ){
+ rc = sqlite3_table_column_metadata(
+ db, pRtree->zDb, "sqlite_stat1",0,0,0,0,0,0
+ );
+ if( rc!=SQLITE_OK ){
pRtree->nRowEst = RTREE_DEFAULT_ROWEST;
- return SQLITE_OK;
+ return rc==SQLITE_ERROR ? SQLITE_OK : rc;
}
zSql = sqlite3_mprintf(zFmt, pRtree->zDb, pRtree->zName);
if( zSql==0 ){
@@ -163904,7 +165234,7 @@ static void icuRegexpFunc(sqlite3_context *p, int nArg, sqlite3_value **apArg){
** of upper() or lower().
**
** lower('I', 'en_us') -> 'i'
-** lower('I', 'tr_tr') -> 'ı' (small dotless i)
+** lower('I', 'tr_tr') -> '\u131' (small dotless i)
**
** http://www.icu-project.org/userguide/posix.html#case_mappings
*/
@@ -164566,7 +165896,7 @@ SQLITE_PRIVATE void sqlite3Fts3IcuTokenizerModule(
** may also be named data<integer>_<target>, where <integer> is any sequence
** of zero or more numeric characters (0-9). This can be significant because
** tables within the RBU database are always processed in order sorted by
-** name. By judicious selection of the the <integer> portion of the names
+** name. By judicious selection of the <integer> portion of the names
** of the RBU tables the user can therefore control the order in which they
** are processed. This can be useful, for example, to ensure that "external
** content" FTS4 tables are updated before their underlying content tables.
@@ -164781,16 +166111,22 @@ SQLITE_API sqlite3rbu *sqlite3rbu_open(
** An RBU vacuum is similar to SQLite's built-in VACUUM command, except
** that it can be suspended and resumed like an RBU update.
**
-** The second argument to this function, which may not be NULL, identifies
-** a database in which to store the state of the RBU vacuum operation if
-** it is suspended. The first time sqlite3rbu_vacuum() is called, to start
-** an RBU vacuum operation, the state database should either not exist or
-** be empty (contain no tables). If an RBU vacuum is suspended by calling
+** The second argument to this function identifies a database in which
+** to store the state of the RBU vacuum operation if it is suspended. The
+** first time sqlite3rbu_vacuum() is called, to start an RBU vacuum
+** operation, the state database should either not exist or be empty
+** (contain no tables). If an RBU vacuum is suspended by calling
** sqlite3rbu_close() on the RBU handle before sqlite3rbu_step() has
** returned SQLITE_DONE, the vacuum state is stored in the state database.
** The vacuum can be resumed by calling this function to open a new RBU
** handle specifying the same target and state databases.
**
+** If the second argument passed to this function is NULL, then the
+** name of the state database is "<database>-vacuum", where <database>
+** is the name of the target database file. In this case, on UNIX, if the
+** state database is not already present in the file-system, it is created
+** with the same permissions as the target db is made.
+**
** This function does not delete the state database after an RBU vacuum
** is completed, even if it created it. However, if the call to
** sqlite3rbu_close() returns any value other than SQLITE_OK, the contents
@@ -167282,15 +168618,18 @@ static RbuState *rbuLoadState(sqlite3rbu *p){
** error occurs, leave an error code and message in the RBU handle.
*/
static void rbuOpenDatabase(sqlite3rbu *p){
- assert( p->rc==SQLITE_OK );
- assert( p->dbMain==0 && p->dbRbu==0 );
- assert( rbuIsVacuum(p) || p->zTarget!=0 );
+ assert( p->rc || (p->dbMain==0 && p->dbRbu==0) );
+ assert( p->rc || rbuIsVacuum(p) || p->zTarget!=0 );
/* Open the RBU database */
p->dbRbu = rbuOpenDbhandle(p, p->zRbu, 1);
if( p->rc==SQLITE_OK && rbuIsVacuum(p) ){
sqlite3_file_control(p->dbRbu, "main", SQLITE_FCNTL_RBUCNT, (void*)p);
+ if( p->zState==0 ){
+ const char *zFile = sqlite3_db_filename(p->dbRbu, "main");
+ p->zState = rbuMPrintf(p, "file://%s-vacuum?modeof=%s", zFile, zFile);
+ }
}
/* If using separate RBU and state databases, attach the state database to
@@ -168425,8 +169764,7 @@ static sqlite3rbu *openRbuHandle(
sqlite3rbu *p;
size_t nTarget = zTarget ? strlen(zTarget) : 0;
size_t nRbu = strlen(zRbu);
- size_t nState = zState ? strlen(zState) : 0;
- size_t nByte = sizeof(sqlite3rbu) + nTarget+1 + nRbu+1+ nState+1;
+ size_t nByte = sizeof(sqlite3rbu) + nTarget+1 + nRbu+1;
p = (sqlite3rbu*)sqlite3_malloc64(nByte);
if( p ){
@@ -168448,8 +169786,7 @@ static sqlite3rbu *openRbuHandle(
memcpy(p->zRbu, zRbu, nRbu+1);
pCsr += nRbu+1;
if( zState ){
- p->zState = pCsr;
- memcpy(p->zState, zState, nState+1);
+ p->zState = rbuMPrintf(p, "%s", zState);
}
rbuOpenDatabase(p);
}
@@ -168560,6 +169897,20 @@ static sqlite3rbu *openRbuHandle(
}
/*
+** Allocate and return an RBU handle with all fields zeroed except for the
+** error code, which is set to SQLITE_MISUSE.
+*/
+static sqlite3rbu *rbuMisuseError(void){
+ sqlite3rbu *pRet;
+ pRet = sqlite3_malloc64(sizeof(sqlite3rbu));
+ if( pRet ){
+ memset(pRet, 0, sizeof(sqlite3rbu));
+ pRet->rc = SQLITE_MISUSE;
+ }
+ return pRet;
+}
+
+/*
** Open and return a new RBU handle.
*/
SQLITE_API sqlite3rbu *sqlite3rbu_open(
@@ -168567,6 +169918,7 @@ SQLITE_API sqlite3rbu *sqlite3rbu_open(
const char *zRbu,
const char *zState
){
+ if( zTarget==0 || zRbu==0 ){ return rbuMisuseError(); }
/* TODO: Check that zTarget and zRbu are non-NULL */
return openRbuHandle(zTarget, zRbu, zState);
}
@@ -168578,6 +169930,7 @@ SQLITE_API sqlite3rbu *sqlite3rbu_vacuum(
const char *zTarget,
const char *zState
){
+ if( zTarget==0 ){ return rbuMisuseError(); }
/* TODO: Check that both arguments are non-NULL */
return openRbuHandle(0, zTarget, zState);
}
@@ -168655,6 +170008,7 @@ SQLITE_API int sqlite3rbu_close(sqlite3rbu *p, char **pzErrmsg){
rbuEditErrmsg(p);
rc = p->rc;
*pzErrmsg = p->zErrmsg;
+ sqlite3_free(p->zState);
sqlite3_free(p);
}else{
rc = SQLITE_NOMEM;
@@ -170266,7 +171620,7 @@ static int statFilter(
" UNION ALL "
"SELECT name, rootpage, type"
" FROM \"%w\".%s WHERE rootpage!=0"
- " ORDER BY name", pTab->db->aDb[pCsr->iDb].zName, zMaster);
+ " ORDER BY name", pTab->db->aDb[pCsr->iDb].zDbSName, zMaster);
if( zSql==0 ){
return SQLITE_NOMEM_BKPT;
}else{
@@ -170320,7 +171674,7 @@ static int statColumn(
default: { /* schema */
sqlite3 *db = sqlite3_context_db_handle(ctx);
int iDb = pCsr->iDb;
- sqlite3_result_text(ctx, db->aDb[iDb].zName, -1, SQLITE_STATIC);
+ sqlite3_result_text(ctx, db->aDb[iDb].zDbSName, -1, SQLITE_STATIC);
break;
}
}
@@ -178557,6 +179911,7 @@ static void sqlite3Fts5ParseNodeFree(Fts5ExprNode*);
static void sqlite3Fts5ParseSetDistance(Fts5Parse*, Fts5ExprNearset*, Fts5Token*);
static void sqlite3Fts5ParseSetColset(Fts5Parse*, Fts5ExprNearset*, Fts5Colset*);
+static Fts5Colset *sqlite3Fts5ParseColsetInvert(Fts5Parse*, Fts5Colset*);
static void sqlite3Fts5ParseFinished(Fts5Parse *pParse, Fts5ExprNode *p);
static void sqlite3Fts5ParseNear(Fts5Parse *pParse, Fts5Token*);
@@ -178614,12 +179969,13 @@ static int sqlite3Fts5UnicodeFold(int c, int bRemoveDiacritic);
#define FTS5_COLON 5
#define FTS5_LP 6
#define FTS5_RP 7
-#define FTS5_LCP 8
-#define FTS5_RCP 9
-#define FTS5_STRING 10
-#define FTS5_COMMA 11
-#define FTS5_PLUS 12
-#define FTS5_STAR 13
+#define FTS5_MINUS 8
+#define FTS5_LCP 9
+#define FTS5_RCP 10
+#define FTS5_STRING 11
+#define FTS5_COMMA 12
+#define FTS5_PLUS 13
+#define FTS5_STAR 14
/*
** 2000-05-29
@@ -178733,17 +180089,17 @@ static int sqlite3Fts5UnicodeFold(int c, int bRemoveDiacritic);
#endif
/************* Begin control #defines *****************************************/
#define fts5YYCODETYPE unsigned char
-#define fts5YYNOCODE 27
+#define fts5YYNOCODE 28
#define fts5YYACTIONTYPE unsigned char
#define sqlite3Fts5ParserFTS5TOKENTYPE Fts5Token
typedef union {
int fts5yyinit;
sqlite3Fts5ParserFTS5TOKENTYPE fts5yy0;
- Fts5Colset* fts5yy3;
- Fts5ExprPhrase* fts5yy11;
- Fts5ExprNode* fts5yy18;
- int fts5yy20;
- Fts5ExprNearset* fts5yy26;
+ int fts5yy4;
+ Fts5Colset* fts5yy11;
+ Fts5ExprNode* fts5yy24;
+ Fts5ExprNearset* fts5yy46;
+ Fts5ExprPhrase* fts5yy53;
} fts5YYMINORTYPE;
#ifndef fts5YYSTACKDEPTH
#define fts5YYSTACKDEPTH 100
@@ -178752,16 +180108,16 @@ typedef union {
#define sqlite3Fts5ParserARG_PDECL ,Fts5Parse *pParse
#define sqlite3Fts5ParserARG_FETCH Fts5Parse *pParse = fts5yypParser->pParse
#define sqlite3Fts5ParserARG_STORE fts5yypParser->pParse = pParse
-#define fts5YYNSTATE 26
-#define fts5YYNRULE 24
-#define fts5YY_MAX_SHIFT 25
-#define fts5YY_MIN_SHIFTREDUCE 40
-#define fts5YY_MAX_SHIFTREDUCE 63
-#define fts5YY_MIN_REDUCE 64
-#define fts5YY_MAX_REDUCE 87
-#define fts5YY_ERROR_ACTION 88
-#define fts5YY_ACCEPT_ACTION 89
-#define fts5YY_NO_ACTION 90
+#define fts5YYNSTATE 29
+#define fts5YYNRULE 26
+#define fts5YY_MAX_SHIFT 28
+#define fts5YY_MIN_SHIFTREDUCE 45
+#define fts5YY_MAX_SHIFTREDUCE 70
+#define fts5YY_MIN_REDUCE 71
+#define fts5YY_MAX_REDUCE 96
+#define fts5YY_ERROR_ACTION 97
+#define fts5YY_ACCEPT_ACTION 98
+#define fts5YY_NO_ACTION 99
/************* End control #defines *******************************************/
/* Define the fts5yytestcase() macro to be a no-op if is not already defined
@@ -178793,7 +180149,7 @@ typedef union {
**
** N between fts5YY_MIN_REDUCE Reduce by rule N-fts5YY_MIN_REDUCE
** and fts5YY_MAX_REDUCE
-
+**
** N == fts5YY_ERROR_ACTION A syntax error has occurred.
**
** N == fts5YY_ACCEPT_ACTION The parser accepts its input.
@@ -178802,16 +180158,20 @@ typedef union {
** slots in the fts5yy_action[] table.
**
** The action table is constructed as a single large table named fts5yy_action[].
-** Given state S and lookahead X, the action is computed as
+** Given state S and lookahead X, the action is computed as either:
**
-** fts5yy_action[ fts5yy_shift_ofst[S] + X ]
+** (A) N = fts5yy_action[ fts5yy_shift_ofst[S] + X ]
+** (B) N = fts5yy_default[S]
**
-** If the index value fts5yy_shift_ofst[S]+X is out of range or if the value
-** fts5yy_lookahead[fts5yy_shift_ofst[S]+X] is not equal to X or if fts5yy_shift_ofst[S]
-** is equal to fts5YY_SHIFT_USE_DFLT, it means that the action is not in the table
-** and that fts5yy_default[S] should be used instead.
+** The (A) formula is preferred. The B formula is used instead if:
+** (1) The fts5yy_shift_ofst[S]+X value is out of range, or
+** (2) fts5yy_lookahead[fts5yy_shift_ofst[S]+X] is not equal to X, or
+** (3) fts5yy_shift_ofst[S] equal fts5YY_SHIFT_USE_DFLT.
+** (Implementation note: fts5YY_SHIFT_USE_DFLT is chosen so that
+** fts5YY_SHIFT_USE_DFLT+X will be out of range for all possible lookaheads X.
+** Hence only tests (1) and (2) need to be evaluated.)
**
-** The formula above is for computing the action when the lookahead is
+** The formulas above are for computing the action when the lookahead is
** a terminal symbol. If the lookahead is a non-terminal (as occurs after
** a reduce action) then the fts5yy_reduce_ofst[] array is used in place of
** the fts5yy_shift_ofst[] array and fts5YY_REDUCE_USE_DFLT is used in place of
@@ -178829,48 +180189,50 @@ typedef union {
** fts5yy_default[] Default action for each state.
**
*********** Begin parsing tables **********************************************/
-#define fts5YY_ACTTAB_COUNT (78)
+#define fts5YY_ACTTAB_COUNT (85)
static const fts5YYACTIONTYPE fts5yy_action[] = {
- /* 0 */ 89, 15, 46, 5, 48, 24, 12, 19, 23, 14,
- /* 10 */ 46, 5, 48, 24, 20, 21, 23, 43, 46, 5,
- /* 20 */ 48, 24, 6, 18, 23, 17, 46, 5, 48, 24,
- /* 30 */ 75, 7, 23, 25, 46, 5, 48, 24, 62, 47,
- /* 40 */ 23, 48, 24, 7, 11, 23, 9, 3, 4, 2,
- /* 50 */ 62, 50, 52, 44, 64, 3, 4, 2, 49, 4,
- /* 60 */ 2, 1, 23, 11, 16, 9, 12, 2, 10, 61,
- /* 70 */ 53, 59, 62, 60, 22, 13, 55, 8,
+ /* 0 */ 98, 16, 51, 5, 53, 27, 83, 7, 26, 15,
+ /* 10 */ 51, 5, 53, 27, 13, 69, 26, 48, 51, 5,
+ /* 20 */ 53, 27, 19, 11, 26, 9, 20, 51, 5, 53,
+ /* 30 */ 27, 13, 22, 26, 28, 51, 5, 53, 27, 68,
+ /* 40 */ 1, 26, 19, 11, 17, 9, 52, 10, 53, 27,
+ /* 50 */ 23, 24, 26, 54, 3, 4, 2, 26, 6, 21,
+ /* 60 */ 49, 71, 3, 4, 2, 7, 56, 59, 55, 59,
+ /* 70 */ 4, 2, 12, 69, 58, 60, 18, 67, 62, 69,
+ /* 80 */ 25, 66, 8, 14, 2,
};
static const fts5YYCODETYPE fts5yy_lookahead[] = {
- /* 0 */ 15, 16, 17, 18, 19, 20, 10, 11, 23, 16,
- /* 10 */ 17, 18, 19, 20, 23, 24, 23, 16, 17, 18,
- /* 20 */ 19, 20, 22, 23, 23, 16, 17, 18, 19, 20,
- /* 30 */ 5, 6, 23, 16, 17, 18, 19, 20, 13, 17,
- /* 40 */ 23, 19, 20, 6, 8, 23, 10, 1, 2, 3,
- /* 50 */ 13, 9, 10, 7, 0, 1, 2, 3, 19, 2,
- /* 60 */ 3, 6, 23, 8, 21, 10, 10, 3, 10, 25,
- /* 70 */ 10, 10, 13, 25, 12, 10, 7, 5,
+ /* 0 */ 16, 17, 18, 19, 20, 21, 5, 6, 24, 17,
+ /* 10 */ 18, 19, 20, 21, 11, 14, 24, 17, 18, 19,
+ /* 20 */ 20, 21, 8, 9, 24, 11, 17, 18, 19, 20,
+ /* 30 */ 21, 11, 12, 24, 17, 18, 19, 20, 21, 26,
+ /* 40 */ 6, 24, 8, 9, 22, 11, 18, 11, 20, 21,
+ /* 50 */ 24, 25, 24, 20, 1, 2, 3, 24, 23, 24,
+ /* 60 */ 7, 0, 1, 2, 3, 6, 10, 11, 10, 11,
+ /* 70 */ 2, 3, 9, 14, 11, 11, 22, 26, 7, 14,
+ /* 80 */ 13, 11, 5, 11, 3,
};
-#define fts5YY_SHIFT_USE_DFLT (-5)
-#define fts5YY_SHIFT_COUNT (25)
-#define fts5YY_SHIFT_MIN (-4)
-#define fts5YY_SHIFT_MAX (72)
-static const signed char fts5yy_shift_ofst[] = {
- /* 0 */ 55, 55, 55, 55, 55, 36, -4, 56, 58, 25,
- /* 10 */ 37, 60, 59, 59, 46, 54, 42, 57, 62, 61,
- /* 20 */ 62, 69, 65, 62, 72, 64,
+#define fts5YY_SHIFT_USE_DFLT (85)
+#define fts5YY_SHIFT_COUNT (28)
+#define fts5YY_SHIFT_MIN (0)
+#define fts5YY_SHIFT_MAX (81)
+static const unsigned char fts5yy_shift_ofst[] = {
+ /* 0 */ 34, 34, 34, 34, 34, 14, 20, 3, 36, 1,
+ /* 10 */ 59, 64, 64, 65, 65, 53, 61, 56, 58, 63,
+ /* 20 */ 68, 67, 70, 67, 71, 72, 67, 77, 81,
};
-#define fts5YY_REDUCE_USE_DFLT (-16)
-#define fts5YY_REDUCE_COUNT (13)
-#define fts5YY_REDUCE_MIN (-15)
-#define fts5YY_REDUCE_MAX (48)
+#define fts5YY_REDUCE_USE_DFLT (-17)
+#define fts5YY_REDUCE_COUNT (14)
+#define fts5YY_REDUCE_MIN (-16)
+#define fts5YY_REDUCE_MAX (54)
static const signed char fts5yy_reduce_ofst[] = {
- /* 0 */ -15, -7, 1, 9, 17, 22, -9, 0, 39, 44,
- /* 10 */ 44, 43, 44, 48,
+ /* 0 */ -16, -8, 0, 9, 17, 28, 26, 35, 33, 13,
+ /* 10 */ 13, 22, 54, 13, 51,
};
static const fts5YYACTIONTYPE fts5yy_default[] = {
- /* 0 */ 88, 88, 88, 88, 88, 69, 82, 88, 88, 87,
- /* 10 */ 87, 88, 87, 87, 88, 88, 88, 66, 80, 88,
- /* 20 */ 81, 88, 88, 78, 88, 65,
+ /* 0 */ 97, 97, 97, 97, 97, 76, 91, 97, 97, 96,
+ /* 10 */ 96, 97, 97, 96, 96, 97, 97, 97, 97, 97,
+ /* 20 */ 73, 89, 97, 90, 97, 97, 87, 97, 72,
};
/********** End of lemon-generated parsing tables *****************************/
@@ -178977,11 +180339,11 @@ static void sqlite3Fts5ParserTrace(FILE *TraceFILE, char *zTracePrompt){
static const char *const fts5yyTokenName[] = {
"$", "OR", "AND", "NOT",
"TERM", "COLON", "LP", "RP",
- "LCP", "RCP", "STRING", "COMMA",
- "PLUS", "STAR", "error", "input",
- "expr", "cnearset", "exprlist", "nearset",
- "colset", "colsetlist", "nearphrases", "phrase",
- "neardist_opt", "star_opt",
+ "MINUS", "LCP", "RCP", "STRING",
+ "COMMA", "PLUS", "STAR", "error",
+ "input", "expr", "cnearset", "exprlist",
+ "nearset", "colset", "colsetlist", "nearphrases",
+ "phrase", "neardist_opt", "star_opt",
};
#endif /* NDEBUG */
@@ -178999,20 +180361,22 @@ static const char *const fts5yyRuleName[] = {
/* 7 */ "exprlist ::= exprlist cnearset",
/* 8 */ "cnearset ::= nearset",
/* 9 */ "cnearset ::= colset COLON nearset",
- /* 10 */ "colset ::= LCP colsetlist RCP",
- /* 11 */ "colset ::= STRING",
- /* 12 */ "colsetlist ::= colsetlist STRING",
- /* 13 */ "colsetlist ::= STRING",
- /* 14 */ "nearset ::= phrase",
- /* 15 */ "nearset ::= STRING LP nearphrases neardist_opt RP",
- /* 16 */ "nearphrases ::= phrase",
- /* 17 */ "nearphrases ::= nearphrases phrase",
- /* 18 */ "neardist_opt ::=",
- /* 19 */ "neardist_opt ::= COMMA STRING",
- /* 20 */ "phrase ::= phrase PLUS STRING star_opt",
- /* 21 */ "phrase ::= STRING star_opt",
- /* 22 */ "star_opt ::= STAR",
- /* 23 */ "star_opt ::=",
+ /* 10 */ "colset ::= MINUS LCP colsetlist RCP",
+ /* 11 */ "colset ::= LCP colsetlist RCP",
+ /* 12 */ "colset ::= STRING",
+ /* 13 */ "colset ::= MINUS STRING",
+ /* 14 */ "colsetlist ::= colsetlist STRING",
+ /* 15 */ "colsetlist ::= STRING",
+ /* 16 */ "nearset ::= phrase",
+ /* 17 */ "nearset ::= STRING LP nearphrases neardist_opt RP",
+ /* 18 */ "nearphrases ::= phrase",
+ /* 19 */ "nearphrases ::= nearphrases phrase",
+ /* 20 */ "neardist_opt ::=",
+ /* 21 */ "neardist_opt ::= COMMA STRING",
+ /* 22 */ "phrase ::= phrase PLUS STRING star_opt",
+ /* 23 */ "phrase ::= STRING star_opt",
+ /* 24 */ "star_opt ::= STAR",
+ /* 25 */ "star_opt ::=",
};
#endif /* NDEBUG */
@@ -179122,33 +180486,33 @@ static void fts5yy_destructor(
** inside the C code.
*/
/********* Begin destructor definitions ***************************************/
- case 15: /* input */
+ case 16: /* input */
{
(void)pParse;
}
break;
- case 16: /* expr */
- case 17: /* cnearset */
- case 18: /* exprlist */
+ case 17: /* expr */
+ case 18: /* cnearset */
+ case 19: /* exprlist */
{
- sqlite3Fts5ParseNodeFree((fts5yypminor->fts5yy18));
+ sqlite3Fts5ParseNodeFree((fts5yypminor->fts5yy24));
}
break;
- case 19: /* nearset */
- case 22: /* nearphrases */
+ case 20: /* nearset */
+ case 23: /* nearphrases */
{
- sqlite3Fts5ParseNearsetFree((fts5yypminor->fts5yy26));
+ sqlite3Fts5ParseNearsetFree((fts5yypminor->fts5yy46));
}
break;
- case 20: /* colset */
- case 21: /* colsetlist */
+ case 21: /* colset */
+ case 22: /* colsetlist */
{
- sqlite3_free((fts5yypminor->fts5yy3));
+ sqlite3_free((fts5yypminor->fts5yy11));
}
break;
- case 23: /* phrase */
+ case 24: /* phrase */
{
- sqlite3Fts5ParsePhraseFree((fts5yypminor->fts5yy11));
+ sqlite3Fts5ParsePhraseFree((fts5yypminor->fts5yy53));
}
break;
/********* End destructor definitions *****************************************/
@@ -179225,50 +180589,47 @@ static unsigned int fts5yy_find_shift_action(
assert( stateno <= fts5YY_SHIFT_COUNT );
do{
i = fts5yy_shift_ofst[stateno];
- if( i==fts5YY_SHIFT_USE_DFLT ) return fts5yy_default[stateno];
assert( iLookAhead!=fts5YYNOCODE );
i += iLookAhead;
if( i<0 || i>=fts5YY_ACTTAB_COUNT || fts5yy_lookahead[i]!=iLookAhead ){
- if( iLookAhead>0 ){
#ifdef fts5YYFALLBACK
- fts5YYCODETYPE iFallback; /* Fallback token */
- if( iLookAhead<sizeof(fts5yyFallback)/sizeof(fts5yyFallback[0])
- && (iFallback = fts5yyFallback[iLookAhead])!=0 ){
+ fts5YYCODETYPE iFallback; /* Fallback token */
+ if( iLookAhead<sizeof(fts5yyFallback)/sizeof(fts5yyFallback[0])
+ && (iFallback = fts5yyFallback[iLookAhead])!=0 ){
#ifndef NDEBUG
- if( fts5yyTraceFILE ){
- fprintf(fts5yyTraceFILE, "%sFALLBACK %s => %s\n",
- fts5yyTracePrompt, fts5yyTokenName[iLookAhead], fts5yyTokenName[iFallback]);
- }
-#endif
- assert( fts5yyFallback[iFallback]==0 ); /* Fallback loop must terminate */
- iLookAhead = iFallback;
- continue;
+ if( fts5yyTraceFILE ){
+ fprintf(fts5yyTraceFILE, "%sFALLBACK %s => %s\n",
+ fts5yyTracePrompt, fts5yyTokenName[iLookAhead], fts5yyTokenName[iFallback]);
}
#endif
+ assert( fts5yyFallback[iFallback]==0 ); /* Fallback loop must terminate */
+ iLookAhead = iFallback;
+ continue;
+ }
+#endif
#ifdef fts5YYWILDCARD
- {
- int j = i - iLookAhead + fts5YYWILDCARD;
- if(
+ {
+ int j = i - iLookAhead + fts5YYWILDCARD;
+ if(
#if fts5YY_SHIFT_MIN+fts5YYWILDCARD<0
- j>=0 &&
+ j>=0 &&
#endif
#if fts5YY_SHIFT_MAX+fts5YYWILDCARD>=fts5YY_ACTTAB_COUNT
- j<fts5YY_ACTTAB_COUNT &&
+ j<fts5YY_ACTTAB_COUNT &&
#endif
- fts5yy_lookahead[j]==fts5YYWILDCARD
- ){
+ fts5yy_lookahead[j]==fts5YYWILDCARD && iLookAhead>0
+ ){
#ifndef NDEBUG
- if( fts5yyTraceFILE ){
- fprintf(fts5yyTraceFILE, "%sWILDCARD %s => %s\n",
- fts5yyTracePrompt, fts5yyTokenName[iLookAhead],
- fts5yyTokenName[fts5YYWILDCARD]);
- }
-#endif /* NDEBUG */
- return fts5yy_action[j];
+ if( fts5yyTraceFILE ){
+ fprintf(fts5yyTraceFILE, "%sWILDCARD %s => %s\n",
+ fts5yyTracePrompt, fts5yyTokenName[iLookAhead],
+ fts5yyTokenName[fts5YYWILDCARD]);
}
+#endif /* NDEBUG */
+ return fts5yy_action[j];
}
-#endif /* fts5YYWILDCARD */
}
+#endif /* fts5YYWILDCARD */
return fts5yy_default[stateno];
}else{
return fts5yy_action[i];
@@ -179395,30 +180756,32 @@ static const struct {
fts5YYCODETYPE lhs; /* Symbol on the left-hand side of the rule */
unsigned char nrhs; /* Number of right-hand side symbols in the rule */
} fts5yyRuleInfo[] = {
- { 15, 1 },
- { 16, 3 },
- { 16, 3 },
- { 16, 3 },
- { 16, 3 },
{ 16, 1 },
- { 18, 1 },
- { 18, 2 },
- { 17, 1 },
{ 17, 3 },
- { 20, 3 },
- { 20, 1 },
- { 21, 2 },
- { 21, 1 },
+ { 17, 3 },
+ { 17, 3 },
+ { 17, 3 },
+ { 17, 1 },
{ 19, 1 },
- { 19, 5 },
- { 22, 1 },
+ { 19, 2 },
+ { 18, 1 },
+ { 18, 3 },
+ { 21, 4 },
+ { 21, 3 },
+ { 21, 1 },
+ { 21, 2 },
{ 22, 2 },
- { 24, 0 },
- { 24, 2 },
- { 23, 4 },
+ { 22, 1 },
+ { 20, 1 },
+ { 20, 5 },
+ { 23, 1 },
{ 23, 2 },
- { 25, 1 },
{ 25, 0 },
+ { 25, 2 },
+ { 24, 4 },
+ { 24, 2 },
+ { 26, 1 },
+ { 26, 0 },
};
static void fts5yy_accept(fts5yyParser*); /* Forward Declaration */
@@ -179483,120 +180846,131 @@ static void fts5yy_reduce(
/********** Begin reduce actions **********************************************/
fts5YYMINORTYPE fts5yylhsminor;
case 0: /* input ::= expr */
-{ sqlite3Fts5ParseFinished(pParse, fts5yymsp[0].minor.fts5yy18); }
+{ sqlite3Fts5ParseFinished(pParse, fts5yymsp[0].minor.fts5yy24); }
break;
case 1: /* expr ::= expr AND expr */
{
- fts5yylhsminor.fts5yy18 = sqlite3Fts5ParseNode(pParse, FTS5_AND, fts5yymsp[-2].minor.fts5yy18, fts5yymsp[0].minor.fts5yy18, 0);
+ fts5yylhsminor.fts5yy24 = sqlite3Fts5ParseNode(pParse, FTS5_AND, fts5yymsp[-2].minor.fts5yy24, fts5yymsp[0].minor.fts5yy24, 0);
}
- fts5yymsp[-2].minor.fts5yy18 = fts5yylhsminor.fts5yy18;
+ fts5yymsp[-2].minor.fts5yy24 = fts5yylhsminor.fts5yy24;
break;
case 2: /* expr ::= expr OR expr */
{
- fts5yylhsminor.fts5yy18 = sqlite3Fts5ParseNode(pParse, FTS5_OR, fts5yymsp[-2].minor.fts5yy18, fts5yymsp[0].minor.fts5yy18, 0);
+ fts5yylhsminor.fts5yy24 = sqlite3Fts5ParseNode(pParse, FTS5_OR, fts5yymsp[-2].minor.fts5yy24, fts5yymsp[0].minor.fts5yy24, 0);
}
- fts5yymsp[-2].minor.fts5yy18 = fts5yylhsminor.fts5yy18;
+ fts5yymsp[-2].minor.fts5yy24 = fts5yylhsminor.fts5yy24;
break;
case 3: /* expr ::= expr NOT expr */
{
- fts5yylhsminor.fts5yy18 = sqlite3Fts5ParseNode(pParse, FTS5_NOT, fts5yymsp[-2].minor.fts5yy18, fts5yymsp[0].minor.fts5yy18, 0);
+ fts5yylhsminor.fts5yy24 = sqlite3Fts5ParseNode(pParse, FTS5_NOT, fts5yymsp[-2].minor.fts5yy24, fts5yymsp[0].minor.fts5yy24, 0);
}
- fts5yymsp[-2].minor.fts5yy18 = fts5yylhsminor.fts5yy18;
+ fts5yymsp[-2].minor.fts5yy24 = fts5yylhsminor.fts5yy24;
break;
case 4: /* expr ::= LP expr RP */
-{fts5yymsp[-2].minor.fts5yy18 = fts5yymsp[-1].minor.fts5yy18;}
+{fts5yymsp[-2].minor.fts5yy24 = fts5yymsp[-1].minor.fts5yy24;}
break;
case 5: /* expr ::= exprlist */
case 6: /* exprlist ::= cnearset */ fts5yytestcase(fts5yyruleno==6);
-{fts5yylhsminor.fts5yy18 = fts5yymsp[0].minor.fts5yy18;}
- fts5yymsp[0].minor.fts5yy18 = fts5yylhsminor.fts5yy18;
+{fts5yylhsminor.fts5yy24 = fts5yymsp[0].minor.fts5yy24;}
+ fts5yymsp[0].minor.fts5yy24 = fts5yylhsminor.fts5yy24;
break;
case 7: /* exprlist ::= exprlist cnearset */
{
- fts5yylhsminor.fts5yy18 = sqlite3Fts5ParseImplicitAnd(pParse, fts5yymsp[-1].minor.fts5yy18, fts5yymsp[0].minor.fts5yy18);
+ fts5yylhsminor.fts5yy24 = sqlite3Fts5ParseImplicitAnd(pParse, fts5yymsp[-1].minor.fts5yy24, fts5yymsp[0].minor.fts5yy24);
}
- fts5yymsp[-1].minor.fts5yy18 = fts5yylhsminor.fts5yy18;
+ fts5yymsp[-1].minor.fts5yy24 = fts5yylhsminor.fts5yy24;
break;
case 8: /* cnearset ::= nearset */
{
- fts5yylhsminor.fts5yy18 = sqlite3Fts5ParseNode(pParse, FTS5_STRING, 0, 0, fts5yymsp[0].minor.fts5yy26);
+ fts5yylhsminor.fts5yy24 = sqlite3Fts5ParseNode(pParse, FTS5_STRING, 0, 0, fts5yymsp[0].minor.fts5yy46);
}
- fts5yymsp[0].minor.fts5yy18 = fts5yylhsminor.fts5yy18;
+ fts5yymsp[0].minor.fts5yy24 = fts5yylhsminor.fts5yy24;
break;
case 9: /* cnearset ::= colset COLON nearset */
{
- sqlite3Fts5ParseSetColset(pParse, fts5yymsp[0].minor.fts5yy26, fts5yymsp[-2].minor.fts5yy3);
- fts5yylhsminor.fts5yy18 = sqlite3Fts5ParseNode(pParse, FTS5_STRING, 0, 0, fts5yymsp[0].minor.fts5yy26);
+ sqlite3Fts5ParseSetColset(pParse, fts5yymsp[0].minor.fts5yy46, fts5yymsp[-2].minor.fts5yy11);
+ fts5yylhsminor.fts5yy24 = sqlite3Fts5ParseNode(pParse, FTS5_STRING, 0, 0, fts5yymsp[0].minor.fts5yy46);
}
- fts5yymsp[-2].minor.fts5yy18 = fts5yylhsminor.fts5yy18;
+ fts5yymsp[-2].minor.fts5yy24 = fts5yylhsminor.fts5yy24;
break;
- case 10: /* colset ::= LCP colsetlist RCP */
-{ fts5yymsp[-2].minor.fts5yy3 = fts5yymsp[-1].minor.fts5yy3; }
+ case 10: /* colset ::= MINUS LCP colsetlist RCP */
+{
+ fts5yymsp[-3].minor.fts5yy11 = sqlite3Fts5ParseColsetInvert(pParse, fts5yymsp[-1].minor.fts5yy11);
+}
+ break;
+ case 11: /* colset ::= LCP colsetlist RCP */
+{ fts5yymsp[-2].minor.fts5yy11 = fts5yymsp[-1].minor.fts5yy11; }
+ break;
+ case 12: /* colset ::= STRING */
+{
+ fts5yylhsminor.fts5yy11 = sqlite3Fts5ParseColset(pParse, 0, &fts5yymsp[0].minor.fts5yy0);
+}
+ fts5yymsp[0].minor.fts5yy11 = fts5yylhsminor.fts5yy11;
break;
- case 11: /* colset ::= STRING */
+ case 13: /* colset ::= MINUS STRING */
{
- fts5yylhsminor.fts5yy3 = sqlite3Fts5ParseColset(pParse, 0, &fts5yymsp[0].minor.fts5yy0);
+ fts5yymsp[-1].minor.fts5yy11 = sqlite3Fts5ParseColset(pParse, 0, &fts5yymsp[0].minor.fts5yy0);
+ fts5yymsp[-1].minor.fts5yy11 = sqlite3Fts5ParseColsetInvert(pParse, fts5yymsp[-1].minor.fts5yy11);
}
- fts5yymsp[0].minor.fts5yy3 = fts5yylhsminor.fts5yy3;
break;
- case 12: /* colsetlist ::= colsetlist STRING */
+ case 14: /* colsetlist ::= colsetlist STRING */
{
- fts5yylhsminor.fts5yy3 = sqlite3Fts5ParseColset(pParse, fts5yymsp[-1].minor.fts5yy3, &fts5yymsp[0].minor.fts5yy0); }
- fts5yymsp[-1].minor.fts5yy3 = fts5yylhsminor.fts5yy3;
+ fts5yylhsminor.fts5yy11 = sqlite3Fts5ParseColset(pParse, fts5yymsp[-1].minor.fts5yy11, &fts5yymsp[0].minor.fts5yy0); }
+ fts5yymsp[-1].minor.fts5yy11 = fts5yylhsminor.fts5yy11;
break;
- case 13: /* colsetlist ::= STRING */
+ case 15: /* colsetlist ::= STRING */
{
- fts5yylhsminor.fts5yy3 = sqlite3Fts5ParseColset(pParse, 0, &fts5yymsp[0].minor.fts5yy0);
+ fts5yylhsminor.fts5yy11 = sqlite3Fts5ParseColset(pParse, 0, &fts5yymsp[0].minor.fts5yy0);
}
- fts5yymsp[0].minor.fts5yy3 = fts5yylhsminor.fts5yy3;
+ fts5yymsp[0].minor.fts5yy11 = fts5yylhsminor.fts5yy11;
break;
- case 14: /* nearset ::= phrase */
-{ fts5yylhsminor.fts5yy26 = sqlite3Fts5ParseNearset(pParse, 0, fts5yymsp[0].minor.fts5yy11); }
- fts5yymsp[0].minor.fts5yy26 = fts5yylhsminor.fts5yy26;
+ case 16: /* nearset ::= phrase */
+{ fts5yylhsminor.fts5yy46 = sqlite3Fts5ParseNearset(pParse, 0, fts5yymsp[0].minor.fts5yy53); }
+ fts5yymsp[0].minor.fts5yy46 = fts5yylhsminor.fts5yy46;
break;
- case 15: /* nearset ::= STRING LP nearphrases neardist_opt RP */
+ case 17: /* nearset ::= STRING LP nearphrases neardist_opt RP */
{
sqlite3Fts5ParseNear(pParse, &fts5yymsp[-4].minor.fts5yy0);
- sqlite3Fts5ParseSetDistance(pParse, fts5yymsp[-2].minor.fts5yy26, &fts5yymsp[-1].minor.fts5yy0);
- fts5yylhsminor.fts5yy26 = fts5yymsp[-2].minor.fts5yy26;
+ sqlite3Fts5ParseSetDistance(pParse, fts5yymsp[-2].minor.fts5yy46, &fts5yymsp[-1].minor.fts5yy0);
+ fts5yylhsminor.fts5yy46 = fts5yymsp[-2].minor.fts5yy46;
}
- fts5yymsp[-4].minor.fts5yy26 = fts5yylhsminor.fts5yy26;
+ fts5yymsp[-4].minor.fts5yy46 = fts5yylhsminor.fts5yy46;
break;
- case 16: /* nearphrases ::= phrase */
+ case 18: /* nearphrases ::= phrase */
{
- fts5yylhsminor.fts5yy26 = sqlite3Fts5ParseNearset(pParse, 0, fts5yymsp[0].minor.fts5yy11);
+ fts5yylhsminor.fts5yy46 = sqlite3Fts5ParseNearset(pParse, 0, fts5yymsp[0].minor.fts5yy53);
}
- fts5yymsp[0].minor.fts5yy26 = fts5yylhsminor.fts5yy26;
+ fts5yymsp[0].minor.fts5yy46 = fts5yylhsminor.fts5yy46;
break;
- case 17: /* nearphrases ::= nearphrases phrase */
+ case 19: /* nearphrases ::= nearphrases phrase */
{
- fts5yylhsminor.fts5yy26 = sqlite3Fts5ParseNearset(pParse, fts5yymsp[-1].minor.fts5yy26, fts5yymsp[0].minor.fts5yy11);
+ fts5yylhsminor.fts5yy46 = sqlite3Fts5ParseNearset(pParse, fts5yymsp[-1].minor.fts5yy46, fts5yymsp[0].minor.fts5yy53);
}
- fts5yymsp[-1].minor.fts5yy26 = fts5yylhsminor.fts5yy26;
+ fts5yymsp[-1].minor.fts5yy46 = fts5yylhsminor.fts5yy46;
break;
- case 18: /* neardist_opt ::= */
+ case 20: /* neardist_opt ::= */
{ fts5yymsp[1].minor.fts5yy0.p = 0; fts5yymsp[1].minor.fts5yy0.n = 0; }
break;
- case 19: /* neardist_opt ::= COMMA STRING */
+ case 21: /* neardist_opt ::= COMMA STRING */
{ fts5yymsp[-1].minor.fts5yy0 = fts5yymsp[0].minor.fts5yy0; }
break;
- case 20: /* phrase ::= phrase PLUS STRING star_opt */
+ case 22: /* phrase ::= phrase PLUS STRING star_opt */
{
- fts5yylhsminor.fts5yy11 = sqlite3Fts5ParseTerm(pParse, fts5yymsp[-3].minor.fts5yy11, &fts5yymsp[-1].minor.fts5yy0, fts5yymsp[0].minor.fts5yy20);
+ fts5yylhsminor.fts5yy53 = sqlite3Fts5ParseTerm(pParse, fts5yymsp[-3].minor.fts5yy53, &fts5yymsp[-1].minor.fts5yy0, fts5yymsp[0].minor.fts5yy4);
}
- fts5yymsp[-3].minor.fts5yy11 = fts5yylhsminor.fts5yy11;
+ fts5yymsp[-3].minor.fts5yy53 = fts5yylhsminor.fts5yy53;
break;
- case 21: /* phrase ::= STRING star_opt */
+ case 23: /* phrase ::= STRING star_opt */
{
- fts5yylhsminor.fts5yy11 = sqlite3Fts5ParseTerm(pParse, 0, &fts5yymsp[-1].minor.fts5yy0, fts5yymsp[0].minor.fts5yy20);
+ fts5yylhsminor.fts5yy53 = sqlite3Fts5ParseTerm(pParse, 0, &fts5yymsp[-1].minor.fts5yy0, fts5yymsp[0].minor.fts5yy4);
}
- fts5yymsp[-1].minor.fts5yy11 = fts5yylhsminor.fts5yy11;
+ fts5yymsp[-1].minor.fts5yy53 = fts5yylhsminor.fts5yy53;
break;
- case 22: /* star_opt ::= STAR */
-{ fts5yymsp[0].minor.fts5yy20 = 1; }
+ case 24: /* star_opt ::= STAR */
+{ fts5yymsp[0].minor.fts5yy4 = 1; }
break;
- case 23: /* star_opt ::= */
-{ fts5yymsp[1].minor.fts5yy20 = 0; }
+ case 25: /* star_opt ::= */
+{ fts5yymsp[1].minor.fts5yy4 = 0; }
break;
default:
break;
@@ -179790,7 +181164,7 @@ static void sqlite3Fts5Parser(
fts5yy_destructor(fts5yypParser, (fts5YYCODETYPE)fts5yymajor, &fts5yyminorunion);
fts5yymajor = fts5YYNOCODE;
}else{
- while( fts5yypParser->fts5yytos >= &fts5yypParser->fts5yystack
+ while( fts5yypParser->fts5yytos >= fts5yypParser->fts5yystack
&& fts5yymx != fts5YYERRORSYMBOL
&& (fts5yyact = fts5yy_find_reduce_action(
fts5yypParser->fts5yytos->stateno,
@@ -180054,7 +181428,7 @@ static int fts5HighlightCb(
if( p->iRangeEnd>0 && iPos==p->iRangeEnd ){
fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff);
p->iOff = iEndOff;
- if( iPos<p->iter.iEnd ){
+ if( iPos>=p->iter.iStart && iPos<p->iter.iEnd ){
fts5HighlightAppend(&rc, p, p->zClose, -1);
}
}
@@ -180112,6 +181486,118 @@ static void fts5HighlightFunction(
**************************************************************************/
/*
+** Context object passed to the fts5SentenceFinderCb() function.
+*/
+typedef struct Fts5SFinder Fts5SFinder;
+struct Fts5SFinder {
+ int iPos; /* Current token position */
+ int nFirstAlloc; /* Allocated size of aFirst[] */
+ int nFirst; /* Number of entries in aFirst[] */
+ int *aFirst; /* Array of first token in each sentence */
+ const char *zDoc; /* Document being tokenized */
+};
+
+/*
+** Add an entry to the Fts5SFinder.aFirst[] array. Grow the array if
+** necessary. Return SQLITE_OK if successful, or SQLITE_NOMEM if an
+** error occurs.
+*/
+static int fts5SentenceFinderAdd(Fts5SFinder *p, int iAdd){
+ if( p->nFirstAlloc==p->nFirst ){
+ int nNew = p->nFirstAlloc ? p->nFirstAlloc*2 : 64;
+ int *aNew;
+
+ aNew = (int*)sqlite3_realloc(p->aFirst, nNew*sizeof(int));
+ if( aNew==0 ) return SQLITE_NOMEM;
+ p->aFirst = aNew;
+ p->nFirstAlloc = nNew;
+ }
+ p->aFirst[p->nFirst++] = iAdd;
+ return SQLITE_OK;
+}
+
+/*
+** This function is an xTokenize() callback used by the auxiliary snippet()
+** function. Its job is to identify tokens that are the first in a sentence.
+** For each such token, an entry is added to the SFinder.aFirst[] array.
+*/
+static int fts5SentenceFinderCb(
+ void *pContext, /* Pointer to HighlightContext object */
+ int tflags, /* Mask of FTS5_TOKEN_* flags */
+ const char *pToken, /* Buffer containing token */
+ int nToken, /* Size of token in bytes */
+ int iStartOff, /* Start offset of token */
+ int iEndOff /* End offset of token */
+){
+ int rc = SQLITE_OK;
+
+ UNUSED_PARAM2(pToken, nToken);
+ UNUSED_PARAM(iEndOff);
+
+ if( (tflags & FTS5_TOKEN_COLOCATED)==0 ){
+ Fts5SFinder *p = (Fts5SFinder*)pContext;
+ if( p->iPos>0 ){
+ int i;
+ char c = 0;
+ for(i=iStartOff-1; i>=0; i--){
+ c = p->zDoc[i];
+ if( c!=' ' && c!='\t' && c!='\n' && c!='\r' ) break;
+ }
+ if( i!=iStartOff-1 && (c=='.' || c==':') ){
+ rc = fts5SentenceFinderAdd(p, p->iPos);
+ }
+ }else{
+ rc = fts5SentenceFinderAdd(p, 0);
+ }
+ p->iPos++;
+ }
+ return rc;
+}
+
+static int fts5SnippetScore(
+ const Fts5ExtensionApi *pApi, /* API offered by current FTS version */
+ Fts5Context *pFts, /* First arg to pass to pApi functions */
+ int nDocsize, /* Size of column in tokens */
+ unsigned char *aSeen, /* Array with one element per query phrase */
+ int iCol, /* Column to score */
+ int iPos, /* Starting offset to score */
+ int nToken, /* Max tokens per snippet */
+ int *pnScore, /* OUT: Score */
+ int *piPos /* OUT: Adjusted offset */
+){
+ int rc;
+ int i;
+ int ip = 0;
+ int ic = 0;
+ int iOff = 0;
+ int iFirst = -1;
+ int nInst;
+ int nScore = 0;
+ int iLast = 0;
+
+ rc = pApi->xInstCount(pFts, &nInst);
+ for(i=0; i<nInst && rc==SQLITE_OK; i++){
+ rc = pApi->xInst(pFts, i, &ip, &ic, &iOff);
+ if( rc==SQLITE_OK && ic==iCol && iOff>=iPos && iOff<(iPos+nToken) ){
+ nScore += (aSeen[ip] ? 1 : 1000);
+ aSeen[ip] = 1;
+ if( iFirst<0 ) iFirst = iOff;
+ iLast = iOff + pApi->xPhraseSize(pFts, ip);
+ }
+ }
+
+ *pnScore = nScore;
+ if( piPos ){
+ int iAdj = iFirst - (nToken - (iLast-iFirst)) / 2;
+ if( (iAdj+nToken)>nDocsize ) iAdj = nDocsize - nToken;
+ if( iAdj<0 ) iAdj = 0;
+ *piPos = iAdj;
+ }
+
+ return rc;
+}
+
+/*
** Implementation of snippet() function.
*/
static void fts5SnippetFunction(
@@ -180132,9 +181618,10 @@ static void fts5SnippetFunction(
unsigned char *aSeen; /* Array of "seen instance" flags */
int iBestCol; /* Column containing best snippet */
int iBestStart = 0; /* First token of best snippet */
- int iBestLast; /* Last token of best snippet */
int nBestScore = 0; /* Score of best snippet */
int nColSize = 0; /* Total size of iBestCol in tokens */
+ Fts5SFinder sFinder; /* Used to find the beginnings of sentences */
+ int nCol;
if( nVal!=5 ){
const char *zErr = "wrong number of arguments to function snippet()";
@@ -180142,13 +181629,13 @@ static void fts5SnippetFunction(
return;
}
+ nCol = pApi->xColumnCount(pFts);
memset(&ctx, 0, sizeof(HighlightContext));
iCol = sqlite3_value_int(apVal[0]);
ctx.zOpen = (const char*)sqlite3_value_text(apVal[1]);
ctx.zClose = (const char*)sqlite3_value_text(apVal[2]);
zEllips = (const char*)sqlite3_value_text(apVal[3]);
nToken = sqlite3_value_int(apVal[4]);
- iBestLast = nToken-1;
iBestCol = (iCol>=0 ? iCol : 0);
nPhrase = pApi->xPhraseCount(pFts);
@@ -180156,65 +181643,94 @@ static void fts5SnippetFunction(
if( aSeen==0 ){
rc = SQLITE_NOMEM;
}
-
if( rc==SQLITE_OK ){
rc = pApi->xInstCount(pFts, &nInst);
}
- for(i=0; rc==SQLITE_OK && i<nInst; i++){
- int ip, iSnippetCol, iStart;
- memset(aSeen, 0, nPhrase);
- rc = pApi->xInst(pFts, i, &ip, &iSnippetCol, &iStart);
- if( rc==SQLITE_OK && (iCol<0 || iSnippetCol==iCol) ){
- int nScore = 1000;
- int iLast = iStart - 1 + pApi->xPhraseSize(pFts, ip);
- int j;
- aSeen[ip] = 1;
- for(j=i+1; rc==SQLITE_OK && j<nInst; j++){
- int ic; int io; int iFinal;
- rc = pApi->xInst(pFts, j, &ip, &ic, &io);
- iFinal = io + pApi->xPhraseSize(pFts, ip) - 1;
- if( rc==SQLITE_OK && ic==iSnippetCol && iLast<iStart+nToken ){
- nScore += aSeen[ip] ? 1000 : 1;
- aSeen[ip] = 1;
- if( iFinal>iLast ) iLast = iFinal;
+ memset(&sFinder, 0, sizeof(Fts5SFinder));
+ for(i=0; i<nCol; i++){
+ if( iCol<0 || iCol==i ){
+ int nDoc;
+ int nDocsize;
+ int ii;
+ sFinder.iPos = 0;
+ sFinder.nFirst = 0;
+ rc = pApi->xColumnText(pFts, i, &sFinder.zDoc, &nDoc);
+ if( rc!=SQLITE_OK ) break;
+ rc = pApi->xTokenize(pFts,
+ sFinder.zDoc, nDoc, (void*)&sFinder,fts5SentenceFinderCb
+ );
+ if( rc!=SQLITE_OK ) break;
+ rc = pApi->xColumnSize(pFts, i, &nDocsize);
+ if( rc!=SQLITE_OK ) break;
+
+ for(ii=0; rc==SQLITE_OK && ii<nInst; ii++){
+ int ip, ic, io;
+ int iAdj;
+ int nScore;
+ int jj;
+
+ rc = pApi->xInst(pFts, ii, &ip, &ic, &io);
+ if( ic!=i || rc!=SQLITE_OK ) continue;
+ memset(aSeen, 0, nPhrase);
+ rc = fts5SnippetScore(pApi, pFts, nDocsize, aSeen, i,
+ io, nToken, &nScore, &iAdj
+ );
+ if( rc==SQLITE_OK && nScore>nBestScore ){
+ nBestScore = nScore;
+ iBestCol = i;
+ iBestStart = iAdj;
+ nColSize = nDocsize;
}
- }
- if( rc==SQLITE_OK && nScore>nBestScore ){
- iBestCol = iSnippetCol;
- iBestStart = iStart;
- iBestLast = iLast;
- nBestScore = nScore;
+ if( rc==SQLITE_OK && sFinder.nFirst && nDocsize>nToken ){
+ for(jj=0; jj<(sFinder.nFirst-1); jj++){
+ if( sFinder.aFirst[jj+1]>io ) break;
+ }
+
+ if( sFinder.aFirst[jj]<io ){
+ memset(aSeen, 0, nPhrase);
+ rc = fts5SnippetScore(pApi, pFts, nDocsize, aSeen, i,
+ sFinder.aFirst[jj], nToken, &nScore, 0
+ );
+
+ nScore += (sFinder.aFirst[jj]==0 ? 120 : 100);
+ if( rc==SQLITE_OK && nScore>nBestScore ){
+ nBestScore = nScore;
+ iBestCol = i;
+ iBestStart = sFinder.aFirst[jj];
+ nColSize = nDocsize;
+ }
+ }
+ }
}
}
}
if( rc==SQLITE_OK ){
- rc = pApi->xColumnSize(pFts, iBestCol, &nColSize);
- }
- if( rc==SQLITE_OK ){
rc = pApi->xColumnText(pFts, iBestCol, &ctx.zIn, &ctx.nIn);
}
+ if( rc==SQLITE_OK && nColSize==0 ){
+ rc = pApi->xColumnSize(pFts, iBestCol, &nColSize);
+ }
if( ctx.zIn ){
if( rc==SQLITE_OK ){
rc = fts5CInstIterInit(pApi, pFts, iBestCol, &ctx.iter);
}
- if( (iBestStart+nToken-1)>iBestLast ){
- iBestStart -= (iBestStart+nToken-1-iBestLast) / 2;
- }
- if( iBestStart+nToken>nColSize ){
- iBestStart = nColSize - nToken;
- }
- if( iBestStart<0 ) iBestStart = 0;
-
ctx.iRangeStart = iBestStart;
ctx.iRangeEnd = iBestStart + nToken - 1;
if( iBestStart>0 ){
fts5HighlightAppend(&rc, &ctx, zEllips, -1);
}
+
+ /* Advance iterator ctx.iter so that it points to the first coalesced
+ ** phrase instance at or following position iBestStart. */
+ while( ctx.iter.iStart>=0 && ctx.iter.iStart<iBestStart && rc==SQLITE_OK ){
+ rc = fts5CInstIterNext(&ctx.iter);
+ }
+
if( rc==SQLITE_OK ){
rc = pApi->xTokenize(pFts, ctx.zIn, ctx.nIn, (void*)&ctx,fts5HighlightCb);
}
@@ -180223,15 +181739,15 @@ static void fts5SnippetFunction(
}else{
fts5HighlightAppend(&rc, &ctx, zEllips, -1);
}
-
- if( rc==SQLITE_OK ){
- sqlite3_result_text(pCtx, (const char*)ctx.zOut, -1, SQLITE_TRANSIENT);
- }else{
- sqlite3_result_error_code(pCtx, rc);
- }
- sqlite3_free(ctx.zOut);
}
+ if( rc==SQLITE_OK ){
+ sqlite3_result_text(pCtx, (const char*)ctx.zOut, -1, SQLITE_TRANSIENT);
+ }else{
+ sqlite3_result_error_code(pCtx, rc);
+ }
+ sqlite3_free(ctx.zOut);
sqlite3_free(aSeen);
+ sqlite3_free(sFinder.aFirst);
}
/************************************************************************/
@@ -181950,6 +183466,7 @@ static int fts5ExprGetToken(
case ',': tok = FTS5_COMMA; break;
case '+': tok = FTS5_PLUS; break;
case '*': tok = FTS5_STAR; break;
+ case '-': tok = FTS5_MINUS; break;
case '\0': tok = FTS5_EOF; break;
case '"': {
@@ -182536,6 +184053,7 @@ static int fts5ExprNearInitAll(
Fts5ExprNearset *pNear = pNode->pNear;
int i, j;
int rc = SQLITE_OK;
+ int bEof = 1;
assert( pNode->bNomatch==0 );
for(i=0; rc==SQLITE_OK && i<pNear->nPhrase; i++){
@@ -182543,7 +184061,6 @@ static int fts5ExprNearInitAll(
for(j=0; j<pPhrase->nTerm; j++){
Fts5ExprTerm *pTerm = &pPhrase->aTerm[j];
Fts5ExprTerm *p;
- int bEof = 1;
for(p=pTerm; p && rc==SQLITE_OK; p=p->pSynonym){
if( p->pIter ){
@@ -182563,13 +184080,12 @@ static int fts5ExprNearInitAll(
}
}
- if( bEof ){
- pNode->bEof = 1;
- return rc;
- }
+ if( bEof ) break;
}
+ if( bEof ) break;
}
+ pNode->bEof = bEof;
return rc;
}
@@ -183420,7 +184936,6 @@ static int sqlite3Fts5ExprClonePhrase(
){
int rc = SQLITE_OK; /* Return code */
Fts5ExprPhrase *pOrig; /* The phrase extracted from pExpr */
- int i; /* Used to iterate through phrase terms */
Fts5Expr *pNew = 0; /* Expression to return via *ppNew */
TokenCtx sCtx = {0,0}; /* Context object for fts5ParseTokenize */
@@ -183441,7 +184956,7 @@ static int sqlite3Fts5ExprClonePhrase(
if( rc==SQLITE_OK ){
Fts5Colset *pColsetOrig = pOrig->pNode->pNear->pColset;
if( pColsetOrig ){
- int nByte = sizeof(Fts5Colset) + pColsetOrig->nCol * sizeof(int);
+ int nByte = sizeof(Fts5Colset) + (pColsetOrig->nCol-1) * sizeof(int);
Fts5Colset *pColset = (Fts5Colset*)sqlite3Fts5MallocZero(&rc, nByte);
if( pColset ){
memcpy(pColset, pColsetOrig, nByte);
@@ -183450,18 +184965,25 @@ static int sqlite3Fts5ExprClonePhrase(
}
}
- for(i=0; rc==SQLITE_OK && i<pOrig->nTerm; i++){
- int tflags = 0;
- Fts5ExprTerm *p;
- for(p=&pOrig->aTerm[i]; p && rc==SQLITE_OK; p=p->pSynonym){
- const char *zTerm = p->zTerm;
- rc = fts5ParseTokenize((void*)&sCtx, tflags, zTerm, (int)strlen(zTerm),
- 0, 0);
- tflags = FTS5_TOKEN_COLOCATED;
- }
- if( rc==SQLITE_OK ){
- sCtx.pPhrase->aTerm[i].bPrefix = pOrig->aTerm[i].bPrefix;
+ if( pOrig->nTerm ){
+ int i; /* Used to iterate through phrase terms */
+ for(i=0; rc==SQLITE_OK && i<pOrig->nTerm; i++){
+ int tflags = 0;
+ Fts5ExprTerm *p;
+ for(p=&pOrig->aTerm[i]; p && rc==SQLITE_OK; p=p->pSynonym){
+ const char *zTerm = p->zTerm;
+ rc = fts5ParseTokenize((void*)&sCtx, tflags, zTerm, (int)strlen(zTerm),
+ 0, 0);
+ tflags = FTS5_TOKEN_COLOCATED;
+ }
+ if( rc==SQLITE_OK ){
+ sCtx.pPhrase->aTerm[i].bPrefix = pOrig->aTerm[i].bPrefix;
+ }
}
+ }else{
+ /* This happens when parsing a token or quoted phrase that contains
+ ** no token characters at all. (e.g ... MATCH '""'). */
+ sCtx.pPhrase = sqlite3Fts5MallocZero(&rc, sizeof(Fts5ExprPhrase));
}
if( rc==SQLITE_OK ){
@@ -183576,6 +185098,34 @@ static Fts5Colset *fts5ParseColset(
return pNew;
}
+/*
+** Allocate and return an Fts5Colset object specifying the inverse of
+** the colset passed as the second argument. Free the colset passed
+** as the second argument before returning.
+*/
+static Fts5Colset *sqlite3Fts5ParseColsetInvert(Fts5Parse *pParse, Fts5Colset *p){
+ Fts5Colset *pRet;
+ int nCol = pParse->pConfig->nCol;
+
+ pRet = (Fts5Colset*)sqlite3Fts5MallocZero(&pParse->rc,
+ sizeof(Fts5Colset) + sizeof(int)*nCol
+ );
+ if( pRet ){
+ int i;
+ int iOld = 0;
+ for(i=0; i<nCol; i++){
+ if( iOld>=p->nCol || p->aiCol[iOld]!=i ){
+ pRet->aiCol[pRet->nCol++] = i;
+ }else{
+ iOld++;
+ }
+ }
+ }
+
+ sqlite3_free(p);
+ return pRet;
+}
+
static Fts5Colset *sqlite3Fts5ParseColset(
Fts5Parse *pParse, /* Store SQLITE_NOMEM here if required */
Fts5Colset *pColset, /* Existing colset object */
@@ -185671,7 +187221,6 @@ static Fts5Data *fts5DataRead(Fts5Index *p, i64 iRowid){
return pRet;
}
-
/*
** Release a reference to data record returned by an earlier call to
** fts5DataRead().
@@ -185680,6 +187229,18 @@ static void fts5DataRelease(Fts5Data *pData){
sqlite3_free(pData);
}
+static Fts5Data *fts5LeafRead(Fts5Index *p, i64 iRowid){
+ Fts5Data *pRet = fts5DataRead(p, iRowid);
+ if( pRet ){
+ if( pRet->szLeaf>pRet->nn ){
+ p->rc = FTS5_CORRUPT;
+ fts5DataRelease(pRet);
+ pRet = 0;
+ }
+ }
+ return pRet;
+}
+
static int fts5IndexPrepareStmt(
Fts5Index *p,
sqlite3_stmt **ppStmt,
@@ -186488,7 +188049,7 @@ static void fts5SegIterNextPage(
pIter->pLeaf = pIter->pNextLeaf;
pIter->pNextLeaf = 0;
}else if( pIter->iLeafPgno<=pSeg->pgnoLast ){
- pIter->pLeaf = fts5DataRead(p,
+ pIter->pLeaf = fts5LeafRead(p,
FTS5_SEGMENT_ROWID(pSeg->iSegid, pIter->iLeafPgno)
);
}else{
@@ -186991,9 +188552,8 @@ static void fts5SegIterNext(
if( pLeaf->nn>pLeaf->szLeaf ){
pIter->iPgidxOff = pLeaf->szLeaf + fts5GetVarint32(
&pLeaf->p[pLeaf->szLeaf], pIter->iEndofDoclist
- );
+ );
}
-
}
else if( pLeaf->nn>pLeaf->szLeaf ){
pIter->iPgidxOff = pLeaf->szLeaf + fts5GetVarint32(
@@ -187238,6 +188798,11 @@ static void fts5LeafSeek(
iTermOff += nKeep;
iOff = iTermOff;
+ if( iOff>=n ){
+ p->rc = FTS5_CORRUPT;
+ return;
+ }
+
/* Read the nKeep field of the next term. */
fts5FastGetVarint32(a, iOff, nKeep);
}
@@ -188165,6 +189730,15 @@ static void fts5IterSetOutputs_Nocolset(Fts5Iter *pIter, Fts5SegIter *pSeg){
}
/*
+** xSetOutputs callback used when the Fts5Colset object has nCol==0 (match
+** against no columns at all).
+*/
+static void fts5IterSetOutputs_ZeroColset(Fts5Iter *pIter, Fts5SegIter *pSeg){
+ UNUSED_PARAM(pSeg);
+ pIter->base.nData = 0;
+}
+
+/*
** xSetOutputs callback used by detail=col when there is a column filter
** and there are 100 or more columns. Also called as a fallback from
** fts5IterSetOutputs_Col100 if the column-list spans more than one page.
@@ -188269,6 +189843,10 @@ static void fts5IterSetOutputCb(int *pRc, Fts5Iter *pIter){
pIter->xSetOutputs = fts5IterSetOutputs_Nocolset;
}
+ else if( pIter->pColset->nCol==0 ){
+ pIter->xSetOutputs = fts5IterSetOutputs_ZeroColset;
+ }
+
else if( pConfig->eDetail==FTS5_DETAIL_FULL ){
pIter->xSetOutputs = fts5IterSetOutputs_Full;
}
@@ -194045,7 +195623,7 @@ static void fts5SourceIdFunc(
){
assert( nArg==0 );
UNUSED_PARAM2(nArg, apUnused);
- sqlite3_result_text(pCtx, "fts5: 2016-09-12 18:50:49 29dbef4b8585f753861a36d6dd102ca634197bd6", -1, SQLITE_TRANSIENT);
+ sqlite3_result_text(pCtx, "fts5: 2016-11-04 12:08:49 1136863c76576110e710dd5d69ab6bf347c65e36", -1, SQLITE_TRANSIENT);
}
static int fts5Init(sqlite3 *db){
@@ -197527,8 +199105,19 @@ static int fts5VocabBestIndexMethod(
}
}
- pInfo->idxNum = idxNum;
+ /* This virtual table always delivers results in ascending order of
+ ** the "term" column (column 0). So if the user has requested this
+ ** specifically - "ORDER BY term" or "ORDER BY term ASC" - set the
+ ** sqlite3_index_info.orderByConsumed flag to tell the core the results
+ ** are already in sorted order. */
+ if( pInfo->nOrderBy==1
+ && pInfo->aOrderBy[0].iColumn==0
+ && pInfo->aOrderBy[0].desc==0
+ ){
+ pInfo->orderByConsumed = 1;
+ }
+ pInfo->idxNum = idxNum;
return SQLITE_OK;
}
diff --git a/ext/sqlite3/libsqlite/sqlite3.h b/ext/sqlite3/libsqlite/sqlite3.h
index cfbba628e0..d02aeb187e 100644
--- a/ext/sqlite3/libsqlite/sqlite3.h
+++ b/ext/sqlite3/libsqlite/sqlite3.h
@@ -108,7 +108,8 @@ extern "C" {
** be held constant and Z will be incremented or else Y will be incremented
** and Z will be reset to zero.
**
-** Since version 3.6.18, SQLite source code has been stored in the
+** Since [version 3.6.18] ([dateof:3.6.18]),
+** SQLite source code has been stored in the
** <a href="http://www.fossil-scm.org/">Fossil configuration management
** system</a>. ^The SQLITE_SOURCE_ID macro evaluates to
** a string which identifies a particular check-in of SQLite
@@ -120,9 +121,9 @@ extern "C" {
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
-#define SQLITE_VERSION "3.14.2"
-#define SQLITE_VERSION_NUMBER 3014002
-#define SQLITE_SOURCE_ID "2016-09-12 18:50:49 29dbef4b8585f753861a36d6dd102ca634197bd6"
+#define SQLITE_VERSION "3.15.1"
+#define SQLITE_VERSION_NUMBER 3015001
+#define SQLITE_SOURCE_ID "2016-11-04 12:08:49 1136863c76576110e710dd5d69ab6bf347c65e36"
/*
** CAPI3REF: Run-Time Library Version Numbers
@@ -452,7 +453,8 @@ SQLITE_API int sqlite3_exec(
** [result codes]. However, experience has shown that many of
** these result codes are too coarse-grained. They do not provide as
** much information about problems as programmers might like. In an effort to
-** address this, newer versions of SQLite (version 3.3.8 and later) include
+** address this, newer versions of SQLite (version 3.3.8 [dateof:3.3.8]
+** and later) include
** support for additional result codes that provide more detailed information
** about errors. These [extended result codes] are enabled or disabled
** on a per database connection basis using the
@@ -976,6 +978,12 @@ struct sqlite3_io_methods {
** on whether or not the file has been renamed, moved, or deleted since it
** was first opened.
**
+** <li>[[SQLITE_FCNTL_WIN32_GET_HANDLE]]
+** The [SQLITE_FCNTL_WIN32_GET_HANDLE] opcode can be used to obtain the
+** underlying native file handle associated with a file handle. This file
+** control interprets its argument as a pointer to a native file handle and
+** writes the resulting value there.
+**
** <li>[[SQLITE_FCNTL_WIN32_SET_HANDLE]]
** The [SQLITE_FCNTL_WIN32_SET_HANDLE] opcode is used for debugging. This
** opcode causes the xFileControl method to swap the file handle with the one
@@ -1026,6 +1034,7 @@ struct sqlite3_io_methods {
#define SQLITE_FCNTL_RBU 26
#define SQLITE_FCNTL_VFS_POINTER 27
#define SQLITE_FCNTL_JOURNAL_POINTER 28
+#define SQLITE_FCNTL_WIN32_GET_HANDLE 29
/* deprecated names */
#define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE
@@ -1969,8 +1978,18 @@ struct sqlite3_mem_methods {
** be a NULL pointer, in which case the new setting is not reported back.
** </dd>
**
+** <dt>SQLITE_DBCONFIG_MAINDBNAME</dt>
+** <dd> ^This option is used to change the name of the "main" database
+** schema. ^The sole argument is a pointer to a constant UTF8 string
+** which will become the new schema name in place of "main". ^SQLite
+** does not make a copy of the new main schema name string, so the application
+** must ensure that the argument passed into this DBCONFIG option is unchanged
+** until after the database connection closes.
+** </dd>
+**
** </dl>
*/
+#define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */
#define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */
#define SQLITE_DBCONFIG_ENABLE_FKEY 1002 /* int int* */
#define SQLITE_DBCONFIG_ENABLE_TRIGGER 1003 /* int int* */
@@ -4041,7 +4060,8 @@ SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt*,int);
** other than [SQLITE_ROW] before any subsequent invocation of
** sqlite3_step(). Failure to reset the prepared statement using
** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from
-** sqlite3_step(). But after version 3.6.23.1, sqlite3_step() began
+** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1],
+** sqlite3_step() began
** calling [sqlite3_reset()] automatically in this circumstance rather
** than returning [SQLITE_MISUSE]. This is not considered a compatibility
** break because any application that ever receives an SQLITE_MISUSE error
@@ -5404,7 +5424,8 @@ SQLITE_API void *sqlite3_update_hook(
** and disabled if the argument is false.)^
**
** ^Cache sharing is enabled and disabled for an entire process.
-** This is a change as of SQLite version 3.5.0. In prior versions of SQLite,
+** This is a change as of SQLite [version 3.5.0] ([dateof:3.5.0]).
+** In prior versions of SQLite,
** sharing was enabled or disabled for each thread separately.
**
** ^(The cache sharing mode set by this interface effects all subsequent
@@ -5498,7 +5519,8 @@ SQLITE_API int sqlite3_db_release_memory(sqlite3*);
** from the heap.
** </ul>)^
**
-** Beginning with SQLite version 3.7.3, the soft heap limit is enforced
+** Beginning with SQLite [version 3.7.3] ([dateof:3.7.3]),
+** the soft heap limit is enforced
** regardless of whether or not the [SQLITE_ENABLE_MEMORY_MANAGEMENT]
** compile-time option is invoked. With [SQLITE_ENABLE_MEMORY_MANAGEMENT],
** the soft heap limit is enforced on every memory allocation. Without
@@ -5892,13 +5914,15 @@ struct sqlite3_module {
** the xUpdate method are automatically rolled back by SQLite.
**
** IMPORTANT: The estimatedRows field was added to the sqlite3_index_info
-** structure for SQLite version 3.8.2. If a virtual table extension is
+** structure for SQLite [version 3.8.2] ([dateof:3.8.2]).
+** If a virtual table extension is
** used with an SQLite version earlier than 3.8.2, the results of attempting
** to read or write the estimatedRows field are undefined (but are likely
** to included crashing the application). The estimatedRows field should
** therefore only be used if [sqlite3_libversion_number()] returns a
** value greater than or equal to 3008002. Similarly, the idxFlags field
-** was added for version 3.9.0. It may therefore only be used if
+** was added for [version 3.9.0] ([dateof:3.9.0]).
+** It may therefore only be used if
** sqlite3_libversion_number() returns a value greater than or equal to
** 3009000.
*/
@@ -6596,7 +6620,7 @@ SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex*);
#define SQLITE_MUTEX_STATIC_MEM 3 /* sqlite3_malloc() */
#define SQLITE_MUTEX_STATIC_MEM2 4 /* NOT USED */
#define SQLITE_MUTEX_STATIC_OPEN 4 /* sqlite3BtreeOpen() */
-#define SQLITE_MUTEX_STATIC_PRNG 5 /* sqlite3_random() */
+#define SQLITE_MUTEX_STATIC_PRNG 5 /* sqlite3_randomness() */
#define SQLITE_MUTEX_STATIC_LRU 6 /* lru page list */
#define SQLITE_MUTEX_STATIC_LRU2 7 /* NOT USED */
#define SQLITE_MUTEX_STATIC_PMEM 7 /* sqlite3PageMalloc() */
@@ -6700,6 +6724,7 @@ SQLITE_API int sqlite3_test_control(int op, ...);
#define SQLITE_TESTCTRL_SCRATCHMALLOC 17
#define SQLITE_TESTCTRL_LOCALTIME_FAULT 18
#define SQLITE_TESTCTRL_EXPLAIN_STMT 19 /* NOT USED */
+#define SQLITE_TESTCTRL_ONCE_RESET_THRESHOLD 19
#define SQLITE_TESTCTRL_NEVER_CORRUPT 20
#define SQLITE_TESTCTRL_VDBE_COVERAGE 21
#define SQLITE_TESTCTRL_BYTEORDER 22
@@ -8639,7 +8664,7 @@ int sqlite3session_attach(
** CAPI3REF: Set a table filter on a Session Object.
**
** The second argument (xFilter) is the "filter callback". For changes to rows
-** in tables that are not attached to the Session oject, the filter is called
+** in tables that are not attached to the Session object, the filter is called
** to determine whether changes to the table's rows should be tracked or not.
** If xFilter returns 0, changes is not tracked. Note that once a table is
** attached, xFilter will not be called again.
@@ -8905,7 +8930,7 @@ int sqlite3session_isempty(sqlite3_session *pSession);
** [sqlite3changeset_invert()] functions, all changes within the changeset
** that apply to a single table are grouped together. This means that when
** an application iterates through a changeset using an iterator created by
-** this function, all changes that relate to a single table are visted
+** this function, all changes that relate to a single table are visited
** consecutively. There is no chance that the iterator will visit a change
** the applies to table X, then one for table Y, and then later on visit
** another change for table X.
@@ -8992,7 +9017,7 @@ int sqlite3changeset_op(
** 0x01 if the corresponding column is part of the tables primary key, or
** 0x00 if it is not.
**
-** If argumet pnCol is not NULL, then *pnCol is set to the number of columns
+** If argument pnCol is not NULL, then *pnCol is set to the number of columns
** in the table.
**
** If this function is called when the iterator does not point to a valid
@@ -9209,12 +9234,12 @@ int sqlite3changeset_concat(
/*
-** Changegroup handle.
+** CAPI3REF: Changegroup Handle
*/
typedef struct sqlite3_changegroup sqlite3_changegroup;
/*
-** CAPI3REF: Combine two or more changesets into a single changeset.
+** CAPI3REF: Create A New Changegroup Object
**
** An sqlite3_changegroup object is used to combine two or more changesets
** (or patchsets) into a single changeset (or patchset). A single changegroup
@@ -9251,6 +9276,8 @@ typedef struct sqlite3_changegroup sqlite3_changegroup;
int sqlite3changegroup_new(sqlite3_changegroup **pp);
/*
+** CAPI3REF: Add A Changeset To A Changegroup
+**
** Add all changes within the changeset (or patchset) in buffer pData (size
** nData bytes) to the changegroup.
**
@@ -9265,7 +9292,7 @@ int sqlite3changegroup_new(sqlite3_changegroup **pp);
** apply to the same row as a change already present in the changegroup if
** the two rows have the same primary key.
**
-** Changes to rows that that do not already appear in the changegroup are
+** Changes to rows that do not already appear in the changegroup are
** simply copied into it. Or, if both the new changeset and the changegroup
** contain changes that apply to a single row, the final contents of the
** changegroup depends on the type of each change, as follows:
@@ -9326,6 +9353,8 @@ int sqlite3changegroup_new(sqlite3_changegroup **pp);
int sqlite3changegroup_add(sqlite3_changegroup*, int nData, void *pData);
/*
+** CAPI3REF: Obtain A Composite Changeset From A Changegroup
+**
** Obtain a buffer containing a changeset (or patchset) representing the
** current contents of the changegroup. If the inputs to the changegroup
** were themselves changesets, the output is a changeset. Or, if the
@@ -9354,7 +9383,7 @@ int sqlite3changegroup_output(
);
/*
-** Delete a changegroup object.
+** CAPI3REF: Delete A Changegroup Object
*/
void sqlite3changegroup_delete(sqlite3_changegroup*);
diff --git a/ext/sqlite3/sqlite3.c b/ext/sqlite3/sqlite3.c
index dcbc03cf84..45a1b847bf 100644
--- a/ext/sqlite3/sqlite3.c
+++ b/ext/sqlite3/sqlite3.c
@@ -123,14 +123,6 @@ PHP_METHOD(sqlite3, open)
return;
}
-#if PHP_API_VERSION < 20100412
- if (PG(safe_mode) && (!php_checkuid(fullpath, NULL, CHECKUID_CHECK_FILE_AND_DIR))) {
- zend_throw_exception_ex(zend_ce_exception, 0, "safe_mode prohibits opening %s", fullpath);
- efree(fullpath);
- return;
- }
-#endif
-
if (php_check_open_basedir(fullpath)) {
zend_throw_exception_ex(zend_ce_exception, 0, "open_basedir prohibits opening %s", fullpath);
efree(fullpath);
@@ -166,11 +158,7 @@ PHP_METHOD(sqlite3, open)
db_obj->initialised = 1;
-#if PHP_API_VERSION < 20100412
- if (PG(safe_mode) || (PG(open_basedir) && *PG(open_basedir))) {
-#else
if (PG(open_basedir) && *PG(open_basedir)) {
-#endif
sqlite3_set_authorizer(db_obj->db, php_sqlite3_authorizer, NULL);
}
@@ -708,9 +696,7 @@ static int sqlite3_do_callback(struct php_sqlite3_fci *fc, zval *cb, int argc, s
fake_argc = argc + is_agg;
fc->fci.size = sizeof(fc->fci);
- fc->fci.function_table = EG(function_table);
ZVAL_COPY_VALUE(&fc->fci.function_name, cb);
- fc->fci.symbol_table = NULL;
fc->fci.object = NULL;
fc->fci.retval = &retval;
fc->fci.param_count = fake_argc;
@@ -867,9 +853,7 @@ static int php_sqlite3_callback_compare(void *coll, int a_len, const void *a, in
int ret;
collation->fci.fci.size = (sizeof(collation->fci.fci));
- collation->fci.fci.function_table = EG(function_table);
ZVAL_COPY_VALUE(&collation->fci.fci.function_name, &collation->cmp_func);
- collation->fci.fci.symbol_table = NULL;
collation->fci.fci.object = NULL;
collation->fci.fci.retval = &retval;
collation->fci.fci.param_count = 2;
@@ -910,7 +894,7 @@ static int php_sqlite3_callback_compare(void *coll, int a_len, const void *a, in
}
/* }}} */
-/* {{{ proto bool SQLite3::createFunction(string name, mixed callback [, int argcount])
+/* {{{ proto bool SQLite3::createFunction(string name, mixed callback [, int argcount, int flags])
Allows registration of a PHP function as a SQLite UDF that can be called within SQL statements. */
PHP_METHOD(sqlite3, createFunction)
{
@@ -922,11 +906,12 @@ PHP_METHOD(sqlite3, createFunction)
zval *callback_func;
zend_string *callback_name;
zend_long sql_func_num_args = -1;
+ zend_long flags = 0;
db_obj = Z_SQLITE3_DB_P(object);
SQLITE3_CHECK_INITIALIZED(db_obj, db_obj->initialised, SQLite3)
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz|l", &sql_func, &sql_func_len, &callback_func, &sql_func_num_args) == FAILURE) {
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz|ll", &sql_func, &sql_func_len, &callback_func, &sql_func_num_args, &flags) == FAILURE) {
return;
}
@@ -943,7 +928,7 @@ PHP_METHOD(sqlite3, createFunction)
func = (php_sqlite3_func *)ecalloc(1, sizeof(*func));
- if (sqlite3_create_function(db_obj->db, sql_func, sql_func_num_args, SQLITE_UTF8, func, php_sqlite3_callback_func, NULL, NULL) == SQLITE_OK) {
+ if (sqlite3_create_function(db_obj->db, sql_func, sql_func_num_args, flags | SQLITE_UTF8, func, php_sqlite3_callback_func, NULL, NULL) == SQLITE_OK) {
func->func_name = estrdup(sql_func);
ZVAL_COPY(&func->func, callback_func);
@@ -1198,7 +1183,8 @@ static php_stream_ops php_stream_sqlite3_ops = {
"SQLite3",
php_sqlite3_stream_seek,
php_sqlite3_stream_cast,
- php_sqlite3_stream_stat
+ php_sqlite3_stream_stat,
+ NULL
};
/* {{{ proto resource SQLite3::openBlob(string table, string column, int rowid [, string dbname])
@@ -1912,6 +1898,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_sqlite3_createfunction, 0, 0, 2)
ZEND_ARG_INFO(0, name)
ZEND_ARG_INFO(0, callback)
ZEND_ARG_INFO(0, argument_count)
+ ZEND_ARG_INFO(0, flags)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_sqlite3_createaggregate, 0, 0, 3)
@@ -2034,13 +2021,6 @@ static int php_sqlite3_authorizer(void *autharg, int access_type, const char *ar
case SQLITE_ATTACH:
{
if (memcmp(arg3, ":memory:", sizeof(":memory:")) && *arg3) {
-
-#if PHP_API_VERSION < 20100412
- if (PG(safe_mode) && (!php_checkuid(arg3, NULL, CHECKUID_CHECK_FILE_AND_DIR))) {
- return SQLITE_DENY;
- }
-#endif
-
if (php_check_open_basedir(arg3)) {
return SQLITE_DENY;
}
@@ -2306,6 +2286,10 @@ PHP_MINIT_FUNCTION(sqlite3)
REGISTER_LONG_CONSTANT("SQLITE3_OPEN_READWRITE", SQLITE_OPEN_READWRITE, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SQLITE3_OPEN_CREATE", SQLITE_OPEN_CREATE, CONST_CS | CONST_PERSISTENT);
+#ifdef SQLITE_DETERMINISTIC
+ REGISTER_LONG_CONSTANT("SQLITE3_DETERMINISTIC", SQLITE_DETERMINISTIC, CONST_CS | CONST_PERSISTENT);
+#endif
+
return SUCCESS;
}
/* }}} */
diff --git a/ext/sqlite3/tests/sqlite3_01_open-mb.phpt b/ext/sqlite3/tests/sqlite3_01_open-mb.phpt
new file mode 100644
index 0000000000..ad9b147d9d
--- /dev/null
+++ b/ext/sqlite3/tests/sqlite3_01_open-mb.phpt
@@ -0,0 +1,22 @@
+--TEST--
+SQLite3::open/close tests
+--SKIPIF--
+<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
+--FILE--
+<?php
+
+$db_file = dirname(__FILE__) . DIRECTORY_SEPARATOR . '私はガラスを食べられます.db';
+$db = new SQLite3($db_file);
+//require_once(dirname(__FILE__) . '/new_db.inc');
+
+var_dump($db);
+var_dump($db->close());
+
+unlink($db_file);
+echo "Done\n";
+?>
+--EXPECTF--
+object(SQLite3)#%d (0) {
+}
+bool(true)
+Done
diff --git a/ext/sqlite3/tests/sqlite3_37_createfunction_flags.phpt b/ext/sqlite3/tests/sqlite3_37_createfunction_flags.phpt
new file mode 100644
index 0000000000..0c5bd860f6
--- /dev/null
+++ b/ext/sqlite3/tests/sqlite3_37_createfunction_flags.phpt
@@ -0,0 +1,32 @@
+--TEST--
+SQLite3::createFunction - Test with flags
+--SKIPIF--
+<?php
+require_once(__DIR__ . '/skipif.inc');
+if (!defined('SQLITE3_DETERMINISTIC')) die('skip system sqlite is too old');
+?>
+--FILE--
+<?php
+
+require_once(__DIR__ . '/new_db.inc');
+
+$func = 'strtoupper';
+var_dump($db->createfunction($func, $func, 1, SQLITE3_DETERMINISTIC));
+var_dump($db->querySingle('SELECT strtoupper("test")'));
+
+$func2 = 'strtolower';
+var_dump($db->createfunction($func2, $func2, 1, SQLITE3_DETERMINISTIC));
+var_dump($db->querySingle('SELECT strtolower("TEST")'));
+
+var_dump($db->createfunction($func, $func2, 1, SQLITE3_DETERMINISTIC));
+var_dump($db->querySingle('SELECT strtoupper("tEst")'));
+
+
+?>
+--EXPECTF--
+bool(true)
+string(4) "TEST"
+bool(true)
+string(4) "test"
+bool(true)
+string(4) "test"
diff --git a/ext/standard/array.c b/ext/standard/array.c
index eb5d7c35be..966e0a3af2 100644
--- a/ext/standard/array.c
+++ b/ext/standard/array.c
@@ -47,9 +47,8 @@
#include "php_rand.h"
#include "php_math.h"
#include "zend_smart_str.h"
-#ifdef HAVE_SPL
+#include "zend_bitset.h"
#include "ext/spl/spl_array.h"
-#endif
/* {{{ defines */
#define EXTR_OVERWRITE 0
@@ -804,9 +803,7 @@ PHP_FUNCTION(count)
RETURN_LONG(cnt);
break;
case IS_OBJECT: {
-#ifdef HAVE_SPL
zval retval;
-#endif
/* first, we check if the handler is defined */
if (Z_OBJ_HT_P(array)->count_elements) {
RETVAL_LONG(1);
@@ -814,7 +811,6 @@ PHP_FUNCTION(count)
return;
}
}
-#ifdef HAVE_SPL
/* if not and the object implements Countable we call its count() method */
if (instanceof_function(Z_OBJCE_P(array), spl_ce_Countable)) {
zend_call_method_with_0_params(array, NULL, NULL, "count", &retval);
@@ -824,7 +820,6 @@ PHP_FUNCTION(count)
}
return;
}
-#endif
}
default:
RETURN_LONG(1);
@@ -1359,11 +1354,15 @@ PHP_FUNCTION(max)
}
/* }}} */
-static int php_array_walk(HashTable *target_hash, zval *userdata, int recursive) /* {{{ */
+static int php_array_walk(zval *array, zval *userdata, int recursive) /* {{{ */
{
zval args[3], /* Arguments to userland function */
retval, /* Return value - unused */
*zv;
+ HashTable *target_hash = HASH_OF(array);
+ HashPosition pos;
+ uint32_t ht_iter;
+ int result = SUCCESS;
/* Set up known arguments */
ZVAL_UNDEF(&args[1]);
@@ -1376,89 +1375,112 @@ static int php_array_walk(HashTable *target_hash, zval *userdata, int recursive)
BG(array_walk_fci).params = args;
BG(array_walk_fci).no_separation = 0;
+ zend_hash_internal_pointer_reset_ex(target_hash, &pos);
+ ht_iter = zend_hash_iterator_add(target_hash, pos);
+
/* Iterate through hash */
- zend_hash_internal_pointer_reset(target_hash);
- while (!EG(exception) && (zv = zend_hash_get_current_data(target_hash)) != NULL) {
+ do {
+ /* Retrieve value */
+ zv = zend_hash_get_current_data_ex(target_hash, &pos);
+ if (zv == NULL) {
+ break;
+ }
+
+ /* Skip undefined indirect elements */
if (Z_TYPE_P(zv) == IS_INDIRECT) {
zv = Z_INDIRECT_P(zv);
if (Z_TYPE_P(zv) == IS_UNDEF) {
- zend_hash_move_forward(target_hash);
+ zend_hash_move_forward_ex(target_hash, &pos);
continue;
}
}
- if (recursive &&
- (Z_TYPE_P(zv) == IS_ARRAY ||
- (Z_ISREF_P(zv) && Z_TYPE_P(Z_REFVAL_P(zv)) == IS_ARRAY))) {
+
+ /* Ensure the value is a reference. Otherwise the location of the value may be freed. */
+ ZVAL_MAKE_REF(zv);
+
+ /* Retrieve key */
+ zend_hash_get_current_key_zval_ex(target_hash, &args[1], &pos);
+
+ /* Move to next element already now -- this mirrors the approach used by foreach
+ * and ensures proper behavior with regard to modifications. */
+ zend_hash_move_forward_ex(target_hash, &pos);
+
+ /* Back up hash position, as it may change */
+ EG(ht_iterators)[ht_iter].pos = pos;
+
+ if (recursive && Z_TYPE_P(Z_REFVAL_P(zv)) == IS_ARRAY) {
HashTable *thash;
zend_fcall_info orig_array_walk_fci;
zend_fcall_info_cache orig_array_walk_fci_cache;
+ zval ref;
+ ZVAL_COPY_VALUE(&ref, zv);
ZVAL_DEREF(zv);
SEPARATE_ARRAY(zv);
thash = Z_ARRVAL_P(zv);
if (thash->u.v.nApplyCount > 1) {
php_error_docref(NULL, E_WARNING, "recursion detected");
- if (userdata) {
- zval_ptr_dtor(&args[2]);
- }
- return 0;
+ result = FAILURE;
+ break;
}
/* backup the fcall info and cache */
orig_array_walk_fci = BG(array_walk_fci);
orig_array_walk_fci_cache = BG(array_walk_fci_cache);
+ Z_ADDREF(ref);
thash->u.v.nApplyCount++;
- php_array_walk(thash, userdata, recursive);
- thash->u.v.nApplyCount--;
+ result = php_array_walk(zv, userdata, recursive);
+ if (Z_TYPE_P(Z_REFVAL(ref)) == IS_ARRAY && thash == Z_ARRVAL_P(Z_REFVAL(ref))) {
+ /* If the hashtable changed in the meantime, we'll "leak" this apply count
+ * increment -- our reference to thash is no longer valid. */
+ thash->u.v.nApplyCount--;
+ }
+ zval_ptr_dtor(&ref);
/* restore the fcall info and cache */
BG(array_walk_fci) = orig_array_walk_fci;
BG(array_walk_fci_cache) = orig_array_walk_fci_cache;
} else {
- int was_ref = Z_ISREF_P(zv);
-
ZVAL_COPY(&args[0], zv);
- /* Allocate space for key */
- zend_hash_get_current_key_zval(target_hash, &args[1]);
-
/* Call the userland function */
- if (zend_call_function(&BG(array_walk_fci), &BG(array_walk_fci_cache)) == SUCCESS) {
- if (!was_ref && Z_ISREF(args[0])) {
- /* copy reference back */
- zval garbage;
- if (Z_REFCOUNT(args[0]) == 1) {
- ZVAL_UNREF(&args[0]);
- }
- ZVAL_COPY_VALUE(&garbage, zv);
- ZVAL_COPY_VALUE(zv, &args[0]);
- zval_ptr_dtor(&garbage);
- } else {
- zval_ptr_dtor(&args[0]);
- }
+ result = zend_call_function(&BG(array_walk_fci), &BG(array_walk_fci_cache));
+ if (result == SUCCESS) {
zval_ptr_dtor(&retval);
- } else {
- zval_ptr_dtor(&args[0]);
- if (Z_TYPE(args[1]) != IS_UNDEF) {
- zval_ptr_dtor(&args[1]);
- ZVAL_UNDEF(&args[1]);
- }
- break;
}
+
+ zval_ptr_dtor(&args[0]);
}
if (Z_TYPE(args[1]) != IS_UNDEF) {
zval_ptr_dtor(&args[1]);
ZVAL_UNDEF(&args[1]);
}
- zend_hash_move_forward(target_hash);
- }
+
+ if (result == FAILURE) {
+ break;
+ }
+
+ /* Reload array and position -- both may have changed */
+ if (Z_TYPE_P(array) == IS_ARRAY) {
+ pos = zend_hash_iterator_pos_ex(ht_iter, array);
+ target_hash = Z_ARRVAL_P(array);
+ } else if (Z_TYPE_P(array) == IS_OBJECT) {
+ target_hash = Z_OBJPROP_P(array);
+ pos = zend_hash_iterator_pos(ht_iter, target_hash);
+ } else {
+ php_error_docref(NULL, E_WARNING, "Iterated value is no longer an array or object");
+ result = FAILURE;
+ break;
+ }
+ } while (!EG(exception));
if (userdata) {
zval_ptr_dtor(&args[2]);
}
- return 0;
+ zend_hash_iterator_del(ht_iter);
+ return result;
}
/* }}} */
@@ -1466,7 +1488,7 @@ static int php_array_walk(HashTable *target_hash, zval *userdata, int recursive)
Apply a user function to every member of an array */
PHP_FUNCTION(array_walk)
{
- HashTable *array;
+ zval *array;
zval *userdata = NULL;
zend_fcall_info orig_array_walk_fci;
zend_fcall_info_cache orig_array_walk_fci_cache;
@@ -1475,7 +1497,7 @@ PHP_FUNCTION(array_walk)
orig_array_walk_fci_cache = BG(array_walk_fci_cache);
ZEND_PARSE_PARAMETERS_START(2, 3)
- Z_PARAM_ARRAY_OR_OBJECT_HT_EX(array, 0, 1)
+ Z_PARAM_ARRAY_OR_OBJECT_EX(array, 0, 1)
Z_PARAM_FUNC(BG(array_walk_fci), BG(array_walk_fci_cache))
Z_PARAM_OPTIONAL
Z_PARAM_ZVAL_EX(userdata, 0, 1)
@@ -1496,7 +1518,7 @@ PHP_FUNCTION(array_walk)
Apply a user function recursively to every member of an array */
PHP_FUNCTION(array_walk_recursive)
{
- HashTable *array;
+ zval *array;
zval *userdata = NULL;
zend_fcall_info orig_array_walk_fci;
zend_fcall_info_cache orig_array_walk_fci_cache;
@@ -1504,7 +1526,7 @@ PHP_FUNCTION(array_walk_recursive)
orig_array_walk_fci = BG(array_walk_fci);
orig_array_walk_fci_cache = BG(array_walk_fci_cache);
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "H/f|z/", &array, &BG(array_walk_fci), &BG(array_walk_fci_cache), &userdata) == FAILURE) {
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "A/f|z/", &array, &BG(array_walk_fci), &BG(array_walk_fci_cache), &userdata) == FAILURE) {
BG(array_walk_fci) = orig_array_walk_fci;
BG(array_walk_fci_cache) = orig_array_walk_fci_cache;
return;
@@ -1554,7 +1576,6 @@ static inline void php_search_array(INTERNAL_FUNCTION_PARAMETERS, int behavior)
}
} ZEND_HASH_FOREACH_END();
} else {
- ZVAL_DEREF(value);
if (Z_TYPE_P(value) == IS_LONG) {
ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
if (fast_equal_check_long(value, entry)) {
@@ -1706,6 +1727,7 @@ PHP_FUNCTION(extract)
zend_ulong num_key;
int var_exists, count = 0;
int extract_refs = 0;
+ int exception_thrown = 0;
zend_array *symbol_table;
zval var_array;
@@ -1740,6 +1762,10 @@ PHP_FUNCTION(extract)
}
}
+ if (zend_forbid_dynamic_call("extract()") == FAILURE) {
+ return;
+ }
+
symbol_table = zend_rebuild_symbol_table();
#if 0
if (!symbol_table) {
@@ -1778,9 +1804,6 @@ PHP_FUNCTION(extract)
if (var_exists && ZSTR_LEN(var_name) == sizeof("GLOBALS")-1 && !strcmp(ZSTR_VAL(var_name), "GLOBALS")) {
break;
}
- if (var_exists && ZSTR_LEN(var_name) == sizeof("this")-1 && !strcmp(ZSTR_VAL(var_name), "this") && EG(scope) && ZSTR_LEN(EG(scope)->name) != 0) {
- break;
- }
ZVAL_STR_COPY(&final_name, var_name);
break;
@@ -1821,6 +1844,15 @@ PHP_FUNCTION(extract)
if (Z_TYPE(final_name) == IS_STRING && php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
zval *orig_var;
+
+ if (Z_STRLEN(final_name) == sizeof("this")-1 && !strcmp(Z_STRVAL(final_name), "this")) {
+ if (!exception_thrown) {
+ exception_thrown = 1;
+ zend_throw_error(NULL, "Cannot re-assign $this");
+ }
+ zval_dtor(&final_name);
+ continue;
+ }
if (extract_refs) {
ZVAL_MAKE_REF(entry);
@@ -1870,6 +1902,14 @@ static void php_compact_var(HashTable *eg_active_symbol_table, zval *return_valu
ZVAL_COPY(&data, value_ptr);
zend_hash_update(Z_ARRVAL_P(return_value), Z_STR_P(entry), &data);
}
+ if (zend_string_equals_literal(Z_STR_P(entry), "this")) {
+ zend_object *object = zend_get_this_object(EG(current_execute_data));
+ if (object) {
+ GC_REFCOUNT(object)++;
+ ZVAL_OBJ(&data, object);
+ zend_hash_update(Z_ARRVAL_P(return_value), Z_STR_P(entry), &data);
+ }
+ }
} else if (Z_TYPE_P(entry) == IS_ARRAY) {
if ((Z_ARRVAL_P(entry)->u.v.nApplyCount > 1)) {
php_error_docref(NULL, E_WARNING, "recursion detected");
@@ -1901,8 +1941,11 @@ PHP_FUNCTION(compact)
return;
}
- symbol_table = zend_rebuild_symbol_table();
+ if (zend_forbid_dynamic_call("compact()") == FAILURE) {
+ return;
+ }
+ symbol_table = zend_rebuild_symbol_table();
if (UNEXPECTED(symbol_table == NULL)) {
return;
}
@@ -2035,7 +2078,7 @@ PHP_FUNCTION(array_fill_keys)
#define RANGE_CHECK_LONG_INIT_ARRAY(start, end) do { \
zend_ulong __calc_size = (start - end) / lstep; \
if (__calc_size >= HT_MAX_SIZE - 1) { \
- php_error_docref(NULL, E_WARNING, "The supplied range exceeds the maximum array size: start=%pd end=%pd", end, start); \
+ php_error_docref(NULL, E_WARNING, "The supplied range exceeds the maximum array size: start=" ZEND_LONG_FMT " end=" ZEND_LONG_FMT, end, start); \
RETURN_FALSE; \
} \
size = (uint32_t)(__calc_size + 1); \
@@ -2202,7 +2245,7 @@ long_str:
Z_TYPE_INFO(tmp) = IS_LONG;
if (low > high) { /* Negative steps */
- if (low - high < lstep) {
+ if ((zend_ulong)(low - high) < lstep) {
err = 1;
goto err;
}
@@ -2216,7 +2259,7 @@ long_str:
}
} ZEND_HASH_FILL_END();
} else if (high > low) { /* Positive steps */
- if (high - low < lstep) {
+ if ((zend_ulong)(high - low) < lstep) {
err = 1;
goto err;
}
@@ -2252,7 +2295,7 @@ static void php_array_data_shuffle(zval *array) /* {{{ */
Bucket *p, temp;
HashTable *hash;
zend_long rnd_idx;
- uint32_t n_left;
+ zend_long n_left;
n_elems = zend_hash_num_elements(Z_ARRVAL_P(array));
@@ -2275,7 +2318,6 @@ static void php_array_data_shuffle(zval *array) /* {{{ */
}
}
while (--n_left) {
- rnd_idx = php_rand();
RAND_RANGE(rnd_idx, 0, n_left, PHP_RAND_MAX);
if (rnd_idx != n_left) {
temp = hash->arData[n_left];
@@ -2301,7 +2343,6 @@ static void php_array_data_shuffle(zval *array) /* {{{ */
}
}
while (--n_left) {
- rnd_idx = php_rand();
RAND_RANGE(rnd_idx, 0, n_left, PHP_RAND_MAX);
if (rnd_idx != n_left) {
temp = hash->arData[n_left];
@@ -2311,7 +2352,6 @@ static void php_array_data_shuffle(zval *array) /* {{{ */
}
}
}
- HANDLE_BLOCK_INTERRUPTIONS();
hash->nNumUsed = n_elems;
hash->nInternalPointer = 0;
@@ -2327,7 +2367,6 @@ static void php_array_data_shuffle(zval *array) /* {{{ */
if (!(hash->u.flags & HASH_FLAG_PACKED)) {
zend_hash_to_packed(hash);
}
- HANDLE_UNBLOCK_INTERRUPTIONS();
}
/* }}} */
@@ -2347,12 +2386,12 @@ PHP_FUNCTION(shuffle)
}
/* }}} */
-static void php_splice(HashTable *in_hash, int offset, int length, HashTable *replace, HashTable *removed) /* {{{ */
+static void php_splice(HashTable *in_hash, zend_long offset, zend_long length, HashTable *replace, HashTable *removed) /* {{{ */
{
HashTable out_hash; /* Output hashtable */
- int num_in, /* Number of entries in the input hashtable */
- pos; /* Current position in the hashtable */
- uint idx;
+ zend_long num_in; /* Number of entries in the input hashtable */
+ zend_long pos; /* Current position in the hashtable */
+ uint32_t idx;
Bucket *p; /* Pointer to hash bucket */
zval *entry; /* Hash entry */
uint32_t iter_pos = zend_hash_iterators_lower_pos(in_hash, 0);
@@ -2391,7 +2430,7 @@ static void php_splice(HashTable *in_hash, int offset, int length, HashTable *re
zend_hash_add_new(&out_hash, p->key, entry);
}
if (idx == iter_pos) {
- if (idx != pos) {
+ if ((zend_long)idx != pos) {
zend_hash_iterators_update(in_hash, idx, pos);
}
iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos + 1);
@@ -2461,7 +2500,7 @@ static void php_splice(HashTable *in_hash, int offset, int length, HashTable *re
zend_hash_add_new(&out_hash, p->key, entry);
}
if (idx == iter_pos) {
- if (idx != pos) {
+ if ((zend_long)idx != pos) {
zend_hash_iterators_update(in_hash, idx, pos);
}
iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos + 1);
@@ -2808,7 +2847,7 @@ PHP_FUNCTION(array_splice)
}
/* Perform splice */
- php_splice(Z_ARRVAL_P(array), (int)offset, (int)length, repl_array ? Z_ARRVAL_P(repl_array) : NULL, rem_hash);
+ php_splice(Z_ARRVAL_P(array), offset, length, repl_array ? Z_ARRVAL_P(repl_array) : NULL, rem_hash);
}
/* }}} */
@@ -2870,7 +2909,9 @@ PHP_FUNCTION(array_slice)
/* Start at the beginning and go until we hit offset */
pos = 0;
- if (!preserve_keys && (Z_ARRVAL_P(input)->u.flags & HASH_FLAG_PACKED)) {
+ if (HT_IS_PACKED(Z_ARRVAL_P(input)) &&
+ (!preserve_keys ||
+ (offset == 0 && HT_IS_WITHOUT_HOLES(Z_ARRVAL_P(input))))) {
zend_hash_real_init(Z_ARRVAL_P(return_value), 1);
ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(input), entry) {
@@ -2881,8 +2922,12 @@ PHP_FUNCTION(array_slice)
if (pos > offset + length) {
break;
}
+ if (UNEXPECTED(Z_ISREF_P(entry)) &&
+ UNEXPECTED(Z_REFCOUNT_P(entry) == 1)) {
+ ZVAL_UNREF(entry);
+ }
+ Z_TRY_ADDREF_P(entry);
ZEND_HASH_FILL_ADD(entry);
- zval_add_ref(entry);
} ZEND_HASH_FOREACH_END();
} ZEND_HASH_FILL_END();
} else {
@@ -2940,7 +2985,7 @@ PHPAPI int php_array_merge_recursive(HashTable *dest, HashTable *src) /* {{{ */
convert_to_array_ex(dest_zval);
add_next_index_null(dest_zval);
} else if (Z_TYPE_P(dest_zval) == IS_ARRAY) {
- if (UNEXPECTED(Z_ARRVAL_P(dest_zval)->nNextFreeElement > Z_ARRVAL_P(dest_zval)->nNumUsed)) {
+ if (UNEXPECTED(Z_ARRVAL_P(dest_zval)->nNextFreeElement > (zend_long)Z_ARRVAL_P(dest_zval)->nNumUsed)) {
Z_ARRVAL_P(dest_zval)->nNextFreeElement = Z_ARRVAL_P(dest_zval)->nNumUsed;
}
} else {
@@ -2988,24 +3033,32 @@ PHPAPI int php_array_merge(HashTable *dest, HashTable *src) /* {{{ */
zval *src_entry;
zend_string *string_key;
- ZEND_HASH_FOREACH_STR_KEY_VAL(src, string_key, src_entry) {
- if (Z_REFCOUNTED_P(src_entry)) {
- if (UNEXPECTED(Z_ISREF_P(src_entry))
- && UNEXPECTED(Z_REFCOUNT_P(src_entry) == 1)) {
- ZVAL_UNREF(src_entry);
- if (Z_REFCOUNTED_P(src_entry)) {
- Z_ADDREF_P(src_entry);
+ if ((dest->u.flags & HASH_FLAG_PACKED) && (src->u.flags & HASH_FLAG_PACKED)) {
+ zend_hash_extend(dest, zend_hash_num_elements(dest) + zend_hash_num_elements(src), 1);
+ ZEND_HASH_FILL_PACKED(dest) {
+ ZEND_HASH_FOREACH_VAL(src, src_entry) {
+ if (UNEXPECTED(Z_ISREF_P(src_entry)) &&
+ UNEXPECTED(Z_REFCOUNT_P(src_entry) == 1)) {
+ ZVAL_UNREF(src_entry);
}
+ Z_TRY_ADDREF_P(src_entry);
+ ZEND_HASH_FILL_ADD(src_entry);
+ } ZEND_HASH_FOREACH_END();
+ } ZEND_HASH_FILL_END();
+ } else {
+ ZEND_HASH_FOREACH_STR_KEY_VAL(src, string_key, src_entry) {
+ if (UNEXPECTED(Z_ISREF_P(src_entry) &&
+ Z_REFCOUNT_P(src_entry) == 1)) {
+ ZVAL_UNREF(src_entry);
+ }
+ Z_TRY_ADDREF_P(src_entry);
+ if (string_key) {
+ zend_hash_update(dest, string_key, src_entry);
} else {
- Z_ADDREF_P(src_entry);
+ zend_hash_next_index_insert_new(dest, src_entry);
}
- }
- if (string_key) {
- zend_hash_update(dest, string_key, src_entry);
- } else {
- zend_hash_next_index_insert_new(dest, src_entry);
- }
- } ZEND_HASH_FOREACH_END();
+ } ZEND_HASH_FOREACH_END();
+ }
return 1;
}
/* }}} */
@@ -3084,7 +3137,7 @@ static inline void php_array_merge_or_replace_wrapper(INTERNAL_FUNCTION_PARAMETE
{
zval *args = NULL;
zval *arg;
- int argc, i, init_size = 0;
+ int argc, i;
ZEND_PARSE_PARAMETERS_START(1, -1)
Z_PARAM_VARIADIC('+', args, argc)
@@ -3097,96 +3150,79 @@ static inline void php_array_merge_or_replace_wrapper(INTERNAL_FUNCTION_PARAMETE
if (Z_TYPE_P(arg) != IS_ARRAY) {
php_error_docref(NULL, E_WARNING, "Argument #%d is not an array", i + 1);
RETURN_NULL();
- } else {
- int num = zend_hash_num_elements(Z_ARRVAL_P(arg));
-
- if (num > init_size) {
- init_size = num;
- }
}
}
- array_init_size(return_value, init_size);
if (replace) {
- zend_string *string_key;
- zval *src_entry;
- zend_ulong idx;
- HashTable *src, *dest;
+ HashTable *dest;
/* copy first array */
arg = args;
ZVAL_DEREF(arg);
- src = Z_ARRVAL_P(arg);
- dest = Z_ARRVAL_P(return_value);
- ZEND_HASH_FOREACH_KEY_VAL(src, idx, string_key, src_entry) {
- if (UNEXPECTED(Z_ISREF_P(src_entry) && Z_REFCOUNT_P(src_entry) == 1)) {
- src_entry = Z_REFVAL_P(src_entry);
- }
- if (string_key) {
- if (Z_REFCOUNTED_P(src_entry)) {
- Z_ADDREF_P(src_entry);
- }
- zend_hash_add_new(dest, string_key, src_entry);
- } else {
- if (Z_REFCOUNTED_P(src_entry)) {
- Z_ADDREF_P(src_entry);
- }
- zend_hash_index_add_new(dest, idx, src_entry);
- }
- } ZEND_HASH_FOREACH_END();
-
+ dest = zend_array_dup(Z_ARRVAL_P(arg));
+ ZVAL_ARR(return_value, dest);
if (recursive) {
for (i = 1; i < argc; i++) {
arg = args + i;
ZVAL_DEREF(arg);
- php_array_replace_recursive(Z_ARRVAL_P(return_value), Z_ARRVAL_P(arg));
+ php_array_replace_recursive(dest, Z_ARRVAL_P(arg));
}
} else {
for (i = 1; i < argc; i++) {
arg = args + i;
ZVAL_DEREF(arg);
- zend_hash_merge(Z_ARRVAL_P(return_value), Z_ARRVAL_P(arg), zval_add_ref, 1);
+ zend_hash_merge(dest, Z_ARRVAL_P(arg), zval_add_ref, 1);
}
}
} else {
- zend_string *string_key;
zval *src_entry;
HashTable *src, *dest;
- /* copy first array */
arg = args;
ZVAL_DEREF(arg);
src = Z_ARRVAL_P(arg);
+ /* copy first array */
+ array_init_size(return_value, zend_hash_num_elements(src));
dest = Z_ARRVAL_P(return_value);
- ZEND_HASH_FOREACH_STR_KEY_VAL(src, string_key, src_entry) {
- if (UNEXPECTED(Z_ISREF_P(src_entry) && Z_REFCOUNT_P(src_entry) == 1)) {
- src_entry = Z_REFVAL_P(src_entry);
- }
- if (string_key) {
- if (Z_REFCOUNTED_P(src_entry)) {
- Z_ADDREF_P(src_entry);
+ if (src->u.flags & HASH_FLAG_PACKED) {
+ zend_hash_real_init(dest, 1);
+ ZEND_HASH_FILL_PACKED(dest) {
+ ZEND_HASH_FOREACH_VAL(src, src_entry) {
+ if (UNEXPECTED(Z_ISREF_P(src_entry) &&
+ Z_REFCOUNT_P(src_entry) == 1)) {
+ ZVAL_UNREF(src_entry);
+ }
+ Z_TRY_ADDREF_P(src_entry);
+ ZEND_HASH_FILL_ADD(src_entry);
+ } ZEND_HASH_FOREACH_END();
+ } ZEND_HASH_FILL_END();
+ } else {
+ zend_string *string_key;
+ ZEND_HASH_FOREACH_STR_KEY_VAL(src, string_key, src_entry) {
+ if (UNEXPECTED(Z_ISREF_P(src_entry) &&
+ Z_REFCOUNT_P(src_entry) == 1)) {
+ ZVAL_UNREF(src_entry);
}
- zend_hash_add_new(dest, string_key, src_entry);
- } else {
- if (Z_REFCOUNTED_P(src_entry)) {
- Z_ADDREF_P(src_entry);
+ Z_TRY_ADDREF_P(src_entry);
+ if (string_key) {
+ zend_hash_add_new(dest, string_key, src_entry);
+ } else {
+ zend_hash_next_index_insert_new(dest, src_entry);
}
- zend_hash_next_index_insert_new(dest, src_entry);
- }
- } ZEND_HASH_FOREACH_END();
-
+ } ZEND_HASH_FOREACH_END();
+ }
if (recursive) {
for (i = 1; i < argc; i++) {
arg = args + i;
ZVAL_DEREF(arg);
- php_array_merge_recursive(Z_ARRVAL_P(return_value), Z_ARRVAL_P(arg));
+ php_array_merge_recursive(dest, Z_ARRVAL_P(arg));
}
} else {
for (i = 1; i < argc; i++) {
arg = args + i;
ZVAL_DEREF(arg);
- php_array_merge(Z_ARRVAL_P(return_value), Z_ARRVAL_P(arg));
+ php_array_merge(dest, Z_ARRVAL_P(arg));
}
}
}
@@ -3395,7 +3431,7 @@ zend_bool array_column_param_helper(zval *param,
}
/* }}} */
-static inline zval *array_column_fetch_prop(zval *data, zval *name, zval *rv)
+static inline zval *array_column_fetch_prop(zval *data, zval *name, zval *rv) /* {{{ */
{
zval *prop = NULL;
@@ -3425,6 +3461,7 @@ static inline zval *array_column_fetch_prop(zval *data, zval *name, zval *rv)
return prop;
}
+/* }}} */
/* {{{ proto array array_column(array input, mixed column_key[, mixed index_key])
Return the values from a single column in the input array, identified by the
@@ -3444,42 +3481,62 @@ PHP_FUNCTION(array_column)
RETURN_FALSE;
}
- array_init(return_value);
- ZEND_HASH_FOREACH_VAL(arr_hash, data) {
- ZVAL_DEREF(data);
-
- if (!zcolumn) {
- zcolval = data;
- } else if ((zcolval = array_column_fetch_prop(data, zcolumn, &rvc)) == NULL) {
- continue;
- }
+ array_init_size(return_value, zend_hash_num_elements(arr_hash));
+ if (!zkey) {
+ zend_hash_real_init(Z_ARRVAL_P(return_value), 1);
+ ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
+ ZEND_HASH_FOREACH_VAL(arr_hash, data) {
+ ZVAL_DEREF(data);
+ if (!zcolumn) {
+ zcolval = data;
+ Z_TRY_ADDREF_P(zcolval);
+ } else if ((zcolval = array_column_fetch_prop(data, zcolumn, &rvc)) == NULL) {
+ continue;
+ } else if (zcolval != &rvc) {
+ Z_TRY_ADDREF_P(zcolval);
+ }
+ ZEND_HASH_FILL_ADD(zcolval);
+ } ZEND_HASH_FOREACH_END();
+ } ZEND_HASH_FILL_END();
+ } else {
+ ZEND_HASH_FOREACH_VAL(arr_hash, data) {
+ ZVAL_DEREF(data);
- /* Failure will leave zkeyval alone which will land us on the final else block below
- * which is to append the value as next_index
- */
- if (zkey) {
- zkeyval = array_column_fetch_prop(data, zkey, &rvk);
- }
+ if (!zcolumn) {
+ zcolval = data;
+ Z_TRY_ADDREF_P(zcolval);
+ } else if ((zcolval = array_column_fetch_prop(data, zcolumn, &rvc)) == NULL) {
+ continue;
+ } else if (zcolval != &rvc) {
+ Z_TRY_ADDREF_P(zcolval);
+ }
- Z_TRY_ADDREF_P(zcolval);
- if (zkeyval && Z_TYPE_P(zkeyval) == IS_STRING) {
- zend_symtable_update(Z_ARRVAL_P(return_value), Z_STR_P(zkeyval), zcolval);
- } else if (zkeyval && Z_TYPE_P(zkeyval) == IS_LONG) {
- add_index_zval(return_value, Z_LVAL_P(zkeyval), zcolval);
- } else if (zkeyval && Z_TYPE_P(zkeyval) == IS_OBJECT) {
- zend_string *key = zval_get_string(zkeyval);
- zend_symtable_update(Z_ARRVAL_P(return_value), key, zcolval);
- zend_string_release(key);
- } else {
- add_next_index_zval(return_value, zcolval);
- }
- if (zcolval == &rvc) {
- zval_ptr_dtor(&rvc);
- }
- if (zkeyval == &rvk) {
- zval_ptr_dtor(&rvk);
- }
- } ZEND_HASH_FOREACH_END();
+ /* Failure will leave zkeyval alone which will land us on the final else block below
+ * which is to append the value as next_index
+ */
+ if (zkey) {
+ zkeyval = array_column_fetch_prop(data, zkey, &rvk);
+ }
+ if (zkeyval) {
+ if (Z_TYPE_P(zkeyval) == IS_STRING) {
+ zend_symtable_update(Z_ARRVAL_P(return_value), Z_STR_P(zkeyval), zcolval);
+ } else if (Z_TYPE_P(zkeyval) == IS_LONG) {
+ add_index_zval(return_value, Z_LVAL_P(zkeyval), zcolval);
+ } else if (Z_TYPE_P(zkeyval) == IS_OBJECT) {
+ zend_string *key = zval_get_string(zkeyval);
+ zend_symtable_update(Z_ARRVAL_P(return_value), key, zcolval);
+ zend_string_release(key);
+ } else {
+ add_next_index_zval(return_value, zcolval);
+ }
+ if (zkeyval == &rvk) {
+ zval_ptr_dtor(&rvk);
+ }
+ } else {
+ add_next_index_zval(return_value, zcolval);
+ }
+ } ZEND_HASH_FOREACH_END();
+ }
}
/* }}} */
@@ -3499,20 +3556,32 @@ PHP_FUNCTION(array_reverse)
/* Initialize return array */
array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(input)));
-
- ZEND_HASH_REVERSE_FOREACH_KEY_VAL(Z_ARRVAL_P(input), num_key, string_key, entry) {
- if (string_key) {
- entry = zend_hash_add_new(Z_ARRVAL_P(return_value), string_key, entry);
- } else {
- if (preserve_keys) {
- entry = zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, entry);
+ if ((Z_ARRVAL_P(input)->u.flags & HASH_FLAG_PACKED) && !preserve_keys) {
+ zend_hash_real_init(Z_ARRVAL_P(return_value), 1);
+ ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
+ ZEND_HASH_REVERSE_FOREACH_VAL(Z_ARRVAL_P(input), entry) {
+ if (UNEXPECTED(Z_ISREF_P(entry) &&
+ Z_REFCOUNT_P(entry) == 1)) {
+ ZVAL_UNREF(entry);
+ }
+ Z_TRY_ADDREF_P(entry);
+ ZEND_HASH_FILL_ADD(entry);
+ } ZEND_HASH_FOREACH_END();
+ } ZEND_HASH_FILL_END();
+ } else {
+ ZEND_HASH_REVERSE_FOREACH_KEY_VAL(Z_ARRVAL_P(input), num_key, string_key, entry) {
+ if (string_key) {
+ entry = zend_hash_add_new(Z_ARRVAL_P(return_value), string_key, entry);
} else {
- entry = zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), entry);
+ if (preserve_keys) {
+ entry = zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, entry);
+ } else {
+ entry = zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), entry);
+ }
}
- }
-
- zval_add_ref(entry);
- } ZEND_HASH_FOREACH_END();
+ zval_add_ref(entry);
+ } ZEND_HASH_FOREACH_END();
+ }
}
/* }}} */
@@ -3549,31 +3618,56 @@ PHP_FUNCTION(array_pad)
}
num_pads = pad_size_abs - input_size;
- array_init_size(return_value, pad_size_abs);
if (Z_REFCOUNTED_P(pad_value)) {
GC_REFCOUNT(Z_COUNTED_P(pad_value)) += num_pads;
}
- if (pad_size < 0) {
- for (i = 0; i < num_pads; i++) {
- zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), pad_value);
+ array_init_size(return_value, pad_size_abs);
+ if (Z_ARRVAL_P(input)->u.flags & HASH_FLAG_PACKED) {
+ zend_hash_real_init(Z_ARRVAL_P(return_value), 1);
+
+ if (pad_size < 0) {
+ ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
+ for (i = 0; i < num_pads; i++) {
+ ZEND_HASH_FILL_ADD(pad_value);
+ }
+ } ZEND_HASH_FILL_END();
}
- }
- ZEND_HASH_FOREACH_STR_KEY_VAL_IND(Z_ARRVAL_P(input), key, value) {
- if (Z_REFCOUNTED_P(value)) {
- Z_ADDREF_P(value);
+ ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
+ ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(input), value) {
+ Z_TRY_ADDREF_P(value);
+ ZEND_HASH_FILL_ADD(value);
+ } ZEND_HASH_FOREACH_END();
+ } ZEND_HASH_FILL_END();
+
+ if (pad_size > 0) {
+ ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
+ for (i = 0; i < num_pads; i++) {
+ ZEND_HASH_FILL_ADD(pad_value);
+ }
+ } ZEND_HASH_FILL_END();
}
- if (key) {
- zend_hash_add_new(Z_ARRVAL_P(return_value), key, value);
- } else {
- zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), value);
+ } else {
+ if (pad_size < 0) {
+ for (i = 0; i < num_pads; i++) {
+ zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), pad_value);
+ }
}
- } ZEND_HASH_FOREACH_END();
- if (pad_size > 0) {
- for (i = 0; i < num_pads; i++) {
- zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), pad_value);
+ ZEND_HASH_FOREACH_STR_KEY_VAL_IND(Z_ARRVAL_P(input), key, value) {
+ Z_TRY_ADDREF_P(value);
+ if (key) {
+ zend_hash_add_new(Z_ARRVAL_P(return_value), key, value);
+ } else {
+ zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), value);
+ }
+ } ZEND_HASH_FOREACH_END();
+
+ if (pad_size > 0) {
+ for (i = 0; i < num_pads; i++) {
+ zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), pad_value);
+ }
}
}
}
@@ -4820,7 +4914,7 @@ PHP_FUNCTION(array_multisort)
/* Make sure the arrays are of the same size. */
array_size = zend_hash_num_elements(Z_ARRVAL_P(arrays[0]));
for (i = 0; i < num_arrays; i++) {
- if (zend_hash_num_elements(Z_ARRVAL_P(arrays[i])) != array_size) {
+ if (zend_hash_num_elements(Z_ARRVAL_P(arrays[i])) != (uint32_t)array_size) {
php_error_docref(NULL, E_WARNING, "Array sizes are inconsistent");
MULTISORT_ABORT;
}
@@ -4855,10 +4949,9 @@ PHP_FUNCTION(array_multisort)
}
/* Do the actual sort magic - bada-bim, bada-boom. */
- zend_qsort(indirect, array_size, sizeof(Bucket *), php_multisort_compare, (swap_func_t)array_bucket_p_sawp);
+ zend_sort(indirect, array_size, sizeof(Bucket *), php_multisort_compare, (swap_func_t)array_bucket_p_sawp);
/* Restructure the arrays based on sorted indirect - this is mostly taken from zend_hash_sort() function. */
- HANDLE_BLOCK_INTERRUPTIONS();
for (i = 0; i < num_arrays; i++) {
int repack;
@@ -4882,7 +4975,6 @@ PHP_FUNCTION(array_multisort)
zend_hash_rehash(hash);
}
}
- HANDLE_UNBLOCK_INTERRUPTIONS();
/* Clean up. */
for (i = 0; i < array_size; i++) {
@@ -4900,10 +4992,15 @@ PHP_FUNCTION(array_multisort)
PHP_FUNCTION(array_rand)
{
zval *input;
- zend_long randval, num_req = 1;
- int num_avail;
+ zend_long num_req = 1;
zend_string *string_key;
zend_ulong num_key;
+ int i;
+ int num_avail;
+ zend_bitset bitset;
+ int negative_bitset = 0;
+ uint32_t bitset_len;
+ ALLOCA_FLAG(use_heap)
if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|l", &input, &num_req) == FAILURE) {
return;
@@ -4911,46 +5008,92 @@ PHP_FUNCTION(array_rand)
num_avail = zend_hash_num_elements(Z_ARRVAL_P(input));
- if (ZEND_NUM_ARGS() > 1) {
- if (num_req <= 0 || num_req > num_avail) {
- php_error_docref(NULL, E_WARNING, "Second argument has to be between 1 and the number of elements in the array");
- return;
+ if (num_avail == 0) {
+ php_error_docref(NULL, E_WARNING, "Array is empty");
+ return;
+ }
+
+ if (num_req == 1) {
+ HashTable *ht = Z_ARRVAL_P(input);
+
+ if (num_avail < ht->nNumUsed - (ht->nNumUsed>>1)) {
+ /* If less than 1/2 of elements are used, don't sample. Instead search for a
+ * specific offset using linear scan. */
+ zend_long i = 0, randval = php_mt_rand_range(0, num_avail - 1);
+ ZEND_HASH_FOREACH_KEY(Z_ARRVAL_P(input), num_key, string_key) {
+ if (i == randval) {
+ if (string_key) {
+ RETURN_STR_COPY(string_key);
+ } else {
+ RETURN_LONG(num_key);
+ }
+ }
+ i++;
+ } ZEND_HASH_FOREACH_END();
}
+
+ /* Sample random buckets until we hit one that is not empty.
+ * The worst case probability of hitting an empty element is 1-1/2. The worst case
+ * probability of hitting N empty elements in a row is (1-1/2)**N.
+ * For N=10 this becomes smaller than 0.1%. */
+ do {
+ zend_long randval = php_mt_rand_range(0, ht->nNumUsed - 1);
+ Bucket *bucket = &ht->arData[randval];
+ if (!Z_ISUNDEF(bucket->val)) {
+ if (bucket->key) {
+ RETURN_STR_COPY(bucket->key);
+ } else {
+ RETURN_LONG(bucket->h);
+ }
+ }
+ } while (1);
+ }
+
+ if (num_req <= 0 || num_req > num_avail) {
+ php_error_docref(NULL, E_WARNING, "Second argument has to be between 1 and the number of elements in the array");
+ return;
}
/* Make the return value an array only if we need to pass back more than one result. */
- if (num_req > 1) {
- array_init_size(return_value, (uint32_t)num_req);
+ array_init_size(return_value, (uint32_t)num_req);
+ if (num_req > (num_avail >> 1)) {
+ negative_bitset = 1;
+ num_req = num_avail - num_req;
}
- /* We can't use zend_hash_index_find() because the array may have string keys or gaps. */
- ZEND_HASH_FOREACH_KEY(Z_ARRVAL_P(input), num_key, string_key) {
- if (!num_req) {
- break;
- }
+ bitset_len = zend_bitset_len(num_avail);
+ bitset = ZEND_BITSET_ALLOCA(bitset_len, use_heap);
+ zend_bitset_clear(bitset, bitset_len);
- randval = php_rand();
+ i = num_req;
+ while (i) {
+ zend_long randval = php_mt_rand_range(0, num_avail - 1);
+ if (!zend_bitset_in(bitset, randval)) {
+ zend_bitset_incl(bitset, randval);
+ i--;
+ }
+ }
+ /* i = 0; */
- if ((double) (randval / (PHP_RAND_MAX + 1.0)) < (double) num_req / (double) num_avail) {
- /* If we are returning a single result, just do it. */
- if (Z_TYPE_P(return_value) != IS_ARRAY) {
- if (string_key) {
- RETURN_STR_COPY(string_key);
- } else {
- RETURN_LONG(num_key);
- }
- } else {
- /* Append the result to the return value. */
+ zend_hash_real_init(Z_ARRVAL_P(return_value), 1);
+ ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
+ zval zv;
+ /* We can't use zend_hash_index_find()
+ * because the array may have string keys or gaps. */
+ ZEND_HASH_FOREACH_KEY(Z_ARRVAL_P(input), num_key, string_key) {
+ if (zend_bitset_in(bitset, i) ^ negative_bitset) {
if (string_key) {
- add_next_index_str(return_value, zend_string_copy(string_key));
+ ZVAL_STR_COPY(&zv, string_key);
} else {
- add_next_index_long(return_value, num_key);
+ ZVAL_LONG(&zv, num_key);
}
+ ZEND_HASH_FILL_ADD(&zv);
}
- num_req--;
- }
- num_avail--;
- } ZEND_HASH_FOREACH_END();
+ i++;
+ } ZEND_HASH_FOREACH_END();
+ } ZEND_HASH_FILL_END();
+
+ free_alloca(bitset, use_heap);
}
/* }}} */
diff --git a/ext/standard/assert.c b/ext/standard/assert.c
index 02e330cba5..f9de154233 100644
--- a/ext/standard/assert.c
+++ b/ext/standard/assert.c
@@ -164,7 +164,10 @@ PHP_FUNCTION(assert)
if (Z_TYPE_P(assertion) == IS_STRING) {
zval retval;
int old_error_reporting = 0; /* shut up gcc! */
- zend_class_entry *orig_scope = EG(scope);
+
+ if (zend_forbid_dynamic_call("assert() with string argument") == FAILURE) {
+ RETURN_FALSE;
+ }
myeval = Z_STRVAL_P(assertion);
@@ -177,10 +180,10 @@ PHP_FUNCTION(assert)
if (zend_eval_stringl(myeval, Z_STRLEN_P(assertion), &retval, compiled_string_description) == FAILURE) {
efree(compiled_string_description);
if (!description) {
- php_error_docref(NULL, E_RECOVERABLE_ERROR, "Failure evaluating code: %s%s", PHP_EOL, myeval);
+ zend_throw_error(NULL, "Failure evaluating code: %s%s", PHP_EOL, myeval);
} else {
zend_string *str = zval_get_string(description);
- php_error_docref(NULL, E_RECOVERABLE_ERROR, "Failure evaluating code: %s%s:\"%s\"", PHP_EOL, ZSTR_VAL(str), myeval);
+ zend_throw_error(NULL, "Failure evaluating code: %s%s:\"%s\"", PHP_EOL, ZSTR_VAL(str), myeval);
zend_string_release(str);
}
if (ASSERTG(bail)) {
@@ -194,8 +197,6 @@ PHP_FUNCTION(assert)
EG(error_reporting) = old_error_reporting;
}
- EG(scope) = orig_scope;
-
convert_to_boolean(&retval);
val = Z_TYPE(retval) == IS_TRUE;
} else {
@@ -369,7 +370,7 @@ PHP_FUNCTION(assert_options)
break;
default:
- php_error_docref(NULL, E_WARNING, "Unknown value %pd", what);
+ php_error_docref(NULL, E_WARNING, "Unknown value " ZEND_LONG_FMT, what);
break;
}
diff --git a/ext/standard/base64.c b/ext/standard/base64.c
index 86f8a82689..2485418e8f 100644
--- a/ext/standard/base64.c
+++ b/ext/standard/base64.c
@@ -144,17 +144,7 @@ PHPAPI zend_string *php_base64_decode_ex(const unsigned char *str, size_t length
/* run through the whole string, converting as we go */
while (length-- > 0) {
ch = *current++;
- /* stop on null byte in non-strict mode (FIXME: is this really desired?) */
- if (ch == 0 && !strict) {
- break;
- }
if (ch == base64_pad) {
- /* fail if the padding character is second in a group (like V===) */
- /* FIXME: why do we still allow invalid padding in other places in the middle of the string? */
- if (i % 4 == 1) {
- zend_string_free(result);
- return NULL;
- }
padding++;
continue;
}
@@ -172,8 +162,7 @@ PHPAPI zend_string *php_base64_decode_ex(const unsigned char *str, size_t length
}
/* fail on bad characters or if any data follows padding */
if (ch == -2 || padding) {
- zend_string_free(result);
- return NULL;
+ goto fail;
}
}
@@ -195,11 +184,24 @@ PHPAPI zend_string *php_base64_decode_ex(const unsigned char *str, size_t length
}
i++;
}
+ /* fail if the input is truncated (only one char in last group) */
+ if (strict && i % 4 == 1) {
+ goto fail;
+ }
+ /* fail if the padding length is wrong (not VV==, VVV=), but accept zero padding
+ * RFC 4648: "In some circumstances, the use of padding [--] is not required" */
+ if (strict && padding && (padding > 2 || (i + padding) % 4 != 0)) {
+ goto fail;
+ }
ZSTR_LEN(result) = j;
ZSTR_VAL(result)[ZSTR_LEN(result)] = '\0';
return result;
+
+fail:
+ zend_string_free(result);
+ return NULL;
}
/* }}} */
diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c
index 7671cabbb5..ca615c03b9 100644
--- a/ext/standard/basic_functions.c
+++ b/ext/standard/basic_functions.c
@@ -23,6 +23,7 @@
#include "php_streams.h"
#include "php_main.h"
#include "php_globals.h"
+#include "php_variables.h"
#include "php_ini.h"
#include "php_standard.h"
#include "php_math.h"
@@ -34,6 +35,7 @@
#include "zend_operators.h"
#include "ext/standard/php_dns.h"
#include "ext/standard/php_uuencode.h"
+#include "ext/standard/php_mt_rand.h"
#ifdef PHP_WIN32
#include "win32/php_win32_globals.h"
@@ -646,6 +648,7 @@ ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_getopt, 0, 0, 1)
ZEND_ARG_INFO(0, options)
ZEND_ARG_INFO(0, opts) /* ARRAY_INFO(0, opts, 1) */
+ ZEND_ARG_INFO(1, optind)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(arginfo_flush, 0)
@@ -873,12 +876,10 @@ ZEND_END_ARG_INFO()
/* }}} */
/* {{{ crypt.c */
-#if HAVE_CRYPT
ZEND_BEGIN_ARG_INFO_EX(arginfo_crypt, 0, 0, 1)
ZEND_ARG_INFO(0, str)
ZEND_ARG_INFO(0, salt)
ZEND_END_ARG_INFO()
-#endif
/* }}} */
/* {{{ cyr_convert.c */
ZEND_BEGIN_ARG_INFO(arginfo_convert_cyr_string, 0)
@@ -1811,9 +1812,10 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_pack, 0, 0, 2)
ZEND_ARG_VARIADIC_INFO(0, args)
ZEND_END_ARG_INFO()
-ZEND_BEGIN_ARG_INFO(arginfo_unpack, 0)
+ZEND_BEGIN_ARG_INFO_EX(arginfo_unpack, 0, 0, 2)
ZEND_ARG_INFO(0, format)
ZEND_ARG_INFO(0, input)
+ ZEND_ARG_INFO(0, offset)
ZEND_END_ARG_INFO()
/* }}} */
/* {{{ pageinfo.c */
@@ -1886,18 +1888,10 @@ ZEND_BEGIN_ARG_INFO(arginfo_quoted_printable_encode, 0)
ZEND_ARG_INFO(0, str)
ZEND_END_ARG_INFO()
/* }}} */
-/* {{{ rand.c */
-ZEND_BEGIN_ARG_INFO_EX(arginfo_srand, 0, 0, 0)
- ZEND_ARG_INFO(0, seed)
-ZEND_END_ARG_INFO()
-
+/* {{{ mt_rand.c */
ZEND_BEGIN_ARG_INFO_EX(arginfo_mt_srand, 0, 0, 0)
ZEND_ARG_INFO(0, seed)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_rand, 0, 0, 0)
- ZEND_ARG_INFO(0, min)
- ZEND_ARG_INFO(0, max)
+ ZEND_ARG_INFO(0, mode)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_mt_rand, 0, 0, 0)
@@ -1905,9 +1899,6 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_mt_rand, 0, 0, 0)
ZEND_ARG_INFO(0, max)
ZEND_END_ARG_INFO()
-ZEND_BEGIN_ARG_INFO(arginfo_getrandmax, 0)
-ZEND_END_ARG_INFO()
-
ZEND_BEGIN_ARG_INFO(arginfo_mt_getrandmax, 0)
ZEND_END_ARG_INFO()
/* }}} */
@@ -2567,6 +2558,10 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_is_callable, 0, 0, 1)
ZEND_ARG_INFO(0, syntax_only)
ZEND_ARG_INFO(1, callable_name)
ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_is_iterable, 0, 0, 1)
+ ZEND_ARG_INFO(0, var)
+ZEND_END_ARG_INFO()
/* }}} */
/* {{{ uniqid.c */
#ifdef HAVE_GETTIMEOFDAY
@@ -2601,6 +2596,7 @@ ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_get_headers, 0, 0, 1)
ZEND_ARG_INFO(0, url)
ZEND_ARG_INFO(0, format)
+ ZEND_ARG_INFO(0, context)
ZEND_END_ARG_INFO()
/* }}} */
/* {{{ user_filters.c */
@@ -2678,6 +2674,25 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_version_compare, 0, 0, 2)
ZEND_ARG_INFO(0, oper)
ZEND_END_ARG_INFO()
/* }}} */
+/* {{{ win32/codepage.c */
+#ifdef PHP_WIN32
+ZEND_BEGIN_ARG_INFO_EX(arginfo_sapi_windows_cp_set, 0, 0, 1)
+ ZEND_ARG_TYPE_INFO(0, code_page, IS_LONG, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_sapi_windows_cp_get, 0, 0, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_sapi_windows_cp_is_utf8, 0, 0, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_sapi_windows_cp_conv, 0, 0, 3)
+ ZEND_ARG_INFO(0, in_codepage)
+ ZEND_ARG_INFO(0, out_codepage)
+ ZEND_ARG_TYPE_INFO(0, subject, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+#endif
+/* }}} */
/* }}} */
const zend_function_entry basic_functions[] = { /* {{{ */
@@ -2839,10 +2854,10 @@ const zend_function_entry basic_functions[] = { /* {{{ */
PHP_FE(proc_nice, arginfo_proc_nice)
#endif
- PHP_FE(rand, arginfo_rand)
- PHP_FE(srand, arginfo_srand)
- PHP_FE(getrandmax, arginfo_getrandmax)
- PHP_FE(mt_rand, arginfo_mt_rand)
+ PHP_FE(rand, arginfo_mt_rand)
+ PHP_FALIAS(srand, mt_srand, arginfo_mt_srand)
+ PHP_FALIAS(getrandmax, mt_getrandmax, arginfo_mt_getrandmax)
+ PHP_FE(mt_rand, arginfo_mt_rand)
PHP_FE(mt_srand, arginfo_mt_srand)
PHP_FE(mt_getrandmax, arginfo_mt_getrandmax)
@@ -3063,6 +3078,7 @@ const zend_function_entry basic_functions[] = { /* {{{ */
PHP_FE(is_object, arginfo_is_object)
PHP_FE(is_scalar, arginfo_is_scalar)
PHP_FE(is_callable, arginfo_is_callable)
+ PHP_FE(is_iterable, arginfo_is_iterable)
/* functions from file.c */
PHP_FE(pclose, arginfo_pclose)
@@ -3171,10 +3187,8 @@ const zend_function_entry basic_functions[] = { /* {{{ */
/* functions from browscap.c */
PHP_FE(get_browser, arginfo_get_browser)
-#if HAVE_CRYPT
/* functions from crypt.c */
PHP_FE(crypt, arginfo_crypt)
-#endif
/* functions from dir.c */
PHP_FE(opendir, arginfo_opendir)
@@ -3373,6 +3387,12 @@ const zend_function_entry basic_functions[] = { /* {{{ */
PHP_FE(sys_get_temp_dir, arginfo_sys_get_temp_dir)
+#ifdef PHP_WIN32
+ PHP_FE(sapi_windows_cp_set, arginfo_sapi_windows_cp_set)
+ PHP_FE(sapi_windows_cp_get, arginfo_sapi_windows_cp_get)
+ PHP_FE(sapi_windows_cp_is_utf8, arginfo_sapi_windows_cp_is_utf8)
+ PHP_FE(sapi_windows_cp_conv, arginfo_sapi_windows_cp_conv)
+#endif
PHP_FE_END
};
/* }}} */
@@ -3452,8 +3472,8 @@ static void php_putenv_destructor(zval *zv) /* {{{ */
static void basic_globals_ctor(php_basic_globals *basic_globals_p) /* {{{ */
{
- BG(rand_is_seeded) = 0;
BG(mt_rand_is_seeded) = 0;
+ BG(mt_rand_mode) = MT_RAND_MT19937;
BG(umask) = -1;
BG(next) = NULL;
BG(left) = -1;
@@ -3464,7 +3484,14 @@ static void basic_globals_ctor(php_basic_globals *basic_globals_p) /* {{{ */
memset(&BG(serialize), 0, sizeof(BG(serialize)));
memset(&BG(unserialize), 0, sizeof(BG(unserialize)));
- memset(&BG(url_adapt_state_ex), 0, sizeof(BG(url_adapt_state_ex)));
+ memset(&BG(url_adapt_session_ex), 0, sizeof(BG(url_adapt_session_ex)));
+ memset(&BG(url_adapt_output_ex), 0, sizeof(BG(url_adapt_output_ex)));
+
+ BG(url_adapt_session_ex).type = 1;
+ BG(url_adapt_output_ex).type = 0;
+
+ zend_hash_init(&BG(url_adapt_session_hosts_ht), 0, NULL, NULL, 1);
+ zend_hash_init(&BG(url_adapt_output_hosts_ht), 0, NULL, NULL, 1);
#if defined(_REENTRANT) && defined(HAVE_MBRLEN) && defined(HAVE_MBSTATE_T)
memset(&BG(mblen_state), 0, sizeof(BG(mblen_state)));
@@ -3478,10 +3505,17 @@ static void basic_globals_ctor(php_basic_globals *basic_globals_p) /* {{{ */
static void basic_globals_dtor(php_basic_globals *basic_globals_p) /* {{{ */
{
- if (basic_globals_p->url_adapt_state_ex.tags) {
- zend_hash_destroy(basic_globals_p->url_adapt_state_ex.tags);
- free(basic_globals_p->url_adapt_state_ex.tags);
+ if (basic_globals_p->url_adapt_session_ex.tags) {
+ zend_hash_destroy(basic_globals_p->url_adapt_session_ex.tags);
+ free(basic_globals_p->url_adapt_session_ex.tags);
}
+ if (basic_globals_p->url_adapt_output_ex.tags) {
+ zend_hash_destroy(basic_globals_p->url_adapt_output_ex.tags);
+ free(basic_globals_p->url_adapt_output_ex.tags);
+ }
+
+ zend_hash_destroy(&basic_globals_p->url_adapt_session_hosts_ht);
+ zend_hash_destroy(&basic_globals_p->url_adapt_output_hosts_ht);
}
/* }}} */
@@ -3494,8 +3528,8 @@ PHPAPI double php_get_nan(void) /* {{{ */
return HUGE_VAL + -HUGE_VAL;
#elif defined(__i386__) || defined(_X86_) || defined(ALPHA) || defined(_ALPHA) || defined(__alpha)
double val = 0.0;
- ((php_uint32*)&val)[1] = PHP_DOUBLE_QUIET_NAN_HIGH;
- ((php_uint32*)&val)[0] = 0;
+ ((uint32_t*)&val)[1] = PHP_DOUBLE_QUIET_NAN_HIGH;
+ ((uint32_t*)&val)[0] = 0;
return val;
#elif HAVE_ATOF_ACCEPTS_NAN
return atof("NAN");
@@ -3511,8 +3545,8 @@ PHPAPI double php_get_inf(void) /* {{{ */
return HUGE_VAL;
#elif defined(__i386__) || defined(_X86_) || defined(ALPHA) || defined(_ALPHA) || defined(__alpha)
double val = 0.0;
- ((php_uint32*)&val)[1] = PHP_DOUBLE_INFINITY_HIGH;
- ((php_uint32*)&val)[0] = 0;
+ ((uint32_t*)&val)[1] = PHP_DOUBLE_INFINITY_HIGH;
+ ((uint32_t*)&val)[0] = 0;
return val;
#elif HAVE_ATOF_ACCEPTS_INF
return atof("INF");
@@ -3635,6 +3669,7 @@ PHP_MINIT_FUNCTION(basic) /* {{{ */
BASIC_MINIT_SUBMODULE(standard_filters)
BASIC_MINIT_SUBMODULE(user_filters)
BASIC_MINIT_SUBMODULE(password)
+ BASIC_MINIT_SUBMODULE(mt_rand)
#if defined(HAVE_LOCALECONV) && defined(ZTS)
BASIC_MINIT_SUBMODULE(localeconv)
@@ -3644,10 +3679,7 @@ PHP_MINIT_FUNCTION(basic) /* {{{ */
BASIC_MINIT_SUBMODULE(nl_langinfo)
#endif
-#if HAVE_CRYPT
BASIC_MINIT_SUBMODULE(crypt)
-#endif
-
BASIC_MINIT_SUBMODULE(lcg)
BASIC_MINIT_SUBMODULE(dir)
@@ -3716,10 +3748,7 @@ PHP_MSHUTDOWN_FUNCTION(basic) /* {{{ */
#if defined(HAVE_LOCALECONV) && defined(ZTS)
BASIC_MSHUTDOWN_SUBMODULE(localeconv)
#endif
-#if HAVE_CRYPT
BASIC_MSHUTDOWN_SUBMODULE(crypt)
-#endif
-
BASIC_MSHUTDOWN_SUBMODULE(random)
zend_hash_destroy(&basic_submodules);
@@ -3782,6 +3811,8 @@ PHP_RSHUTDOWN_FUNCTION(basic) /* {{{ */
zend_hash_destroy(&BG(putenv_ht));
#endif
+ BG(mt_rand_is_seeded) = 0;
+
if (BG(umask) != -1) {
umask(BG(umask));
}
@@ -3845,22 +3876,25 @@ PHP_FUNCTION(constant)
{
zend_string *const_name;
zval *c;
+ zend_class_entry *scope;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &const_name) == FAILURE) {
return;
}
- c = zend_get_constant_ex(const_name, NULL, ZEND_FETCH_CLASS_SILENT);
+ scope = zend_get_executed_scope();
+ c = zend_get_constant_ex(const_name, scope, ZEND_FETCH_CLASS_SILENT);
if (c) {
- ZVAL_COPY_VALUE(return_value, c);
+ ZVAL_DUP(return_value, c);
if (Z_CONSTANT_P(return_value)) {
- if (UNEXPECTED(zval_update_constant_ex(return_value, 1, NULL) != SUCCESS)) {
+ if (UNEXPECTED(zval_update_constant_ex(return_value, scope) != SUCCESS)) {
return;
}
}
- zval_copy_ctor(return_value);
} else {
- php_error_docref(NULL, E_WARNING, "Couldn't find constant %s", ZSTR_VAL(const_name));
+ if (!EG(exception)) {
+ php_error_docref(NULL, E_WARNING, "Couldn't find constant %s", ZSTR_VAL(const_name));
+ }
RETURN_NULL();
}
}
@@ -3980,22 +4014,17 @@ PHP_FUNCTION(ip2long)
Converts an (IPv4) Internet network address into a string in Internet standard dotted format */
PHP_FUNCTION(long2ip)
{
- /* "It's a long but it's not, PHP ints are signed */
- char *ip;
- size_t ip_len;
- uint32_t n;
+ zend_ulong ip;
struct in_addr myaddr;
#ifdef HAVE_INET_PTON
char str[40];
#endif
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &ip, &ip_len) == FAILURE) {
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &ip) == FAILURE) {
return;
}
- n = strtoul(ip, NULL, 0);
-
- myaddr.s_addr = htonl(n);
+ myaddr.s_addr = htonl(ip);
#ifdef HAVE_INET_PTON
if (inet_ntop(AF_INET, &myaddr, str, sizeof(str))) {
RETURN_STRING(str);
@@ -4012,18 +4041,25 @@ PHP_FUNCTION(long2ip)
* System Functions *
********************/
-/* {{{ proto string getenv(string varname[, bool local_only])
- Get the value of an environment variable */
+/* {{{ proto string getenv(string varname[, bool local_only]
+ Get the value of an environment variable or every available environment variable
+ if no varname is present */
PHP_FUNCTION(getenv)
{
- char *ptr, *str;
+ char *ptr, *str = NULL;
size_t str_len;
zend_bool local_only = 0;
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|b", &str, &str_len, &local_only) == FAILURE) {
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "|sb", &str, &str_len, &local_only) == FAILURE) {
RETURN_FALSE;
}
+ if (!str) {
+ array_init(return_value);
+ php_import_environment_variables(return_value);
+ return;
+ }
+
if (!local_only) {
/* SAPI method returns an emalloc()'d string */
ptr = sapi_getenv(str, str_len);
@@ -4250,7 +4286,7 @@ static int parse_opts(char * opts, opt_struct ** result)
}
/* }}} */
-/* {{{ proto array getopt(string options [, array longopts])
+/* {{{ proto array getopt(string options [, array longopts [, int &optind]])
Get options from the command line argument list */
PHP_FUNCTION(getopt)
{
@@ -4262,13 +4298,20 @@ PHP_FUNCTION(getopt)
char *php_optarg = NULL;
int php_optind = 1;
zval val, *args = NULL, *p_longopts = NULL;
+ zval *zoptind = NULL;
int optname_len = 0;
opt_struct *opts, *orig_opts;
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|a", &options, &options_len, &p_longopts) == FAILURE) {
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|az/", &options, &options_len, &p_longopts, &zoptind) == FAILURE) {
RETURN_FALSE;
}
+ /* Init zoptind to 1 */
+ if (zoptind) {
+ zval_dtor(zoptind);
+ ZVAL_LONG(zoptind, 1);
+ }
+
/* Get argv from the global symbol table. We calculate argc ourselves
* in order to be on the safe side, even though it is also available
* from the symbol table. */
@@ -4410,6 +4453,11 @@ PHP_FUNCTION(getopt)
php_optarg = NULL;
}
+ /* Set zoptind to php_optind */
+ if (zoptind) {
+ ZVAL_LONG(zoptind, php_optind);
+ }
+
free_longopts(orig_opts);
efree(orig_opts);
free_argv(argv, argc);
@@ -4712,14 +4760,14 @@ PHPAPI int _php_error_log_ex(int opt_err, char *message, size_t message_len, cha
case 4: /* send to SAPI */
if (sapi_module.log_message) {
- sapi_module.log_message(message);
+ sapi_module.log_message(message, -1);
} else {
return FAILURE;
}
break;
default:
- php_log_err(message);
+ php_log_err_with_severity(message, LOG_NOTICE);
break;
}
return SUCCESS;
@@ -4784,6 +4832,9 @@ PHP_FUNCTION(call_user_func)
fci.retval = &retval;
if (zend_call_function(&fci, &fci_cache) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) {
+ if (Z_ISREF(retval)) {
+ zend_unwrap_reference(&retval);
+ }
ZVAL_COPY_VALUE(return_value, &retval);
}
}
@@ -4807,6 +4858,9 @@ PHP_FUNCTION(call_user_func_array)
fci.retval = &retval;
if (zend_call_function(&fci, &fci_cache) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) {
+ if (Z_ISREF(retval)) {
+ zend_unwrap_reference(&retval);
+ }
ZVAL_COPY_VALUE(return_value, &retval);
}
@@ -4828,7 +4882,8 @@ PHP_FUNCTION(forward_static_call)
}
if (!EX(prev_execute_data)->func->common.scope) {
- zend_error(E_ERROR, "Cannot call forward_static_call() when no class scope is active");
+ zend_throw_error(NULL, "Cannot call forward_static_call() when no class scope is active");
+ return;
}
fci.retval = &retval;
@@ -4840,6 +4895,9 @@ PHP_FUNCTION(forward_static_call)
}
if (zend_call_function(&fci, &fci_cache) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) {
+ if (Z_ISREF(retval)) {
+ zend_unwrap_reference(&retval);
+ }
ZVAL_COPY_VALUE(return_value, &retval);
}
}
@@ -4868,6 +4926,9 @@ PHP_FUNCTION(forward_static_call_array)
}
if (zend_call_function(&fci, &fci_cache) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) {
+ if (Z_ISREF(retval)) {
+ zend_unwrap_reference(&retval);
+ }
ZVAL_COPY_VALUE(return_value, &retval);
}
@@ -5118,7 +5179,8 @@ ZEND_API void php_get_highlight_struct(zend_syntax_highlighter_ini *syntax_highl
PHP_FUNCTION(highlight_file)
{
char *filename;
- size_t filename_len, ret;
+ size_t filename_len;
+ int ret;
zend_syntax_highlighter_ini syntax_highlighter_ini;
zend_bool i = 0;
@@ -5161,7 +5223,7 @@ PHP_FUNCTION(php_strip_whitespace)
char *filename;
size_t filename_len;
zend_lex_state original_lex_state;
- zend_file_handle file_handle = {{0}};
+ zend_file_handle file_handle;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "p", &filename, &filename_len) == FAILURE) {
RETURN_FALSE;
@@ -5169,6 +5231,7 @@ PHP_FUNCTION(php_strip_whitespace)
php_output_start_default();
+ memset(&file_handle, 0, sizeof(file_handle));
file_handle.type = ZEND_HANDLE_FILENAME;
file_handle.filename = filename;
file_handle.free_filename = 0;
@@ -5480,15 +5543,9 @@ PHP_FUNCTION(print_r)
}
if (do_return) {
- php_output_start_default();
- }
-
- zend_print_zval_r(var, 0);
-
- if (do_return) {
- php_output_get_contents(return_value);
- php_output_discard();
+ RETURN_STR(zend_print_zval_r_to_str(var, 0));
} else {
+ zend_print_zval_r(var, 0);
RETURN_TRUE;
}
}
diff --git a/ext/standard/basic_functions.h b/ext/standard/basic_functions.h
index 43b80054cd..cfca0e03ad 100644
--- a/ext/standard/basic_functions.h
+++ b/ext/standard/basic_functions.h
@@ -133,6 +133,13 @@ PHP_FUNCTION(parse_ini_string);
PHP_FUNCTION(config_get_hash);
#endif
+#if defined(PHP_WIN32)
+PHP_FUNCTION(sapi_windows_cp_set);
+PHP_FUNCTION(sapi_windows_cp_get);
+PHP_FUNCTION(sapi_windows_cp_is_utf8);
+PHP_FUNCTION(sapi_windows_cp_conv);
+#endif
+
PHP_FUNCTION(str_rot13);
PHP_FUNCTION(stream_get_filters);
PHP_FUNCTION(stream_filter_register);
@@ -149,20 +156,12 @@ PHPAPI int _php_error_log(int opt_err, char *message, char *opt, char *headers);
PHPAPI int _php_error_log_ex(int opt_err, char *message, size_t message_len, char *opt, char *headers);
PHPAPI int php_prefix_varname(zval *result, zval *prefix, char *var_name, size_t var_name_len, zend_bool add_underscore);
-#if SIZEOF_INT == 4
-/* Most 32-bit and 64-bit systems have 32-bit ints */
-typedef unsigned int php_uint32;
-typedef signed int php_int32;
-#elif SIZEOF_LONG == 4
-/* 16-bit systems? */
-typedef unsigned long php_uint32;
-typedef signed long php_int32;
-#else
-#error Need type which holds 32 bits
-#endif
-
#define MT_N (624)
+/* Deprecated type aliases -- use the standard types instead */
+typedef uint32_t php_uint32;
+typedef int32_t php_int32;
+
typedef struct _php_basic_globals {
HashTable *user_shutdown_function_names;
HashTable putenv_ht;
@@ -192,15 +191,13 @@ typedef struct _php_basic_globals {
char *CurrentStatFile, *CurrentLStatFile;
php_stream_statbuf ssb, lssb;
- /* rand.c */
- php_uint32 state[MT_N+1]; /* state vector + 1 extra to not violate ANSI C */
- php_uint32 *next; /* next random value is computed from here */
+ /* mt_rand.c */
+ uint32_t state[MT_N+1]; /* state vector + 1 extra to not violate ANSI C */
+ uint32_t *next; /* next random value is computed from here */
int left; /* can *next++ this many times before reloading */
- unsigned int rand_seed; /* Seed for rand(), in ts version */
-
- zend_bool rand_is_seeded; /* Whether rand() has been seeded */
zend_bool mt_rand_is_seeded; /* Whether mt_rand() has been seeded */
+ zend_long mt_rand_mode;
/* syslog.c */
char *syslog_device;
@@ -218,7 +215,10 @@ typedef struct _php_basic_globals {
} unserialize;
/* url_scanner_ex.re */
- url_adapt_state_ex_t url_adapt_state_ex;
+ url_adapt_state_ex_t url_adapt_session_ex;
+ HashTable url_adapt_session_hosts_ht;
+ url_adapt_state_ex_t url_adapt_output_ex;
+ HashTable url_adapt_output_hosts_ht;
#ifdef HAVE_MMAP
void *mmap_file;
diff --git a/ext/standard/browscap.c b/ext/standard/browscap.c
index 1a8d0bf86a..863c128ec9 100644
--- a/ext/standard/browscap.c
+++ b/ext/standard/browscap.c
@@ -153,7 +153,7 @@ static size_t browscap_compute_regex_len(zend_string *pattern) {
static zend_string *browscap_convert_pattern(zend_string *pattern, int persistent) /* {{{ */
{
- int i, j=0;
+ size_t i, j=0;
char *t;
zend_string *res;
char *lc_pattern;
@@ -392,7 +392,7 @@ static void php_browscap_parser_cb(zval *arg1, zval *arg2, zval *arg3, int callb
static int browscap_read_file(char *filename, browser_data *browdata, int persistent) /* {{{ */
{
- zend_file_handle fh = {{0}};
+ zend_file_handle fh;
browscap_parser_ctx ctx = {0};
if (filename == NULL || filename[0] == '\0') {
diff --git a/ext/standard/config.m4 b/ext/standard/config.m4
index f9541badf9..61eefdc339 100644
--- a/ext/standard/config.m4
+++ b/ext/standard/config.m4
@@ -242,7 +242,7 @@ int main() {
dnl
dnl If one of them is missing, use our own implementation, portable code is then possible
dnl
-if test "$ac_cv_crypt_blowfish" = "no" || test "$ac_cv_crypt_des" = "no" || test "$ac_cv_crypt_ext_des" = "no" || test "x$php_crypt_r" = "x0"; then
+if test "$ac_cv_crypt_blowfish" = "no" || test "$ac_cv_crypt_des" = "no" || test "$ac_cv_crypt_ext_des" = "no" || test "$ac_cv_crypt_md5" = "no" || test "$ac_cv_crypt_sha512" = "no" || test "$ac_cv_crypt_sha256" = "no" || test "x$php_crypt_r" = "x0"; then
dnl
dnl Check for __alignof__ support in the compiler
@@ -260,90 +260,29 @@ if test "$ac_cv_crypt_blowfish" = "no" || test "$ac_cv_crypt_des" = "no" || test
AC_DEFINE([HAVE_ALIGNOF], 1, [whether the compiler supports __alignof__])
fi
- dnl
- dnl Check for __attribute__ ((__aligned__)) support in the compiler
- dnl
- AC_CACHE_CHECK(whether the compiler supports aligned attribute, ac_cv_attribute_aligned,[
- AC_TRY_COMPILE([
- ],[
- unsigned char test[32] __attribute__ ((__aligned__ (__alignof__ (int))));
- ],[
- ac_cv_attribute_aligned=yes
- ],[
- ac_cv_attribute_aligned=no
- ])])
- if test "$ac_cv_attribute_aligned" = "yes"; then
- AC_DEFINE([HAVE_ATTRIBUTE_ALIGNED], 1, [whether the compiler supports __attribute__ ((__aligned__))])
- fi
-
-
AC_DEFINE_UNQUOTED(PHP_USE_PHP_CRYPT_R, 1, [Whether PHP has to use its own crypt_r for blowfish, des, ext des and md5])
- AC_DEFINE_UNQUOTED(PHP_STD_DES_CRYPT, 1, [Whether the system supports standard DES salt])
- AC_DEFINE_UNQUOTED(PHP_BLOWFISH_CRYPT, 1, [Whether the system supports BlowFish salt])
- AC_DEFINE_UNQUOTED(PHP_EXT_DES_CRYPT, 1, [Whether the system supports extended DES salt])
- AC_DEFINE_UNQUOTED(PHP_MD5_CRYPT, 1, [Whether the system supports MD5 salt])
- AC_DEFINE_UNQUOTED(PHP_SHA512_CRYPT, 1, [Whether the system supports SHA512 salt])
- AC_DEFINE_UNQUOTED(PHP_SHA256_CRYPT, 1, [Whether the system supports SHA256 salt])
PHP_ADD_SOURCES(PHP_EXT_DIR(standard), crypt_freesec.c crypt_blowfish.c crypt_sha512.c crypt_sha256.c php_crypt_r.c)
else
- if test "$ac_cv_crypt_des" = "yes"; then
- ac_result=1
- ac_crypt_des=1
- else
- ac_result=0
- ac_crypt_des=0
- fi
- AC_DEFINE_UNQUOTED(PHP_STD_DES_CRYPT, $ac_result, [Whether the system supports standard DES salt])
-
- if test "$ac_cv_crypt_blowfish" = "yes"; then
- ac_result=1
- ac_crypt_blowfish=1
- else
- ac_result=0
- ac_crypt_blowfish=0
- fi
- AC_DEFINE_UNQUOTED(PHP_BLOWFISH_CRYPT, $ac_result, [Whether the system supports BlowFish salt])
-
- if test "$ac_cv_crypt_ext_des" = "yes"; then
- ac_result=1
- ac_crypt_edes=1
- else
- ac_result=0
- ac_crypt_edes=0
- fi
- AC_DEFINE_UNQUOTED(PHP_EXT_DES_CRYPT, $ac_result, [Whether the system supports extended DES salt])
-
- if test "$ac_cv_crypt_md5" = "yes"; then
- ac_result=1
- ac_crypt_md5=1
- else
- ac_result=0
- ac_crypt_md5=0
- fi
- AC_DEFINE_UNQUOTED(PHP_MD5_CRYPT, $ac_result, [Whether the system supports MD5 salt])
-
- if test "$ac_cv_crypt_sha512" = "yes"; then
- ac_result=1
- ac_crypt_sha512=1
- else
- ac_result=0
- ac_crypt_sha512=0
- fi
- AC_DEFINE_UNQUOTED(PHP_SHA512_CRYPT, $ac_result, [Whether the system supports SHA512 salt])
-
- if test "$ac_cv_crypt_sha256" = "yes"; then
- ac_result=1
- ac_crypt_sha256=1
- else
- ac_result=0
- ac_crypt_sha256=0
- fi
- AC_DEFINE_UNQUOTED(PHP_SHA256_CRYPT, $ac_result, [Whether the system supports SHA256 salt])
-
AC_DEFINE_UNQUOTED(PHP_USE_PHP_CRYPT_R, 0, [Whether PHP has to use its own crypt_r for blowfish, des and ext des])
fi
+dnl
+dnl Check for __attribute__ ((__aligned__)) support in the compiler
+dnl
+AC_CACHE_CHECK(whether the compiler supports aligned attribute, ac_cv_attribute_aligned,[
+AC_TRY_COMPILE([
+],[
+ unsigned char test[32] __attribute__ ((__aligned__ (__alignof__ (int))));
+],[
+ ac_cv_attribute_aligned=yes
+],[
+ ac_cv_attribute_aligned=no
+])])
+if test "$ac_cv_attribute_aligned" = "yes"; then
+ AC_DEFINE([HAVE_ATTRIBUTE_ALIGNED], 1, [whether the compiler supports __attribute__ ((__aligned__))])
+fi
+
dnl
dnl Check for available functions
dnl
@@ -615,7 +554,7 @@ PHP_NEW_EXTENSION(standard, array.c base64.c basic_functions.c browscap.c crc32.
cyr_convert.c datetime.c dir.c dl.c dns.c exec.c file.c filestat.c \
flock_compat.c formatted_print.c fsock.c head.c html.c image.c \
info.c iptc.c lcg.c link.c mail.c math.c md5.c metaphone.c \
- microtime.c pack.c pageinfo.c quot_print.c rand.c \
+ microtime.c pack.c pageinfo.c quot_print.c rand.c mt_rand.c \
soundex.c string.c scanf.c syslog.c type.c uniqid.c url.c \
var.c versioning.c assert.c strnatcmp.c levenshtein.c \
incomplete_class.c url_scanner_ex.c ftp_fopen_wrapper.c \
diff --git a/ext/standard/config.w32 b/ext/standard/config.w32
index adff3d8c87..ee5c319aa7 100644
--- a/ext/standard/config.w32
+++ b/ext/standard/config.w32
@@ -14,7 +14,7 @@ EXTENSION("standard", "array.c base64.c basic_functions.c browscap.c \
cyr_convert.c datetime.c dir.c dl.c dns.c dns_win32.c exec.c \
file.c filestat.c formatted_print.c fsock.c head.c html.c image.c \
info.c iptc.c lcg.c link_win32.c mail.c math.c md5.c metaphone.c microtime.c \
- pack.c pageinfo.c quot_print.c rand.c soundex.c \
+ pack.c pageinfo.c quot_print.c rand.c mt_rand.c soundex.c \
string.c scanf.c syslog.c type.c uniqid.c url.c var.c \
versioning.c assert.c strnatcmp.c levenshtein.c incomplete_class.c \
url_scanner_ex.c ftp_fopen_wrapper.c http_fopen_wrapper.c \
diff --git a/ext/standard/crc32.c b/ext/standard/crc32.c
index 6a5910a25e..725109fbae 100644
--- a/ext/standard/crc32.c
+++ b/ext/standard/crc32.c
@@ -28,8 +28,8 @@ PHP_NAMED_FUNCTION(php_if_crc32)
{
char *p;
size_t nr;
- php_uint32 crcinit = 0;
- register php_uint32 crc;
+ uint32_t crcinit = 0;
+ register uint32_t crc;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &p, &nr) == FAILURE) {
return;
diff --git a/ext/standard/credits_ext.h b/ext/standard/credits_ext.h
index 68afee6735..a0f578bda8 100644
--- a/ext/standard/credits_ext.h
+++ b/ext/standard/credits_ext.h
@@ -17,8 +17,8 @@ CREDIT_LINE("COM and .Net", "Wez Furlong");
CREDIT_LINE("ctype", "Hartmut Holzgraefe");
CREDIT_LINE("cURL", "Sterling Hughes");
CREDIT_LINE("Date/Time Support", "Derick Rethans");
-CREDIT_LINE("DBA", "Sascha Schumann, Marcus Boerger");
CREDIT_LINE("DB-LIB (MS SQL, Sybase)", "Wez Furlong, Frank M. Kromann");
+CREDIT_LINE("DBA", "Sascha Schumann, Marcus Boerger");
CREDIT_LINE("DOM", "Christian Stocker, Rob Richards, Marcus Boerger");
CREDIT_LINE("enchant", "Pierre-Alain Joye, Ilia Alshanetsky");
CREDIT_LINE("EXIF", "Rasmus Lerdorf, Marcus Boerger");
@@ -66,17 +66,17 @@ CREDIT_LINE("SNMP", "Rasmus Lerdorf, Harrie Hazewinkel, Mike Jackson, Steven Law
CREDIT_LINE("SOAP", "Brad Lafountain, Shane Caraveo, Dmitry Stogov");
CREDIT_LINE("Sockets", "Chris Vandomelen, Sterling Hughes, Daniel Beulshausen, Jason Greene");
CREDIT_LINE("SPL", "Marcus Boerger, Etienne Kneuss");
-CREDIT_LINE("SQLite3", "Scott MacVicar, Ilia Alshanetsky, Brad Dewar");
CREDIT_LINE("SQLite 3.x driver for PDO", "Wez Furlong");
+CREDIT_LINE("SQLite3", "Scott MacVicar, Ilia Alshanetsky, Brad Dewar");
CREDIT_LINE("System V Message based IPC", "Wez Furlong");
CREDIT_LINE("System V Semaphores", "Tom May");
CREDIT_LINE("System V Shared Memory", "Christian Cartus");
CREDIT_LINE("tidy", "John Coggeshall, Ilia Alshanetsky");
CREDIT_LINE("tokenizer", "Andrei Zmievski, Johannes Schlueter");
CREDIT_LINE("WDDX", "Andrei Zmievski");
+CREDIT_LINE("XML", "Stig Bakken, Thies C. Arntzen, Sterling Hughes");
CREDIT_LINE("XMLReader", "Rob Richards");
CREDIT_LINE("xmlrpc", "Dan Libby");
-CREDIT_LINE("XML", "Stig Bakken, Thies C. Arntzen, Sterling Hughes");
CREDIT_LINE("XMLWriter", "Rob Richards, Pierre-Alain Joye");
CREDIT_LINE("XSL", "Christian Stocker, Rob Richards");
CREDIT_LINE("Zip", "Pierre-Alain Joye, Remi Collet");
diff --git a/ext/standard/crypt.c b/ext/standard/crypt.c
index 13a7cec5b9..3a6826e9fd 100644
--- a/ext/standard/crypt.c
+++ b/ext/standard/crypt.c
@@ -24,7 +24,6 @@
#include <stdlib.h>
#include "php.h"
-#if HAVE_CRYPT
#if HAVE_UNISTD_H
#include <unistd.h>
@@ -55,50 +54,11 @@
#include <process.h>
#endif
-#include "php_lcg.h"
#include "php_crypt.h"
-#include "php_rand.h"
+#include "php_random.h"
-/* The capabilities of the crypt() function is determined by the test programs
- * run by configure from aclocal.m4. They will set PHP_STD_DES_CRYPT,
- * PHP_EXT_DES_CRYPT, PHP_MD5_CRYPT and PHP_BLOWFISH_CRYPT as appropriate
- * for the target platform. */
-
-#if PHP_STD_DES_CRYPT
-#define PHP_MAX_SALT_LEN 2
-#endif
-
-#if PHP_EXT_DES_CRYPT
-#undef PHP_MAX_SALT_LEN
-#define PHP_MAX_SALT_LEN 9
-#endif
-
-#if PHP_MD5_CRYPT
-#undef PHP_MAX_SALT_LEN
-#define PHP_MAX_SALT_LEN 12
-#endif
-
-#if PHP_BLOWFISH_CRYPT
-#undef PHP_MAX_SALT_LEN
-#define PHP_MAX_SALT_LEN 60
-#endif
-
-#if PHP_SHA512_CRYPT
-#undef PHP_MAX_SALT_LEN
+/* sha512 crypt has the maximal salt length of 123 characters */
#define PHP_MAX_SALT_LEN 123
-#endif
-
-
-/* If the configure-time checks fail, we provide DES.
- * XXX: This is a hack. Fix the real problem! */
-
-#ifndef PHP_MAX_SALT_LEN
-#define PHP_MAX_SALT_LEN 2
-#undef PHP_STD_DES_CRYPT
-#define PHP_STD_DES_CRYPT 1
-#endif
-
-#define PHP_CRYPT_RAND php_rand()
/* Used to check DES salts to ensure that they contain only valid characters */
#define IS_VALID_SALT_CHARACTER(c) (((c) >= '.' && (c) <= '9') || ((c) >= 'A' && (c) <= 'Z') || ((c) >= 'a' && (c) <= 'z'))
@@ -109,18 +69,12 @@
PHP_MINIT_FUNCTION(crypt) /* {{{ */
{
REGISTER_LONG_CONSTANT("CRYPT_SALT_LENGTH", PHP_MAX_SALT_LEN, CONST_CS | CONST_PERSISTENT);
- REGISTER_LONG_CONSTANT("CRYPT_STD_DES", PHP_STD_DES_CRYPT, CONST_CS | CONST_PERSISTENT);
- REGISTER_LONG_CONSTANT("CRYPT_EXT_DES", PHP_EXT_DES_CRYPT, CONST_CS | CONST_PERSISTENT);
- REGISTER_LONG_CONSTANT("CRYPT_MD5", PHP_MD5_CRYPT, CONST_CS | CONST_PERSISTENT);
- REGISTER_LONG_CONSTANT("CRYPT_BLOWFISH", PHP_BLOWFISH_CRYPT, CONST_CS | CONST_PERSISTENT);
-
-#ifdef PHP_SHA256_CRYPT
- REGISTER_LONG_CONSTANT("CRYPT_SHA256", PHP_SHA256_CRYPT, CONST_CS | CONST_PERSISTENT);
-#endif
-
-#ifdef PHP_SHA512_CRYPT
- REGISTER_LONG_CONSTANT("CRYPT_SHA512", PHP_SHA512_CRYPT, CONST_CS | CONST_PERSISTENT);
-#endif
+ REGISTER_LONG_CONSTANT("CRYPT_STD_DES", 1, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("CRYPT_EXT_DES", 1, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("CRYPT_MD5", 1, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("CRYPT_BLOWFISH", 1, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("CRYPT_SHA256", 1, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("CRYPT_SHA512", 1, CONST_CS | CONST_PERSISTENT);
#if PHP_USE_PHP_CRYPT_R
php_init_crypt_r();
@@ -142,11 +96,11 @@ PHP_MSHUTDOWN_FUNCTION(crypt) /* {{{ */
static unsigned char itoa64[] = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
-static void php_to64(char *s, zend_long v, int n) /* {{{ */
+static void php_to64(char *s, int n) /* {{{ */
{
while (--n >= 0) {
- *s++ = itoa64[v&0x3f];
- v >>= 6;
+ *s = itoa64[*s & 0x3f];
+ s++;
}
}
/* }}} */
@@ -291,16 +245,16 @@ PHP_FUNCTION(crypt)
size_t str_len, salt_in_len = 0;
zend_string *result;
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|s", &str, &str_len, &salt_in, &salt_in_len) == FAILURE) {
+ return;
+ }
+
salt[0] = salt[PHP_MAX_SALT_LEN] = '\0';
/* This will produce suitable results if people depend on DES-encryption
* available (passing always 2-character salt). At least for glibc6.1 */
memset(&salt[1], '$', PHP_MAX_SALT_LEN - 1);
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|s", &str, &str_len, &salt_in, &salt_in_len) == FAILURE) {
- return;
- }
-
if (salt_in) {
memcpy(salt, salt_in, MIN(PHP_MAX_SALT_LEN, salt_in_len));
} else {
@@ -309,15 +263,10 @@ PHP_FUNCTION(crypt)
/* The automatic salt generation covers standard DES, md5-crypt and Blowfish (simple) */
if (!*salt) {
-#if PHP_MD5_CRYPT
- strncpy(salt, "$1$", PHP_MAX_SALT_LEN);
- php_to64(&salt[3], PHP_CRYPT_RAND, 4);
- php_to64(&salt[7], PHP_CRYPT_RAND, 4);
+ strncpy(salt, "$1$", 3);
+ php_random_bytes_throw(&salt[3], 8);
+ php_to64(&salt[3], 8);
strncpy(&salt[11], "$", PHP_MAX_SALT_LEN - 11);
-#elif PHP_STD_DES_CRYPT
- php_to64(&salt[0], PHP_CRYPT_RAND, 2);
- salt[2] = '\0';
-#endif
salt_in_len = strlen(salt);
} else {
salt_in_len = MIN(PHP_MAX_SALT_LEN, salt_in_len);
@@ -334,7 +283,6 @@ PHP_FUNCTION(crypt)
RETURN_STR(result);
}
/* }}} */
-#endif
/*
* Local variables:
diff --git a/ext/standard/crypt_freesec.c b/ext/standard/crypt_freesec.c
index dddab62a61..ba11bf98e8 100644
--- a/ext/standard/crypt_freesec.c
+++ b/ext/standard/crypt_freesec.c
@@ -626,7 +626,7 @@ _crypt_extended_r(const char *key, const char *setting,
* and padding with zeros.
*/
q = (u_char *) keybuf;
- while (q - (u_char *) keybuf < sizeof(keybuf)) {
+ while ((size_t)(q - (u_char *) keybuf) < sizeof(keybuf)) {
*q++ = *key << 1;
if (*key)
key++;
@@ -667,7 +667,7 @@ _crypt_extended_r(const char *key, const char *setting,
* And XOR with the next 8 characters of the key.
*/
q = (u_char *) keybuf;
- while (q - (u_char *) keybuf < sizeof(keybuf) && *key)
+ while ((size_t)(q - (u_char *) keybuf) < sizeof(keybuf) && *key)
*q++ ^= *key++ << 1;
if (des_setkey((char *) keybuf, data))
diff --git a/ext/standard/dir.c b/ext/standard/dir.c
index b57c63a8f5..398cf88177 100644
--- a/ext/standard/dir.c
+++ b/ext/standard/dir.c
@@ -110,7 +110,7 @@ static const zend_function_entry php_dir_class_functions[] = {
PHP_FALIAS(close, closedir, arginfo_dir)
PHP_FALIAS(rewind, rewinddir, arginfo_dir)
PHP_NAMED_FE(read, php_if_readdir, arginfo_dir)
- {NULL, NULL, NULL}
+ PHP_FE_END
};
@@ -278,7 +278,7 @@ PHP_FUNCTION(closedir)
FETCH_DIRP();
if (!(dirp->flags & PHP_STREAM_FLAG_IS_DIR)) {
- php_error_docref(NULL, E_WARNING, "%pd is not a valid Directory resource", dirp->res->handle);
+ php_error_docref(NULL, E_WARNING, "%d is not a valid Directory resource", dirp->res->handle);
RETURN_FALSE;
}
@@ -394,7 +394,7 @@ PHP_FUNCTION(rewinddir)
FETCH_DIRP();
if (!(dirp->flags & PHP_STREAM_FLAG_IS_DIR)) {
- php_error_docref(NULL, E_WARNING, "%pd is not a valid Directory resource", dirp->res->handle);
+ php_error_docref(NULL, E_WARNING, "%d is not a valid Directory resource", dirp->res->handle);
RETURN_FALSE;
}
@@ -413,7 +413,7 @@ PHP_NAMED_FUNCTION(php_if_readdir)
FETCH_DIRP();
if (!(dirp->flags & PHP_STREAM_FLAG_IS_DIR)) {
- php_error_docref(NULL, E_WARNING, "%pd is not a valid Directory resource", dirp->res->handle);
+ php_error_docref(NULL, E_WARNING, "%d is not a valid Directory resource", dirp->res->handle);
RETURN_FALSE;
}
@@ -439,7 +439,7 @@ PHP_FUNCTION(glob)
size_t pattern_len;
zend_long flags = 0;
glob_t globbuf;
- int n;
+ size_t n;
int ret;
zend_bool basedir_limit = 0;
diff --git a/ext/standard/exec.c b/ext/standard/exec.c
index d6f0cbfeb2..49f2d60a87 100644
--- a/ext/standard/exec.c
+++ b/ext/standard/exec.c
@@ -54,14 +54,14 @@
#include <limits.h>
#endif
-static int cmd_max_len;
+static size_t cmd_max_len;
/* {{{ PHP_MINIT_FUNCTION(exec) */
PHP_MINIT_FUNCTION(exec)
{
#ifdef _SC_ARG_MAX
cmd_max_len = sysconf(_SC_ARG_MAX);
- if (-1 == cmd_max_len) {
+ if ((size_t)-1 == cmd_max_len) {
#ifdef _POSIX_ARG_MAX
cmd_max_len = _POSIX_ARG_MAX;
#else
@@ -278,7 +278,7 @@ PHP_FUNCTION(passthru)
*/
PHPAPI zend_string *php_escape_shell_cmd(char *str)
{
- register int x, y;
+ register size_t x, y;
size_t l = strlen(str);
uint64_t estimate = (2 * (uint64_t)l) + 1;
zend_string *cmd;
@@ -385,7 +385,7 @@ PHPAPI zend_string *php_escape_shell_cmd(char *str)
*/
PHPAPI zend_string *php_escape_shell_arg(char *str)
{
- int x, y = 0;
+ size_t x, y = 0;
size_t l = strlen(str);
zend_string *cmd;
uint64_t estimate = (4 * (uint64_t)l) + 3;
diff --git a/ext/standard/file.c b/ext/standard/file.c
index 0bb81cae8b..52aa07b0b6 100644
--- a/ext/standard/file.c
+++ b/ext/standard/file.c
@@ -262,19 +262,19 @@ PHP_MINIT_FUNCTION(file)
REGISTER_LONG_CONSTANT("STREAM_IPPROTO_IP", IPPROTO_IP, CONST_CS|CONST_PERSISTENT);
#endif
-#ifdef IPPROTO_TCP
+#if defined(IPPROTO_TCP) || defined(PHP_WIN32)
REGISTER_LONG_CONSTANT("STREAM_IPPROTO_TCP", IPPROTO_TCP, CONST_CS|CONST_PERSISTENT);
#endif
-#ifdef IPPROTO_UDP
+#if defined(IPPROTO_UDP) || defined(PHP_WIN32)
REGISTER_LONG_CONSTANT("STREAM_IPPROTO_UDP", IPPROTO_UDP, CONST_CS|CONST_PERSISTENT);
#endif
-#ifdef IPPROTO_ICMP
+#if defined(IPPROTO_ICMP) || defined(PHP_WIN32)
REGISTER_LONG_CONSTANT("STREAM_IPPROTO_ICMP", IPPROTO_ICMP, CONST_CS|CONST_PERSISTENT);
#endif
-#ifdef IPPROTO_RAW
+#if defined(IPPROTO_RAW) || defined(PHP_WIN32)
REGISTER_LONG_CONSTANT("STREAM_IPPROTO_RAW", IPPROTO_RAW, CONST_CS|CONST_PERSISTENT);
#endif
@@ -523,7 +523,7 @@ PHP_FUNCTION(file_get_contents)
size_t filename_len;
zend_bool use_include_path = 0;
php_stream *stream;
- zend_long offset = -1;
+ zend_long offset = 0;
zend_long maxlen = (ssize_t) PHP_STREAM_COPY_ALL;
zval *zcontext = NULL;
php_stream_context *context = NULL;
@@ -548,14 +548,14 @@ PHP_FUNCTION(file_get_contents)
RETURN_FALSE;
}
- if (offset > 0 && php_stream_seek(stream, offset, SEEK_SET) < 0) {
+ if (offset != 0 && php_stream_seek(stream, offset, ((offset > 0) ? SEEK_SET : SEEK_END)) < 0) {
php_error_docref(NULL, E_WARNING, "Failed to seek to position " ZEND_LONG_FMT " in the stream", offset);
php_stream_close(stream);
RETURN_FALSE;
}
if (maxlen > INT_MAX) {
- php_error_docref(NULL, E_WARNING, "maxlen truncated from %pd to %d bytes", maxlen, INT_MAX);
+ php_error_docref(NULL, E_WARNING, "maxlen truncated from " ZEND_LONG_FMT " to %d bytes", maxlen, INT_MAX);
maxlen = INT_MAX;
}
if ((contents = php_stream_copy_to_mem(stream, maxlen, 0)) != NULL) {
@@ -576,7 +576,7 @@ PHP_FUNCTION(file_put_contents)
char *filename;
size_t filename_len;
zval *data;
- zend_long numbytes = 0;
+ size_t numbytes = 0;
zend_long flags = 0;
zval *zcontext = NULL;
php_stream_context *context = NULL;
@@ -695,7 +695,7 @@ PHP_FUNCTION(file_put_contents)
}
php_stream_close(stream);
- if (numbytes < 0) {
+ if (numbytes == (size_t)-1) {
RETURN_FALSE;
}
@@ -891,7 +891,7 @@ PHPAPI PHP_FUNCTION(fclose)
PHP_STREAM_TO_ZVAL(stream, res);
if ((stream->flags & PHP_STREAM_FLAG_NO_FCLOSE) != 0) {
- php_error_docref(NULL, E_WARNING, "%pd is not a valid stream resource", stream->res->handle);
+ php_error_docref(NULL, E_WARNING, "%d is not a valid stream resource", stream->res->handle);
RETURN_FALSE;
}
@@ -1499,7 +1499,7 @@ PHP_NAMED_FUNCTION(php_if_ftruncate)
}
if (size < 0) {
- /* php_error_docref(NULL, E_WARNING, "Negative size is not supported"); */
+ php_error_docref(NULL, E_WARNING, "Negative size is not supported");
RETURN_FALSE;
}
diff --git a/ext/standard/filestat.c b/ext/standard/filestat.c
index 07f1b4b2bb..adbed69931 100644
--- a/ext/standard/filestat.c
+++ b/ext/standard/filestat.c
@@ -110,59 +110,22 @@ PHP_RSHUTDOWN_FUNCTION(filestat) /* {{{ */
static int php_disk_total_space(char *path, double *space) /* {{{ */
#if defined(WINDOWS) /* {{{ */
{
- double bytestotal = 0;
- HINSTANCE kernel32;
- FARPROC gdfse;
- typedef BOOL (WINAPI *gdfse_func)(LPCTSTR, PULARGE_INTEGER, PULARGE_INTEGER, PULARGE_INTEGER);
- gdfse_func func;
-
- /* These are used by GetDiskFreeSpaceEx, if available. */
ULARGE_INTEGER FreeBytesAvailableToCaller;
ULARGE_INTEGER TotalNumberOfBytes;
ULARGE_INTEGER TotalNumberOfFreeBytes;
+ PHP_WIN32_IOUTIL_INIT_W(path)
- /* These are used by GetDiskFreeSpace otherwise. */
- DWORD SectorsPerCluster;
- DWORD BytesPerSector;
- DWORD NumberOfFreeClusters;
- DWORD TotalNumberOfClusters;
-
- /* GetDiskFreeSpaceEx is only available in NT and Win95 post-OSR2,
- so we have to jump through some hoops to see if the function
- exists. */
- kernel32 = LoadLibrary("kernel32.dll");
- if (kernel32) {
- gdfse = GetProcAddress(kernel32, "GetDiskFreeSpaceExA");
- /* It's available, so we can call it. */
- if (gdfse) {
- func = (gdfse_func)gdfse;
- if (func(path,
- &FreeBytesAvailableToCaller,
- &TotalNumberOfBytes,
- &TotalNumberOfFreeBytes) == 0) {
- php_error_docref(NULL, E_WARNING, "%s", php_win_err());
- return FAILURE;
- }
-
- /* i know - this is ugly, but i works <thies@thieso.net> */
- bytestotal = TotalNumberOfBytes.HighPart *
- (double) (((zend_ulong)1) << 31) * 2.0 +
- TotalNumberOfBytes.LowPart;
- } else { /* If it's not available, we just use GetDiskFreeSpace */
- if (GetDiskFreeSpace(path,
- &SectorsPerCluster, &BytesPerSector,
- &NumberOfFreeClusters, &TotalNumberOfClusters) == 0) {
- php_error_docref(NULL, E_WARNING, "%s", php_win_err());
- return FAILURE;
- }
- bytestotal = (double)TotalNumberOfClusters * (double)SectorsPerCluster * (double)BytesPerSector;
- }
- } else {
- php_error_docref(NULL, E_WARNING, "Unable to load kernel32.dll");
+ if (GetDiskFreeSpaceExW(pathw, &FreeBytesAvailableToCaller, &TotalNumberOfBytes, &TotalNumberOfFreeBytes) == 0) {
+ php_error_docref(NULL, E_WARNING, "%s", php_win_err());
+ PHP_WIN32_IOUTIL_CLEANUP_W()
return FAILURE;
}
- *space = bytestotal;
+ /* i know - this is ugly, but i works <thies@thieso.net> */
+ *space = TotalNumberOfBytes.HighPart * (double) (((zend_ulong)1) << 31) * 2.0 + TotalNumberOfBytes.LowPart;
+
+ PHP_WIN32_IOUTIL_CLEANUP_W()
+
return SUCCESS;
}
/* }}} */
@@ -241,60 +204,22 @@ PHP_FUNCTION(disk_total_space)
static int php_disk_free_space(char *path, double *space) /* {{{ */
#if defined(WINDOWS) /* {{{ */
{
- double bytesfree = 0;
-
- HINSTANCE kernel32;
- FARPROC gdfse;
- typedef BOOL (WINAPI *gdfse_func)(LPCTSTR, PULARGE_INTEGER, PULARGE_INTEGER, PULARGE_INTEGER);
- gdfse_func func;
-
- /* These are used by GetDiskFreeSpaceEx, if available. */
ULARGE_INTEGER FreeBytesAvailableToCaller;
ULARGE_INTEGER TotalNumberOfBytes;
ULARGE_INTEGER TotalNumberOfFreeBytes;
+ PHP_WIN32_IOUTIL_INIT_W(path)
- /* These are used by GetDiskFreeSpace otherwise. */
- DWORD SectorsPerCluster;
- DWORD BytesPerSector;
- DWORD NumberOfFreeClusters;
- DWORD TotalNumberOfClusters;
-
- /* GetDiskFreeSpaceEx is only available in NT and Win95 post-OSR2,
- so we have to jump through some hoops to see if the function
- exists. */
- kernel32 = LoadLibrary("kernel32.dll");
- if (kernel32) {
- gdfse = GetProcAddress(kernel32, "GetDiskFreeSpaceExA");
- /* It's available, so we can call it. */
- if (gdfse) {
- func = (gdfse_func)gdfse;
- if (func(path,
- &FreeBytesAvailableToCaller,
- &TotalNumberOfBytes,
- &TotalNumberOfFreeBytes) == 0) {
- php_error_docref(NULL, E_WARNING, "%s", php_win_err());
- return FAILURE;
- }
-
- /* i know - this is ugly, but i works <thies@thieso.net> */
- bytesfree = FreeBytesAvailableToCaller.HighPart *
- (double) (((zend_ulong)1) << 31) * 2.0 +
- FreeBytesAvailableToCaller.LowPart;
- } else { /* If it's not available, we just use GetDiskFreeSpace */
- if (GetDiskFreeSpace(path,
- &SectorsPerCluster, &BytesPerSector,
- &NumberOfFreeClusters, &TotalNumberOfClusters) == 0) {
- php_error_docref(NULL, E_WARNING, "%s", php_win_err());
- return FAILURE;
- }
- bytesfree = (double)NumberOfFreeClusters * (double)SectorsPerCluster * (double)BytesPerSector;
- }
- } else {
- php_error_docref(NULL, E_WARNING, "Unable to load kernel32.dll");
+ if (GetDiskFreeSpaceExW(pathw, &FreeBytesAvailableToCaller, &TotalNumberOfBytes, &TotalNumberOfFreeBytes) == 0) {
+ php_error_docref(NULL, E_WARNING, "%s", php_win_err());
+ PHP_WIN32_IOUTIL_CLEANUP_W()
return FAILURE;
}
- *space = bytesfree;
+ /* i know - this is ugly, but i works <thies@thieso.net> */
+ *space = FreeBytesAvailableToCaller.HighPart * (double) (((zend_ulong)1) << 31) * 2.0 + FreeBytesAvailableToCaller.LowPart;
+
+ PHP_WIN32_IOUTIL_CLEANUP_W()
+
return SUCCESS;
}
/* }}} */
diff --git a/ext/standard/formatted_print.c b/ext/standard/formatted_print.c
index bcb90ebc94..79051b3a10 100644
--- a/ext/standard/formatted_print.c
+++ b/ext/standard/formatted_print.c
@@ -87,7 +87,7 @@ php_sprintf_appendstring(zend_string **buffer, size_t *pos, char *add,
m_width = MAX(min_width, copy_len);
if(m_width > INT_MAX - *pos - 1) {
- zend_error_noreturn(E_ERROR, "Field width %d is too long", m_width);
+ zend_error_noreturn(E_ERROR, "Field width %zd is too long", m_width);
}
req_size = *pos + m_width + 1;
diff --git a/ext/standard/ftok.c b/ext/standard/ftok.c
index f126ea0471..65b30259cd 100644
--- a/ext/standard/ftok.c
+++ b/ext/standard/ftok.c
@@ -26,6 +26,10 @@
#include <sys/ipc.h>
#endif
+#ifdef PHP_WIN32
+#include "win32/ipc.h"
+#endif
+
#if HAVE_FTOK
/* {{{ proto int ftok(string pathname, string proj)
Convert a pathname and a project identifier to a System V IPC key */
diff --git a/ext/standard/ftp_fopen_wrapper.c b/ext/standard/ftp_fopen_wrapper.c
index a294960c3d..2df73fb0a6 100644
--- a/ext/standard/ftp_fopen_wrapper.c
+++ b/ext/standard/ftp_fopen_wrapper.c
@@ -533,10 +533,10 @@ php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, const char *pa
(tmpzval = php_stream_context_get_option(context, "ftp", "resume_pos")) != NULL &&
Z_TYPE_P(tmpzval) == IS_LONG &&
Z_LVAL_P(tmpzval) > 0) {
- php_stream_printf(stream, "REST %pd\r\n", Z_LVAL_P(tmpzval));
+ php_stream_printf(stream, "REST " ZEND_LONG_FMT "\r\n", Z_LVAL_P(tmpzval));
result = GET_FTP_RESULT(stream);
if (result < 300 || result > 399) {
- php_stream_wrapper_log_error(wrapper, options, "Unable to resume from offset %pd", Z_LVAL_P(tmpzval));
+ php_stream_wrapper_log_error(wrapper, options, "Unable to resume from offset " ZEND_LONG_FMT, Z_LVAL_P(tmpzval));
goto errexit;
}
}
@@ -833,11 +833,11 @@ static int php_stream_ftp_url_stat(php_stream_wrapper *wrapper, const char *url,
struct tm tm, tmbuf, *gmt;
time_t stamp;
- while (p - tmp_line < sizeof(tmp_line) && !isdigit(*p)) {
+ while ((size_t)(p - tmp_line) < sizeof(tmp_line) && !isdigit(*p)) {
p++;
}
- if (p - tmp_line > sizeof(tmp_line)) {
+ if ((size_t)(p - tmp_line) > sizeof(tmp_line)) {
goto mdtm_error;
}
@@ -1182,7 +1182,8 @@ static php_stream_wrapper_ops ftp_stream_wops = {
php_stream_ftp_unlink, /* unlink */
php_stream_ftp_rename, /* rename */
php_stream_ftp_mkdir, /* mkdir */
- php_stream_ftp_rmdir /* rmdir */
+ php_stream_ftp_rmdir, /* rmdir */
+ NULL
};
PHPAPI php_stream_wrapper php_stream_ftp_wrapper = {
diff --git a/ext/standard/html.c b/ext/standard/html.c
index 039cda7c98..0f670c1500 100644
--- a/ext/standard/html.c
+++ b/ext/standard/html.c
@@ -375,7 +375,7 @@ static inline unsigned int get_next_char(
* defaults to UTF-8 */
static enum entity_charset determine_charset(char *charset_hint)
{
- int i;
+ size_t i;
enum entity_charset charset = cs_utf_8;
size_t len = 0;
const zend_encoding *zenc;
diff --git a/ext/standard/html_tables.h b/ext/standard/html_tables.h
index 9e134ced0b..8fa58b8041 100644
--- a/ext/standard/html_tables.h
+++ b/ext/standard/html_tables.h
@@ -1437,7 +1437,7 @@ static const entity_stage3_row stage3_table_html5_00000[] = {
{0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {0, { {NULL, 0} } },
{0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {0, { {NULL, 0} } },
{0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {0, { {"colon", 5} } }, {0, { {"semi", 4} } },
- {1, { {(void *)multi_cp_html5_0003C} } }, {1, { {(void *)multi_cp_html5_0003D} } }, {1, { {(void *)multi_cp_html5_0003E} } }, {0, { {"quest", 5} } },
+ {1, { {(void *)multi_cp_html5_0003C, 0} } }, {1, { {(void *)multi_cp_html5_0003D, 0} } }, {1, { {(void *)multi_cp_html5_0003E, 0} } }, {0, { {"quest", 5} } },
};
static const entity_stage3_row stage3_table_html5_00040[] = {
@@ -1450,7 +1450,7 @@ static const entity_stage3_row stage3_table_html5_00040[] = {
{0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {0, { {"lbrack", 6} } },
{0, { {"bsol", 4} } }, {0, { {"rsqb", 4} } }, {0, { {"Hat", 3} } }, {0, { {"lowbar", 6} } },
{0, { {"grave", 5} } }, {0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {0, { {NULL, 0} } },
- {0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {1, { {(void *)multi_cp_html5_00066} } }, {0, { {NULL, 0} } },
+ {0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {1, { {(void *)multi_cp_html5_00066, 0} } }, {0, { {NULL, 0} } },
{0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {0, { {NULL, 0} } },
{0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {0, { {NULL, 0} } },
{0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {0, { {NULL, 0} } },
@@ -1733,7 +1733,7 @@ static const entity_stage3_row stage3_table_html5_02040[] = {
{0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {0, { {NULL, 0} } },
{0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {0, { {"qprime", 6} } },
{0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {0, { {NULL, 0} } },
- {0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {1, { {(void *)multi_cp_html5_0205F} } },
+ {0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {1, { {(void *)multi_cp_html5_0205F, 0} } },
{0, { {"NoBreak", 7} } }, {0, { {"af", 2} } }, {0, { {"InvisibleTimes", 14} } }, {0, { {"ic", 2} } },
{0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {0, { {NULL, 0} } },
{0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {0, { {NULL, 0} } },
@@ -1828,7 +1828,7 @@ static const entity_stage3_row stage3_table_html5_02180[] = {
{0, { {"larr", 4} } }, {0, { {"uarr", 4} } }, {0, { {"srarr", 5} } }, {0, { {"darr", 4} } },
{0, { {"harr", 4} } }, {0, { {"UpDownArrow", 11} } }, {0, { {"nwarrow", 7} } }, {0, { {"UpperRightArrow", 15} } },
{0, { {"LowerRightArrow", 15} } }, {0, { {"swarr", 5} } }, {0, { {"nleftarrow", 10} } }, {0, { {"nrarr", 5} } },
- {0, { {NULL, 0} } }, {1, { {(void *)multi_cp_html5_0219D} } }, {0, { {"Larr", 4} } }, {0, { {"Uarr", 4} } },
+ {0, { {NULL, 0} } }, {1, { {(void *)multi_cp_html5_0219D, 0} } }, {0, { {"Larr", 4} } }, {0, { {"Uarr", 4} } },
{0, { {"twoheadrightarrow", 17} } }, {0, { {"Darr", 4} } }, {0, { {"larrtl", 6} } }, {0, { {"rarrtl", 6} } },
{0, { {"LeftTeeArrow", 12} } }, {0, { {"UpTeeArrow", 10} } }, {0, { {"map", 3} } }, {0, { {"DownTeeArrow", 12} } },
{0, { {NULL, 0} } }, {0, { {"larrhk", 6} } }, {0, { {"rarrhk", 6} } }, {0, { {"larrlp", 6} } },
@@ -1859,7 +1859,7 @@ static const entity_stage3_row stage3_table_html5_021C0[] = {
};
static const entity_stage3_row stage3_table_html5_02200[] = {
- {0, { {"forall", 6} } }, {0, { {"comp", 4} } }, {1, { {(void *)multi_cp_html5_02202} } }, {0, { {"Exists", 6} } },
+ {0, { {"forall", 6} } }, {0, { {"comp", 4} } }, {1, { {(void *)multi_cp_html5_02202, 0} } }, {0, { {"Exists", 6} } },
{0, { {"nexist", 6} } }, {0, { {"empty", 5} } }, {0, { {NULL, 0} } }, {0, { {"nabla", 5} } },
{0, { {"isinv", 5} } }, {0, { {"notin", 5} } }, {0, { {NULL, 0} } }, {0, { {"ReverseElement", 14} } },
{0, { {"notniva", 7} } }, {0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {0, { {"prod", 4} } },
@@ -1867,42 +1867,42 @@ static const entity_stage3_row stage3_table_html5_02200[] = {
{0, { {"plusdo", 6} } }, {0, { {NULL, 0} } }, {0, { {"ssetmn", 6} } }, {0, { {"lowast", 6} } },
{0, { {"compfn", 6} } }, {0, { {NULL, 0} } }, {0, { {"Sqrt", 4} } }, {0, { {NULL, 0} } },
{0, { {NULL, 0} } }, {0, { {"prop", 4} } }, {0, { {"infin", 5} } }, {0, { {"angrt", 5} } },
- {1, { {(void *)multi_cp_html5_02220} } }, {0, { {"angmsd", 6} } }, {0, { {"angsph", 6} } }, {0, { {"mid", 3} } },
+ {1, { {(void *)multi_cp_html5_02220, 0} } }, {0, { {"angmsd", 6} } }, {0, { {"angsph", 6} } }, {0, { {"mid", 3} } },
{0, { {"nshortmid", 9} } }, {0, { {"shortparallel", 13} } }, {0, { {"nparallel", 9} } }, {0, { {"and", 3} } },
- {0, { {"or", 2} } }, {1, { {(void *)multi_cp_html5_02229} } }, {1, { {(void *)multi_cp_html5_0222A} } }, {0, { {"Integral", 8} } },
+ {0, { {"or", 2} } }, {1, { {(void *)multi_cp_html5_02229, 0} } }, {1, { {(void *)multi_cp_html5_0222A, 0} } }, {0, { {"Integral", 8} } },
{0, { {"Int", 3} } }, {0, { {"tint", 4} } }, {0, { {"ContourIntegral", 15} } }, {0, { {"DoubleContourIntegral", 21} } },
{0, { {"Cconint", 7} } }, {0, { {"cwint", 5} } }, {0, { {"cwconint", 8} } }, {0, { {"awconint", 8} } },
{0, { {"there4", 6} } }, {0, { {"Because", 7} } }, {0, { {"ratio", 5} } }, {0, { {"Colon", 5} } },
{0, { {"minusd", 6} } }, {0, { {NULL, 0} } }, {0, { {"mDDot", 5} } }, {0, { {"homtht", 6} } },
- {1, { {(void *)multi_cp_html5_0223C} } }, {1, { {(void *)multi_cp_html5_0223D} } }, {1, { {(void *)multi_cp_html5_0223E} } }, {0, { {"acd", 3} } },
+ {1, { {(void *)multi_cp_html5_0223C, 0} } }, {1, { {(void *)multi_cp_html5_0223D, 0} } }, {1, { {(void *)multi_cp_html5_0223E, 0} } }, {0, { {"acd", 3} } },
};
static const entity_stage3_row stage3_table_html5_02240[] = {
- {0, { {"wr", 2} } }, {0, { {"NotTilde", 8} } }, {1, { {(void *)multi_cp_html5_02242} } }, {0, { {"simeq", 5} } },
+ {0, { {"wr", 2} } }, {0, { {"NotTilde", 8} } }, {1, { {(void *)multi_cp_html5_02242, 0} } }, {0, { {"simeq", 5} } },
{0, { {"nsime", 5} } }, {0, { {"TildeFullEqual", 14} } }, {0, { {"simne", 5} } }, {0, { {"ncong", 5} } },
- {0, { {"approx", 6} } }, {0, { {"napprox", 7} } }, {0, { {"ape", 3} } }, {1, { {(void *)multi_cp_html5_0224B} } },
- {0, { {"bcong", 5} } }, {1, { {(void *)multi_cp_html5_0224D} } }, {1, { {(void *)multi_cp_html5_0224E} } }, {1, { {(void *)multi_cp_html5_0224F} } },
- {1, { {(void *)multi_cp_html5_02250} } }, {0, { {"doteqdot", 8} } }, {0, { {"fallingdotseq", 13} } }, {0, { {"risingdotseq", 12} } },
+ {0, { {"approx", 6} } }, {0, { {"napprox", 7} } }, {0, { {"ape", 3} } }, {1, { {(void *)multi_cp_html5_0224B, 0} } },
+ {0, { {"bcong", 5} } }, {1, { {(void *)multi_cp_html5_0224D, 0} } }, {1, { {(void *)multi_cp_html5_0224E, 0} } }, {1, { {(void *)multi_cp_html5_0224F, 0} } },
+ {1, { {(void *)multi_cp_html5_02250, 0} } }, {0, { {"doteqdot", 8} } }, {0, { {"fallingdotseq", 13} } }, {0, { {"risingdotseq", 12} } },
{0, { {"coloneq", 7} } }, {0, { {"eqcolon", 7} } }, {0, { {"ecir", 4} } }, {0, { {"circeq", 6} } },
{0, { {NULL, 0} } }, {0, { {"wedgeq", 6} } }, {0, { {"veeeq", 5} } }, {0, { {NULL, 0} } },
{0, { {"triangleq", 9} } }, {0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {0, { {"equest", 6} } },
- {0, { {"NotEqual", 8} } }, {1, { {(void *)multi_cp_html5_02261} } }, {0, { {"NotCongruent", 12} } }, {0, { {NULL, 0} } },
- {1, { {(void *)multi_cp_html5_02264} } }, {1, { {(void *)multi_cp_html5_02265} } }, {1, { {(void *)multi_cp_html5_02266} } }, {1, { {(void *)multi_cp_html5_02267} } },
- {1, { {(void *)multi_cp_html5_02268} } }, {1, { {(void *)multi_cp_html5_02269} } }, {1, { {(void *)multi_cp_html5_0226A} } }, {1, { {(void *)multi_cp_html5_0226B} } },
+ {0, { {"NotEqual", 8} } }, {1, { {(void *)multi_cp_html5_02261, 0} } }, {0, { {"NotCongruent", 12} } }, {0, { {NULL, 0} } },
+ {1, { {(void *)multi_cp_html5_02264, 0} } }, {1, { {(void *)multi_cp_html5_02265, 0} } }, {1, { {(void *)multi_cp_html5_02266, 0} } }, {1, { {(void *)multi_cp_html5_02267, 0} } },
+ {1, { {(void *)multi_cp_html5_02268, 0} } }, {1, { {(void *)multi_cp_html5_02269, 0} } }, {1, { {(void *)multi_cp_html5_0226A, 0} } }, {1, { {(void *)multi_cp_html5_0226B, 0} } },
{0, { {"between", 7} } }, {0, { {"NotCupCap", 9} } }, {0, { {"NotLess", 7} } }, {0, { {"ngtr", 4} } },
{0, { {"NotLessEqual", 12} } }, {0, { {"ngeq", 4} } }, {0, { {"LessTilde", 9} } }, {0, { {"GreaterTilde", 12} } },
{0, { {"nlsim", 5} } }, {0, { {"ngsim", 5} } }, {0, { {"lessgtr", 7} } }, {0, { {"gl", 2} } },
{0, { {"ntlg", 4} } }, {0, { {"NotGreaterLess", 14} } }, {0, { {"prec", 4} } }, {0, { {"succ", 4} } },
- {0, { {"PrecedesSlantEqual", 18} } }, {0, { {"succcurlyeq", 11} } }, {0, { {"precsim", 7} } }, {1, { {(void *)multi_cp_html5_0227F} } },
+ {0, { {"PrecedesSlantEqual", 18} } }, {0, { {"succcurlyeq", 11} } }, {0, { {"precsim", 7} } }, {1, { {(void *)multi_cp_html5_0227F, 0} } },
};
static const entity_stage3_row stage3_table_html5_02280[] = {
- {0, { {"npr", 3} } }, {0, { {"NotSucceeds", 11} } }, {1, { {(void *)multi_cp_html5_02282} } }, {1, { {(void *)multi_cp_html5_02283} } },
+ {0, { {"npr", 3} } }, {0, { {"NotSucceeds", 11} } }, {1, { {(void *)multi_cp_html5_02282, 0} } }, {1, { {(void *)multi_cp_html5_02283, 0} } },
{0, { {"nsub", 4} } }, {0, { {"nsup", 4} } }, {0, { {"SubsetEqual", 11} } }, {0, { {"supe", 4} } },
- {0, { {"NotSubsetEqual", 14} } }, {0, { {"NotSupersetEqual", 16} } }, {1, { {(void *)multi_cp_html5_0228A} } }, {1, { {(void *)multi_cp_html5_0228B} } },
- {0, { {NULL, 0} } }, {0, { {"cupdot", 6} } }, {0, { {"UnionPlus", 9} } }, {1, { {(void *)multi_cp_html5_0228F} } },
- {1, { {(void *)multi_cp_html5_02290} } }, {0, { {"SquareSubsetEqual", 17} } }, {0, { {"SquareSupersetEqual", 19} } }, {1, { {(void *)multi_cp_html5_02293} } },
- {1, { {(void *)multi_cp_html5_02294} } }, {0, { {"CirclePlus", 10} } }, {0, { {"ominus", 6} } }, {0, { {"CircleTimes", 11} } },
+ {0, { {"NotSubsetEqual", 14} } }, {0, { {"NotSupersetEqual", 16} } }, {1, { {(void *)multi_cp_html5_0228A, 0} } }, {1, { {(void *)multi_cp_html5_0228B, 0} } },
+ {0, { {NULL, 0} } }, {0, { {"cupdot", 6} } }, {0, { {"UnionPlus", 9} } }, {1, { {(void *)multi_cp_html5_0228F, 0} } },
+ {1, { {(void *)multi_cp_html5_02290, 0} } }, {0, { {"SquareSubsetEqual", 17} } }, {0, { {"SquareSupersetEqual", 19} } }, {1, { {(void *)multi_cp_html5_02293, 0} } },
+ {1, { {(void *)multi_cp_html5_02294, 0} } }, {0, { {"CirclePlus", 10} } }, {0, { {"ominus", 6} } }, {0, { {"CircleTimes", 11} } },
{0, { {"osol", 4} } }, {0, { {"CircleDot", 9} } }, {0, { {"ocir", 4} } }, {0, { {"oast", 4} } },
{0, { {NULL, 0} } }, {0, { {"odash", 5} } }, {0, { {"boxplus", 7} } }, {0, { {"boxminus", 8} } },
{0, { {"timesb", 6} } }, {0, { {"sdotb", 5} } }, {0, { {"vdash", 5} } }, {0, { {"dashv", 5} } },
@@ -1910,7 +1910,7 @@ static const entity_stage3_row stage3_table_html5_02280[] = {
{0, { {"DoubleRightTee", 14} } }, {0, { {"Vdash", 5} } }, {0, { {"Vvdash", 6} } }, {0, { {"VDash", 5} } },
{0, { {"nvdash", 6} } }, {0, { {"nvDash", 6} } }, {0, { {"nVdash", 6} } }, {0, { {"nVDash", 6} } },
{0, { {"prurel", 6} } }, {0, { {NULL, 0} } }, {0, { {"vartriangleleft", 15} } }, {0, { {"vrtri", 5} } },
- {1, { {(void *)multi_cp_html5_022B4} } }, {1, { {(void *)multi_cp_html5_022B5} } }, {0, { {"origof", 6} } }, {0, { {"imof", 4} } },
+ {1, { {(void *)multi_cp_html5_022B4, 0} } }, {1, { {(void *)multi_cp_html5_022B5, 0} } }, {0, { {"origof", 6} } }, {0, { {"imof", 4} } },
{0, { {"mumap", 5} } }, {0, { {"hercon", 6} } }, {0, { {"intcal", 6} } }, {0, { {"veebar", 6} } },
{0, { {NULL, 0} } }, {0, { {"barvee", 6} } }, {0, { {"angrtvb", 7} } }, {0, { {"lrtri", 5} } },
};
@@ -1922,15 +1922,15 @@ static const entity_stage3_row stage3_table_html5_022C0[] = {
{0, { {"rthree", 6} } }, {0, { {"backsimeq", 9} } }, {0, { {"curlyvee", 8} } }, {0, { {"curlywedge", 10} } },
{0, { {"Sub", 3} } }, {0, { {"Supset", 6} } }, {0, { {"Cap", 3} } }, {0, { {"Cup", 3} } },
{0, { {"pitchfork", 9} } }, {0, { {"epar", 4} } }, {0, { {"lessdot", 7} } }, {0, { {"gtrdot", 6} } },
- {1, { {(void *)multi_cp_html5_022D8} } }, {1, { {(void *)multi_cp_html5_022D9} } }, {1, { {(void *)multi_cp_html5_022DA} } }, {1, { {(void *)multi_cp_html5_022DB} } },
+ {1, { {(void *)multi_cp_html5_022D8, 0} } }, {1, { {(void *)multi_cp_html5_022D9, 0} } }, {1, { {(void *)multi_cp_html5_022DA, 0} } }, {1, { {(void *)multi_cp_html5_022DB, 0} } },
{0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {0, { {"curlyeqprec", 11} } }, {0, { {"cuesc", 5} } },
{0, { {"NotPrecedesSlantEqual", 21} } }, {0, { {"NotSucceedsSlantEqual", 21} } }, {0, { {"NotSquareSubsetEqual", 20} } }, {0, { {"NotSquareSupersetEqual", 22} } },
{0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {0, { {"lnsim", 5} } }, {0, { {"gnsim", 5} } },
{0, { {"precnsim", 8} } }, {0, { {"scnsim", 6} } }, {0, { {"nltri", 5} } }, {0, { {"ntriangleright", 14} } },
{0, { {"nltrie", 6} } }, {0, { {"NotRightTriangleEqual", 21} } }, {0, { {"vellip", 6} } }, {0, { {"ctdot", 5} } },
{0, { {"utdot", 5} } }, {0, { {"dtdot", 5} } }, {0, { {"disin", 5} } }, {0, { {"isinsv", 6} } },
- {0, { {"isins", 5} } }, {1, { {(void *)multi_cp_html5_022F5} } }, {0, { {"notinvc", 7} } }, {0, { {"notinvb", 7} } },
- {0, { {NULL, 0} } }, {1, { {(void *)multi_cp_html5_022F9} } }, {0, { {"nisd", 4} } }, {0, { {"xnis", 4} } },
+ {0, { {"isins", 5} } }, {1, { {(void *)multi_cp_html5_022F5, 0} } }, {0, { {"notinvc", 7} } }, {0, { {"notinvb", 7} } },
+ {0, { {NULL, 0} } }, {1, { {(void *)multi_cp_html5_022F9, 0} } }, {0, { {"nisd", 4} } }, {0, { {"xnis", 4} } },
{0, { {"nis", 3} } }, {0, { {"notnivc", 7} } }, {0, { {"notnivb", 7} } }, {0, { {NULL, 0} } },
};
@@ -2232,7 +2232,7 @@ static const entity_stage3_row stage3_table_html5_02900[] = {
{0, { {"nearhk", 6} } }, {0, { {"searhk", 6} } }, {0, { {"swarhk", 6} } }, {0, { {"nwnear", 6} } },
{0, { {"toea", 4} } }, {0, { {"seswar", 6} } }, {0, { {"swnwar", 6} } }, {0, { {NULL, 0} } },
{0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {0, { {NULL, 0} } },
- {0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {1, { {(void *)multi_cp_html5_02933} } },
+ {0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {1, { {(void *)multi_cp_html5_02933, 0} } },
{0, { {NULL, 0} } }, {0, { {"cudarrr", 7} } }, {0, { {"ldca", 4} } }, {0, { {"rdca", 4} } },
{0, { {"cudarrl", 7} } }, {0, { {"larrpl", 6} } }, {0, { {NULL, 0} } }, {0, { {NULL, 0} } },
{0, { {"curarrm", 7} } }, {0, { {"cularrp", 7} } }, {0, { {NULL, 0} } }, {0, { {NULL, 0} } },
@@ -2280,8 +2280,8 @@ static const entity_stage3_row stage3_table_html5_029C0[] = {
{0, { {"olt", 3} } }, {0, { {"ogt", 3} } }, {0, { {"cirscir", 7} } }, {0, { {"cirE", 4} } },
{0, { {"solb", 4} } }, {0, { {"bsolb", 5} } }, {0, { {NULL, 0} } }, {0, { {NULL, 0} } },
{0, { {NULL, 0} } }, {0, { {"boxbox", 6} } }, {0, { {NULL, 0} } }, {0, { {NULL, 0} } },
- {0, { {NULL, 0} } }, {0, { {"trisb", 5} } }, {0, { {"rtriltri", 8} } }, {1, { {(void *)multi_cp_html5_029CF} } },
- {1, { {(void *)multi_cp_html5_029D0} } }, {0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {0, { {NULL, 0} } },
+ {0, { {NULL, 0} } }, {0, { {"trisb", 5} } }, {0, { {"rtriltri", 8} } }, {1, { {(void *)multi_cp_html5_029CF, 0} } },
+ {1, { {(void *)multi_cp_html5_029D0, 0} } }, {0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {0, { {NULL, 0} } },
{0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {0, { {NULL, 0} } },
{0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {0, { {NULL, 0} } },
{0, { {"iinfin", 6} } }, {0, { {"infintie", 8} } }, {0, { {"nvinfin", 7} } }, {0, { {NULL, 0} } },
@@ -2326,11 +2326,11 @@ static const entity_stage3_row stage3_table_html5_02A40[] = {
{0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {0, { {NULL, 0} } },
{0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {0, { {"sdote", 5} } }, {0, { {NULL, 0} } },
{0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {0, { {"simdot", 6} } }, {0, { {NULL, 0} } },
- {0, { {NULL, 0} } }, {1, { {(void *)multi_cp_html5_02A6D} } }, {0, { {"easter", 6} } }, {0, { {"apacir", 6} } },
- {1, { {(void *)multi_cp_html5_02A70} } }, {0, { {"eplus", 5} } }, {0, { {"pluse", 5} } }, {0, { {"Esim", 4} } },
+ {0, { {NULL, 0} } }, {1, { {(void *)multi_cp_html5_02A6D, 0} } }, {0, { {"easter", 6} } }, {0, { {"apacir", 6} } },
+ {1, { {(void *)multi_cp_html5_02A70, 0} } }, {0, { {"eplus", 5} } }, {0, { {"pluse", 5} } }, {0, { {"Esim", 4} } },
{0, { {"Colone", 6} } }, {0, { {"Equal", 5} } }, {0, { {NULL, 0} } }, {0, { {"ddotseq", 7} } },
{0, { {"equivDD", 7} } }, {0, { {"ltcir", 5} } }, {0, { {"gtcir", 5} } }, {0, { {"ltquest", 7} } },
- {0, { {"gtquest", 7} } }, {1, { {(void *)multi_cp_html5_02A7D} } }, {1, { {(void *)multi_cp_html5_02A7E} } }, {0, { {"lesdot", 6} } },
+ {0, { {"gtquest", 7} } }, {1, { {(void *)multi_cp_html5_02A7D, 0} } }, {1, { {(void *)multi_cp_html5_02A7E, 0} } }, {0, { {"lesdot", 6} } },
};
static const entity_stage3_row stage3_table_html5_02A80[] = {
@@ -2342,11 +2342,11 @@ static const entity_stage3_row stage3_table_html5_02A80[] = {
{0, { {"gesles", 6} } }, {0, { {"els", 3} } }, {0, { {"egs", 3} } }, {0, { {"elsdot", 6} } },
{0, { {"egsdot", 6} } }, {0, { {"el", 2} } }, {0, { {"eg", 2} } }, {0, { {NULL, 0} } },
{0, { {NULL, 0} } }, {0, { {"siml", 4} } }, {0, { {"simg", 4} } }, {0, { {"simlE", 5} } },
- {0, { {"simgE", 5} } }, {1, { {(void *)multi_cp_html5_02AA1} } }, {1, { {(void *)multi_cp_html5_02AA2} } }, {0, { {NULL, 0} } },
+ {0, { {"simgE", 5} } }, {1, { {(void *)multi_cp_html5_02AA1, 0} } }, {1, { {(void *)multi_cp_html5_02AA2, 0} } }, {0, { {NULL, 0} } },
{0, { {"glj", 3} } }, {0, { {"gla", 3} } }, {0, { {"ltcc", 4} } }, {0, { {"gtcc", 4} } },
{0, { {"lescc", 5} } }, {0, { {"gescc", 5} } }, {0, { {"smt", 3} } }, {0, { {"lat", 3} } },
- {1, { {(void *)multi_cp_html5_02AAC} } }, {1, { {(void *)multi_cp_html5_02AAD} } }, {0, { {"bumpE", 5} } }, {1, { {(void *)multi_cp_html5_02AAF} } },
- {1, { {(void *)multi_cp_html5_02AB0} } }, {0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {0, { {"prE", 3} } },
+ {1, { {(void *)multi_cp_html5_02AAC, 0} } }, {1, { {(void *)multi_cp_html5_02AAD, 0} } }, {0, { {"bumpE", 5} } }, {1, { {(void *)multi_cp_html5_02AAF, 0} } },
+ {1, { {(void *)multi_cp_html5_02AB0, 0} } }, {0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {0, { {"prE", 3} } },
{0, { {"scE", 3} } }, {0, { {"precneqq", 8} } }, {0, { {"scnE", 4} } }, {0, { {"precapprox", 10} } },
{0, { {"succapprox", 10} } }, {0, { {"precnapprox", 11} } }, {0, { {"succnapprox", 11} } }, {0, { {"Pr", 2} } },
{0, { {"Sc", 2} } }, {0, { {"subdot", 6} } }, {0, { {"supdot", 6} } }, {0, { {"subplus", 7} } },
@@ -2354,9 +2354,9 @@ static const entity_stage3_row stage3_table_html5_02A80[] = {
static const entity_stage3_row stage3_table_html5_02AC0[] = {
{0, { {"supplus", 7} } }, {0, { {"submult", 7} } }, {0, { {"supmult", 7} } }, {0, { {"subedot", 7} } },
- {0, { {"supedot", 7} } }, {1, { {(void *)multi_cp_html5_02AC5} } }, {1, { {(void *)multi_cp_html5_02AC6} } }, {0, { {"subsim", 6} } },
- {0, { {"supsim", 6} } }, {0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {1, { {(void *)multi_cp_html5_02ACB} } },
- {1, { {(void *)multi_cp_html5_02ACC} } }, {0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {0, { {"csub", 4} } },
+ {0, { {"supedot", 7} } }, {1, { {(void *)multi_cp_html5_02AC5, 0} } }, {1, { {(void *)multi_cp_html5_02AC6, 0} } }, {0, { {"subsim", 6} } },
+ {0, { {"supsim", 6} } }, {0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {1, { {(void *)multi_cp_html5_02ACB, 0} } },
+ {1, { {(void *)multi_cp_html5_02ACC, 0} } }, {0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {0, { {"csub", 4} } },
{0, { {"csup", 4} } }, {0, { {"csube", 5} } }, {0, { {"csupe", 5} } }, {0, { {"subsup", 6} } },
{0, { {"supsub", 6} } }, {0, { {"subsub", 6} } }, {0, { {"supsup", 6} } }, {0, { {"suphsub", 7} } },
{0, { {"supdsub", 7} } }, {0, { {"forkv", 5} } }, {0, { {"topfork", 7} } }, {0, { {"mlcp", 4} } },
@@ -2368,7 +2368,7 @@ static const entity_stage3_row stage3_table_html5_02AC0[] = {
{0, { {"midcir", 6} } }, {0, { {"topcir", 6} } }, {0, { {"nhpar", 5} } }, {0, { {"parsim", 6} } },
{0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {0, { {NULL, 0} } },
{0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {0, { {NULL, 0} } }, {0, { {NULL, 0} } },
- {0, { {NULL, 0} } }, {1, { {(void *)multi_cp_html5_02AFD} } }, {0, { {NULL, 0} } }, {0, { {NULL, 0} } },
+ {0, { {NULL, 0} } }, {1, { {(void *)multi_cp_html5_02AFD, 0} } }, {0, { {NULL, 0} } }, {0, { {NULL, 0} } },
};
static const entity_stage3_row stage3_table_html5_0FB00[] = {
diff --git a/ext/standard/html_tables/html_table_gen.php b/ext/standard/html_tables/html_table_gen.php
index d047b9df13..1261ab3df9 100644
--- a/ext/standard/html_tables/html_table_gen.php
+++ b/ext/standard/html_tables/html_table_gen.php
@@ -560,7 +560,7 @@ for ($i = 0; $i < 0x1E; $i++) {
echo "{0, { {\"$z\", ", strlen($z), "} } },";
else
echo "{1, { {(void *)", sprintf("multi_cp_{$ident}_%05X",
- ($i << 12) | ($k << 6) | $y ), "} } },";
+ ($i << 12) | ($k << 6) | $y ), ", 0} } },";
}
echo "\n};\n\n";
diff --git a/ext/standard/http.c b/ext/standard/http.c
index 1ad65d642c..fd28d4926b 100644
--- a/ext/standard/http.c
+++ b/ext/standard/http.c
@@ -110,7 +110,7 @@ PHPAPI int php_url_encode_hash_ex(HashTable *ht, smart_str *formstr,
char *ekey;
size_t ekey_len;
/* Is an integer key */
- ekey_len = spprintf(&ekey, 0, "%pd", idx);
+ ekey_len = spprintf(&ekey, 0, ZEND_LONG_FMT, idx);
newprefix_len = key_prefix_len + num_prefix_len + ekey_len + key_suffix_len + 3 /* %5B */;
newprefix = emalloc(newprefix_len + 1);
p = newprefix;
diff --git a/ext/standard/http_fopen_wrapper.c b/ext/standard/http_fopen_wrapper.c
index 1822566a21..32f06c0750 100644
--- a/ext/standard/http_fopen_wrapper.c
+++ b/ext/standard/http_fopen_wrapper.c
@@ -117,11 +117,9 @@ php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper,
php_url *resource = NULL;
int use_ssl;
int use_proxy = 0;
- char *scratch = NULL;
zend_string *tmp = NULL;
char *ua_str = NULL;
zval *ua_zval = NULL, *tmpzval = NULL, ssl_proxy_peer_name;
- size_t scratch_len = 0;
char location[HTTP_HEADER_BLOCK_SIZE];
zval response_header;
int reqok = 0;
@@ -134,8 +132,6 @@ php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper,
size_t transport_len;
int have_header = 0;
zend_bool request_fulluri = 0, ignore_errors = 0;
- char *protocol_version = NULL;
- int protocol_version_len = 3; /* Default: "1.0" */
struct timeval timeout;
char *user_headers = NULL;
int header_init = ((flags & HTTP_WRAPPER_HEADER_INIT) != 0);
@@ -144,6 +140,8 @@ php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper,
php_stream_filter *transfer_encoding = NULL;
int response_code;
zend_array *symbol_table;
+ smart_str req_buf = {0};
+ zend_bool custom_request_method;
ZVAL_UNDEF(&response_header);
tmp_line[0] = '\0';
@@ -361,6 +359,7 @@ finish:
redirect_max = (int)zval_get_long(tmpzval);
}
+ custom_request_method = 0;
if (context && (tmpzval = php_stream_context_get_option(context, "http", "method")) != NULL) {
if (Z_TYPE_P(tmpzval) == IS_STRING && Z_STRLEN_P(tmpzval) > 0) {
/* As per the RFC, automatically redirected requests MUST NOT use other methods than
@@ -369,22 +368,15 @@ finish:
|| (Z_STRLEN_P(tmpzval) == 3 && memcmp("GET", Z_STRVAL_P(tmpzval), 3) == 0)
|| (Z_STRLEN_P(tmpzval) == 4 && memcmp("HEAD",Z_STRVAL_P(tmpzval), 4) == 0)
) {
- scratch_len = strlen(path) + 29 + Z_STRLEN_P(tmpzval);
- scratch = emalloc(scratch_len);
- strlcpy(scratch, Z_STRVAL_P(tmpzval), Z_STRLEN_P(tmpzval) + 1);
- strncat(scratch, " ", 1);
+ custom_request_method = 1;
+ smart_str_append(&req_buf, Z_STR_P(tmpzval));
+ smart_str_appendc(&req_buf, ' ');
}
}
}
- if (context && (tmpzval = php_stream_context_get_option(context, "http", "protocol_version")) != NULL) {
- protocol_version_len = (int)spprintf(&protocol_version, 0, "%.1F", zval_get_double(tmpzval));
- }
-
- if (!scratch) {
- scratch_len = strlen(path) + 29 + protocol_version_len;
- scratch = emalloc(scratch_len);
- strncpy(scratch, "GET ", scratch_len);
+ if (!custom_request_method) {
+ smart_str_appends(&req_buf, "GET ");
}
/* Should we send the entire path in the request line, default to no. */
@@ -395,36 +387,37 @@ finish:
if (request_fulluri) {
/* Ask for everything */
- strcat(scratch, path);
+ smart_str_appends(&req_buf, path);
} else {
/* Send the traditional /path/to/file?query_string */
/* file */
if (resource->path && *resource->path) {
- strlcat(scratch, resource->path, scratch_len);
+ smart_str_appends(&req_buf, resource->path);
} else {
- strlcat(scratch, "/", scratch_len);
+ smart_str_appendc(&req_buf, '/');
}
/* query string */
if (resource->query) {
- strlcat(scratch, "?", scratch_len);
- strlcat(scratch, resource->query, scratch_len);
+ smart_str_appendc(&req_buf, '?');
+ smart_str_appends(&req_buf, resource->query);
}
}
/* protocol version we are speaking */
- if (protocol_version) {
- strlcat(scratch, " HTTP/", scratch_len);
- strlcat(scratch, protocol_version, scratch_len);
- strlcat(scratch, "\r\n", scratch_len);
+ if (context && (tmpzval = php_stream_context_get_option(context, "http", "protocol_version")) != NULL) {
+ char *protocol_version;
+ spprintf(&protocol_version, 0, "%.1F", zval_get_double(tmpzval));
+
+ smart_str_appends(&req_buf, " HTTP/");
+ smart_str_appends(&req_buf, protocol_version);
+ smart_str_appends(&req_buf, "\r\n");
+ efree(protocol_version);
} else {
- strlcat(scratch, " HTTP/1.0\r\n", scratch_len);
+ smart_str_appends(&req_buf, " HTTP/1.0\r\n");
}
- /* send it */
- php_stream_write(stream, scratch, strlen(scratch));
-
if (context && (tmpzval = php_stream_context_get_option(context, "http", "header")) != NULL) {
tmp = NULL;
@@ -536,11 +529,14 @@ finish:
/* auth header if it was specified */
if (((have_header & HTTP_HEADER_AUTH) == 0) && resource->user) {
+ /* make scratch large enough to hold the whole URL (over-estimate) */
+ size_t scratch_len = strlen(path) + 1;
+ char *scratch = emalloc(scratch_len);
zend_string *stmp;
+
/* decode the strings first */
php_url_decode(resource->user, strlen(resource->user));
- /* scratch is large enough, since it was made large enough for the whole URL */
strcpy(scratch, resource->user);
strcat(scratch, ":");
@@ -552,31 +548,33 @@ finish:
stmp = php_base64_encode((unsigned char*)scratch, strlen(scratch));
- if (snprintf(scratch, scratch_len, "Authorization: Basic %s\r\n", ZSTR_VAL(stmp)) > 0) {
- php_stream_write(stream, scratch, strlen(scratch));
- php_stream_notify_info(context, PHP_STREAM_NOTIFY_AUTH_REQUIRED, NULL, 0);
- }
+ smart_str_appends(&req_buf, "Authorization: Basic ");
+ smart_str_appends(&req_buf, ZSTR_VAL(stmp));
+ smart_str_appends(&req_buf, "\r\n");
+
+ php_stream_notify_info(context, PHP_STREAM_NOTIFY_AUTH_REQUIRED, NULL, 0);
zend_string_free(stmp);
+ efree(scratch);
}
/* if the user has configured who they are, send a From: line */
- if (((have_header & HTTP_HEADER_FROM) == 0) && FG(from_address)) {
- if (snprintf(scratch, scratch_len, "From: %s\r\n", FG(from_address)) > 0)
- php_stream_write(stream, scratch, strlen(scratch));
+ if (!(have_header & HTTP_HEADER_FROM) && FG(from_address)) {
+ smart_str_appends(&req_buf, "From: ");
+ smart_str_appends(&req_buf, FG(from_address));
+ smart_str_appends(&req_buf, "\r\n");
}
/* Send Host: header so name-based virtual hosts work */
if ((have_header & HTTP_HEADER_HOST) == 0) {
+ smart_str_appends(&req_buf, "Host: ");
+ smart_str_appends(&req_buf, resource->host);
if ((use_ssl && resource->port != 443 && resource->port != 0) ||
(!use_ssl && resource->port != 80 && resource->port != 0)) {
- if (snprintf(scratch, scratch_len, "Host: %s:%i\r\n", resource->host, resource->port) > 0)
- php_stream_write(stream, scratch, strlen(scratch));
- } else {
- if (snprintf(scratch, scratch_len, "Host: %s\r\n", resource->host) > 0) {
- php_stream_write(stream, scratch, strlen(scratch));
- }
+ smart_str_appendc(&req_buf, ':');
+ smart_str_append_unsigned(&req_buf, resource->port);
}
+ smart_str_appends(&req_buf, "\r\n");
}
/* Send a Connection: close header to avoid hanging when the server
@@ -586,7 +584,7 @@ finish:
* HTTP/1.0 to avoid issues when the server respond with a HTTP/1.1
* keep-alive response, which is the preferred response type. */
if ((have_header & HTTP_HEADER_CONNECTION) == 0) {
- php_stream_write_string(stream, "Connection: close\r\n");
+ smart_str_appends(&req_buf, "Connection: close\r\n");
}
if (context &&
@@ -609,7 +607,7 @@ finish:
ua = emalloc(ua_len + 1);
if ((ua_len = slprintf(ua, ua_len, _UA_HEADER, ua_str)) > 0) {
ua[ua_len] = 0;
- php_stream_write(stream, ua, ua_len);
+ smart_str_appendl(&req_buf, ua, ua_len);
} else {
php_error_docref(NULL, E_WARNING, "Cannot construct User-agent header");
}
@@ -628,13 +626,14 @@ finish:
(tmpzval = php_stream_context_get_option(context, "http", "content")) != NULL &&
Z_TYPE_P(tmpzval) == IS_STRING && Z_STRLEN_P(tmpzval) > 0
) {
- scratch_len = slprintf(scratch, scratch_len, "Content-Length: %d\r\n", Z_STRLEN_P(tmpzval));
- php_stream_write(stream, scratch, scratch_len);
+ smart_str_appends(&req_buf, "Content-Length: ");
+ smart_str_append_unsigned(&req_buf, Z_STRLEN_P(tmpzval));
+ smart_str_appends(&req_buf, "\r\n");
have_header |= HTTP_HEADER_CONTENT_LENGTH;
}
- php_stream_write(stream, user_headers, strlen(user_headers));
- php_stream_write(stream, "\r\n", sizeof("\r\n")-1);
+ smart_str_appends(&req_buf, user_headers);
+ smart_str_appends(&req_buf, "\r\n");
efree(user_headers);
}
@@ -643,20 +642,23 @@ finish:
(tmpzval = php_stream_context_get_option(context, "http", "content")) != NULL &&
Z_TYPE_P(tmpzval) == IS_STRING && Z_STRLEN_P(tmpzval) > 0) {
if (!(have_header & HTTP_HEADER_CONTENT_LENGTH)) {
- scratch_len = slprintf(scratch, scratch_len, "Content-Length: %d\r\n", Z_STRLEN_P(tmpzval));
- php_stream_write(stream, scratch, scratch_len);
+ smart_str_appends(&req_buf, "Content-Length: ");
+ smart_str_append_unsigned(&req_buf, Z_STRLEN_P(tmpzval));
+ smart_str_appends(&req_buf, "\r\n");
}
if (!(have_header & HTTP_HEADER_TYPE)) {
- php_stream_write(stream, "Content-Type: application/x-www-form-urlencoded\r\n",
- sizeof("Content-Type: application/x-www-form-urlencoded\r\n") - 1);
+ smart_str_appends(&req_buf, "Content-Type: application/x-www-form-urlencoded\r\n");
php_error_docref(NULL, E_NOTICE, "Content-type not specified assuming application/x-www-form-urlencoded");
}
- php_stream_write(stream, "\r\n", sizeof("\r\n")-1);
- php_stream_write(stream, Z_STRVAL_P(tmpzval), Z_STRLEN_P(tmpzval));
+ smart_str_appends(&req_buf, "\r\n");
+ smart_str_appendl(&req_buf, Z_STRVAL_P(tmpzval), Z_STRLEN_P(tmpzval));
} else {
- php_stream_write(stream, "\r\n", sizeof("\r\n")-1);
+ smart_str_appends(&req_buf, "\r\n");
}
+ /* send it */
+ php_stream_write(stream, ZSTR_VAL(req_buf.s), ZSTR_LEN(req_buf.s));
+
location[0] = '\0';
symbol_table = zend_rebuild_symbol_table();
@@ -936,18 +938,13 @@ finish:
}
}
out:
- if (protocol_version) {
- efree(protocol_version);
- }
+
+ smart_str_free(&req_buf);
if (http_header_line) {
efree(http_header_line);
}
- if (scratch) {
- efree(scratch);
- }
-
if (resource) {
php_url_free(resource);
}
@@ -1012,7 +1009,8 @@ static php_stream_wrapper_ops http_stream_wops = {
NULL, /* unlink */
NULL, /* rename */
NULL, /* mkdir */
- NULL /* rmdir */
+ NULL, /* rmdir */
+ NULL
};
PHPAPI php_stream_wrapper php_stream_http_wrapper = {
diff --git a/ext/standard/image.c b/ext/standard/image.c
index 2074d289df..22b2d7ed2f 100644
--- a/ext/standard/image.c
+++ b/ext/standard/image.c
@@ -55,6 +55,8 @@ PHPAPI const char php_sig_jp2[12] = {(char)0x00, (char)0x00, (char)0x00, (char)0
(char)0x0d, (char)0x0a, (char)0x87, (char)0x0a};
PHPAPI const char php_sig_iff[4] = {'F','O','R','M'};
PHPAPI const char php_sig_ico[4] = {(char)0x00, (char)0x00, (char)0x01, (char)0x00};
+PHPAPI const char php_sig_riff[4] = {'R', 'I', 'F', 'F'};
+PHPAPI const char php_sig_webp[4] = {'W', 'E', 'B', 'P'};
/* REMEMBER TO ADD MIME-TYPE TO FUNCTION php_image_type_to_mime_type */
/* PCX must check first 64bytes and byte 0=0x0a and byte2 < 0x06 */
@@ -92,6 +94,7 @@ PHP_MINIT_FUNCTION(imagetypes)
REGISTER_LONG_CONSTANT("IMAGETYPE_JPEG2000",IMAGE_FILETYPE_JPC, CONST_CS | CONST_PERSISTENT); /* keep alias */
REGISTER_LONG_CONSTANT("IMAGETYPE_XBM", IMAGE_FILETYPE_XBM, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("IMAGETYPE_ICO", IMAGE_FILETYPE_ICO, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("IMAGETYPE_WEBP", IMAGE_FILETYPE_WEBP, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("IMAGETYPE_UNKNOWN", IMAGE_FILETYPE_UNKNOWN, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("IMAGETYPE_COUNT", IMAGE_FILETYPE_COUNT, CONST_CS | CONST_PERSISTENT);
return SUCCESS;
@@ -207,23 +210,27 @@ static struct gfxinfo *php_handle_swc(php_stream * stream)
unsigned char *b, *buf = NULL;
zend_string *bufz;
- b = ecalloc(1, len + 1);
-
- if (php_stream_seek(stream, 5, SEEK_CUR))
+ if (php_stream_seek(stream, 5, SEEK_CUR)) {
return NULL;
+ }
- if (php_stream_read(stream, (char *) a, sizeof(a)) != sizeof(a))
+ if (php_stream_read(stream, (char *) a, sizeof(a)) != sizeof(a)) {
return NULL;
+ }
+
+ b = ecalloc(1, len + 1);
if (uncompress(b, &len, a, sizeof(a)) != Z_OK) {
/* failed to decompress the file, will try reading the rest of the file */
if (php_stream_seek(stream, 8, SEEK_SET)) {
+ efree(b);
return NULL;
}
bufz = php_stream_copy_to_mem(stream, PHP_STREAM_COPY_ALL, 0);
if (!bufz) {
+ efree(b);
return NULL;
}
@@ -1115,6 +1122,53 @@ static struct gfxinfo *php_handle_ico(php_stream * stream)
}
/* }}} */
+/* {{{ php_handle_webp
+ */
+static struct gfxinfo *php_handle_webp(php_stream * stream)
+{
+ struct gfxinfo *result = NULL;
+ const char sig[3] = {'V', 'P', '8'};
+ unsigned char buf[18];
+ char format;
+
+ if (php_stream_read(stream, (char *) buf, 18) != 18)
+ return NULL;
+
+ if (memcmp(buf, sig, 3)) {
+ return NULL;
+ }
+ switch (buf[3]) {
+ case ' ':
+ case 'L':
+ case 'X':
+ format = buf[3];
+ break;
+ default:
+ return NULL;
+ }
+
+ result = (struct gfxinfo *) ecalloc(1, sizeof(struct gfxinfo));
+
+ switch (format) {
+ case ' ':
+ result->width = buf[14] + ((buf[15] & 0x3F) << 8);
+ result->height = buf[16] + ((buf[17] & 0x3F) << 8);
+ break;
+ case 'L':
+ result->width = buf[9] + ((buf[10] & 0x3F) << 8) + 1;
+ result->height = (buf[10] >> 6) + (buf[11] << 2) + ((buf[12] & 0xF) << 10) + 1;
+ break;
+ case 'X':
+ result->width = buf[12] + (buf[13] << 8) + (buf[14] << 16) + 1;
+ result->height = buf[15] + (buf[16] << 8) + (buf[17] << 16) + 1;
+ break;
+ }
+ result->bits = 8; /* always 1 byte */
+
+ return result;
+}
+/* }}} */
+
/* {{{ php_image_type_to_mime_type
* Convert internal image_type to mime type */
PHPAPI char * php_image_type_to_mime_type(int image_type)
@@ -1148,6 +1202,8 @@ PHPAPI char * php_image_type_to_mime_type(int image_type)
return "image/xbm";
case IMAGE_FILETYPE_ICO:
return "image/vnd.microsoft.icon";
+ case IMAGE_FILETYPE_WEBP:
+ return "image/webp";
default:
case IMAGE_FILETYPE_UNKNOWN:
return "application/octet-stream"; /* suppose binary format */
@@ -1212,6 +1268,8 @@ PHP_FUNCTION(image_type_to_extension)
RETURN_STRING(".xbm" + !inc_dot);
case IMAGE_FILETYPE_ICO:
RETURN_STRING(".ico" + !inc_dot);
+ case IMAGE_FILETYPE_WEBP:
+ RETURN_STRING(".webp" + !inc_dot);
}
RETURN_FALSE;
@@ -1257,6 +1315,16 @@ PHPAPI int php_getimagetype(php_stream * stream, char *filetype)
return IMAGE_FILETYPE_BMP;
} else if (!memcmp(filetype, php_sig_jpc, 3)) {
return IMAGE_FILETYPE_JPC;
+ } else if (!memcmp(filetype, php_sig_riff, 3)) {
+ if (php_stream_read(stream, filetype+3, 9) != 9) {
+ php_error_docref(NULL, E_NOTICE, "Read error!");
+ return IMAGE_FILETYPE_UNKNOWN;
+ }
+ if (!memcmp(filetype+8, php_sig_webp, 4)) {
+ return IMAGE_FILETYPE_WEBP;
+ } else {
+ return IMAGE_FILETYPE_UNKNOWN;
+ }
}
if (php_stream_read(stream, filetype+3, 1) != 1) {
@@ -1361,6 +1429,9 @@ static void php_getimagesize_from_stream(php_stream *stream, zval *info, INTERNA
case IMAGE_FILETYPE_ICO:
result = php_handle_ico(stream);
break;
+ case IMAGE_FILETYPE_WEBP:
+ result = php_handle_webp(stream);
+ break;
default:
case IMAGE_FILETYPE_UNKNOWN:
break;
diff --git a/ext/standard/incomplete_class.c b/ext/standard/incomplete_class.c
index c2a61f6236..db9b25ef12 100644
--- a/ext/standard/incomplete_class.c
+++ b/ext/standard/incomplete_class.c
@@ -54,7 +54,8 @@ static zval *incomplete_class_get_property(zval *object, zval *member, int type,
incomplete_class_message(object, E_NOTICE);
if (type == BP_VAR_W || type == BP_VAR_RW) {
- return &EG(error_zval);
+ ZVAL_ERROR(rv);
+ return rv;
} else {
return &EG(uninitialized_zval);
}
diff --git a/ext/standard/info.c b/ext/standard/info.c
index 6b06f02e41..76802fa842 100644
--- a/ext/standard/info.c
+++ b/ext/standard/info.c
@@ -229,9 +229,11 @@ static void php_print_gpcse_array(char *name, uint name_length)
}
if (Z_TYPE_P(tmp) == IS_ARRAY) {
if (!sapi_module.phpinfo_as_text) {
+ zend_string *str = zend_print_zval_r_to_str(tmp, 0);
php_info_print("<pre>");
- zend_print_zval_r_ex((zend_write_func_t) php_info_print_html_esc, tmp, 0);
+ php_info_print_html_esc(ZSTR_VAL(str), ZSTR_LEN(str));
php_info_print("</pre>");
+ zend_string_release(str);
} else {
zend_print_zval_r(tmp, 0);
}
diff --git a/ext/standard/iptc.c b/ext/standard/iptc.c
index 1a2cb420f5..3d0cf8d63a 100644
--- a/ext/standard/iptc.c
+++ b/ext/standard/iptc.c
@@ -191,7 +191,7 @@ PHP_FUNCTION(iptcembed)
zend_long spool = 0;
FILE *fp;
unsigned int marker, done = 0;
- int inx;
+ size_t inx;
zend_string *spoolbuf = NULL;
unsigned char *poi = NULL;
zend_stat_t sb;
@@ -314,7 +314,7 @@ PHP_FUNCTION(iptcembed)
Parse binary IPTC-data into associative array */
PHP_FUNCTION(iptcparse)
{
- int inx = 0, len;
+ size_t inx = 0, len;
unsigned int tagsfound = 0;
unsigned char *buffer, recnum, dataset;
char *str, key[16];
@@ -358,7 +358,7 @@ PHP_FUNCTION(iptcparse)
inx += 2;
}
- if ((len < 0) || (len > str_len) || (inx + len) > str_len) {
+ if ((len > str_len) || (inx + len) > str_len) {
break;
}
diff --git a/ext/standard/lcg.c b/ext/standard/lcg.c
index 5128239a99..af826c547b 100644
--- a/ext/standard/lcg.c
+++ b/ext/standard/lcg.c
@@ -54,8 +54,8 @@ static void lcg_seed(void);
PHPAPI double php_combined_lcg(void) /* {{{ */
{
- php_int32 q;
- php_int32 z;
+ int32_t q;
+ int32_t z;
if (!LCG(seeded)) {
lcg_seed();
diff --git a/ext/standard/link_win32.c b/ext/standard/link_win32.c
index 13ee88dfad..53ce7fbb4d 100644
--- a/ext/standard/link_win32.c
+++ b/ext/standard/link_win32.c
@@ -117,22 +117,7 @@ PHP_FUNCTION(symlink)
char dirname[MAXPATHLEN];
size_t len;
DWORD attr;
- HINSTANCE kernel32;
- typedef BOOLEAN (WINAPI *csla_func)(LPCSTR, LPCSTR, DWORD);
- csla_func pCreateSymbolicLinkA;
-
- kernel32 = LoadLibrary("kernel32.dll");
-
- if (kernel32) {
- pCreateSymbolicLinkA = (csla_func)GetProcAddress(kernel32, "CreateSymbolicLinkA");
- if (pCreateSymbolicLinkA == NULL) {
- php_error_docref(NULL, E_WARNING, "Can't call CreateSymbolicLinkA");
- RETURN_FALSE;
- }
- } else {
- php_error_docref(NULL, E_WARNING, "Can't call get a handle on kernel32.dll");
- RETURN_FALSE;
- }
+ wchar_t *dstw, *srcw;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "pp", &topath, &topath_len, &frompath, &frompath_len) == FAILURE) {
return;
@@ -166,21 +151,38 @@ PHP_FUNCTION(symlink)
RETURN_FALSE;
}
- if ((attr = GetFileAttributes(topath)) == INVALID_FILE_ATTRIBUTES) {
- php_error_docref(NULL, E_WARNING, "Could not fetch file information(error %d)", GetLastError());
- RETURN_FALSE;
+ dstw = php_win32_ioutil_any_to_w(topath);
+ if (!dstw) {
+ php_error_docref(NULL, E_WARNING, "UTF-16 conversion failed (error %d)", GetLastError());
+ RETURN_FALSE;
+ }
+ if ((attr = GetFileAttributesW(dstw)) == INVALID_FILE_ATTRIBUTES) {
+ free(dstw);
+ php_error_docref(NULL, E_WARNING, "Could not fetch file information(error %d)", GetLastError());
+ RETURN_FALSE;
}
+ srcw = php_win32_ioutil_any_to_w(source_p);
+ if (!srcw) {
+ free(dstw);
+ php_error_docref(NULL, E_WARNING, "UTF-16 conversion failed (error %d)", GetLastError());
+ RETURN_FALSE;
+ }
/* For the source, an expanded path must be used (in ZTS an other thread could have changed the CWD).
* For the target the exact string given by the user must be used, relative or not, existing or not.
* The target is relative to the link itself, not to the CWD. */
- ret = pCreateSymbolicLinkA(source_p, topath, (attr & FILE_ATTRIBUTE_DIRECTORY ? 1 : 0));
+ ret = CreateSymbolicLinkW(srcw, dstw, (attr & FILE_ATTRIBUTE_DIRECTORY ? 1 : 0));
if (!ret) {
+ free(dstw);
+ free(srcw);
php_error_docref(NULL, E_WARNING, "Cannot create symlink, error code(%d)", GetLastError());
RETURN_FALSE;
}
+ free(dstw);
+ free(srcw);
+
RETURN_TRUE;
}
/* }}} */
diff --git a/ext/standard/mail.c b/ext/standard/mail.c
index e845137375..26272cd76d 100644
--- a/ext/standard/mail.c
+++ b/ext/standard/mail.c
@@ -286,34 +286,35 @@ PHPAPI int php_mail(char *to, char *subject, char *message, char *headers, char
return val; \
if (mail_log && *mail_log) {
- char *tmp;
- time_t curtime;
- size_t l;
- zend_string *date_str;
+ char *logline;
- time(&curtime);
- date_str = php_format_date("d-M-Y H:i:s e", 13, curtime, 1);
-
- l = spprintf(&tmp, 0, "[%s] mail() on [%s:%d]: To: %s -- Headers: %s\n", ZSTR_VAL(date_str), zend_get_executed_filename(), zend_get_executed_lineno(), to, hdr ? hdr : "");
-
- zend_string_free(date_str);
+ spprintf(&logline, 0, "mail() on [%s:%d]: To: %s -- Headers: %s -- Subject: %s", zend_get_executed_filename(), zend_get_executed_lineno(), to, hdr ? hdr : "", subject);
if (hdr) {
- php_mail_log_crlf_to_spaces(tmp);
+ php_mail_log_crlf_to_spaces(logline);
}
if (!strcmp(mail_log, "syslog")) {
- /* Drop the final space when logging to syslog. */
- tmp[l - 1] = 0;
- php_mail_log_to_syslog(tmp);
- }
- else {
- /* Convert the final space to a newline when logging to file. */
- tmp[l - 1] = '\n';
- php_mail_log_to_file(mail_log, tmp, l);
+ php_mail_log_to_syslog(logline);
+ } else {
+ /* Add date when logging to file */
+ char *tmp;
+ time_t curtime;
+ zend_string *date_str;
+ size_t len;
+
+
+ time(&curtime);
+ date_str = php_format_date("d-M-Y H:i:s e", 13, curtime, 1);
+ len = spprintf(&tmp, 0, "[%s] %s%s", date_str->val, logline, PHP_EOL);
+
+ php_mail_log_to_file(mail_log, tmp, len);
+
+ zend_string_free(date_str);
+ efree(tmp);
}
- efree(tmp);
+ efree(logline);
}
if (PG(mail_x_header)) {
diff --git a/ext/standard/math.c b/ext/standard/math.c
index 9850de39b6..eaf4793ba6 100644
--- a/ext/standard/math.c
+++ b/ext/standard/math.c
@@ -93,18 +93,6 @@ static inline double php_intpow10(int power) {
}
/* }}} */
-/* {{{ php_math_is_finite */
-static inline int php_math_is_finite(double value) {
-#if defined(PHP_WIN32)
- return _finite(value);
-#elif defined(isfinite)
- return isfinite(value);
-#else
- return value == value && (value == 0. || value * 2. != value);
-#endif
-}
-/* }}} */
-
/* {{{ php_round_helper
Actually performs the rounding of a value to integer in a certain mode */
static inline double php_round_helper(double value, int mode) {
@@ -142,7 +130,7 @@ PHPAPI double _php_math_round(double value, int places, int mode) {
double tmp_value;
int precision_places;
- if (!php_math_is_finite(value)) {
+ if (!zend_finite(value)) {
return value;
}
@@ -1093,11 +1081,11 @@ PHP_FUNCTION(base_convert)
convert_to_string_ex(number);
if (frombase < 2 || frombase > 36) {
- php_error_docref(NULL, E_WARNING, "Invalid `from base' (%pd)", frombase);
+ php_error_docref(NULL, E_WARNING, "Invalid `from base' (" ZEND_LONG_FMT ")", frombase);
RETURN_FALSE;
}
if (tobase < 2 || tobase > 36) {
- php_error_docref(NULL, E_WARNING, "Invalid `to base' (%pd)", tobase);
+ php_error_docref(NULL, E_WARNING, "Invalid `to base' (" ZEND_LONG_FMT ")", tobase);
RETURN_FALSE;
}
diff --git a/ext/standard/md5.c b/ext/standard/md5.c
index 648d89e71a..9056f5b7ec 100644
--- a/ext/standard/md5.c
+++ b/ext/standard/md5.c
@@ -169,16 +169,16 @@ PHP_NAMED_FUNCTION(php_if_md5_file)
*/
#if defined(__i386__) || defined(__x86_64__) || defined(__vax__)
# define SET(n) \
- (*(php_uint32 *)&ptr[(n) * 4])
+ (*(uint32_t *)&ptr[(n) * 4])
# define GET(n) \
SET(n)
#else
# define SET(n) \
(ctx->block[(n)] = \
- (php_uint32)ptr[(n) * 4] | \
- ((php_uint32)ptr[(n) * 4 + 1] << 8) | \
- ((php_uint32)ptr[(n) * 4 + 2] << 16) | \
- ((php_uint32)ptr[(n) * 4 + 3] << 24))
+ (uint32_t)ptr[(n) * 4] | \
+ ((uint32_t)ptr[(n) * 4 + 1] << 8) | \
+ ((uint32_t)ptr[(n) * 4 + 2] << 16) | \
+ ((uint32_t)ptr[(n) * 4 + 3] << 24))
# define GET(n) \
(ctx->block[(n)])
#endif
@@ -190,8 +190,8 @@ PHP_NAMED_FUNCTION(php_if_md5_file)
static const void *body(PHP_MD5_CTX *ctx, const void *data, size_t size)
{
const unsigned char *ptr;
- php_uint32 a, b, c, d;
- php_uint32 saved_a, saved_b, saved_c, saved_d;
+ uint32_t a, b, c, d;
+ uint32_t saved_a, saved_b, saved_c, saved_d;
ptr = data;
@@ -307,8 +307,8 @@ PHPAPI void PHP_MD5Init(PHP_MD5_CTX *ctx)
PHPAPI void PHP_MD5Update(PHP_MD5_CTX *ctx, const void *data, size_t size)
{
- php_uint32 saved_lo;
- php_uint32 used, free;
+ uint32_t saved_lo;
+ uint32_t used, free;
saved_lo = ctx->lo;
if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo) {
@@ -342,7 +342,7 @@ PHPAPI void PHP_MD5Update(PHP_MD5_CTX *ctx, const void *data, size_t size)
PHPAPI void PHP_MD5Final(unsigned char *result, PHP_MD5_CTX *ctx)
{
- php_uint32 used, free;
+ uint32_t used, free;
used = ctx->lo & 0x3f;
diff --git a/ext/standard/md5.h b/ext/standard/md5.h
index 7c882c5a3e..2747388a6f 100644
--- a/ext/standard/md5.h
+++ b/ext/standard/md5.h
@@ -42,10 +42,10 @@ PHP_NAMED_FUNCTION(php_if_md5_file);
/* MD5 context. */
typedef struct {
- php_uint32 lo, hi;
- php_uint32 a, b, c, d;
+ uint32_t lo, hi;
+ uint32_t a, b, c, d;
unsigned char buffer[64];
- php_uint32 block[16];
+ uint32_t block[16];
} PHP_MD5_CTX;
PHPAPI void PHP_MD5Init(PHP_MD5_CTX *ctx);
diff --git a/ext/standard/metaphone.c b/ext/standard/metaphone.c
index 9bf67bbda8..4adf4eb283 100644
--- a/ext/standard/metaphone.c
+++ b/ext/standard/metaphone.c
@@ -168,7 +168,7 @@ static char Lookahead(char *word, int how_far)
static int metaphone(unsigned char *word, size_t word_len, zend_long max_phonemes, zend_string **phoned_word, int traditional)
{
int w_idx = 0; /* point in the phonization we're at. */
- int p_idx = 0; /* end of the phoned phrase */
+ size_t p_idx = 0; /* end of the phoned phrase */
size_t max_buffer_len = 0; /* maximum length of the destination buffer */
/*-- Parameter checks --*/
@@ -265,7 +265,7 @@ static int metaphone(unsigned char *word, size_t word_len, zend_long max_phoneme
/* On to the metaphoning */
for (; Curr_Letter != '\0' &&
- (max_phonemes == 0 || Phone_Len < max_phonemes);
+ (max_phonemes == 0 || Phone_Len < (size_t)max_phonemes);
w_idx++) {
/* How many letters to skip because an eariler encoding handled
* multiple letters */
diff --git a/ext/standard/mt_rand.c b/ext/standard/mt_rand.c
new file mode 100644
index 0000000000..0e2fe5143a
--- /dev/null
+++ b/ext/standard/mt_rand.c
@@ -0,0 +1,336 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 7 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2017 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Rasmus Lerdorf <rasmus@php.net> |
+ | Zeev Suraski <zeev@zend.com> |
+ | Pedro Melo <melo@ip.pt> |
+ | Sterling Hughes <sterling@php.net> |
+ | |
+ | Based on code from: Richard J. Wagner <rjwagner@writeme.com> |
+ | Makoto Matsumoto <matumoto@math.keio.ac.jp> |
+ | Takuji Nishimura |
+ | Shawn Cokus <Cokus@math.washington.edu> |
+ +----------------------------------------------------------------------+
+ */
+/* $Id$ */
+
+#include "php.h"
+#include "php_rand.h"
+#include "php_mt_rand.h"
+
+/* MT RAND FUNCTIONS */
+
+/*
+ The following php_mt_...() functions are based on a C++ class MTRand by
+ Richard J. Wagner. For more information see the web page at
+ http://www-personal.engin.umich.edu/~wagnerr/MersenneTwister.html
+
+ Mersenne Twister random number generator -- a C++ class MTRand
+ Based on code by Makoto Matsumoto, Takuji Nishimura, and Shawn Cokus
+ Richard J. Wagner v1.0 15 May 2003 rjwagner@writeme.com
+
+ The Mersenne Twister is an algorithm for generating random numbers. It
+ was designed with consideration of the flaws in various other generators.
+ The period, 2^19937-1, and the order of equidistribution, 623 dimensions,
+ are far greater. The generator is also fast; it avoids multiplication and
+ division, and it benefits from caches and pipelines. For more information
+ see the inventors' web page at http://www.math.keio.ac.jp/~matumoto/emt.html
+
+ Reference
+ M. Matsumoto and T. Nishimura, "Mersenne Twister: A 623-Dimensionally
+ Equidistributed Uniform Pseudo-Random Number Generator", ACM Transactions on
+ Modeling and Computer Simulation, Vol. 8, No. 1, January 1998, pp 3-30.
+
+ Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
+ Copyright (C) 2000 - 2003, Richard J. Wagner
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ 3. The names of its contributors may not 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.
+*/
+
+#define N MT_N /* length of state vector */
+#define M (397) /* a period parameter */
+#define hiBit(u) ((u) & 0x80000000U) /* mask all but highest bit of u */
+#define loBit(u) ((u) & 0x00000001U) /* mask all but lowest bit of u */
+#define loBits(u) ((u) & 0x7FFFFFFFU) /* mask the highest bit of u */
+#define mixBits(u, v) (hiBit(u)|loBits(v)) /* move hi bit of u to hi bit of v */
+
+#define twist(m,u,v) (m ^ (mixBits(u,v)>>1) ^ ((uint32_t)(-(int32_t)(loBit(v))) & 0x9908b0dfU))
+#define twist_php(m,u,v) (m ^ (mixBits(u,v)>>1) ^ ((uint32_t)(-(int32_t)(loBit(u))) & 0x9908b0dfU))
+
+/* {{{ php_mt_initialize
+ */
+static inline void php_mt_initialize(uint32_t seed, uint32_t *state)
+{
+ /* Initialize generator state with seed
+ See Knuth TAOCP Vol 2, 3rd Ed, p.106 for multiplier.
+ In previous versions, most significant bits (MSBs) of the seed affect
+ only MSBs of the state array. Modified 9 Jan 2002 by Makoto Matsumoto. */
+
+ register uint32_t *s = state;
+ register uint32_t *r = state;
+ register int i = 1;
+
+ *s++ = seed & 0xffffffffU;
+ for( ; i < N; ++i ) {
+ *s++ = ( 1812433253U * ( *r ^ (*r >> 30) ) + i ) & 0xffffffffU;
+ r++;
+ }
+}
+/* }}} */
+
+/* {{{ php_mt_reload
+ */
+static inline void php_mt_reload(void)
+{
+ /* Generate N new values in state
+ Made clearer and faster by Matthew Bellew (matthew.bellew@home.com) */
+
+ register uint32_t *state = BG(state);
+ register uint32_t *p = state;
+ register int i;
+
+ if (BG(mt_rand_mode) == MT_RAND_MT19937) {
+ for (i = N - M; i--; ++p)
+ *p = twist(p[M], p[0], p[1]);
+ for (i = M; --i; ++p)
+ *p = twist(p[M-N], p[0], p[1]);
+ *p = twist(p[M-N], p[0], state[0]);
+ }
+ else {
+ for (i = N - M; i--; ++p)
+ *p = twist_php(p[M], p[0], p[1]);
+ for (i = M; --i; ++p)
+ *p = twist_php(p[M-N], p[0], p[1]);
+ *p = twist_php(p[M-N], p[0], state[0]);
+ }
+ BG(left) = N;
+ BG(next) = state;
+}
+/* }}} */
+
+/* {{{ php_mt_srand
+ */
+PHPAPI void php_mt_srand(uint32_t seed)
+{
+ /* Seed the generator with a simple uint32 */
+ php_mt_initialize(seed, BG(state));
+ php_mt_reload();
+
+ /* Seed only once */
+ BG(mt_rand_is_seeded) = 1;
+}
+/* }}} */
+
+/* {{{ php_mt_rand
+ */
+PHPAPI uint32_t php_mt_rand(void)
+{
+ /* Pull a 32-bit integer from the generator state
+ Every other access function simply transforms the numbers extracted here */
+
+ register uint32_t s1;
+
+ if (UNEXPECTED(!BG(mt_rand_is_seeded))) {
+ php_mt_srand(GENERATE_SEED());
+ }
+
+ if (BG(left) == 0) {
+ php_mt_reload();
+ }
+ --BG(left);
+
+ s1 = *BG(next)++;
+ s1 ^= (s1 >> 11);
+ s1 ^= (s1 << 7) & 0x9d2c5680U;
+ s1 ^= (s1 << 15) & 0xefc60000U;
+ return ( s1 ^ (s1 >> 18) );
+}
+/* }}} */
+
+/* {{{ proto void mt_srand([int seed])
+ Seeds Mersenne Twister random number generator */
+PHP_FUNCTION(mt_srand)
+{
+ zend_long seed = 0;
+ zend_long mode = MT_RAND_MT19937;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "|ll", &seed, &mode) == FAILURE)
+ return;
+
+ if (ZEND_NUM_ARGS() == 0)
+ seed = GENERATE_SEED();
+
+ switch (mode) {
+ case MT_RAND_PHP:
+ BG(mt_rand_mode) = MT_RAND_PHP;
+ break;
+ default:
+ BG(mt_rand_mode) = MT_RAND_MT19937;
+ }
+
+ php_mt_srand(seed);
+}
+/* }}} */
+
+/* {{{ php_mt_rand_range
+ */
+PHPAPI zend_long php_mt_rand_range(zend_long min, zend_long max)
+{
+ zend_ulong umax = max - min;
+ zend_ulong limit;
+ zend_ulong result;
+
+ result = php_mt_rand();
+#if ZEND_ULONG_MAX > UINT32_MAX
+ if (umax > UINT32_MAX) {
+ result = (result << 32) | php_mt_rand();
+ }
+#endif
+
+ /* Special case where no modulus is required */
+ if (UNEXPECTED(umax == ZEND_ULONG_MAX)) {
+ return (zend_long)result;
+ }
+
+ /* Increment the max so the range is inclusive of max */
+ umax++;
+
+ /* Powers of two are not biased */
+ if (EXPECTED((umax & (umax - 1)) != 0)) {
+ /* Ceiling under which ZEND_LONG_MAX % max == 0 */
+ limit = ZEND_ULONG_MAX - (ZEND_ULONG_MAX % umax) - 1;
+
+ /* Discard numbers over the limit to avoid modulo bias */
+ while (UNEXPECTED(result > limit)) {
+#if ZEND_ULONG_MAX > UINT32_MAX
+ if (umax > UINT32_MAX) {
+ result = (result << 32) | php_mt_rand();
+ }
+ else {
+ result = php_mt_rand();
+ }
+#else
+ result = php_mt_rand();
+#endif
+ }
+ }
+
+ return (zend_long)((result % umax) + min);
+}
+/* }}} */
+
+/* {{{ php_mt_rand_common
+ * rand() allows min > max, mt_rand does not */
+PHPAPI zend_long php_mt_rand_common(zend_long min, zend_long max)
+{
+ zend_long n;
+
+ if (BG(mt_rand_mode) == MT_RAND_MT19937) {
+ return php_mt_rand_range(min, max);
+ }
+
+ /* Legacy mode deliberately not inside php_mt_rand_range()
+ * to prevent other functions being affected */
+ n = (zend_long)php_mt_rand() >> 1;
+ RAND_RANGE_BADSCALING(n, min, max, PHP_MT_RAND_MAX);
+
+ return n;
+}
+/* }}} */
+
+/* {{{ proto int mt_rand([int min, int max])
+ Returns a random number from Mersenne Twister */
+PHP_FUNCTION(mt_rand)
+{
+ zend_long min;
+ zend_long max;
+ int argc = ZEND_NUM_ARGS();
+
+ if (argc == 0) {
+ // genrand_int31 in mt19937ar.c performs a right shift
+ RETURN_LONG(php_mt_rand() >> 1);
+ }
+
+ if (zend_parse_parameters(argc, "ll", &min, &max) == FAILURE) {
+ return;
+ }
+
+ if (UNEXPECTED(max < min)) {
+ php_error_docref(NULL, E_WARNING, "max(" ZEND_LONG_FMT ") is smaller than min(" ZEND_LONG_FMT ")", max, min);
+ RETURN_FALSE;
+ }
+
+ RETURN_LONG(php_mt_rand_common(min, max));
+}
+/* }}} */
+
+/* {{{ proto int mt_getrandmax(void)
+ Returns the maximum value a random number from Mersenne Twister can have */
+PHP_FUNCTION(mt_getrandmax)
+{
+ if (zend_parse_parameters_none() == FAILURE) {
+ return;
+ }
+
+ /*
+ * Melo: it could be 2^^32 but we only use 2^^31 to maintain
+ * compatibility with the previous php_rand
+ */
+ RETURN_LONG(PHP_MT_RAND_MAX); /* 2^^31 */
+}
+/* }}} */
+
+PHP_MINIT_FUNCTION(mt_rand)
+{
+ REGISTER_LONG_CONSTANT("MT_RAND_MT19937", MT_RAND_MT19937, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("MT_RAND_PHP", MT_RAND_PHP, CONST_CS | CONST_PERSISTENT);
+
+ return SUCCESS;
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
+
diff --git a/ext/standard/pack.c b/ext/standard/pack.c
index d15154df31..1e1348a27c 100644
--- a/ext/standard/pack.c
+++ b/ext/standard/pack.c
@@ -92,7 +92,7 @@ static int little_endian_longlong_map[8];
*/
static void php_pack(zval *val, size_t size, int *map, char *output)
{
- int i;
+ size_t i;
char *v;
convert_to_long_ex(val);
@@ -236,13 +236,14 @@ static double php_pack_parse_double(int is_little_endian, void * src)
PHP_FUNCTION(pack)
{
zval *argv = NULL;
- int num_args = 0, i;
+ int num_args = 0;
+ size_t i;
int currentarg;
char *format;
size_t formatlen;
char *formatcodes;
int *formatargs;
- int formatcount = 0;
+ size_t formatcount = 0;
int outputpos = 0, outputsize = 0;
zend_string *output;
@@ -466,7 +467,7 @@ PHP_FUNCTION(pack)
case 'a':
case 'A':
case 'Z': {
- int arg_cp = (code != 'Z') ? arg : MAX(0, arg - 1);
+ size_t arg_cp = (code != 'Z') ? arg : MAX(0, arg - 1);
zend_string *str = zval_get_string(&argv[currentarg++]);
@@ -488,7 +489,7 @@ PHP_FUNCTION(pack)
char *v = ZSTR_VAL(str);
outputpos--;
- if(arg > ZSTR_LEN(str)) {
+ if ((size_t)arg > ZSTR_LEN(str)) {
php_error_docref(NULL, E_WARNING, "Type %c: not enough characters in string", code);
arg = ZSTR_LEN(str);
}
@@ -691,7 +692,7 @@ static zend_long php_unpack(char *data, size_t size, int issigned, int *map)
{
zend_long result;
char *cresult = (char *) &result;
- int i;
+ size_t i;
result = issigned ? -1 : 0;
@@ -724,9 +725,10 @@ PHP_FUNCTION(unpack)
zend_string *formatarg, *inputarg;
zend_long formatlen, inputpos, inputlen;
int i;
+ zend_long offset = 0;
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS", &formatarg,
- &inputarg) == FAILURE) {
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS|l", &formatarg,
+ &inputarg, &offset) == FAILURE) {
return;
}
@@ -736,6 +738,14 @@ PHP_FUNCTION(unpack)
inputlen = ZSTR_LEN(inputarg);
inputpos = 0;
+
+ if (offset < 0 || offset > inputlen) {
+ php_error_docref(NULL, E_WARNING, "Offset " ZEND_LONG_FMT " is out of input range" , offset);
+ RETURN_FALSE;
+ }
+ input += offset;
+ inputlen -= offset;
+
array_init(return_value);
while (formatlen-- > 0) {
@@ -894,7 +904,7 @@ PHP_FUNCTION(unpack)
switch ((int) type) {
case 'a': {
/* a will not strip any trailing whitespace or null padding */
- size_t len = inputlen - inputpos; /* Remaining string */
+ zend_long len = inputlen - inputpos; /* Remaining string */
/* If size was given take minimum of len and size */
if ((size >= 0) && (len > size)) {
@@ -936,7 +946,7 @@ PHP_FUNCTION(unpack)
case 'Z': {
/* Z will strip everything after the first null character */
char pad = '\0';
- size_t s,
+ zend_long s,
len = inputlen - inputpos; /* Remaining string */
/* If size was given take minimum of len and size */
@@ -960,11 +970,11 @@ PHP_FUNCTION(unpack)
case 'h':
case 'H': {
- size_t len = (inputlen - inputpos) * 2; /* Remaining */
+ zend_long len = (inputlen - inputpos) * 2; /* Remaining */
int nibbleshift = (type == 'h') ? 0 : 4;
int first = 1;
char *buf;
- size_t ipos, opos;
+ zend_long ipos, opos;
/* If size was given take minimum of len and size */
if (size >= 0 && len > (size * 2)) {
diff --git a/ext/standard/password.c b/ext/standard/password.c
index 33c6e5c718..58f06f81f8 100644
--- a/ext/standard/password.c
+++ b/ext/standard/password.c
@@ -21,7 +21,6 @@
#include <stdlib.h>
#include "php.h"
-#if HAVE_CRYPT
#include "fcntl.h"
#include "php_password.h"
@@ -195,7 +194,7 @@ PHP_FUNCTION(password_needs_rehash)
algo = php_password_determine_algo(hash, (size_t) hash_len);
- if (algo != new_algo) {
+ if ((zend_long)algo != new_algo) {
RETURN_TRUE;
}
@@ -225,8 +224,8 @@ PHP_FUNCTION(password_needs_rehash)
Verify a hash created using crypt() or password_hash() */
PHP_FUNCTION(password_verify)
{
- int status = 0, i;
- size_t password_len, hash_len;
+ int status = 0;
+ size_t i, password_len, hash_len;
char *password, *hash;
zend_string *ret;
@@ -384,7 +383,6 @@ PHP_FUNCTION(password_hash)
}
/* }}} */
-#endif /* HAVE_CRYPT */
/*
* Local variables:
* tab-width: 4
diff --git a/ext/standard/php_crypt.h b/ext/standard/php_crypt.h
index fef1891657..5d33460c5f 100644
--- a/ext/standard/php_crypt.h
+++ b/ext/standard/php_crypt.h
@@ -25,11 +25,9 @@
PHPAPI zend_string *php_crypt(const char *password, const int pass_len, const char *salt, int salt_len, zend_bool quiet);
PHP_FUNCTION(crypt);
-#if HAVE_CRYPT
PHP_MINIT_FUNCTION(crypt);
PHP_MSHUTDOWN_FUNCTION(crypt);
PHP_RINIT_FUNCTION(crypt);
-#endif
#endif
diff --git a/ext/standard/php_crypt_r.c b/ext/standard/php_crypt_r.c
index 01627337f1..d3ecc002a8 100644
--- a/ext/standard/php_crypt_r.c
+++ b/ext/standard/php_crypt_r.c
@@ -323,7 +323,7 @@ char * php_md5_crypt_r(const char *pw, const char *salt, char *out)
unsigned char final[16];
unsigned int i, sl, pwl;
PHP_MD5_CTX ctx, ctx1;
- php_uint32 l;
+ uint32_t l;
int pl;
pwl = strlen(pw);
diff --git a/ext/standard/php_fopen_wrapper.c b/ext/standard/php_fopen_wrapper.c
index 6b799203d3..0031440704 100644
--- a/ext/standard/php_fopen_wrapper.c
+++ b/ext/standard/php_fopen_wrapper.c
@@ -199,7 +199,7 @@ php_stream * php_stream_url_wrap_php(php_stream_wrapper *wrapper, const char *pa
path += 11;
max_memory = ZEND_STRTOL(path, NULL, 10);
if (max_memory < 0) {
- php_error_docref(NULL, E_RECOVERABLE_ERROR, "Max memory must be >= 0");
+ zend_throw_error(NULL, "Max memory must be >= 0");
return NULL;
}
}
@@ -357,7 +357,7 @@ php_stream * php_stream_url_wrap_php(php_stream_wrapper *wrapper, const char *pa
pathdup = estrndup(path + 6, strlen(path + 6));
p = strstr(pathdup, "/resource=");
if (!p) {
- php_error_docref(NULL, E_RECOVERABLE_ERROR, "No URL resource specified");
+ zend_throw_error(NULL, "No URL resource specified");
efree(pathdup);
return NULL;
}
@@ -441,7 +441,8 @@ static php_stream_wrapper_ops php_stdio_wops = {
NULL, /* unlink */
NULL, /* rename */
NULL, /* mkdir */
- NULL /* rmdir */
+ NULL, /* rmdir */
+ NULL
};
PHPAPI php_stream_wrapper php_stream_php_wrapper = {
diff --git a/ext/standard/php_image.h b/ext/standard/php_image.h
index 53749e47c9..935a8ba689 100644
--- a/ext/standard/php_image.h
+++ b/ext/standard/php_image.h
@@ -52,6 +52,7 @@ typedef enum
/* IMAGE_FILETYPE_JPEG2000 is a userland alias for IMAGE_FILETYPE_JPC */
IMAGE_FILETYPE_XBM,
IMAGE_FILETYPE_ICO,
+ IMAGE_FILETYPE_WEBP,
/* WHEN EXTENDING: PLEASE ALSO REGISTER IN image.c:PHP_MINIT_FUNCTION(imagetypes) */
IMAGE_FILETYPE_COUNT
} image_filetype;
diff --git a/ext/standard/php_incomplete_class.h b/ext/standard/php_incomplete_class.h
index df062be22f..f58e98248a 100644
--- a/ext/standard/php_incomplete_class.h
+++ b/ext/standard/php_incomplete_class.h
@@ -43,7 +43,7 @@
#define PHP_CLASS_ATTRIBUTES \
zend_string *class_name; \
- zend_bool incomplete_class = 0
+ zend_bool incomplete_class ZEND_ATTRIBUTE_UNUSED = 0
#define INCOMPLETE_CLASS "__PHP_Incomplete_Class"
#define MAGIC_MEMBER "__PHP_Incomplete_Class_Name"
diff --git a/ext/standard/php_lcg.h b/ext/standard/php_lcg.h
index dba0469602..8c84e4a85e 100644
--- a/ext/standard/php_lcg.h
+++ b/ext/standard/php_lcg.h
@@ -24,8 +24,8 @@
#include "ext/standard/basic_functions.h"
typedef struct {
- php_int32 s1;
- php_int32 s2;
+ int32_t s1;
+ int32_t s2;
int seeded;
} php_lcg_globals;
diff --git a/ext/standard/php_math.h b/ext/standard/php_math.h
index c53c6a7c04..a7b42eb02a 100644
--- a/ext/standard/php_math.h
+++ b/ext/standard/php_math.h
@@ -46,9 +46,7 @@ PHP_FUNCTION(is_infinite);
PHP_FUNCTION(is_nan);
PHP_FUNCTION(pow);
PHP_FUNCTION(sqrt);
-PHP_FUNCTION(srand);
PHP_FUNCTION(rand);
-PHP_FUNCTION(getrandmax);
PHP_FUNCTION(mt_srand);
PHP_FUNCTION(mt_rand);
PHP_FUNCTION(mt_getrandmax);
diff --git a/ext/standard/php_mt_rand.h b/ext/standard/php_mt_rand.h
new file mode 100644
index 0000000000..0e10177b6e
--- /dev/null
+++ b/ext/standard/php_mt_rand.h
@@ -0,0 +1,44 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 7 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2017 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Rasmus Lerdorf <rasmus@php.net> |
+ | Zeev Suraski <zeev@zend.com> |
+ | Pedro Melo <melo@ip.pt> |
+ | Sterling Hughes <sterling@php.net> |
+ | |
+ | Based on code from: Shawn Cokus <Cokus@math.washington.edu> |
+ +----------------------------------------------------------------------+
+ */
+/* $Id$ */
+
+#ifndef PHP_MT_RAND_H
+#define PHP_MT_RAND_H
+
+#include "php_lcg.h"
+#include "php_rand.h"
+
+#define PHP_MT_RAND_MAX ((zend_long) (0x7FFFFFFF)) /* (1<<31) - 1 */
+
+#define MT_RAND_MT19937 0
+#define MT_RAND_PHP 1
+
+PHPAPI void php_mt_srand(uint32_t seed);
+PHPAPI uint32_t php_mt_rand(void);
+PHPAPI zend_long php_mt_rand_range(zend_long min, zend_long max);
+PHPAPI zend_long php_mt_rand_common(zend_long min, zend_long max);
+
+PHP_MINIT_FUNCTION(mt_rand);
+
+#endif /* PHP_MT_RAND_H */
+
diff --git a/ext/standard/php_rand.h b/ext/standard/php_rand.h
index 4b8335d1d9..b4a6613bee 100644
--- a/ext/standard/php_rand.h
+++ b/ext/standard/php_rand.h
@@ -25,27 +25,46 @@
#ifndef PHP_RAND_H
#define PHP_RAND_H
-#include <stdlib.h>
-#include "basic_functions.h"
#include "php_lcg.h"
+#include "php_mt_rand.h"
/* System Rand functions */
#ifndef RAND_MAX
-#define RAND_MAX (1<<15)
+#define RAND_MAX PHP_MT_RAND_MAX
#endif
-/* In ZTS mode we rely on rand_r() so we must use RAND_MAX. */
-#if !defined(ZTS) && (defined(HAVE_LRAND48) || defined(HAVE_RANDOM))
-#define PHP_RAND_MAX 2147483647
-#else
-#define PHP_RAND_MAX RAND_MAX
-#endif
+#define PHP_RAND_MAX PHP_MT_RAND_MAX
-#define RAND_RANGE(__n, __min, __max, __tmax) \
- (__n) = (__min) + (zend_long) ((double) ( (double) (__max) - (__min) + 1.0) * ((__n) / ((__tmax) + 1.0)))
+/*
+ * A bit of tricky math here. We want to avoid using a modulus because
+ * that simply tosses the high-order bits and might skew the distribution
+ * of random values over the range. Instead we map the range directly.
+ *
+ * We need to map the range from 0...M evenly to the range a...b
+ * Let n = the random number and n' = the mapped random number
+ *
+ * Then we have: n' = a + n(b-a)/M
+ *
+ * We have a problem here in that only n==M will get mapped to b which
+ # means the chances of getting b is much much less than getting any of
+ # the other values in the range. We can fix this by increasing our range
+ # artificially and using:
+ #
+ # n' = a + n(b-a+1)/M
+ *
+ # Now we only have a problem if n==M which would cause us to produce a
+ # number of b+1 which would be bad. So we bump M up by one to make sure
+ # this will never happen, and the final algorithm looks like this:
+ #
+ # n' = a + n(b-a+1)/(M+1)
+ *
+ * -RL
+ */
+#define RAND_RANGE_BADSCALING(__n, __min, __max, __tmax) \
+ (__n) = (__min) + (zend_long) ((double) ( (double) (__max) - (__min) + 1.0) * ((__n) / ((__tmax) + 1.0)))
-/* MT Rand */
-#define PHP_MT_RAND_MAX ((zend_long) (0x7FFFFFFF)) /* (1<<31) - 1 */
+#define RAND_RANGE(__n, __min, __max, __tmax) \
+ (__n) = php_mt_rand_range((__min), (__max))
#ifdef PHP_WIN32
#define GENERATE_SEED() (((zend_long) (time(0) * GetCurrentProcessId())) ^ ((zend_long) (1000000.0 * php_combined_lcg())))
@@ -55,7 +74,5 @@
PHPAPI void php_srand(zend_long seed);
PHPAPI zend_long php_rand(void);
-PHPAPI void php_mt_srand(php_uint32 seed);
-PHPAPI php_uint32 php_mt_rand(void);
#endif /* PHP_RAND_H */
diff --git a/ext/standard/php_smart_string.h b/ext/standard/php_smart_string.h
index 45e0146691..adb78c0318 100644
--- a/ext/standard/php_smart_string.h
+++ b/ext/standard/php_smart_string.h
@@ -96,7 +96,7 @@
smart_string_append_unsigned_ex((dest), (val), 0)
#define smart_string_appendc_ex(dest, ch, what) do { \
- register size_t __nl; \
+ size_t __nl; \
smart_string_alloc4((dest), 1, (what), __nl); \
(dest)->len = __nl; \
((unsigned char *) (dest)->c)[(dest)->len - 1] = (ch); \
@@ -112,7 +112,7 @@
} while (0)
#define smart_string_appendl_ex(dest, src, nlen, what) do { \
- register size_t __nl; \
+ size_t __nl; \
smart_string *__dest = (smart_string *) (dest); \
\
smart_string_alloc4(__dest, (nlen), (what), __nl); \
diff --git a/ext/standard/php_type.h b/ext/standard/php_type.h
index cc7a9f6871..cfd6eb815b 100644
--- a/ext/standard/php_type.h
+++ b/ext/standard/php_type.h
@@ -38,5 +38,6 @@ PHP_FUNCTION(is_array);
PHP_FUNCTION(is_object);
PHP_FUNCTION(is_scalar);
PHP_FUNCTION(is_callable);
+PHP_FUNCTION(is_iterable);
#endif
diff --git a/ext/standard/php_var.h b/ext/standard/php_var.h
index e734bd7166..e5ee358192 100644
--- a/ext/standard/php_var.h
+++ b/ext/standard/php_var.h
@@ -38,18 +38,6 @@ PHPAPI void php_var_export_ex(zval *struc, int level, smart_str *buf);
PHPAPI void php_debug_zval_dump(zval *struc, int level);
-struct php_serialize_data {
- HashTable ht;
- uint32_t n;
-};
-
-struct php_unserialize_data {
- void *first;
- void *last;
- void *first_dtor;
- void *last_dtor;
-};
-
typedef struct php_serialize_data *php_serialize_data_t;
typedef struct php_unserialize_data *php_unserialize_data_t;
@@ -57,63 +45,25 @@ PHPAPI void php_var_serialize(smart_str *buf, zval *struc, php_serialize_data_t
PHPAPI int php_var_unserialize(zval *rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash);
PHPAPI int php_var_unserialize_ref(zval *rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash);
PHPAPI int php_var_unserialize_intern(zval *rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash);
-PHPAPI int php_var_unserialize_ex(zval *rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash, HashTable *classes);
+
+PHPAPI php_serialize_data_t php_var_serialize_init(void);
+PHPAPI void php_var_serialize_destroy(php_serialize_data_t d);
+PHPAPI php_unserialize_data_t php_var_unserialize_init(void);
+PHPAPI void php_var_unserialize_destroy(php_unserialize_data_t d);
+PHPAPI HashTable *php_var_unserialize_get_allowed_classes(php_unserialize_data_t d);
+PHPAPI void php_var_unserialize_set_allowed_classes(php_unserialize_data_t d, HashTable *classes);
#define PHP_VAR_SERIALIZE_INIT(d) \
-do { \
- /* fprintf(stderr, "SERIALIZE_INIT == lock: %u, level: %u\n", BG(serialize_lock), BG(serialize).level); */ \
- if (BG(serialize_lock) || !BG(serialize).level) { \
- (d) = (php_serialize_data_t) emalloc(sizeof(struct php_serialize_data)); \
- zend_hash_init(&(d)->ht, 16, NULL, ZVAL_PTR_DTOR, 0); \
- (d)->n = 0; \
- if (!BG(serialize_lock)) { \
- BG(serialize).data = d; \
- BG(serialize).level = 1; \
- } \
- } else { \
- (d) = BG(serialize).data; \
- ++BG(serialize).level; \
- } \
-} while(0)
+ (d) = php_var_serialize_init()
#define PHP_VAR_SERIALIZE_DESTROY(d) \
-do { \
- /* fprintf(stderr, "SERIALIZE_DESTROY == lock: %u, level: %u\n", BG(serialize_lock), BG(serialize).level); */ \
- if (BG(serialize_lock) || BG(serialize).level == 1) { \
- zend_hash_destroy(&(d)->ht); \
- efree((d)); \
- } \
- if (!BG(serialize_lock) && !--BG(serialize).level) { \
- BG(serialize).data = NULL; \
- } \
-} while (0)
+ php_var_serialize_destroy(d)
#define PHP_VAR_UNSERIALIZE_INIT(d) \
-do { \
- /* fprintf(stderr, "UNSERIALIZE_INIT == lock: %u, level: %u\n", BG(serialize_lock), BG(unserialize).level); */ \
- if (BG(serialize_lock) || !BG(unserialize).level) { \
- (d) = (php_unserialize_data_t)ecalloc(1, sizeof(struct php_unserialize_data)); \
- if (!BG(serialize_lock)) { \
- BG(unserialize).data = (d); \
- BG(unserialize).level = 1; \
- } \
- } else { \
- (d) = BG(unserialize).data; \
- ++BG(unserialize).level; \
- } \
-} while (0)
+ (d) = php_var_unserialize_init()
#define PHP_VAR_UNSERIALIZE_DESTROY(d) \
-do { \
- /* fprintf(stderr, "UNSERIALIZE_DESTROY == lock: %u, level: %u\n", BG(serialize_lock), BG(unserialize).level); */ \
- if (BG(serialize_lock) || BG(unserialize).level == 1) { \
- var_destroy(&(d)); \
- efree((d)); \
- } \
- if (!BG(serialize_lock) && !--BG(unserialize).level) { \
- BG(unserialize).data = NULL; \
- } \
-} while (0)
+ php_var_unserialize_destroy(d)
PHPAPI void var_replace(php_unserialize_data_t *var_hash, zval *ozval, zval *nzval);
PHPAPI void var_push_dtor(php_unserialize_data_t *var_hash, zval *val);
diff --git a/ext/standard/proc_open.c b/ext/standard/proc_open.c
index b274dc462b..de1304d48e 100644
--- a/ext/standard/proc_open.c
+++ b/ext/standard/proc_open.c
@@ -429,13 +429,14 @@ PHP_FUNCTION(proc_open)
#ifdef PHP_WIN32
PROCESS_INFORMATION pi;
HANDLE childHandle;
- STARTUPINFO si;
+ STARTUPINFOW si;
BOOL newprocok;
SECURITY_ATTRIBUTES security;
DWORD dwCreateFlags = 0;
- char *command_with_cmd;
UINT old_error_mode;
char cur_cwd[MAXPATHLEN];
+ wchar_t *cmdw = NULL, *cwdw = NULL, *envpw = NULL;
+ size_t tmp_len;
#endif
#ifdef NETWARE
char** child_argv = NULL;
@@ -543,7 +544,7 @@ PHP_FUNCTION(proc_open)
#else
descriptors[ndesc].childend = dup(fd);
if (descriptors[ndesc].childend < 0) {
- php_error_docref(NULL, E_WARNING, "unable to dup File-Handle for descriptor %pd - %s", nindex, strerror(errno));
+ php_error_docref(NULL, E_WARNING, "unable to dup File-Handle for descriptor " ZEND_ULONG_FMT " - %s", nindex, strerror(errno));
goto exit_fail;
}
#endif
@@ -685,6 +686,11 @@ PHP_FUNCTION(proc_open)
}
cwd = cur_cwd;
}
+ cwdw = php_win32_cp_any_to_w(cwd);
+ if (!cwdw) {
+ php_error_docref(NULL, E_WARNING, "CWD conversion failed");
+ goto exit_fail;
+ }
memset(&si, 0, sizeof(si));
si.cb = sizeof(si);
@@ -721,16 +727,55 @@ PHP_FUNCTION(proc_open)
dwCreateFlags |= CREATE_NO_WINDOW;
}
+ envpw = php_win32_cp_env_any_to_w(env.envp);
+ if (envpw) {
+ dwCreateFlags |= CREATE_UNICODE_ENVIRONMENT;
+ } else {
+ if (env.envp) {
+ php_error_docref(NULL, E_WARNING, "ENV conversion failed");
+ goto exit_fail;
+ }
+ }
+
+ cmdw = php_win32_cp_conv_any_to_w(command, command_len, &tmp_len);
+ if (!cmdw) {
+ php_error_docref(NULL, E_WARNING, "Command conversion failed");
+ goto exit_fail;
+ }
+
if (bypass_shell) {
- newprocok = CreateProcess(NULL, command, &security, &security, TRUE, dwCreateFlags, env.envp, cwd, &si, &pi);
+ newprocok = CreateProcessW(NULL, cmdw, &security, &security, TRUE, dwCreateFlags, envpw, cwdw, &si, &pi);
} else {
- spprintf(&command_with_cmd, 0, "%s /c %s", COMSPEC_NT, command);
+ int ret;
+ size_t len;
+ wchar_t *cmdw2;
- newprocok = CreateProcess(NULL, command_with_cmd, &security, &security, TRUE, dwCreateFlags, env.envp, cwd, &si, &pi);
- efree(command_with_cmd);
+ len = (sizeof(COMSPEC_NT) + sizeof(" /c ") + tmp_len + 1);
+ cmdw2 = (wchar_t *)malloc(len * sizeof(wchar_t));
+ if (!cmdw2) {
+ php_error_docref(NULL, E_WARNING, "Command conversion failed");
+ goto exit_fail;
+ }
+ ret = _snwprintf(cmdw2, len, L"%hs /c %s", COMSPEC_NT, cmdw);
+
+ if (-1 == ret) {
+ free(cmdw2);
+ php_error_docref(NULL, E_WARNING, "Command conversion failed");
+ goto exit_fail;
+ }
+
+ newprocok = CreateProcessW(NULL, cmdw2, &security, &security, TRUE, dwCreateFlags, envpw, cwdw, &si, &pi);
+ free(cmdw2);
}
+ free(cwdw);
+ cwdw = NULL;
+ free(cmdw);
+ cmdw = NULL;
+ free(envpw);
+ envpw = NULL;
+
if (suppress_errors) {
SetErrorMode(old_error_mode);
}
@@ -962,6 +1007,11 @@ exit_fail:
efree(descriptors);
_php_free_envp(env, is_persistent);
pefree(command, is_persistent);
+#ifdef PHP_WIN32
+ free(cwdw);
+ free(cmdw);
+ free(envpw);
+#endif
#if PHP_CAN_DO_PTS
if (dev_ptmx >= 0) {
close(dev_ptmx);
diff --git a/ext/standard/rand.c b/ext/standard/rand.c
index 708e0c376e..0b804fa5aa 100644
--- a/ext/standard/rand.c
+++ b/ext/standard/rand.c
@@ -25,35 +25,15 @@
*/
/* $Id$ */
-#include <stdlib.h>
-
#include "php.h"
-#include "php_math.h"
#include "php_rand.h"
-
-#include "basic_functions.h"
-
-
-/* SYSTEM RAND FUNCTIONS */
+#include "php_mt_rand.h"
/* {{{ php_srand
*/
PHPAPI void php_srand(zend_long seed)
{
-#ifdef ZTS
- BG(rand_seed) = (unsigned int) seed;
-#else
-# if defined(HAVE_SRANDOM)
- srandom((unsigned int) seed);
-# elif defined(HAVE_SRAND48)
- srand48(seed);
-# else
- srand((unsigned int) seed);
-# endif
-#endif
-
- /* Seed only once */
- BG(rand_is_seeded) = 1;
+ php_mt_srand(seed);
}
/* }}} */
@@ -61,314 +41,31 @@ PHPAPI void php_srand(zend_long seed)
*/
PHPAPI zend_long php_rand(void)
{
- zend_long ret;
-
- if (!BG(rand_is_seeded)) {
- php_srand(GENERATE_SEED());
- }
-
-#ifdef ZTS
- ret = php_rand_r(&BG(rand_seed));
-#else
-# if defined(HAVE_RANDOM)
- ret = random();
-# elif defined(HAVE_LRAND48)
- ret = lrand48();
-# else
- ret = rand();
-# endif
-#endif
-
- return ret;
-}
-/* }}} */
-
-
-/* MT RAND FUNCTIONS */
-
-/*
- The following php_mt_...() functions are based on a C++ class MTRand by
- Richard J. Wagner. For more information see the web page at
- http://www-personal.engin.umich.edu/~wagnerr/MersenneTwister.html
-
- Mersenne Twister random number generator -- a C++ class MTRand
- Based on code by Makoto Matsumoto, Takuji Nishimura, and Shawn Cokus
- Richard J. Wagner v1.0 15 May 2003 rjwagner@writeme.com
-
- The Mersenne Twister is an algorithm for generating random numbers. It
- was designed with consideration of the flaws in various other generators.
- The period, 2^19937-1, and the order of equidistribution, 623 dimensions,
- are far greater. The generator is also fast; it avoids multiplication and
- division, and it benefits from caches and pipelines. For more information
- see the inventors' web page at http://www.math.keio.ac.jp/~matumoto/emt.html
-
- Reference
- M. Matsumoto and T. Nishimura, "Mersenne Twister: A 623-Dimensionally
- Equidistributed Uniform Pseudo-Random Number Generator", ACM Transactions on
- Modeling and Computer Simulation, Vol. 8, No. 1, January 1998, pp 3-30.
-
- Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
- Copyright (C) 2000 - 2003, Richard J. Wagner
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
-
- 1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
- 2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
- 3. The names of its contributors may not 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.
-*/
-
-#define N MT_N /* length of state vector */
-#define M (397) /* a period parameter */
-#define hiBit(u) ((u) & 0x80000000U) /* mask all but highest bit of u */
-#define loBit(u) ((u) & 0x00000001U) /* mask all but lowest bit of u */
-#define loBits(u) ((u) & 0x7FFFFFFFU) /* mask the highest bit of u */
-#define mixBits(u, v) (hiBit(u)|loBits(v)) /* move hi bit of u to hi bit of v */
-
-#define twist(m,u,v) (m ^ (mixBits(u,v)>>1) ^ ((php_uint32)(-(php_int32)(loBit(u))) & 0x9908b0dfU))
-
-/* {{{ php_mt_initialize
- */
-static inline void php_mt_initialize(php_uint32 seed, php_uint32 *state)
-{
- /* Initialize generator state with seed
- See Knuth TAOCP Vol 2, 3rd Ed, p.106 for multiplier.
- In previous versions, most significant bits (MSBs) of the seed affect
- only MSBs of the state array. Modified 9 Jan 2002 by Makoto Matsumoto. */
-
- register php_uint32 *s = state;
- register php_uint32 *r = state;
- register int i = 1;
-
- *s++ = seed & 0xffffffffU;
- for( ; i < N; ++i ) {
- *s++ = ( 1812433253U * ( *r ^ (*r >> 30) ) + i ) & 0xffffffffU;
- r++;
- }
-}
-/* }}} */
-
-/* {{{ php_mt_reload
- */
-static inline void php_mt_reload(void)
-{
- /* Generate N new values in state
- Made clearer and faster by Matthew Bellew (matthew.bellew@home.com) */
-
- register php_uint32 *state = BG(state);
- register php_uint32 *p = state;
- register int i;
-
- for (i = N - M; i--; ++p)
- *p = twist(p[M], p[0], p[1]);
- for (i = M; --i; ++p)
- *p = twist(p[M-N], p[0], p[1]);
- *p = twist(p[M-N], p[0], state[0]);
- BG(left) = N;
- BG(next) = state;
-}
-/* }}} */
-
-/* {{{ php_mt_srand
- */
-PHPAPI void php_mt_srand(php_uint32 seed)
-{
- /* Seed the generator with a simple uint32 */
- php_mt_initialize(seed, BG(state));
- php_mt_reload();
-
- /* Seed only once */
- BG(mt_rand_is_seeded) = 1;
-}
-/* }}} */
-
-/* {{{ php_mt_rand
- */
-PHPAPI php_uint32 php_mt_rand(void)
-{
- /* Pull a 32-bit integer from the generator state
- Every other access function simply transforms the numbers extracted here */
-
- register php_uint32 s1;
-
- if (BG(left) == 0) {
- php_mt_reload();
- }
- --BG(left);
-
- s1 = *BG(next)++;
- s1 ^= (s1 >> 11);
- s1 ^= (s1 << 7) & 0x9d2c5680U;
- s1 ^= (s1 << 15) & 0xefc60000U;
- return ( s1 ^ (s1 >> 18) );
-}
-/* }}} */
-
-/* {{{ proto void srand([int seed])
- Seeds random number generator */
-PHP_FUNCTION(srand)
-{
- zend_long seed = 0;
-
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &seed) == FAILURE)
- return;
-
- if (ZEND_NUM_ARGS() == 0)
- seed = GENERATE_SEED();
-
- php_srand(seed);
-}
-/* }}} */
-
-/* {{{ proto void mt_srand([int seed])
- Seeds Mersenne Twister random number generator */
-PHP_FUNCTION(mt_srand)
-{
- zend_long seed = 0;
-
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &seed) == FAILURE)
- return;
-
- if (ZEND_NUM_ARGS() == 0)
- seed = GENERATE_SEED();
-
- php_mt_srand(seed);
-}
-/* }}} */
-
-
-/*
- * A bit of tricky math here. We want to avoid using a modulus because
- * that simply tosses the high-order bits and might skew the distribution
- * of random values over the range. Instead we map the range directly.
- *
- * We need to map the range from 0...M evenly to the range a...b
- * Let n = the random number and n' = the mapped random number
- *
- * Then we have: n' = a + n(b-a)/M
- *
- * We have a problem here in that only n==M will get mapped to b which
- # means the chances of getting b is much much less than getting any of
- # the other values in the range. We can fix this by increasing our range
- # artificially and using:
- #
- # n' = a + n(b-a+1)/M
- *
- # Now we only have a problem if n==M which would cause us to produce a
- # number of b+1 which would be bad. So we bump M up by one to make sure
- # this will never happen, and the final algorithm looks like this:
- #
- # n' = a + n(b-a+1)/(M+1)
- *
- * -RL
- */
-
-/* {{{ proto int rand([int min, int max])
- Returns a random number */
-PHP_FUNCTION(rand)
-{
- zend_long min;
- zend_long max;
- zend_long number;
- int argc = ZEND_NUM_ARGS();
-
- if (argc != 0 && zend_parse_parameters(argc, "ll", &min, &max) == FAILURE)
- return;
-
- number = php_rand();
- if (argc == 2) {
- RAND_RANGE(number, min, max, PHP_RAND_MAX);
- }
-
- RETURN_LONG(number);
+ return php_mt_rand();
}
/* }}} */
/* {{{ proto int mt_rand([int min, int max])
Returns a random number from Mersenne Twister */
-PHP_FUNCTION(mt_rand)
+PHP_FUNCTION(rand)
{
zend_long min;
zend_long max;
- zend_long number;
- int argc = ZEND_NUM_ARGS();
+ int argc = ZEND_NUM_ARGS();
- if (argc != 0) {
- if (zend_parse_parameters(argc, "ll", &min, &max) == FAILURE) {
- return;
- } else if (max < min) {
- php_error_docref(NULL, E_WARNING, "max(" ZEND_LONG_FMT ") is smaller than min(" ZEND_LONG_FMT ")", max, min);
- RETURN_FALSE;
- }
+ if (argc == 0) {
+ RETURN_LONG(php_mt_rand() >> 1);
}
- if (!BG(mt_rand_is_seeded)) {
- php_mt_srand(GENERATE_SEED());
- }
-
- /*
- * Melo: hmms.. randomMT() returns 32 random bits...
- * Yet, the previous php_rand only returns 31 at most.
- * So I put a right shift to loose the lsb. It *seems*
- * better than clearing the msb.
- * Update:
- * I talked with Cokus via email and it won't ruin the algorithm
- */
- number = (zend_long) (php_mt_rand() >> 1);
- if (argc == 2) {
- RAND_RANGE(number, min, max, PHP_MT_RAND_MAX);
- }
-
- RETURN_LONG(number);
-}
-/* }}} */
-
-/* {{{ proto int getrandmax(void)
- Returns the maximum value a random number can have */
-PHP_FUNCTION(getrandmax)
-{
- if (zend_parse_parameters_none() == FAILURE) {
+ if (zend_parse_parameters(argc, "ll", &min, &max) == FAILURE) {
return;
}
- RETURN_LONG(PHP_RAND_MAX);
-}
-/* }}} */
-
-/* {{{ proto int mt_getrandmax(void)
- Returns the maximum value a random number from Mersenne Twister can have */
-PHP_FUNCTION(mt_getrandmax)
-{
- if (zend_parse_parameters_none() == FAILURE) {
- return;
+ if (max < min) {
+ RETURN_LONG(php_mt_rand_common(max, min));
}
- /*
- * Melo: it could be 2^^32 but we only use 2^^31 to maintain
- * compatibility with the previous php_rand
- */
- RETURN_LONG(PHP_MT_RAND_MAX); /* 2^^31 */
+ RETURN_LONG(php_mt_rand_common(min, max));
}
/* }}} */
diff --git a/ext/standard/sha1.c b/ext/standard/sha1.c
index aa88e988d2..b3341cb8e6 100644
--- a/ext/standard/sha1.c
+++ b/ext/standard/sha1.c
@@ -103,9 +103,9 @@ PHP_FUNCTION(sha1_file)
/* }}} */
-static void SHA1Transform(php_uint32[5], const unsigned char[64]);
-static void SHA1Encode(unsigned char *, php_uint32 *, unsigned int);
-static void SHA1Decode(php_uint32 *, const unsigned char *, unsigned int);
+static void SHA1Transform(uint32_t[5], const unsigned char[64]);
+static void SHA1Encode(unsigned char *, uint32_t *, unsigned int);
+static void SHA1Decode(uint32_t *, const unsigned char *, unsigned int);
static unsigned char PADDING[64] =
{
@@ -133,22 +133,22 @@ static unsigned char PADDING[64] =
/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
*/
#define FF(a, b, c, d, e, w) { \
- (e) += F ((b), (c), (d)) + (w) + (php_uint32)(0x5A827999); \
+ (e) += F ((b), (c), (d)) + (w) + (uint32_t)(0x5A827999); \
(e) += ROTATE_LEFT ((a), 5); \
(b) = ROTATE_LEFT((b), 30); \
}
#define GG(a, b, c, d, e, w) { \
- (e) += G ((b), (c), (d)) + (w) + (php_uint32)(0x6ED9EBA1); \
+ (e) += G ((b), (c), (d)) + (w) + (uint32_t)(0x6ED9EBA1); \
(e) += ROTATE_LEFT ((a), 5); \
(b) = ROTATE_LEFT((b), 30); \
}
#define HH(a, b, c, d, e, w) { \
- (e) += H ((b), (c), (d)) + (w) + (php_uint32)(0x8F1BBCDC); \
+ (e) += H ((b), (c), (d)) + (w) + (uint32_t)(0x8F1BBCDC); \
(e) += ROTATE_LEFT ((a), 5); \
(b) = ROTATE_LEFT((b), 30); \
}
#define II(a, b, c, d, e, w) { \
- (e) += I ((b), (c), (d)) + (w) + (php_uint32)(0xCA62C1D6); \
+ (e) += I ((b), (c), (d)) + (w) + (uint32_t)(0xCA62C1D6); \
(e) += ROTATE_LEFT ((a), 5); \
(b) = ROTATE_LEFT((b), 30); \
}
@@ -184,10 +184,10 @@ PHPAPI void PHP_SHA1Update(PHP_SHA1_CTX * context, const unsigned char *input,
index = (unsigned int) ((context->count[0] >> 3) & 0x3F);
/* Update number of bits */
- if ((context->count[0] += ((php_uint32) inputLen << 3))
- < ((php_uint32) inputLen << 3))
+ if ((context->count[0] += ((uint32_t) inputLen << 3))
+ < ((uint32_t) inputLen << 3))
context->count[1]++;
- context->count[1] += ((php_uint32) inputLen >> 29);
+ context->count[1] += ((uint32_t) inputLen >> 29);
partLen = 64 - index;
@@ -253,11 +253,11 @@ PHPAPI void PHP_SHA1Final(unsigned char digest[20], PHP_SHA1_CTX * context)
* SHA1 basic transformation. Transforms state based on block.
*/
static void SHA1Transform(state, block)
-php_uint32 state[5];
+uint32_t state[5];
const unsigned char block[64];
{
- php_uint32 a = state[0], b = state[1], c = state[2];
- php_uint32 d = state[3], e = state[4], x[16], tmp;
+ uint32_t a = state[0], b = state[1], c = state[2];
+ uint32_t d = state[3], e = state[4], x[16], tmp;
SHA1Decode(x, block, 64);
@@ -361,12 +361,12 @@ const unsigned char block[64];
/* }}} */
/* {{{ SHA1Encode
- Encodes input (php_uint32) into output (unsigned char). Assumes len is
+ Encodes input (uint32_t) into output (unsigned char). Assumes len is
a multiple of 4.
*/
static void SHA1Encode(output, input, len)
unsigned char *output;
-php_uint32 *input;
+uint32_t *input;
unsigned int len;
{
unsigned int i, j;
@@ -381,19 +381,19 @@ unsigned int len;
/* }}} */
/* {{{ SHA1Decode
- Decodes input (unsigned char) into output (php_uint32). Assumes len is
+ Decodes input (unsigned char) into output (uint32_t). Assumes len is
a multiple of 4.
*/
static void SHA1Decode(output, input, len)
-php_uint32 *output;
+uint32_t *output;
const unsigned char *input;
unsigned int len;
{
unsigned int i, j;
for (i = 0, j = 0; j < len; i++, j += 4)
- output[i] = ((php_uint32) input[j + 3]) | (((php_uint32) input[j + 2]) << 8) |
- (((php_uint32) input[j + 1]) << 16) | (((php_uint32) input[j]) << 24);
+ output[i] = ((uint32_t) input[j + 3]) | (((uint32_t) input[j + 2]) << 8) |
+ (((uint32_t) input[j + 1]) << 16) | (((uint32_t) input[j]) << 24);
}
/* }}} */
diff --git a/ext/standard/sha1.h b/ext/standard/sha1.h
index d7ff5027d1..3f5639c2ee 100644
--- a/ext/standard/sha1.h
+++ b/ext/standard/sha1.h
@@ -25,8 +25,8 @@
/* SHA1 context. */
typedef struct {
- php_uint32 state[5]; /* state (ABCD) */
- php_uint32 count[2]; /* number of bits, modulo 2^64 (lsb first) */
+ uint32_t state[5]; /* state (ABCD) */
+ uint32_t count[2]; /* number of bits, modulo 2^64 (lsb first) */
unsigned char buffer[64]; /* input buffer */
} PHP_SHA1_CTX;
diff --git a/ext/standard/streamsfuncs.c b/ext/standard/streamsfuncs.c
index 877247e8eb..9c2f8f6d58 100644
--- a/ext/standard/streamsfuncs.c
+++ b/ext/standard/streamsfuncs.c
@@ -431,13 +431,13 @@ PHP_FUNCTION(stream_get_contents)
if (seek_res != 0) {
php_error_docref(NULL, E_WARNING,
- "Failed to seek to position %pd in the stream", desiredpos);
+ "Failed to seek to position " ZEND_LONG_FMT " in the stream", desiredpos);
RETURN_FALSE;
}
}
if (maxlen > INT_MAX) {
- php_error_docref(NULL, E_WARNING, "maxlen truncated from %pd to %d bytes", maxlen, INT_MAX);
+ php_error_docref(NULL, E_WARNING, "maxlen truncated from " ZEND_LONG_FMT " to %d bytes", maxlen, INT_MAX);
maxlen = INT_MAX;
}
if ((contents = php_stream_copy_to_mem(stream, maxlen, 0))) {
diff --git a/ext/standard/string.c b/ext/standard/string.c
index a8b39ee615..565caa3bfb 100644
--- a/ext/standard/string.c
+++ b/ext/standard/string.c
@@ -967,7 +967,7 @@ PHP_FUNCTION(wordwrap)
newtext = zend_string_init(ZSTR_VAL(text), ZSTR_LEN(text), 0);
laststart = lastspace = 0;
- for (current = 0; current < ZSTR_LEN(text); current++) {
+ for (current = 0; current < (zend_long)ZSTR_LEN(text); current++) {
if (ZSTR_VAL(text)[current] == breakchar[0]) {
laststart = lastspace = current + 1;
} else if (ZSTR_VAL(text)[current] == ' ') {
@@ -999,7 +999,7 @@ PHP_FUNCTION(wordwrap)
newtextlen = 0;
laststart = lastspace = 0;
- for (current = 0; current < ZSTR_LEN(text); current++) {
+ for (current = 0; current < (zend_long)ZSTR_LEN(text); current++) {
if (chk <= 0) {
alloced += (size_t) (((ZSTR_LEN(text) - current + 1)/linelength + 1) * breakchar_len) + 1;
newtext = zend_string_extend(newtext, alloced, 0);
@@ -1410,7 +1410,7 @@ PHPAPI zend_string *php_string_toupper(zend_string *s)
e = c + ZSTR_LEN(s);
while (c < e) {
- if (!isupper(*c)) {
+ if (islower(*c)) {
register unsigned char *r;
zend_string *res = zend_string_alloc(ZSTR_LEN(s), 0);
@@ -1473,7 +1473,7 @@ PHPAPI zend_string *php_string_tolower(zend_string *s)
e = c + ZSTR_LEN(s);
while (c < e) {
- if (!islower(*c)) {
+ if (isupper(*c)) {
register unsigned char *r;
zend_string *res = zend_string_alloc(ZSTR_LEN(s), 0);
@@ -1629,7 +1629,11 @@ PHP_FUNCTION(dirname)
if (levels == 1) {
/* Defaut case */
+#ifdef PHP_WIN32
+ ZSTR_LEN(ret) = php_win32_ioutil_dirname(ZSTR_VAL(ret), str_len);
+#else
ZSTR_LEN(ret) = zend_dirname(ZSTR_VAL(ret), str_len);
+#endif
} else if (levels < 1) {
php_error_docref(NULL, E_WARNING, "Invalid argument, levels must be >= 1");
zend_string_free(ret);
@@ -1637,7 +1641,11 @@ PHP_FUNCTION(dirname)
} else {
/* Some levels up */
do {
+#ifdef PHP_WIN32
+ ZSTR_LEN(ret) = php_win32_ioutil_dirname(ZSTR_VAL(ret), str_len = ZSTR_LEN(ret));
+#else
ZSTR_LEN(ret) = zend_dirname(ZSTR_VAL(ret), str_len = ZSTR_LEN(ret));
+#endif
} while (ZSTR_LEN(ret) < str_len && --levels);
}
@@ -1705,7 +1713,7 @@ PHP_FUNCTION(pathinfo)
p = zend_memrchr(ZSTR_VAL(ret), '.', ZSTR_LEN(ret));
- idx = p ? (p - ZSTR_VAL(ret)) : ZSTR_LEN(ret);
+ idx = p ? (p - ZSTR_VAL(ret)) : (ptrdiff_t)ZSTR_LEN(ret);
add_assoc_stringl(&tmp, "filename", ZSTR_VAL(ret), idx);
}
@@ -1921,6 +1929,9 @@ PHP_FUNCTION(strpos)
Z_PARAM_LONG(offset)
ZEND_PARSE_PARAMETERS_END();
+ if (offset < 0) {
+ offset += (zend_long)ZSTR_LEN(haystack);
+ }
if (offset < 0 || (size_t)offset > ZSTR_LEN(haystack)) {
php_error_docref(NULL, E_WARNING, "Offset not contained in string");
RETURN_FALSE;
@@ -1971,6 +1982,9 @@ PHP_FUNCTION(stripos)
return;
}
+ if (offset < 0) {
+ offset += (zend_long)ZSTR_LEN(haystack);
+ }
if (offset < 0 || (size_t)offset > ZSTR_LEN(haystack)) {
php_error_docref(NULL, E_WARNING, "Offset not contained in string");
RETURN_FALSE;
@@ -2064,7 +2078,7 @@ PHP_FUNCTION(strrpos)
RETURN_FALSE;
}
p = ZSTR_VAL(haystack);
- if (-offset < needle_len) {
+ if ((size_t)-offset < needle_len) {
e = ZSTR_VAL(haystack) + ZSTR_LEN(haystack);
} else {
e = ZSTR_VAL(haystack) + ZSTR_LEN(haystack) + offset + needle_len;
@@ -2165,7 +2179,7 @@ PHP_FUNCTION(strripos)
RETURN_FALSE;
}
p = ZSTR_VAL(haystack_dup);
- if (-offset < ZSTR_LEN(needle)) {
+ if ((size_t)-offset < ZSTR_LEN(needle)) {
e = ZSTR_VAL(haystack_dup) + ZSTR_LEN(haystack);
} else {
e = ZSTR_VAL(haystack_dup) + ZSTR_LEN(haystack) + offset + ZSTR_LEN(needle);
@@ -2339,7 +2353,7 @@ PHP_FUNCTION(substr)
if (f > (zend_long)ZSTR_LEN(str)) {
RETURN_FALSE;
- } else if (f < 0 && -f > ZSTR_LEN(str)) {
+ } else if (f < 0 && (size_t)-f > ZSTR_LEN(str)) {
f = 0;
}
@@ -2449,7 +2463,7 @@ PHP_FUNCTION(substr_replace)
if (f < 0) {
f = 0;
}
- } else if (f > Z_STRLEN_P(str)) {
+ } else if ((size_t)f > Z_STRLEN_P(str)) {
f = Z_STRLEN_P(str);
}
/* if "length" position is negative, set it to the length
@@ -2462,7 +2476,7 @@ PHP_FUNCTION(substr_replace)
}
}
- if (l > Z_STRLEN_P(str) || (l < 0 && (size_t)(-l) > Z_STRLEN_P(str))) {
+ if ((size_t)l > Z_STRLEN_P(str) || (l < 0 && (size_t)(-l) > Z_STRLEN_P(str))) {
l = Z_STRLEN_P(str);
}
@@ -2686,7 +2700,8 @@ PHP_FUNCTION(quotemeta)
/* }}} */
/* {{{ proto int ord(string character)
- Returns ASCII value of character */
+ Returns ASCII value of character
+ Warning: This function is special-cased by zend_compile.c and so is bypassed for constant string argument */
PHP_FUNCTION(ord)
{
char *str;
@@ -2701,7 +2716,8 @@ PHP_FUNCTION(ord)
/* }}} */
/* {{{ proto string chr(int ascii)
- Converts ASCII code to a character */
+ Converts ASCII code to a character
+ Warning: This function is special-cased by zend_compile.c and so is bypassed for constant integer argument */
PHP_FUNCTION(chr)
{
zend_long c;
@@ -4100,8 +4116,8 @@ static void php_hebrev(INTERNAL_FUNCTION_PARAMETERS, int convert_newlines)
char *str;
char *heb_str, *tmp, *target;
size_t block_start, block_end, block_type, block_length, i;
- zend_long max_chars=0;
- size_t begin, end, char_count, orig_begin;
+ zend_long max_chars=0, char_count;
+ size_t begin, end, orig_begin;
size_t str_len;
zend_string *broken_str;
@@ -4495,10 +4511,18 @@ PHP_FUNCTION(parse_str)
if (arrayArg == NULL) {
zval tmp;
- zend_array *symbol_table = zend_rebuild_symbol_table();
+ zend_array *symbol_table;
+ if (zend_forbid_dynamic_call("parse_str() with a single argument") == FAILURE) {
+ efree(res);
+ return;
+ }
+ symbol_table = zend_rebuild_symbol_table();
ZVAL_ARR(&tmp, symbol_table);
sapi_module.treat_data(PARSE_STRING, res, &tmp);
+ if (UNEXPECTED(zend_hash_del(symbol_table, CG(known_strings)[ZEND_STR_THIS]) == SUCCESS)) {
+ zend_throw_error(NULL, "Cannot re-assign $this");
+ }
} else {
zval ret;
@@ -5185,24 +5209,21 @@ PHP_FUNCTION(substr_count)
endp = p + haystack_len;
if (offset < 0) {
- php_error_docref(NULL, E_WARNING, "Offset should be greater than or equal to 0");
- RETURN_FALSE;
+ offset += (zend_long)haystack_len;
}
-
- if ((size_t)offset > haystack_len) {
- php_error_docref(NULL, E_WARNING, "Offset value " ZEND_LONG_FMT " exceeds string length", offset);
+ if ((offset < 0) || ((size_t)offset > haystack_len)) {
+ php_error_docref(NULL, E_WARNING, "Offset not contained in string");
RETURN_FALSE;
}
p += offset;
if (ac == 4) {
- if (length <= 0) {
- php_error_docref(NULL, E_WARNING, "Length should be greater than 0");
- RETURN_FALSE;
+ if (length < 0) {
+ length += (haystack_len - offset);
}
- if (length > (haystack_len - offset)) {
- php_error_docref(NULL, E_WARNING, "Length value " ZEND_LONG_FMT " exceeds string length", length);
+ if (length < 0 || ((size_t)length > (haystack_len - offset))) {
+ php_error_docref(NULL, E_WARNING, "Invalid length value");
RETURN_FALSE;
}
endp = p + length;
@@ -5632,7 +5653,7 @@ PHP_FUNCTION(substr_compare)
RETURN_FALSE;
}
- cmp_len = (size_t) (len ? len : MAX(ZSTR_LEN(s2), (ZSTR_LEN(s1) - offset)));
+ cmp_len = len ? (size_t)len : MAX(ZSTR_LEN(s2), (ZSTR_LEN(s1) - offset));
if (!cs) {
RETURN_LONG(zend_binary_strncmp(ZSTR_VAL(s1) + offset, (ZSTR_LEN(s1) - offset), ZSTR_VAL(s2), ZSTR_LEN(s2), cmp_len));
diff --git a/ext/standard/tests/array/array_filter_variation10.phpt b/ext/standard/tests/array/array_filter_variation10.phpt
index f0a6115f79..b23618794e 100644
--- a/ext/standard/tests/array/array_filter_variation10.phpt
+++ b/ext/standard/tests/array/array_filter_variation10.phpt
@@ -34,7 +34,11 @@ var_dump( array_filter($input, 'dump2', true) );
echo "*** Testing array_filter() : usage variations - 'callback' expecting second argument ***\n";
-var_dump( array_filter($small, 'dump', false) );
+try {
+ var_dump( array_filter($small, 'dump', false) );
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
echo "*** Testing array_filter() with various use types ***\n";
@@ -70,13 +74,7 @@ array(3) {
NULL
}
*** Testing array_filter() : usage variations - 'callback' expecting second argument ***
-
-Warning: Missing argument 2 for dump() in %s on line %d
-
-Notice: Undefined variable: key in %s on line %d
- = 123
-array(0) {
-}
+Exception: Too few arguments to function dump(), 1 passed and exactly 2 expected
*** Testing array_filter() with various use types ***
array(2) {
[1]=>
@@ -91,13 +89,13 @@ array(2) {
int(2)
}
-Warning: is_numeric() expects exactly 1 parameter, 2 given in %s on line 44
+Warning: is_numeric() expects exactly 1 parameter, 2 given in %s on line 48
-Warning: is_numeric() expects exactly 1 parameter, 2 given in %s on line 44
+Warning: is_numeric() expects exactly 1 parameter, 2 given in %s on line 48
-Warning: is_numeric() expects exactly 1 parameter, 2 given in %s on line 44
+Warning: is_numeric() expects exactly 1 parameter, 2 given in %s on line 48
-Warning: is_numeric() expects exactly 1 parameter, 2 given in %s on line 44
+Warning: is_numeric() expects exactly 1 parameter, 2 given in %s on line 48
array(0) {
}
Done
diff --git a/ext/standard/tests/array/array_map_error.phpt b/ext/standard/tests/array/array_map_error.phpt
index 7c623ec4ea..56dd033521 100644
--- a/ext/standard/tests/array/array_map_error.phpt
+++ b/ext/standard/tests/array/array_map_error.phpt
@@ -18,14 +18,22 @@ echo "\n-- Testing array_map() function with one less than expected no. of argum
function callback1() {
return 1;
}
-var_dump( array_map('callback1') );
+try {
+ var_dump( array_map('callback1') );
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
echo "\n-- Testing array_map() function with less no. of arrays than callback function arguments --\n";
$arr1 = array(1, 2);
function callback2($p, $q) {
return $p * $q;
}
-var_dump( array_map('callback2', $arr1) );
+try {
+ var_dump( array_map('callback2', $arr1) );
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
echo "\n-- Testing array_map() function with more no. of arrays than callback function arguments --\n";
$arr2 = array(3, 4);
@@ -48,20 +56,7 @@ Warning: array_map() expects at least 2 parameters, 1 given in %s on line %d%d
NULL
-- Testing array_map() function with less no. of arrays than callback function arguments --
-
-Warning: Missing argument 2 for callback2() in %s on line %d%d
-
-Notice: Undefined variable: q in %s on line %d%d
-
-Warning: Missing argument 2 for callback2() in %s on line %d%d
-
-Notice: Undefined variable: q in %s on line %d%d
-array(2) {
- [0]=>
- int(0)
- [1]=>
- int(0)
-}
+Exception: Too few arguments to function callback2(), 1 passed and exactly 2 expected
-- Testing array_map() function with more no. of arrays than callback function arguments --
array(2) {
diff --git a/ext/standard/tests/array/array_map_variation10.phpt b/ext/standard/tests/array/array_map_variation10.phpt
index cc75436999..ecf9157620 100644
--- a/ext/standard/tests/array/array_map_variation10.phpt
+++ b/ext/standard/tests/array/array_map_variation10.phpt
@@ -20,7 +20,11 @@ echo "-- anonymous function with all parameters and body --\n";
var_dump( array_map( create_function('$a, $b', 'return array($a, $b);'), $array1, $array2));
echo "-- anonymous function with two parameters and passing one array --\n";
-var_dump( array_map( create_function('$a, $b', 'return array($a, $b);'), $array1));
+try {
+ var_dump( array_map( create_function('$a, $b', 'return array($a, $b);'), $array1));
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
echo "-- anonymous function with NULL parameter --\n";
var_dump( array_map( create_function(NULL, 'return NULL;'), $array1));
@@ -60,41 +64,7 @@ array(3) {
}
}
-- anonymous function with two parameters and passing one array --
-
-Warning: Missing argument 2 for __lambda_func() in %s(20) : runtime-created function on line %d
-
-Notice: Undefined variable: b in %s(20) : runtime-created function on line %d
-
-Warning: Missing argument 2 for __lambda_func() in %s(20) : runtime-created function on line %d
-
-Notice: Undefined variable: b in %s(20) : runtime-created function on line %d
-
-Warning: Missing argument 2 for __lambda_func() in %s(20) : runtime-created function on line %d
-
-Notice: Undefined variable: b in %s(20) : runtime-created function on line %d
-array(3) {
- [0]=>
- array(2) {
- [0]=>
- int(1)
- [1]=>
- NULL
- }
- [1]=>
- array(2) {
- [0]=>
- int(2)
- [1]=>
- NULL
- }
- [2]=>
- array(2) {
- [0]=>
- int(3)
- [1]=>
- NULL
- }
-}
+Exception: Too few arguments to function __lambda_func(), 1 passed and exactly 2 expected
-- anonymous function with NULL parameter --
array(3) {
[0]=>
diff --git a/ext/standard/tests/array/array_map_variation9.phpt b/ext/standard/tests/array/array_map_variation9.phpt
index f029beccd6..f33b717c6c 100644
--- a/ext/standard/tests/array/array_map_variation9.phpt
+++ b/ext/standard/tests/array/array_map_variation9.phpt
@@ -29,7 +29,11 @@ echo "-- checking binary safe array with one parameter callback function --\n";
var_dump( array_map('callback1', $arr1) );
echo "-- checking binary safe array with two parameter callback function --\n";
-var_dump( array_map(b"callback2", $arr1) );
+try {
+ var_dump( array_map(b"callback2", $arr1) );
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
echo "Done";
?>
@@ -47,42 +51,5 @@ array(4) {
string(5) "22.22"
}
-- checking binary safe array with two parameter callback function --
-
-Warning: Missing argument 2 for callback2() in %s on line %d%d
-
-Notice: Undefined variable: b in %s on line %d%d
-
-Warning: Missing argument 2 for callback2() in %s on line %d%d
-
-Notice: Undefined variable: b in %s on line %d%d
-
-Warning: Missing argument 2 for callback2() in %s on line %d%d
-
-Notice: Undefined variable: b in %s on line %d%d
-
-Warning: Missing argument 2 for callback2() in %s on line %d%d
-
-Notice: Undefined variable: b in %s on line %d%d
-array(4) {
- [0]=>
- array(1) {
- ["hello"]=>
- NULL
- }
- [1]=>
- array(1) {
- ["world"]=>
- NULL
- }
- [2]=>
- array(1) {
- [1]=>
- NULL
- }
- [3]=>
- array(1) {
- ["22.22"]=>
- NULL
- }
-}
+Exception: Too few arguments to function callback2(), 1 passed and exactly 2 expected
Done
diff --git a/ext/standard/tests/array/array_multisort_variation7.phpt b/ext/standard/tests/array/array_multisort_variation7.phpt
index 4e9feb5126..10980be592 100644
--- a/ext/standard/tests/array/array_multisort_variation7.phpt
+++ b/ext/standard/tests/array/array_multisort_variation7.phpt
@@ -41,28 +41,28 @@ var_dump($inputs);
*** Testing array_multisort() : usage variation - test sort order of all types***
bool(true)
array(10) {
- ["empty string DQ"]=>
- string(0) ""
+ ["float -10.5"]=>
+ float(-10.5)
["int 0"]=>
int(0)
+ [0]=>
+ array(0) {
+ }
["uppercase NULL"]=>
NULL
+ ["empty string DQ"]=>
+ string(0) ""
["undefined var"]=>
NULL
- [0]=>
- array(0) {
- }
+ ["lowercase true"]=>
+ bool(true)
["instance of classWithToString"]=>
object(classWithToString)#1 (0) {
}
+ ["string DQ"]=>
+ string(6) "string"
["instance of classWithoutToString"]=>
object(classWithoutToString)#2 (0) {
}
- ["lowercase true"]=>
- bool(true)
- ["float -10.5"]=>
- float(-10.5)
- ["string DQ"]=>
- string(6) "string"
}
===DONE===
diff --git a/ext/standard/tests/array/array_multisort_variation8.phpt b/ext/standard/tests/array/array_multisort_variation8.phpt
index 1995ee8d2b..6b50e0ebbb 100644
--- a/ext/standard/tests/array/array_multisort_variation8.phpt
+++ b/ext/standard/tests/array/array_multisort_variation8.phpt
@@ -47,15 +47,15 @@ var_dump($inputs);
*** Testing array_multisort() : usage variation - test sort order of all types***
bool(true)
array(10) {
- ["empty string DQ"]=>
- string(0) ""
["uppercase NULL"]=>
NULL
- ["undefined var"]=>
- NULL
+ ["empty string DQ"]=>
+ string(0) ""
["instance of classWithoutToString"]=>
object(classWithoutToString)#2 (0) {
}
+ ["undefined var"]=>
+ NULL
["float -10.5"]=>
float(-10.5)
["int 0"]=>
diff --git a/ext/standard/tests/array/array_multisort_variation9.phpt b/ext/standard/tests/array/array_multisort_variation9.phpt
index eebd19fda0..cc4b8d147b 100644
--- a/ext/standard/tests/array/array_multisort_variation9.phpt
+++ b/ext/standard/tests/array/array_multisort_variation9.phpt
@@ -42,9 +42,7 @@ var_dump($inputs);
Notice: Object of class classWithToString could not be converted to float in %sarray_multisort_variation9.php on line %d
-Notice: Object of class classWithoutToString could not be converted to float in %sarray_multisort_variation9.php on line %d
-
-Notice: Object of class classWithoutToString could not be converted to float in %sarray_multisort_variation9.php on line %d
+Notice: Object of class classWithToString could not be converted to float in %sarray_multisort_variation9.php on line %d
Notice: Object of class classWithoutToString could not be converted to float in %sarray_multisort_variation9.php on line %d
@@ -53,26 +51,26 @@ bool(true)
array(10) {
["float -10.5"]=>
float(-10.5)
- ["string DQ"]=>
- string(6) "string"
- ["undefined var"]=>
- NULL
- ["empty string DQ"]=>
- string(0) ""
- ["uppercase NULL"]=>
- NULL
["int 0"]=>
int(0)
[0]=>
array(0) {
}
- ["instance of classWithoutToString"]=>
- object(classWithoutToString)#2 (0) {
- }
+ ["uppercase NULL"]=>
+ NULL
+ ["empty string DQ"]=>
+ string(0) ""
+ ["string DQ"]=>
+ string(6) "string"
+ ["undefined var"]=>
+ NULL
["lowercase true"]=>
bool(true)
["instance of classWithToString"]=>
object(classWithToString)#1 (0) {
}
+ ["instance of classWithoutToString"]=>
+ object(classWithoutToString)#2 (0) {
+ }
}
-===DONE=== \ No newline at end of file
+===DONE===
diff --git a/ext/standard/tests/array/array_rand.phpt b/ext/standard/tests/array/array_rand.phpt
index 1f495f4b12..c158b0f74b 100644
--- a/ext/standard/tests/array/array_rand.phpt
+++ b/ext/standard/tests/array/array_rand.phpt
@@ -18,9 +18,11 @@ echo "Done\n";
--EXPECTF--
Warning: array_rand() expects at least 1 parameter, 0 given in %s on line %d
NULL
+
+Warning: array_rand(): Array is empty in %s on line %d
NULL
-Warning: array_rand(): Second argument has to be between 1 and the number of elements in the array in %s on line %d
+Warning: array_rand(): Array is empty in %s on line %d
NULL
Warning: array_rand() expects parameter 1 to be array, integer given in %s on line %d
diff --git a/ext/standard/tests/array/array_reduce_variation1.phpt b/ext/standard/tests/array/array_reduce_variation1.phpt
index b02a82a7ca..adffeb53d4 100644
--- a/ext/standard/tests/array/array_reduce_variation1.phpt
+++ b/ext/standard/tests/array/array_reduce_variation1.phpt
@@ -25,7 +25,11 @@ echo "\n--- Testing with a callback with too few parameters ---\n";
var_dump(array_reduce($array, "oneArg", 2));
echo "\n--- Testing with a callback with too many parameters ---\n";
-var_dump(array_reduce($array, "threeArgs", 2));
+try {
+ var_dump(array_reduce($array, "threeArgs", 2));
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
?>
===DONE===
@@ -36,9 +40,5 @@ var_dump(array_reduce($array, "threeArgs", 2));
int(2)
--- Testing with a callback with too many parameters ---
-
-Warning: Missing argument 3 for threeArgs() in %sarray_reduce_variation1.php on line %d
-
-Notice: Undefined variable: x in %sarray_reduce_variation1.php on line %d
-int(3)
-===DONE=== \ No newline at end of file
+Exception: Too few arguments to function threeArgs(), 2 passed and exactly 3 expected
+===DONE===
diff --git a/ext/standard/tests/array/array_udiff_assoc_variation5.phpt b/ext/standard/tests/array/array_udiff_assoc_variation5.phpt
index 69380767bb..6b7f014467 100644
--- a/ext/standard/tests/array/array_udiff_assoc_variation5.phpt
+++ b/ext/standard/tests/array/array_udiff_assoc_variation5.phpt
@@ -24,7 +24,11 @@ echo "\n-- comparison function taking too many parameters --\n";
function too_many_parameters ($val1, $val2, $val3) {
return 1;
}
-var_dump(array_udiff_assoc($arr1, $arr2, 'too_many_parameters'));
+try {
+ var_dump(array_udiff_assoc($arr1, $arr2, 'too_many_parameters'));
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
echo "\n-- comparison function taking too few parameters --\n";
function too_few_parameters ($val1) {
@@ -32,7 +36,6 @@ function too_few_parameters ($val1) {
}
var_dump(array_udiff_assoc($arr1, $arr2, 'too_few_parameters'));
-
?>
===DONE===
--EXPECTF--
@@ -45,12 +48,7 @@ array(1) {
}
-- comparison function taking too many parameters --
-
-Warning: Missing argument 3 for too_many_parameters() in %sarray_udiff_assoc_variation5.php on line %d
-array(1) {
- [0]=>
- int(1)
-}
+Exception: Too few arguments to function too_many_parameters(), 2 passed and exactly 3 expected
-- comparison function taking too few parameters --
array(1) {
diff --git a/ext/standard/tests/array/array_udiff_uassoc_variation6.phpt b/ext/standard/tests/array/array_udiff_uassoc_variation6.phpt
index ec752a31bb..bf2c0f3d2d 100644
--- a/ext/standard/tests/array/array_udiff_uassoc_variation6.phpt
+++ b/ext/standard/tests/array/array_udiff_uassoc_variation6.phpt
@@ -23,7 +23,11 @@ echo "\n-- comparison function taking too many parameters --\n";
function too_many_parameters ($val1, $val2, $val3) {
return 1;
}
-var_dump(array_udiff_uassoc($arr1, $arr2, 'too_many_parameters', 'too_many_parameters'));
+try {
+ var_dump(array_udiff_uassoc($arr1, $arr2, 'too_many_parameters', 'too_many_parameters'));
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
echo "\n-- comparison function taking too few parameters --\n";
function too_few_parameters ($val1) {
@@ -43,12 +47,7 @@ array(1) {
}
-- comparison function taking too many parameters --
-
-Warning: Missing argument 3 for too_many_parameters() in %sarray_udiff_uassoc_variation6.php on line %d
-array(1) {
- [0]=>
- int(1)
-}
+Exception: Too few arguments to function too_many_parameters(), 2 passed and exactly 3 expected
-- comparison function taking too few parameters --
array(1) {
diff --git a/ext/standard/tests/array/array_udiff_variation5.phpt b/ext/standard/tests/array/array_udiff_variation5.phpt
index 49405d40be..ce6362fc08 100644
--- a/ext/standard/tests/array/array_udiff_variation5.phpt
+++ b/ext/standard/tests/array/array_udiff_variation5.phpt
@@ -24,7 +24,11 @@ echo "\n-- comparison function taking too many parameters --\n";
function too_many_parameters ($val1, $val2, $val3) {
return 0;
}
-var_dump(array_udiff($arr1, $arr2, 'too_many_parameters'));
+try {
+ var_dump(array_udiff($arr1, $arr2, 'too_many_parameters'));
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
echo "\n-- comparison function taking too few parameters --\n";
function too_few_parameters ($val1) {
@@ -44,10 +48,7 @@ array(1) {
}
-- comparison function taking too many parameters --
-
-Warning: Missing argument 3 for too_many_parameters() in %sarray_udiff_variation5.php on line %d
-array(0) {
-}
+Exception: Too few arguments to function too_many_parameters(), 2 passed and exactly 3 expected
-- comparison function taking too few parameters --
array(0) {
diff --git a/ext/standard/tests/array/array_uintersect_assoc_variation5.phpt b/ext/standard/tests/array/array_uintersect_assoc_variation5.phpt
index e2d7bd03a6..bebe5b52c9 100644
--- a/ext/standard/tests/array/array_uintersect_assoc_variation5.phpt
+++ b/ext/standard/tests/array/array_uintersect_assoc_variation5.phpt
@@ -23,7 +23,11 @@ echo "\n-- comparison function taking too many parameters --\n";
function too_many_parameters ($val1, $val2, $val3) {
return 1;
}
-var_dump(array_uintersect_assoc($arr1, $arr2, 'too_many_parameters'));
+try {
+ var_dump(array_uintersect_assoc($arr1, $arr2, 'too_many_parameters'));
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
echo "\n-- comparison function taking too few parameters --\n";
function too_few_parameters ($val1) {
@@ -42,10 +46,7 @@ array(0) {
}
-- comparison function taking too many parameters --
-
-Warning: Missing argument 3 for too_many_parameters() in %sarray_uintersect_assoc_variation5.php on line %d
-array(0) {
-}
+Exception: Too few arguments to function too_many_parameters(), 2 passed and exactly 3 expected
-- comparison function taking too few parameters --
array(0) {
diff --git a/ext/standard/tests/array/array_uintersect_uassoc_variation6.phpt b/ext/standard/tests/array/array_uintersect_uassoc_variation6.phpt
index 6ed86f8417..a5317c1c0d 100644
--- a/ext/standard/tests/array/array_uintersect_uassoc_variation6.phpt
+++ b/ext/standard/tests/array/array_uintersect_uassoc_variation6.phpt
@@ -23,7 +23,11 @@ echo "\n-- comparison function taking too many parameters --\n";
function too_many_parameters ($val1, $val2, $val3) {
return 1;
}
-var_dump(array_uintersect_uassoc($arr1, $arr2, 'too_many_parameters', 'too_many_parameters'));
+try {
+ var_dump(array_uintersect_uassoc($arr1, $arr2, 'too_many_parameters', 'too_many_parameters'));
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
echo "\n-- comparison function taking too few parameters --\n";
function too_few_parameters ($val1) {
@@ -41,14 +45,7 @@ array(0) {
}
-- comparison function taking too many parameters --
-
-Warning: Missing argument 3 for too_many_parameters() in %sarray_uintersect_uassoc_variation6.php on line %d
-
-Warning: Missing argument 3 for too_many_parameters() in %sarray_uintersect_uassoc_variation6.php on line %d
-
-Warning: Missing argument 3 for too_many_parameters() in %sarray_uintersect_uassoc_variation6.php on line %d
-array(0) {
-}
+Exception: Too few arguments to function too_many_parameters(), 2 passed and exactly 3 expected
-- comparison function taking too few parameters --
array(0) {
diff --git a/ext/standard/tests/array/array_uintersect_variation5.phpt b/ext/standard/tests/array/array_uintersect_variation5.phpt
index 75cf08e270..642ebc0077 100644
--- a/ext/standard/tests/array/array_uintersect_variation5.phpt
+++ b/ext/standard/tests/array/array_uintersect_variation5.phpt
@@ -23,7 +23,11 @@ echo "\n-- comparison function taking too many parameters --\n";
function too_many_parameters ($val1, $val2, $val3) {
return 1;
}
-var_dump(array_uintersect($arr1, $arr2, 'too_many_parameters'));
+try {
+ var_dump(array_uintersect($arr1, $arr2, 'too_many_parameters'));
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
echo "\n-- comparison function taking too few parameters --\n";
function too_few_parameters ($val1) {
@@ -42,14 +46,7 @@ array(0) {
}
-- comparison function taking too many parameters --
-
-Warning: Missing argument 3 for too_many_parameters() in %sarray_uintersect_variation5.php on line %d
-
-Warning: Missing argument 3 for too_many_parameters() in %sarray_uintersect_variation5.php on line %d
-
-Warning: Missing argument 3 for too_many_parameters() in %sarray_uintersect_variation5.php on line %d
-array(0) {
-}
+Exception: Too few arguments to function too_many_parameters(), 2 passed and exactly 3 expected
-- comparison function taking too few parameters --
array(0) {
diff --git a/ext/standard/tests/array/array_walk_error2.phpt b/ext/standard/tests/array/array_walk_error2.phpt
index 63c5f51ee0..fd3cbf5037 100644
--- a/ext/standard/tests/array/array_walk_error2.phpt
+++ b/ext/standard/tests/array/array_walk_error2.phpt
@@ -22,36 +22,44 @@ function callback2($value, $key, $user_data1, $user_data2) {
echo "*** Testing array_walk() : error conditions - callback parameters ***\n";
// expected: Missing argument Warning
-var_dump( array_walk($input, "callback1") );
-var_dump( array_walk($input, "callback2", 4) );
+try {
+ var_dump( array_walk($input, "callback1") );
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
+try {
+ var_dump( array_walk($input, "callback2", 4) );
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
// expected: Warning is suppressed
-var_dump( @array_walk($input, "callback1") );
-var_dump( @array_walk($input, "callback2", 4) );
+try {
+ var_dump( @array_walk($input, "callback1") );
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
+try {
+ var_dump( @array_walk($input, "callback2", 4) );
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
echo "-- Testing array_walk() function with too many callback parameters --\n";
-var_dump( array_walk($input, "callback1", 20, 10) );
+try {
+ var_dump( array_walk($input, "callback1", 20, 10) );
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
echo "Done";
?>
--EXPECTF--
*** Testing array_walk() : error conditions - callback parameters ***
-
-Warning: Missing argument 3 for callback1() in %s on line %d
-
-callback1() invoked
-bool(true)
-
-Warning: Missing argument 4 for callback2() in %s on line %d
-
-callback2() invoked
-bool(true)
-
-callback1() invoked
-bool(true)
-
-callback2() invoked
-bool(true)
+Exception: Too few arguments to function callback1(), 2 passed and exactly 3 expected
+Exception: Too few arguments to function callback2(), 3 passed and exactly 4 expected
+Exception: Too few arguments to function callback1(), 2 passed and exactly 3 expected
+Exception: Too few arguments to function callback2(), 3 passed and exactly 4 expected
-- Testing array_walk() function with too many callback parameters --
Warning: array_walk() expects at most 3 parameters, 4 given in %s on line %d
diff --git a/ext/standard/tests/array/array_walk_recursive_error2.phpt b/ext/standard/tests/array/array_walk_recursive_error2.phpt
index 8e0c8829e6..5a077c6886 100644
--- a/ext/standard/tests/array/array_walk_recursive_error2.phpt
+++ b/ext/standard/tests/array/array_walk_recursive_error2.phpt
@@ -22,36 +22,44 @@ function callback2($value, $key, $user_data1, $user_data2) {
echo "*** Testing array_walk_recursive() : error conditions - callback parameters ***\n";
// expected: Missing argument Warning
-var_dump( array_walk_recursive($input, "callback1") );
-var_dump( array_walk_recursive($input, "callback2", 4) );
+try {
+ var_dump( array_walk_recursive($input, "callback1") );
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
+try {
+ var_dump( array_walk_recursive($input, "callback2", 4) );
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
// expected: Warning is suppressed
-var_dump( @array_walk_recursive($input, "callback1") );
-var_dump( @array_walk_recursive($input, "callback2", 4) );
+try {
+ var_dump( @array_walk_recursive($input, "callback1") );
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
+try {
+ var_dump( @array_walk_recursive($input, "callback2", 4) );
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
echo "-- Testing array_walk_recursive() function with too many callback parameters --\n";
-var_dump( array_walk_recursive($input, "callback1", 20, 10) );
+try {
+ var_dump( array_walk_recursive($input, "callback1", 20, 10) );
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
echo "Done";
?>
--EXPECTF--
*** Testing array_walk_recursive() : error conditions - callback parameters ***
-
-Warning: Missing argument 3 for callback1() in %s on line %d
-
-callback1() invoked
-bool(true)
-
-Warning: Missing argument 4 for callback2() in %s on line %d
-
-callback2() invoked
-bool(true)
-
-callback1() invoked
-bool(true)
-
-callback2() invoked
-bool(true)
+Exception: Too few arguments to function callback1(), 2 passed and exactly 3 expected
+Exception: Too few arguments to function callback2(), 3 passed and exactly 4 expected
+Exception: Too few arguments to function callback1(), 2 passed and exactly 3 expected
+Exception: Too few arguments to function callback2(), 3 passed and exactly 4 expected
-- Testing array_walk_recursive() function with too many callback parameters --
Warning: array_walk_recursive() expects at most 3 parameters, 4 given in %s on line %d
diff --git a/ext/standard/tests/array/bug61730.phpt b/ext/standard/tests/array/bug61730.phpt
index 0fe9f22212..0761fee774 100644
--- a/ext/standard/tests/array/bug61730.phpt
+++ b/ext/standard/tests/array/bug61730.phpt
@@ -28,10 +28,9 @@ array_walk(
print_r($myArray);
--EXPECT--
int(0)
-int(4)
-int(8)
+int(3)
+int(6)
+int(9)
Array
(
- [3] => 1
- [7] => 1
)
diff --git a/ext/standard/tests/array/bug61967.phpt b/ext/standard/tests/array/bug61967.phpt
new file mode 100644
index 0000000000..7fc65c8d90
--- /dev/null
+++ b/ext/standard/tests/array/bug61967.phpt
@@ -0,0 +1,25 @@
+--TEST--
+Bug #61967: unset array item in array_walk_recursive cause inconsistent array
+--FILE--
+<?php
+$arr = array(
+ range(1, 5),
+ range(1, 5),
+ range(1, 5),
+ range(1, 5),
+ range(1, 5),
+);
+
+array_walk_recursive($arr,
+ function (&$value, $key) use(&$arr) {
+ var_dump($key);
+ unset($arr[$key]);
+ }
+);
+?>
+--EXPECT--
+int(0)
+int(1)
+int(2)
+int(3)
+int(4)
diff --git a/ext/standard/tests/array/bug62607.phpt b/ext/standard/tests/array/bug62607.phpt
new file mode 100644
index 0000000000..d9d529d2cf
--- /dev/null
+++ b/ext/standard/tests/array/bug62607.phpt
@@ -0,0 +1,12 @@
+--TEST--
+Bug #62607: array_walk_recursive move internal pointer
+--FILE--
+<?php
+$arr = array('a'=>'b');
+echo 'Before -> '.current($arr).PHP_EOL;
+array_walk_recursive($arr, function(&$val){});
+echo 'After -> '.current($arr);
+?>
+--EXPECT--
+Before -> b
+After -> b
diff --git a/ext/standard/tests/array/bug69068.phpt b/ext/standard/tests/array/bug69068.phpt
new file mode 100644
index 0000000000..e87b759db0
--- /dev/null
+++ b/ext/standard/tests/array/bug69068.phpt
@@ -0,0 +1,26 @@
+--TEST--
+Bug #69068: Exchanging array during array_walk -> memory errors
+--FILE--
+<?php
+
+$array = [1, 2, 3];
+array_walk($array, function($value, $key) {
+ var_dump($value);
+ if ($value == 2) {
+ $GLOBALS['array'] = [4, 5];
+ }
+});
+var_dump($array);
+
+?>
+--EXPECT--
+int(1)
+int(2)
+int(4)
+int(5)
+array(2) {
+ [0]=>
+ int(4)
+ [1]=>
+ int(5)
+}
diff --git a/ext/standard/tests/array/bug69068_2.phpt b/ext/standard/tests/array/bug69068_2.phpt
new file mode 100644
index 0000000000..ce3e3650a3
--- /dev/null
+++ b/ext/standard/tests/array/bug69068_2.phpt
@@ -0,0 +1,34 @@
+--TEST--
+Bug #69068: Exchanging array during array_walk -> memory errors (variation)
+--FILE--
+<?php
+
+$array = [1, 2, 3];
+$array2 = [4, 5];
+array_walk($array, function(&$value, $key) use ($array2) {
+ var_dump($value);
+ if ($value == 2) {
+ $GLOBALS['array'] = $array2;
+ }
+ $value *= 10;
+});
+var_dump($array, $array2);
+
+?>
+--EXPECT--
+int(1)
+int(2)
+int(4)
+int(5)
+array(2) {
+ [0]=>
+ int(40)
+ [1]=>
+ int(50)
+}
+array(2) {
+ [0]=>
+ int(4)
+ [1]=>
+ int(5)
+}
diff --git a/ext/standard/tests/array/bug70713.phpt b/ext/standard/tests/array/bug70713.phpt
new file mode 100644
index 0000000000..4e2792ab4b
--- /dev/null
+++ b/ext/standard/tests/array/bug70713.phpt
@@ -0,0 +1,26 @@
+--TEST--
+Bug #70713: Use After Free Vulnerability in array_walk()/array_walk_recursive()
+--FILE--
+<?php
+
+class obj
+{
+ function __tostring()
+ {
+ global $arr;
+
+ $arr = 1;
+ for ($i = 0; $i < 5; $i++) {
+ $v[$i] = 'hi'.$i;
+ }
+
+ return 'hi';
+ }
+}
+
+$arr = array('string' => new obj);
+array_walk_recursive($arr, 'settype');
+
+?>
+--EXPECTF--
+Warning: array_walk_recursive(): Iterated value is no longer an array or object in %s on line %d
diff --git a/ext/standard/tests/array/bug74361.phpt b/ext/standard/tests/array/bug74361.phpt
new file mode 100644
index 0000000000..6e7459024c
--- /dev/null
+++ b/ext/standard/tests/array/bug74361.phpt
@@ -0,0 +1,11 @@
+--TEST--
+Bug #74361: Compaction in array_rand() violates COW
+--FILE--
+<?php
+
+$array = [4 => 4];
+var_dump(array_rand($array));
+
+?>
+--EXPECT--
+int(4)
diff --git a/ext/standard/tests/array/bug74361_2.phpt b/ext/standard/tests/array/bug74361_2.phpt
new file mode 100644
index 0000000000..4f4bdcf5a4
--- /dev/null
+++ b/ext/standard/tests/array/bug74361_2.phpt
@@ -0,0 +1,24 @@
+--TEST--
+Bug #74361: Compaction in array_rand() violates COW (variation)
+--FILE--
+<?php
+
+$array = range(0, 15);
+for ($i = 0; $i <= 8; $i++) {
+ unset($array[$i]);
+}
+
+foreach ($array as $x) {
+ var_dump($x);
+ array_rand($array, 1);
+}
+
+?>
+--EXPECT--
+int(9)
+int(10)
+int(11)
+int(12)
+int(13)
+int(14)
+int(15)
diff --git a/ext/standard/tests/array/compact_no_this.phpt b/ext/standard/tests/array/compact_no_this.phpt
new file mode 100644
index 0000000000..df294f0525
--- /dev/null
+++ b/ext/standard/tests/array/compact_no_this.phpt
@@ -0,0 +1,25 @@
+--TEST--
+compact() without object context
+--FILE--
+<?php
+
+var_dump(
+ (new class {
+ function test(){
+ return (static function(){ return compact('this'); })();
+ }
+ })->test()
+);
+
+var_dump(compact('this'));
+
+var_dump((function(){ return compact('this'); })());
+
+?>
+--EXPECT--
+array(0) {
+}
+array(0) {
+}
+array(0) {
+}
diff --git a/ext/standard/tests/array/compact_order.phpt b/ext/standard/tests/array/compact_order.phpt
new file mode 100644
index 0000000000..bf6051cc49
--- /dev/null
+++ b/ext/standard/tests/array/compact_order.phpt
@@ -0,0 +1,25 @@
+--TEST--
+compact() and hashmap order
+--FILE--
+<?php
+
+$foo = null;
+$bar = null;
+
+var_dump(compact('foo', 'bar'));
+var_dump(compact('bar', 'foo'));
+
+?>
+--EXPECT--
+array(2) {
+ ["foo"]=>
+ NULL
+ ["bar"]=>
+ NULL
+}
+array(2) {
+ ["bar"]=>
+ NULL
+ ["foo"]=>
+ NULL
+}
diff --git a/ext/standard/tests/array/compact_this.phpt b/ext/standard/tests/array/compact_this.phpt
new file mode 100644
index 0000000000..f3677e03e2
--- /dev/null
+++ b/ext/standard/tests/array/compact_this.phpt
@@ -0,0 +1,46 @@
+--TEST--
+compact() with object context
+--FILE--
+<?php
+
+var_dump(
+ (new class {
+ function test(){
+ return compact('this');
+ }
+ })->test()
+);
+
+var_dump(
+ (new class {
+ function test(){
+ return compact([['this']]);
+ }
+ })->test()
+);
+
+var_dump(
+ (new class {
+ function test(){
+ return (function(){ return compact('this'); })();
+ }
+ })->test()
+);
+
+?>
+--EXPECT--
+array(1) {
+ ["this"]=>
+ object(class@anonymous)#1 (0) {
+ }
+}
+array(1) {
+ ["this"]=>
+ object(class@anonymous)#1 (0) {
+ }
+}
+array(1) {
+ ["this"]=>
+ object(class@anonymous)#1 (0) {
+ }
+}
diff --git a/ext/standard/tests/assert/assert02.phpt b/ext/standard/tests/assert/assert02.phpt
index 723eeb9564..db60f41466 100644
--- a/ext/standard/tests/assert/assert02.phpt
+++ b/ext/standard/tests/assert/assert02.phpt
@@ -8,41 +8,47 @@ assert.bail=0
assert.quiet_eval=0
--FILE--
<?php
-function handler($errno, $errstr) {
- echo "in handler()\n";
- assert(E_RECOVERABLE_ERROR === $errno);
- var_dump($errstr);
-}
-
-set_error_handler('handler', E_RECOVERABLE_ERROR);
assert(1);
assert('1');
assert('$a');
-assert('aa=sd+as+safsafasfasafsaf');
+try {
+ assert('aa=sd+as+safsafasfasafsaf');
+} catch (Throwable $e) {
+ echo $e->getMessage(), "\n";
+}
assert('0');
assert_options(ASSERT_BAIL, 1);
-assert('aa=sd+as+safsafasfasafsaf');
+
+try {
+ assert('aa=sd+as+safsafasfasafsaf');
+} catch (Throwable $e) {
+ echo $e->getMessage(), "\n";
+}
echo "done\n";
?>
--EXPECTF--
-Notice: Undefined variable: a in %sassert02.php(12) : assert code on line 1
+Notice: Undefined variable: a in %sassert02.php(%d) : assert code on line 1
-Warning: assert(): Assertion "$a" failed in %sassert02.php on line 12
+Warning: assert(): Assertion "$a" failed in %sassert02.php on line %d
+Failure evaluating code:
+aa=sd+as+safsafasfasafsaf
-Parse error: %s error%sin %sassert02.php(14) : assert code on line 1
-in handler()
-%string|unicode%(%d) "assert(): Failure evaluating code:
-aa=sd+as+safsafasfasafsaf"
+Warning: assert(): Assertion "0" failed in %sassert02.php on line %d
-Warning: assert(): Assertion "0" failed in %sassert02.php on line 16
+Fatal error: Uncaught ParseError: syntax error, unexpected '=', expecting ';' in %s(%d) : assert code:1
+Stack trace:
+#0 %s(%d): assert('aa=sd+as+safsaf...')
+#1 {main}
-Parse error: %s error%sin %sassert02.php(19) : assert code on line 1
-in handler()
-%string|unicode%(%d) "assert(): Failure evaluating code:
-aa=sd+as+safsafasfasafsaf"
+Next Error: Failure evaluating code:
+aa=sd+as+safsafasfasafsaf in %s:%d
+Stack trace:
+#0 %s(%d): assert('aa=sd+as+safsaf...')
+#1 {main}
+ thrown in %s on line %d \ No newline at end of file
diff --git a/ext/standard/tests/assert/assert_error2.phpt b/ext/standard/tests/assert/assert_error2.phpt
index 41a7197535..f9018db05b 100644
--- a/ext/standard/tests/assert/assert_error2.phpt
+++ b/ext/standard/tests/assert/assert_error2.phpt
@@ -22,8 +22,11 @@ echo "If this is printed BAIL hasn't worked";
--EXPECTF--
int(0)
-Warning: Missing argument 4 for f1() in %s on line 2
-f1 called
-
Warning: assert(): Assertion "0 != 0" failed in %s on line 9
+Fatal error: Uncaught ArgumentCountError: Too few arguments to function f1(), 3 passed and exactly 4 expected in %sassert_error2.php:2
+Stack trace:
+#0 [internal function]: f1('%s', 9, '0 != 0')
+#1 %sassert_error2.php(9): assert('0 != 0')
+#2 {main}
+ thrown in %sassert_error2.php on line 2
diff --git a/ext/standard/tests/assert/assert_error3.phpt b/ext/standard/tests/assert/assert_error3.phpt
index d14397515f..80947421f1 100644
--- a/ext/standard/tests/assert/assert_error3.phpt
+++ b/ext/standard/tests/assert/assert_error3.phpt
@@ -13,8 +13,14 @@ display_errors = 1
var_dump($r2 = assert("0 $ 0"));
--EXPECTF--
-Parse error: syntax error, unexpected '$', expecting ';' in %s(2) : assert code on line 1
-
-Catchable fatal error: assert(): Failure evaluating code:
-0 $ 0 in %s on line 2
+Fatal error: Uncaught ParseError: syntax error, unexpected '$', expecting ';' in %s(%d) : assert code:1
+Stack trace:
+#0 %s(%d): assert('0 $ 0')
+#1 {main}
+Next Error: Failure evaluating code:
+0 $ 0 in %s:%d
+Stack trace:
+#0 %s(%d): assert('0 $ 0')
+#1 {main}
+ thrown in %s on line %d \ No newline at end of file
diff --git a/ext/standard/tests/assert/assert_error4.phpt b/ext/standard/tests/assert/assert_error4.phpt
index 41d404b1f3..e4d27aecef 100644
--- a/ext/standard/tests/assert/assert_error4.phpt
+++ b/ext/standard/tests/assert/assert_error4.phpt
@@ -14,8 +14,14 @@ $sa = "0 $ 0";
var_dump($r2 = assert($sa, "Describing what was asserted"));
--EXPECTF--
-Parse error: syntax error, %s in %s(3) : assert code on line 1
-
-Catchable fatal error: assert(): Failure evaluating code:
-Describing what was asserted:"0 $ 0" in %s on line 3
+Fatal error: Uncaught ParseError: syntax error, unexpected '$', expecting ';' in %s(%d) : assert code:1
+Stack trace:
+#0 %s(%d): assert('0 $ 0', 'Describing what...')
+#1 {main}
+Next Error: Failure evaluating code:
+Describing what was asserted:"0 $ 0" in %s:%d
+Stack trace:
+#0 %s(%d): assert('0 $ 0', 'Describing what...')
+#1 {main}
+ thrown in %s on line %d
diff --git a/ext/standard/tests/assert/bug73303.phpt b/ext/standard/tests/assert/bug73303.phpt
new file mode 100644
index 0000000000..57990aba60
--- /dev/null
+++ b/ext/standard/tests/assert/bug73303.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Bug #73303: Scope not inherited by eval in assert()
+--FILE--
+<?php
+
+class Test {
+ public $prop;
+
+ public function main(){
+ assert('self::checkCacheKey(get_object_vars($this))');
+ echo 'Success';
+ }
+ private static function checkCacheKey($obj_properties){
+ return count($obj_properties) == 1;
+ }
+}
+
+$obj = new Test();
+$obj->main();
+
+?>
+--EXPECT--
+Success
diff --git a/ext/standard/tests/class_object/forward_static_call_002.phpt b/ext/standard/tests/class_object/forward_static_call_002.phpt
index 58c4efd0cf..64406feb3e 100644
--- a/ext/standard/tests/class_object/forward_static_call_002.phpt
+++ b/ext/standard/tests/class_object/forward_static_call_002.phpt
@@ -18,4 +18,9 @@ test();
?>
--EXPECTF--
-Fatal error: Cannot call forward_static_call() when no class scope is active in %s on line %d
+Fatal error: Uncaught Error: Cannot call forward_static_call() when no class scope is active in %s:%d
+Stack trace:
+#0 %s(%d): forward_static_call(Array)
+#1 %s(%d): test()
+#2 {main}
+ thrown in %s on line %d
diff --git a/ext/standard/tests/dir/bug72625.phpt b/ext/standard/tests/dir/bug72625.phpt
new file mode 100644
index 0000000000..b64010fcd4
--- /dev/null
+++ b/ext/standard/tests/dir/bug72625.phpt
@@ -0,0 +1,53 @@
+--TEST--
+Bug #72625 realpath() fails on very long argument.
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die("skip Valid only on Windows");
+}
+?>
+--FILE--
+<?php
+
+$base = sys_get_temp_dir() . "/" . md5(uniqid());
+while (strlen($base) < 260) {
+ $base = "$base/" . md5(uniqid());
+}
+
+$f0 = "$base/_test/documents/projects/myproject/vendor/name/library/classpath";
+$f1 = "$f0/../../../../../../../../_test/documents/projects/myproject/vendor/name/library/../../../../../../../_test/documents/projects/myproject/vendor/name/library/classpath";
+
+
+mkdir($f0, 0777, true);
+
+
+var_dump(
+ $f0,
+ file_exists($f0),
+ realpath($f0),
+ dirname($f0),
+
+ $f1,
+ file_exists($f1),
+ realpath($f1),
+ dirname($f1)
+);
+
+$tmp = $f0;
+while ($tmp > $base) {
+ rmdir($tmp);
+ $tmp = dirname($tmp);
+}
+
+?>
+===DONE===
+--EXPECTF--
+string(%d) "%s/_test/documents/projects/myproject/vendor/name/library/classpath"
+bool(true)
+string(%d) "%s\_test\documents\projects\myproject\vendor\name\library\classpath"
+string(%d) "%s/_test/documents/projects/myproject/vendor/name/library"
+string(%d) "%s/_test/documents/projects/myproject/vendor/name/library/classpath/../../../../../../../../_test/documents/projects/myproject/vendor/name/library/../../../../../../../_test/documents/projects/myproject/vendor/name/library/classpath"
+bool(true)
+string(%d) "%s\_test\documents\projects\myproject\vendor\name\library\classpath"
+string(%d) "%s/_test/documents/projects/myproject/vendor/name/library"
+===DONE===
diff --git a/ext/standard/tests/dir/bug73877.phpt b/ext/standard/tests/dir/bug73877.phpt
new file mode 100644
index 0000000000..bade168b4a
--- /dev/null
+++ b/ext/standard/tests/dir/bug73877.phpt
@@ -0,0 +1,50 @@
+--TEST--
+Bug #73877 readlink() returns garbage for UTF-8 paths
+File type functions
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die('skip only for Windows');
+}
+?>
+--FILE--
+<?php
+
+$base = dirname(__FILE__) . DIRECTORY_SEPARATOR . "bug73877";
+$dir0 = $base . DIRECTORY_SEPARATOR . "bug73877";
+$dir1 = $base . DIRECTORY_SEPARATOR . "Серёжка";
+$junk0 = $base . DIRECTORY_SEPARATOR . "Серёжка2";
+
+mkdir($base);
+mkdir($dir0);
+mkdir($dir1);
+`mklink /J $junk0 $dir0`;
+
+var_dump(
+ readlink($dir0),
+ readlink($dir1),
+ readlink($junk0),
+ strlen(readlink($dir0)) === strlen(readlink($junk0))
+);
+
+?>
+--CLEAN--
+<?php
+
+$base = dirname(__FILE__) . DIRECTORY_SEPARATOR . "bug73877";
+$dir0 = $base . DIRECTORY_SEPARATOR . "bug73877";
+$dir1 = $base . DIRECTORY_SEPARATOR . "Серёжка";
+$junk0 = $base . DIRECTORY_SEPARATOR . "Серёжка2";
+
+rmdir($junk0);
+rmdir($dir0);
+rmdir($dir1);
+rmdir($base);
+
+?>
+--EXPECTF--
+string(%d) "%sbug73877"
+string(%d) "%sСерёжка"
+string(%d) "%sbug73877"
+bool(true)
+
diff --git a/ext/standard/tests/dir/chdir_basic-win32-mb.phpt b/ext/standard/tests/dir/chdir_basic-win32-mb.phpt
new file mode 100644
index 0000000000..8d5b2ff986
--- /dev/null
+++ b/ext/standard/tests/dir/chdir_basic-win32-mb.phpt
@@ -0,0 +1,60 @@
+--TEST--
+Test chdir() function : basic functionality
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die("skip Valid only on Windows");
+}
+?>
+--FILE--
+<?php
+/* Prototype : bool chdir(string $directory)
+ * Description: Change the current directory
+ * Source code: ext/standard/dir.c
+ */
+
+/*
+ * Test basic functionality of chdir() with absolute and relative paths
+ */
+
+echo "*** Testing chdir() : basic functionality ***\n";
+$base_dir_path = dirname(__FILE__);
+
+$level_one_dir_name = "私はガラスを食べられますlevel_one";
+$level_one_dir_path = "$base_dir_path/$level_one_dir_name";
+
+$level_two_dir_name = "私はガラスを食べられますlevel_two";
+$level_two_dir_path = "$base_dir_path/$level_one_dir_name/$level_two_dir_name";
+
+// create directories
+mkdir($level_one_dir_path);
+mkdir($level_two_dir_path);
+
+echo "\n-- Testing chdir() with absolute path: --\n";
+chdir($base_dir_path);
+var_dump(chdir($level_one_dir_path));
+var_dump(getcwd());
+
+echo "\n-- Testing chdir() with relative paths: --\n";
+var_dump(chdir($level_two_dir_name));
+var_dump(getcwd());
+?>
+===DONE===
+--CLEAN--
+<?php
+$file_path = dirname(__FILE__);
+chdir($file_path);
+rmdir("$file_path/私はガラスを食べられますlevel_one/私はガラスを食べられますlevel_two");
+rmdir("$file_path/私はガラスを食べられますlevel_one");
+?>
+--EXPECTF--
+*** Testing chdir() : basic functionality ***
+
+-- Testing chdir() with absolute path: --
+bool(true)
+string(%d) "%s私はガラスを食べられますlevel_one"
+
+-- Testing chdir() with relative paths: --
+bool(true)
+string(%d) "%s私はガラスを食べられますlevel_one%e私はガラスを食べられますlevel_two"
+===DONE===
diff --git a/ext/standard/tests/dir/chdir_error2-win32-mb.phpt b/ext/standard/tests/dir/chdir_error2-win32-mb.phpt
new file mode 100644
index 0000000000..2b23c90644
--- /dev/null
+++ b/ext/standard/tests/dir/chdir_error2-win32-mb.phpt
@@ -0,0 +1,32 @@
+--TEST--
+Test chdir() function : error conditions - Non-existent directory
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die("skip Valid only on Windows");
+}
+?>
+--FILE--
+<?php
+/* Prototype : bool chdir(string $directory)
+ * Description: Change the current directory
+ * Source code: ext/standard/dir.c
+ */
+
+/*
+ * Pass a directory that does not exist as $directory to chdir() to test behaviour
+ */
+
+echo "*** Testing chdir() : error conditions ***\n";
+
+$directory = __FILE__ . '/私はガラスを食べられますidonotexist';
+
+var_dump(chdir($directory));
+?>
+===DONE===
+--EXPECTF--
+*** Testing chdir() : error conditions ***
+
+Warning: chdir(): %s (errno %d) in %s on line %d
+bool(false)
+===DONE===
diff --git a/ext/standard/tests/dir/chdir_variation1-win32-mb.phpt b/ext/standard/tests/dir/chdir_variation1-win32-mb.phpt
new file mode 100644
index 0000000000..90402f4c49
--- /dev/null
+++ b/ext/standard/tests/dir/chdir_variation1-win32-mb.phpt
@@ -0,0 +1,241 @@
+--TEST--
+Test chdir() function : usage variations - different data type as $directory arg
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die("skip Valid only on Windows");
+}
+?>
+--FILE--
+<?php
+/* Prototype : bool chdir(string $directory)
+ * Description: Change the current directory
+ * Source code: ext/standard/dir.c
+ */
+
+/*
+ * Pass different data types as $directory argument to test behaviour
+ */
+
+echo "*** Testing chdir() : usage variations ***\n";
+
+// create the temporary directory
+$file_path = dirname(__FILE__);
+$dir_path = $file_path."/私はガラスを食べられますchdir_basic";
+@mkdir($dir_path);
+
+//get an unset variable
+$unset_var = 10;
+unset ($unset_var);
+
+// get a class
+class classA {
+ var $dir_path;
+
+ function __construct($dir) {
+ $this->dir_path = $dir;
+ }
+
+ public function __toString() {
+ return "$this->dir_path";
+ }
+}
+
+// heredoc string
+$heredoc = <<<EOT
+$dir_path
+EOT;
+
+// get a resource variable
+$fp = fopen(__FILE__, "r");
+
+// unexpected values to be passed to $directory argument
+$inputs = array(
+
+ // int data
+/*1*/ 0,
+ 1,
+ 12345,
+ -2345,
+
+ // float data
+/*5*/ 10.5,
+ -10.5,
+ 12.3456789000e10,
+ 12.3456789000E-10,
+ .5,
+
+ // null data
+/*10*/ NULL,
+ null,
+
+ // boolean data
+/*12*/ true,
+ false,
+ TRUE,
+ FALSE,
+
+ // empty data
+/*16*/ "",
+ '',
+ array(),
+
+ // string data
+/*19*/ "$dir_path",
+ 'string',
+ $heredoc,
+
+ // object data
+/*22*/ new classA($dir_path),
+
+ // undefined data
+/*23*/ @$undefined_var,
+
+ // unset data
+/*24*/ @$unset_var,
+
+ // resource variable
+/*25*/ $fp
+);
+
+// loop through each element of $inputs to check the behavior of chdir()
+$iterator = 1;
+foreach($inputs as $input) {
+ echo "\n-- Iteration $iterator --\n";
+ var_dump( chdir($input) );
+ $iterator++;
+};
+
+fclose($fp);
+
+?>
+===DONE===
+--CLEAN--
+<?php
+$file_path = dirname(__FILE__);
+$dir_path = $file_path."/私はガラスを食べられますchdir_basic";
+
+rmdir($dir_path);
+?>
+--EXPECTF--
+*** Testing chdir() : usage variations ***
+
+-- Iteration 1 --
+
+Warning: chdir(): %s (errno %d) in %s on line %d
+bool(false)
+
+-- Iteration 2 --
+
+Warning: chdir(): %s (errno %d) in %s on line %d
+bool(false)
+
+-- Iteration 3 --
+
+Warning: chdir(): %s (errno %d) in %s on line %d
+bool(false)
+
+-- Iteration 4 --
+
+Warning: chdir(): %s (errno %d) in %s on line %d
+bool(false)
+
+-- Iteration 5 --
+
+Warning: chdir(): %s (errno %d) in %s on line %d
+bool(false)
+
+-- Iteration 6 --
+
+Warning: chdir(): %s (errno %d) in %s on line %d
+bool(false)
+
+-- Iteration 7 --
+
+Warning: chdir(): %s (errno %d) in %s on line %d
+bool(false)
+
+-- Iteration 8 --
+
+Warning: chdir(): %s (errno %d) in %s on line %d
+bool(false)
+
+-- Iteration 9 --
+
+Warning: chdir(): %s (errno %d) in %s on line %d
+bool(false)
+
+-- Iteration 10 --
+
+Warning: chdir(): %s (errno %d) in %s on line %d
+bool(false)
+
+-- Iteration 11 --
+
+Warning: chdir(): %s (errno %d) in %s on line %d
+bool(false)
+
+-- Iteration 12 --
+
+Warning: chdir(): %s (errno %d) in %s on line %d
+bool(false)
+
+-- Iteration 13 --
+
+Warning: chdir(): %s (errno %d) in %s on line %d
+bool(false)
+
+-- Iteration 14 --
+
+Warning: chdir(): %s (errno %d) in %s on line %d
+bool(false)
+
+-- Iteration 15 --
+
+Warning: chdir(): %s (errno %d) in %s on line %d
+bool(false)
+
+-- Iteration 16 --
+
+Warning: chdir(): %s (errno %d) in %s on line %d
+bool(false)
+
+-- Iteration 17 --
+
+Warning: chdir(): %s (errno %d) in %s on line %d
+bool(false)
+
+-- Iteration 18 --
+
+Warning: chdir() expects parameter 1 to be a valid path, array given in %s on line %d
+bool(false)
+
+-- Iteration 19 --
+bool(true)
+
+-- Iteration 20 --
+
+Warning: chdir(): %s (errno %d) in %s on line %d
+bool(false)
+
+-- Iteration 21 --
+bool(true)
+
+-- Iteration 22 --
+bool(true)
+
+-- Iteration 23 --
+
+Warning: chdir(): %s (errno %d) in %s on line %d
+bool(false)
+
+-- Iteration 24 --
+
+Warning: chdir(): %s (errno %d) in %s on line %d
+bool(false)
+
+-- Iteration 25 --
+
+Warning: chdir() expects parameter 1 to be a valid path, resource given in %s on line %d
+bool(false)
+===DONE===
diff --git a/ext/standard/tests/dir/chdir_variation2-win32-mb.phpt b/ext/standard/tests/dir/chdir_variation2-win32-mb.phpt
new file mode 100644
index 0000000000..f28d57ab4f
--- /dev/null
+++ b/ext/standard/tests/dir/chdir_variation2-win32-mb.phpt
@@ -0,0 +1,115 @@
+--TEST--
+Test chdir() function : usage variations - relative paths
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die("skip Valid only on Windows");
+}
+?>
+--FILE--
+<?php
+/* Prototype : bool chdir(string $directory)
+ * Description: Change the current directory
+ * Source code: ext/standard/dir.c
+ */
+
+/*
+ * Test chdir() with variations of relative paths
+ */
+
+echo "*** Testing chdir() : usage variations ***\n";
+
+$base_dir_path = dirname(__FILE__);
+
+$level_one_dir_name = "私はガラスを食べられますlevel_one";
+$level_one_dir_path = "$base_dir_path/$level_one_dir_name";
+
+$level_two_dir_name = "私はガラスを食べられますlevel_two";
+$level_two_dir_path = "$base_dir_path/$level_one_dir_name/$level_two_dir_name";
+
+// create directories
+mkdir($level_one_dir_path);
+mkdir($level_two_dir_path);
+
+echo "\n-- \$directory = './私はガラスを食べられますlevel_one': --\n";
+var_dump(chdir($base_dir_path));
+var_dump(chdir("./$level_one_dir_name"));
+var_dump(getcwd());
+
+echo "\n-- \$directory = '私はガラスを食べられますlevel_one/私はガラスを食べられますlevel_two': --\n";
+var_dump(chdir($base_dir_path));
+var_dump(chdir("$level_one_dir_name/$level_two_dir_name"));
+var_dump(getcwd());
+
+echo "\n-- \$directory = '..': --\n";
+var_dump(chdir('..'));
+var_dump(getcwd());
+
+echo "\n-- \$directory = '私はガラスを食べられますlevel_two', '.': --\n";
+var_dump(chdir($level_two_dir_path));
+var_dump(chdir('.'));
+var_dump(getcwd());
+
+echo "\n-- \$directory = '../': --\n";
+var_dump(chdir('../'));
+var_dump(getcwd());
+
+echo "\n-- \$directory = './': --\n";
+var_dump(chdir($level_two_dir_path));
+var_dump(chdir('./'));
+var_dump(getcwd());
+
+echo "\n-- \$directory = '../../'私はガラスを食べられますlevel_one': --\n";
+var_dump(chdir($level_two_dir_path));
+var_dump(chdir("../../$level_one_dir_name"));
+var_dump(getcwd());
+
+$file_path = dirname(__FILE__);
+chdir($file_path);/* not that PWD is accidentialy one of the dirs to be deleted. */
+rmdir("$file_path/私はガラスを食べられますlevel_one/私はガラスを食べられますlevel_two");
+rmdir("$file_path/私はガラスを食べられますlevel_one");
+?>
+===DONE===
+--CLEAN--
+<?php
+$file_path = dirname(__FILE__);
+chdir($file_path);/* not that PWD is accidentialy one of the dirs to be deleted. */
+rmdir("$file_path/私はガラスを食べられますlevel_one/私はガラスを食べられますlevel_two");
+rmdir("$file_path/私はガラスを食べられますlevel_one");
+?>
+--EXPECTF--
+*** Testing chdir() : usage variations ***
+
+-- $directory = './私はガラスを食べられますlevel_one': --
+bool(true)
+bool(true)
+string(%d) "%slevel_one"
+
+-- $directory = '私はガラスを食べられますlevel_one/私はガラスを食べられますlevel_two': --
+bool(true)
+bool(true)
+string(%d) "%s私はガラスを食べられますlevel_one%e私はガラスを食べられますlevel_two"
+
+-- $directory = '..': --
+bool(true)
+string(%d) "%s私はガラスを食べられますlevel_one"
+
+-- $directory = '私はガラスを食べられますlevel_two', '.': --
+bool(true)
+bool(true)
+string(%d) "%s私はガラスを食べられますlevel_one%e私はガラスを食べられますlevel_two"
+
+-- $directory = '../': --
+bool(true)
+string(%d) "%slevel_one"
+
+-- $directory = './': --
+bool(true)
+bool(true)
+string(%d) "%s私はガラスを食べられますlevel_one%e私はガラスを食べられますlevel_two"
+
+-- $directory = '../../'私はガラスを食べられますlevel_one': --
+bool(true)
+bool(true)
+string(%d) "%s私はガラスを食べられますlevel_one"
+===DONE===
diff --git a/ext/standard/tests/dir/closedir_basic-win32-mb.phpt b/ext/standard/tests/dir/closedir_basic-win32-mb.phpt
new file mode 100644
index 0000000000..2602e292ff
--- /dev/null
+++ b/ext/standard/tests/dir/closedir_basic-win32-mb.phpt
@@ -0,0 +1,62 @@
+--TEST--
+Test closedir() function : basic functionality
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die("skip Valid only on Windows");
+}
+?>
+--FILE--
+<?php
+/* Prototype : void closedir([resource $dir_handle])
+ * Description: Close directory connection identified by the dir_handle
+ * Source code: ext/standard/dir.c
+ * Alias to functions: close
+ */
+
+/*
+ * Test basic functionality of closedir()
+ */
+
+echo "*** Testing closedir() : basic functionality ***\n";
+
+$base_dir = dirname(__FILE__);
+$dir_path = $base_dir . '/私はガラスを食べられますclosedir_basic';
+mkdir($dir_path);
+
+echo "\n-- Call closedir() with no arguments: --\n";
+$dh1 = opendir($dir_path);
+var_dump(closedir());
+echo "-- Check Directory Handle: --\n";
+var_dump($dh1);
+
+echo "\n-- Call closedir() with \$dir_handle argument supplied: --\n";
+$dh2 = opendir($dir_path);
+
+if ((int)$dh1 === (int)$dh2) {
+ echo "\nNo new resource created\n";
+}
+var_dump(closedir($dh2));
+echo "-- Check Directory Handle: --\n";
+var_dump($dh2);
+?>
+===DONE===
+--CLEAN--
+<?php
+$base_dir = dirname(__FILE__);
+$dir_path = $base_dir . '/私はガラスを食べられますclosedir_basic';
+rmdir($dir_path);
+?>
+--EXPECTF--
+*** Testing closedir() : basic functionality ***
+
+-- Call closedir() with no arguments: --
+NULL
+-- Check Directory Handle: --
+resource(%d) of type (Unknown)
+
+-- Call closedir() with $dir_handle argument supplied: --
+NULL
+-- Check Directory Handle: --
+resource(%d) of type (Unknown)
+===DONE===
diff --git a/ext/standard/tests/dir/closedir_error-win32-mb.phpt b/ext/standard/tests/dir/closedir_error-win32-mb.phpt
new file mode 100644
index 0000000000..04c614e33d
--- /dev/null
+++ b/ext/standard/tests/dir/closedir_error-win32-mb.phpt
@@ -0,0 +1,51 @@
+--TEST--
+Test closedir() function : error conditions - Pass incorrect number of arguments
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die("skip Valid only on Windows");
+}
+?>
+--FILE--
+<?php
+/* Prototype : void closedir([resource $dir_handle])
+ * Description: Close directory connection identified by the dir_handle
+ * Source code: ext/standard/dir.c
+ * Alias to functions: close
+ */
+
+/*
+ * Pass incorrect number of arguments to closedir() to test behaviour
+ */
+
+echo "*** Testing closedir() : error conditions ***\n";
+
+
+//Test closedir with one more than the expected number of arguments
+echo "\n-- Testing closedir() function with more than expected no. of arguments --\n";
+
+$dir_path = dirname(__FILE__) . '\私はガラスを食べられますclosedir_error';
+mkdir($dir_path);
+$dir_handle = opendir($dir_path);
+
+$extra_arg = 10;
+var_dump( closedir($dir_handle, $extra_arg) );
+
+//successfully close the directory handle so can delete in CLEAN section
+closedir($dir_handle);
+?>
+===DONE===
+--CLEAN--
+<?php
+$base_dir = dirname(__FILE__);
+$dir_path = $base_dir . '\私はガラスを食べられますclosedir_error';
+rmdir($dir_path);
+?>
+--EXPECTF--
+*** Testing closedir() : error conditions ***
+
+-- Testing closedir() function with more than expected no. of arguments --
+
+Warning: closedir() expects at most 1 parameter, 2 given in %s on line %d
+NULL
+===DONE===
diff --git a/ext/standard/tests/dir/closedir_variation2-win32-mb.phpt b/ext/standard/tests/dir/closedir_variation2-win32-mb.phpt
new file mode 100644
index 0000000000..26b4a5b8b7
--- /dev/null
+++ b/ext/standard/tests/dir/closedir_variation2-win32-mb.phpt
@@ -0,0 +1,57 @@
+--TEST--
+Test closedir() function : usage variations - close directory handle twice
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die("skip Valid only on Windows");
+}
+?>
+--FILE--
+<?php
+/* Prototype : void closedir([resource $dir_handle])
+ * Description: Close directory connection identified by the dir_handle
+ * Source code: ext/standard/dir.c
+ * Alias to functions: close
+ */
+
+/*
+ * close the directory handle twice using closedir() to test behaviour
+ */
+
+echo "*** Testing closedir() : usage variations ***\n";
+
+//create temporary directory for test, removed in CLEAN section
+$directory = dirname(__FILE__) . "/私はガラスを食べられますclosedir_variation2";
+mkdir($directory);
+
+$dh = opendir($directory);
+
+echo "\n-- Close directory handle first time: --\n";
+var_dump(closedir($dh));
+echo "Directory Handle: ";
+var_dump($dh);
+
+echo "\n-- Close directory handle second time: --\n";
+var_dump(closedir($dh));
+echo "Directory Handle: ";
+var_dump($dh);
+?>
+===DONE===
+--CLEAN--
+<?php
+$directory = dirname(__FILE__) . "/私はガラスを食べられますclosedir_variation2";
+rmdir($directory);
+?>
+--EXPECTF--
+*** Testing closedir() : usage variations ***
+
+-- Close directory handle first time: --
+NULL
+Directory Handle: resource(%d) of type (Unknown)
+
+-- Close directory handle second time: --
+
+Warning: closedir(): %s is not a valid Directory resource in %s on line %d
+bool(false)
+Directory Handle: resource(%d) of type (Unknown)
+===DONE===
diff --git a/ext/standard/tests/dir/dir_basic-win32-mb.phpt b/ext/standard/tests/dir/dir_basic-win32-mb.phpt
new file mode 100644
index 0000000000..d562c0b7e3
--- /dev/null
+++ b/ext/standard/tests/dir/dir_basic-win32-mb.phpt
@@ -0,0 +1,92 @@
+--TEST--
+Test dir() function : basic functionality
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die("skip Valid only on Windows");
+}
+?>
+--FILE--
+<?php
+/*
+ * Prototype : object dir(string $directory[, resource $context])
+ * Description: Directory class with properties, handle and class and methods read, rewind and close
+ * Source code: ext/standard/dir.c
+ */
+
+echo "*** Testing dir() : basic functionality ***\n";
+
+// include the file.inc for Function: function create_files()
+include(dirname(__FILE__)."/../file/file.inc");
+
+// create the temporary directory
+$file_path = dirname(__FILE__);
+$dir_path = $file_path."/私はガラスを食べられますdir_basic";
+@mkdir($dir_path);
+
+// create files within the temporary directory
+create_files($dir_path, 3, "alphanumeric", 0755, 1, "w", "私はガラスを食べられますdir_basic");
+
+echo "Get Directory instance:\n";
+$d = dir($dir_path);
+var_dump( $d );
+
+echo "\nRead and rewind:\n";
+var_dump( $d->read() );
+var_dump( $d->read() );
+var_dump( $d->rewind() );
+
+echo "\nTest using handle directly:\n";
+var_dump( readdir($d->handle) );
+var_dump( readdir($d->handle) );
+
+echo "\nClose directory:\n";
+var_dump( $d->close() );
+var_dump( $d );
+
+echo "\nTest read after closing the dir:";
+var_dump( $d->read() );
+
+// delete temp files
+delete_files($dir_path, 3, "私はガラスを食べられますdir_basic", 1, ".tmp");
+echo "Done";
+?>
+--CLEAN--
+<?php
+$file_path = dirname(__FILE__);
+$dir_path = $file_path."/私はガラスを食べられますdir_basic";
+
+rmdir($dir_path);
+?>
+--EXPECTF--
+*** Testing dir() : basic functionality ***
+Get Directory instance:
+object(Directory)#%d (2) {
+ ["path"]=>
+ string(%d) "%s/私はガラスを食べられますdir_basic"
+ ["handle"]=>
+ resource(%d) of type (stream)
+}
+
+Read and rewind:
+string(%d) "%s"
+string(%d) "%s"
+NULL
+
+Test using handle directly:
+string(%d) "%s"
+string(%d) "%s"
+
+Close directory:
+NULL
+object(Directory)#%d (2) {
+ ["path"]=>
+ string(%d) "%s私はガラスを食べられますdir_basic"
+ ["handle"]=>
+ resource(%d) of type (Unknown)
+}
+
+Test read after closing the dir:
+Warning: Directory::read(): %s is not a valid Directory resource in %s on line %d
+bool(false)
+Done
diff --git a/ext/standard/tests/dir/dir_variation2-win32-mb.phpt b/ext/standard/tests/dir/dir_variation2-win32-mb.phpt
new file mode 100644
index 0000000000..a2d482f43e
--- /dev/null
+++ b/ext/standard/tests/dir/dir_variation2-win32-mb.phpt
@@ -0,0 +1,229 @@
+--TEST--
+Test dir() function : usage variations - unexpected value for 'context' argument
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die("skip Valid only on Windows");
+}
+?>
+--FILE--
+<?php
+/*
+ * Prototype : object dir(string $directory[, resource $context])
+ * Description: Directory class with properties, handle and class and methods read, rewind and close
+ * Source code: ext/standard/dir.c
+ */
+
+/*
+ * Passing non resource values to 'context' argument of dir() and see
+ * that the function outputs proper warning messages wherever expected.
+ */
+
+echo "*** Testing dir() : unexpected values for \$context argument ***\n";
+
+// create the temporary directory
+$file_path = dirname(__FILE__);
+$directory = $file_path."/私はガラスを食べられますdir_variation2";
+@mkdir($directory);
+
+// get an unset variable
+$unset_var = stream_context_create();
+unset($unset_var);
+
+class classA
+{
+ public $var;
+ public function init() {
+ $this->var = 10;
+ }
+}
+
+// heredoc string
+$heredoc = <<<EOT
+hello world
+EOT;
+
+// unexpected values to be passed to $directory argument
+$unexpected_values = array (
+ // int data
+/*1*/ 0,
+ 1,
+ 12345,
+ -2345,
+
+ // float data
+/*5*/ 10.5,
+ -10.5,
+ 12.3456789000e10,
+ 12.3456789000E-10,
+ .5,
+
+ // array data
+/*10*/ array(),
+ array(0),
+ array(1),
+ array(1, 2),
+ array('color' => 'red', 'item' => 'pen'),
+
+
+ // null data
+/*15*/ NULL,
+ null,
+
+ // boolean data
+/*17*/ true,
+ false,
+ TRUE,
+ FALSE,
+
+ // empty data
+/*21*/ "",
+ '',
+
+ // string data
+/*23*/ "string",
+ 'string',
+ $heredoc,
+
+ // object data
+/*26*/ new classA(),
+
+ // undefined data
+/*27*/ @$undefined_var,
+
+ // unset data
+/*28*/ @$unset_var
+);
+
+// loop through various elements of $unexpected_values to check the behavior of dir()
+$iterator = 1;
+foreach( $unexpected_values as $unexpected_value ) {
+ echo "\n-- Iteration $iterator --";
+ var_dump( dir($directory, $unexpected_value) );
+ $iterator++;
+}
+
+echo "Done";
+?>
+--CLEAN--
+<?php
+$file_path = dirname(__FILE__);
+$directory = $file_path."/私はガラスを食べられますdir_variation2";
+
+rmdir($directory);
+?>
+--EXPECTF--
+*** Testing dir() : unexpected values for $context argument ***
+
+-- Iteration 1 --
+Warning: dir() expects parameter 2 to be resource, integer given in %s on line %d
+NULL
+
+-- Iteration 2 --
+Warning: dir() expects parameter 2 to be resource, integer given in %s on line %d
+NULL
+
+-- Iteration 3 --
+Warning: dir() expects parameter 2 to be resource, integer given in %s on line %d
+NULL
+
+-- Iteration 4 --
+Warning: dir() expects parameter 2 to be resource, integer given in %s on line %d
+NULL
+
+-- Iteration 5 --
+Warning: dir() expects parameter 2 to be resource, float given in %s on line %d
+NULL
+
+-- Iteration 6 --
+Warning: dir() expects parameter 2 to be resource, float given in %s on line %d
+NULL
+
+-- Iteration 7 --
+Warning: dir() expects parameter 2 to be resource, float given in %s on line %d
+NULL
+
+-- Iteration 8 --
+Warning: dir() expects parameter 2 to be resource, float given in %s on line %d
+NULL
+
+-- Iteration 9 --
+Warning: dir() expects parameter 2 to be resource, float given in %s on line %d
+NULL
+
+-- Iteration 10 --
+Warning: dir() expects parameter 2 to be resource, array given in %s on line %d
+NULL
+
+-- Iteration 11 --
+Warning: dir() expects parameter 2 to be resource, array given in %s on line %d
+NULL
+
+-- Iteration 12 --
+Warning: dir() expects parameter 2 to be resource, array given in %s on line %d
+NULL
+
+-- Iteration 13 --
+Warning: dir() expects parameter 2 to be resource, array given in %s on line %d
+NULL
+
+-- Iteration 14 --
+Warning: dir() expects parameter 2 to be resource, array given in %s on line %d
+NULL
+
+-- Iteration 15 --
+Warning: dir() expects parameter 2 to be resource, null given in %s on line %d
+NULL
+
+-- Iteration 16 --
+Warning: dir() expects parameter 2 to be resource, null given in %s on line %d
+NULL
+
+-- Iteration 17 --
+Warning: dir() expects parameter 2 to be resource, boolean given in %s on line %d
+NULL
+
+-- Iteration 18 --
+Warning: dir() expects parameter 2 to be resource, boolean given in %s on line %d
+NULL
+
+-- Iteration 19 --
+Warning: dir() expects parameter 2 to be resource, boolean given in %s on line %d
+NULL
+
+-- Iteration 20 --
+Warning: dir() expects parameter 2 to be resource, boolean given in %s on line %d
+NULL
+
+-- Iteration 21 --
+Warning: dir() expects parameter 2 to be resource, string given in %s on line %d
+NULL
+
+-- Iteration 22 --
+Warning: dir() expects parameter 2 to be resource, string given in %s on line %d
+NULL
+
+-- Iteration 23 --
+Warning: dir() expects parameter 2 to be resource, string given in %s on line %d
+NULL
+
+-- Iteration 24 --
+Warning: dir() expects parameter 2 to be resource, string given in %s on line %d
+NULL
+
+-- Iteration 25 --
+Warning: dir() expects parameter 2 to be resource, string given in %s on line %d
+NULL
+
+-- Iteration 26 --
+Warning: dir() expects parameter 2 to be resource, object given in %s on line %d
+NULL
+
+-- Iteration 27 --
+Warning: dir() expects parameter 2 to be resource, null given in %s on line %d
+NULL
+
+-- Iteration 28 --
+Warning: dir() expects parameter 2 to be resource, null given in %s on line %d
+NULL
+Done
diff --git a/ext/standard/tests/dir/dir_variation4-win32-mb.phpt b/ext/standard/tests/dir/dir_variation4-win32-mb.phpt
new file mode 100644
index 0000000000..156b836562
--- /dev/null
+++ b/ext/standard/tests/dir/dir_variation4-win32-mb.phpt
@@ -0,0 +1,78 @@
+--TEST--
+Test dir() function : usage variations - operate on previously opened directory
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die("skip Valid only on Windows");
+}
+?>
+--FILE--
+<?php
+/*
+ * Prototype : object dir(string $directory[, resource $context])
+ * Description: Directory class with properties, handle and class and methods read, rewind and close
+ * Source code: ext/standard/dir.c
+ */
+
+/*
+ * Testing the behavior of dir() function by trying to open a
+ * directory which is already open.
+ */
+
+echo "*** Testing dir() : operate on previously opened directory ***\n";
+
+// include the file.inc for Function: function create_files()
+include( dirname(__FILE__)."/../file/file.inc");
+
+// create the temporary directory
+$file_path = dirname(__FILE__);
+$dir_path = $file_path."/私はガラスを食べられますdir_variation4";
+@mkdir($dir_path);
+
+// create files within the temporary directory
+create_files($dir_path, 3, "alphanumeric", 0755, 1, "w", "私はガラスを食べられますdir_variation4");
+
+// open the directory
+$d = dir($dir_path);
+var_dump( $d );
+
+// open the same directory again without closing it
+$e = dir($dir_path);
+var_dump( $e );
+
+echo "-- reading directory contents with previous handle --\n";
+var_dump( $d->read() ); // with previous handle
+
+echo "-- reading directory contents with current handle --\n";
+var_dump( $e->read() ); // with current handle
+
+// delete temporary files
+delete_files($dir_path, 3, "私はガラスを食べられますdir_variation4");
+echo "Done";
+?>
+--CLEAN--
+<?php
+$file_path = dirname(__FILE__);
+$dir_path = $file_path."/私はガラスを食べられますdir_variation4";
+
+rmdir($dir_path);
+?>
+--EXPECTF--
+*** Testing dir() : operate on previously opened directory ***
+object(Directory)#%d (2) {
+ ["path"]=>
+ string(%d) "%s/私はガラスを食べられますdir_variation4"
+ ["handle"]=>
+ resource(%d) of type (stream)
+}
+object(Directory)#%d (2) {
+ ["path"]=>
+ string(%d) "%s/私はガラスを食べられますdir_variation4"
+ ["handle"]=>
+ resource(%d) of type (stream)
+}
+-- reading directory contents with previous handle --
+string(%d) "%s"
+-- reading directory contents with current handle --
+string(%d) "%s"
+Done
diff --git a/ext/standard/tests/dir/getcwd_basic-win32-mb.phpt b/ext/standard/tests/dir/getcwd_basic-win32-mb.phpt
new file mode 100644
index 0000000000..7ebbaa470f
--- /dev/null
+++ b/ext/standard/tests/dir/getcwd_basic-win32-mb.phpt
@@ -0,0 +1,40 @@
+--TEST--
+Test getcwd() function : basic functionality
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die("skip Valid only on Windows");
+}
+?>
+--FILE--
+<?php
+/* Prototype : mixed getcwd(void)
+ * Description: Gets the current directory
+ * Source code: ext/standard/dir.c
+ */
+
+/*
+ * Test basic functionality of getcwd()
+ */
+
+echo "*** Testing getcwd() : basic functionality ***\n";
+
+//create temporary directory for test, removed in CLEAN section
+$directory = dirname(__FILE__) . "/私はガラスを食べられますgetcwd_basic";
+mkdir($directory);
+
+var_dump(getcwd());
+chdir($directory);
+var_dump(getcwd());
+?>
+===DONE===
+--CLEAN--
+<?php
+$directory = dirname(__FILE__) . "/私はガラスを食べられますgetcwd_basic";
+rmdir($directory);
+?>
+--EXPECTF--
+*** Testing getcwd() : basic functionality ***
+string(%d) "%s"
+string(%d) "%s%e私はガラスを食べられますgetcwd_basic"
+===DONE===
diff --git a/ext/standard/tests/dir/opendir_basic-win32-mb.phpt b/ext/standard/tests/dir/opendir_basic-win32-mb.phpt
new file mode 100644
index 0000000000..f9d06bfdb7
--- /dev/null
+++ b/ext/standard/tests/dir/opendir_basic-win32-mb.phpt
@@ -0,0 +1,68 @@
+--TEST--
+Test opendir() function : basic functionality
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die("skip Valid only on Windows");
+}
+?>
+--FILE--
+<?php
+/* Prototype : mixed opendir(string $path[, resource $context])
+ * Description: Open a directory and return a dir_handle
+ * Source code: ext/standard/dir.c
+ */
+
+/*
+ * Test basic functionality of opendir() with absolute and relative paths as $path argument
+ */
+
+echo "*** Testing opendir() : basic functionality ***\n";
+
+$base_dir_path = dirname(__FILE__);
+
+$level_one_dir_name = "私はガラスを食べられますlevel_one";
+$level_one_dir_path = "$base_dir_path/$level_one_dir_name";
+
+$level_two_dir_name = "私はガラスを食べられますlevel_two";
+$level_two_dir_path = "$base_dir_path/$level_one_dir_name/$level_two_dir_name";
+
+// create temporary directories - will remove in CLEAN section
+mkdir($level_one_dir_path);
+mkdir($level_two_dir_path);
+
+echo "\n-- Testing opendir() with absolute path: --\n";
+var_dump($dh1 = opendir($level_one_dir_path));
+
+
+echo "\n-- Testing opendir() with relative paths: --\n";
+var_dump(chdir($level_one_dir_path));
+var_dump($dh2 = opendir($level_two_dir_name));
+
+echo "\n-- Close directory handles: --\n";
+closedir($dh1);
+var_dump($dh1);
+closedir($dh2);
+var_dump($dh2);
+?>
+===DONE===
+--CLEAN--
+<?php
+$file_path = dirname(__FILE__);
+rmdir("$file_path/私はガラスを食べられますlevel_one/私はガラスを食べられますlevel_two");
+rmdir("$file_path/私はガラスを食べられますlevel_one");
+?>
+--EXPECTF--
+*** Testing opendir() : basic functionality ***
+
+-- Testing opendir() with absolute path: --
+resource(%d) of type (stream)
+
+-- Testing opendir() with relative paths: --
+bool(true)
+resource(%d) of type (stream)
+
+-- Close directory handles: --
+resource(%d) of type (Unknown)
+resource(%d) of type (Unknown)
+===DONE===
diff --git a/ext/standard/tests/dir/opendir_error1-win32-mb.phpt b/ext/standard/tests/dir/opendir_error1-win32-mb.phpt
new file mode 100644
index 0000000000..e363de4806
--- /dev/null
+++ b/ext/standard/tests/dir/opendir_error1-win32-mb.phpt
@@ -0,0 +1,53 @@
+--TEST--
+Test opendir() function : error conditions - Incorrect number of args
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die("skip Valid only on Windows");
+}
+?>
+--FILE--
+<?php
+/* Prototype : mixed opendir(string $path[, resource $context])
+ * Description: Open a directory and return a dir_handle
+ * Source code: ext/standard/dir.c
+ */
+
+/*
+ * Pass incorrect number of arguments to opendir() to test behaviour
+ */
+
+echo "*** Testing opendir() : error conditions ***\n";
+
+// Zero arguments
+echo "\n-- Testing opendir() function with Zero arguments --\n";
+var_dump( opendir() );
+
+//Test opendir with one more than the expected number of arguments
+echo "\n-- Testing opendir() function with more than expected no. of arguments --\n";
+$path = dirname(__FILE__) . "/私はガラスを食べられますopendir_error";
+mkdir($path);
+$context = stream_context_create();
+
+$extra_arg = 10;
+var_dump( opendir($path, $context, $extra_arg) );
+?>
+===DONE===
+--CLEAN--
+<?php
+$path = dirname(__FILE__) . "/私はガラスを食べられますopendir_error";
+rmdir($path);
+?>
+--EXPECTF--
+*** Testing opendir() : error conditions ***
+
+-- Testing opendir() function with Zero arguments --
+
+Warning: opendir() expects at least 1 parameter, 0 given in %s on line %d
+NULL
+
+-- Testing opendir() function with more than expected no. of arguments --
+
+Warning: opendir() expects at most 2 parameters, 3 given in %s on line %d
+NULL
+===DONE===
diff --git a/ext/standard/tests/dir/opendir_variation2-win32-mb.phpt b/ext/standard/tests/dir/opendir_variation2-win32-mb.phpt
new file mode 100644
index 0000000000..3d54638e0d
--- /dev/null
+++ b/ext/standard/tests/dir/opendir_variation2-win32-mb.phpt
@@ -0,0 +1,245 @@
+--TEST--
+Test opendir() function : usage variations - different data types as $context arg
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die("skip Valid only on Windows");
+}
+?>
+--FILE--
+<?php
+/* Prototype : mixed opendir(string $path[, resource $context])
+ * Description: Open a directory and return a dir_handle
+ * Source code: ext/standard/dir.c
+ */
+
+/*
+ * Pass different data types as $context argument to opendir() to test behaviour
+ */
+
+echo "*** Testing opendir() : usage variation ***\n";
+
+
+// Initialise function arguments not being substituted (if any)
+// create temporary directory for test, removed in CLEAN section
+$path = dirname(__FILE__) . "/私はガラスを食べられますopendir_variation2";
+mkdir($path);
+
+
+//get an unset variable
+$unset_var = 10;
+unset ($unset_var);
+
+// get a class
+class classA
+{
+ public function __toString()
+ {
+ return "Class A object";
+ }
+}
+
+// heredoc string
+$heredoc = <<<EOT
+hello world
+EOT;
+
+// get a resource variable
+$fp = fopen(__FILE__, "r");
+
+// unexpected values to be passed to $context argument
+$inputs = array(
+
+ // int data
+/*1*/ 0,
+ 1,
+ 12345,
+ -2345,
+
+ // float data
+/*5*/ 10.5,
+ -10.5,
+ 12.3456789000e10,
+ 12.3456789000E-10,
+ .5,
+
+ // null data
+/*10*/ NULL,
+ null,
+
+ // boolean data
+/*12*/ true,
+ false,
+ TRUE,
+ FALSE,
+
+ // empty data
+/*16*/ "",
+ '',
+ array(),
+
+ // string data
+/*19*/ "string",
+ 'string',
+ $heredoc,
+
+ // object data
+/*22*/ new classA(),
+
+ // undefined data
+/*23*/ @$undefined_var,
+
+ // unset data
+/*24*/ @$unset_var,
+
+ // resource variable
+/*25*/ $fp
+);
+
+// loop through each element of $inputs to check the behavior of opendir()
+$iterator = 1;
+foreach($inputs as $input) {
+ echo "\n-- Iteration $iterator --\n";
+ var_dump($dh = opendir($path, $input) );#
+ if ($dh) {
+ closedir($dh);
+ }
+ $iterator++;
+};
+
+fclose($fp);
+?>
+===DONE===
+--CLEAN--
+<?php
+$path = dirname(__FILE__) . "/私はガラスを食べられますopendir_variation2";
+rmdir($path);
+?>
+--EXPECTF--
+*** Testing opendir() : usage variation ***
+
+-- Iteration 1 --
+
+Warning: opendir() expects parameter 2 to be resource, integer given in %s on line %d
+NULL
+
+-- Iteration 2 --
+
+Warning: opendir() expects parameter 2 to be resource, integer given in %s on line %d
+NULL
+
+-- Iteration 3 --
+
+Warning: opendir() expects parameter 2 to be resource, integer given in %s on line %d
+NULL
+
+-- Iteration 4 --
+
+Warning: opendir() expects parameter 2 to be resource, integer given in %s on line %d
+NULL
+
+-- Iteration 5 --
+
+Warning: opendir() expects parameter 2 to be resource, float given in %s on line %d
+NULL
+
+-- Iteration 6 --
+
+Warning: opendir() expects parameter 2 to be resource, float given in %s on line %d
+NULL
+
+-- Iteration 7 --
+
+Warning: opendir() expects parameter 2 to be resource, float given in %s on line %d
+NULL
+
+-- Iteration 8 --
+
+Warning: opendir() expects parameter 2 to be resource, float given in %s on line %d
+NULL
+
+-- Iteration 9 --
+
+Warning: opendir() expects parameter 2 to be resource, float given in %s on line %d
+NULL
+
+-- Iteration 10 --
+
+Warning: opendir() expects parameter 2 to be resource, null given in %s on line %d
+NULL
+
+-- Iteration 11 --
+
+Warning: opendir() expects parameter 2 to be resource, null given in %s on line %d
+NULL
+
+-- Iteration 12 --
+
+Warning: opendir() expects parameter 2 to be resource, boolean given in %s on line %d
+NULL
+
+-- Iteration 13 --
+
+Warning: opendir() expects parameter 2 to be resource, boolean given in %s on line %d
+NULL
+
+-- Iteration 14 --
+
+Warning: opendir() expects parameter 2 to be resource, boolean given in %s on line %d
+NULL
+
+-- Iteration 15 --
+
+Warning: opendir() expects parameter 2 to be resource, boolean given in %s on line %d
+NULL
+
+-- Iteration 16 --
+
+Warning: opendir() expects parameter 2 to be resource, string given in %s on line %d
+NULL
+
+-- Iteration 17 --
+
+Warning: opendir() expects parameter 2 to be resource, string given in %s on line %d
+NULL
+
+-- Iteration 18 --
+
+Warning: opendir() expects parameter 2 to be resource, array given in %s on line %d
+NULL
+
+-- Iteration 19 --
+
+Warning: opendir() expects parameter 2 to be resource, string given in %s on line %d
+NULL
+
+-- Iteration 20 --
+
+Warning: opendir() expects parameter 2 to be resource, string given in %s on line %d
+NULL
+
+-- Iteration 21 --
+
+Warning: opendir() expects parameter 2 to be resource, string given in %s on line %d
+NULL
+
+-- Iteration 22 --
+
+Warning: opendir() expects parameter 2 to be resource, object given in %s on line %d
+NULL
+
+-- Iteration 23 --
+
+Warning: opendir() expects parameter 2 to be resource, null given in %s on line %d
+NULL
+
+-- Iteration 24 --
+
+Warning: opendir() expects parameter 2 to be resource, null given in %s on line %d
+NULL
+
+-- Iteration 25 --
+
+Warning: opendir(): supplied resource is not a valid Stream-Context resource in %s on line %d
+resource(%d) of type (stream)
+===DONE===
diff --git a/ext/standard/tests/dir/opendir_variation3-win32-mb.phpt b/ext/standard/tests/dir/opendir_variation3-win32-mb.phpt
new file mode 100644
index 0000000000..74d0a6b982
--- /dev/null
+++ b/ext/standard/tests/dir/opendir_variation3-win32-mb.phpt
@@ -0,0 +1,56 @@
+--TEST--
+Test opendir() function : usage variations - open a directory twice
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die("skip Valid only on Windows");
+}
+?>
+--FILE--
+<?php
+/* Prototype : mixed opendir(string $path[, resource $context])
+ * Description: Open a directory and return a dir_handle
+ * Source code: ext/standard/dir.c
+ */
+
+/*
+ * Call opendir() twice with the same directory as $path argument
+ */
+
+echo "*** Testing opendir() : usage variation ***\n";
+
+$path = dirname(__FILE__) . "/私はガラスを食べられますopendir_variation3";
+mkdir($path);
+
+echo "\n-- Open directory first time: --\n";
+var_dump($dh1 = opendir($path));
+
+echo "\n-- Open directory second time: --\n";
+var_dump($dh2 = opendir($path));
+
+if ($dh1 !== $dh2) {
+ echo "\nNew resource created\n";
+} else {
+ echo "\nNo new resource created\n";
+}
+
+closedir($dh1);
+closedir($dh2);
+?>
+===DONE===
+--CLEAN--
+<?php
+$path = dirname(__FILE__) . "/私はガラスを食べられますopendir_variation3";
+rmdir($path);
+?>
+--EXPECTF--
+*** Testing opendir() : usage variation ***
+
+-- Open directory first time: --
+resource(%d) of type (stream)
+
+-- Open directory second time: --
+resource(%d) of type (stream)
+
+New resource created
+===DONE===
diff --git a/ext/standard/tests/dir/opendir_variation4-win32-mb.phpt b/ext/standard/tests/dir/opendir_variation4-win32-mb.phpt
new file mode 100644
index 0000000000..901b13c4a8
--- /dev/null
+++ b/ext/standard/tests/dir/opendir_variation4-win32-mb.phpt
@@ -0,0 +1,113 @@
+--TEST--
+Test opendir() function : usage variations - different relative paths
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die("skip Valid only on Windows");
+}
+?>
+--FILE--
+<?php
+/* Prototype : mixed opendir(string $path[, resource $context])
+ * Description: Open a directory and return a dir_handle
+ * Source code: ext/standard/dir.c
+ */
+
+/*
+ * Test opendir() with different relative paths as $path argument
+ */
+
+echo "*** Testing opendir() : usage variation ***\n";
+
+$base_dir_path = dirname(__FILE__);
+
+$level_one_dir_name = "私はガラスを食べられますlevel_one";
+$level_one_dir_path = "$base_dir_path/$level_one_dir_name";
+
+$level_two_dir_name = "私はガラスを食べられますlevel_two";
+$level_two_dir_path = "$base_dir_path/$level_one_dir_name/$level_two_dir_name";
+
+// create directories
+mkdir($level_one_dir_path);
+mkdir($level_two_dir_path);
+
+echo "\n-- \$path = './私はガラスを食べられますlevel_one': --\n";
+var_dump(chdir($base_dir_path));
+var_dump($dh = opendir("./$level_one_dir_name"));
+clean_dh($dh);
+
+echo "\n-- \$path = '私はガラスを食べられますlevel_one/私はガラスを食べられますlevel_two': --\n";
+var_dump(chdir($base_dir_path));
+var_dump($dh = opendir("$level_one_dir_name/$level_two_dir_name"));
+clean_dh($dh);
+
+echo "\n-- \$path = '..': --\n";
+var_dump($dh = opendir('..'));
+clean_dh($dh);
+
+echo "\n-- \$path = 'level_two', '.': --\n";
+var_dump(chdir($level_two_dir_path));
+var_dump($dh = opendir('.'));
+clean_dh($dh);
+
+echo "\n-- \$path = '../': --\n";
+var_dump($dh = opendir('../'));
+clean_dh($dh);
+
+echo "\n-- \$path = './': --\n";
+var_dump(chdir($level_two_dir_path));
+var_dump($dh = opendir('./'));
+clean_dh($dh);
+
+echo "\n-- \$path = '../../'私はガラスを食べられますlevel_one': --\n";
+var_dump(chdir($level_two_dir_path));
+var_dump($dh = opendir("../../$level_one_dir_name"));
+clean_dh($dh);
+
+/*
+ * function to remove directory handle before re-using variable name in test
+ * and to ensure directory is not in use at CLEAN section so can me removed
+ */
+function clean_dh($dh){
+ if (is_resource($dh)) {
+ closedir($dh);
+ }
+ unset($dh);
+}
+?>
+===DONE===
+--CLEAN--
+<?php
+$file_path = dirname(__FILE__);
+rmdir("$file_path/私はガラスを食べられますlevel_one/私はガラスを食べられますlevel_two");
+rmdir("$file_path/私はガラスを食べられますlevel_one");
+?>
+--EXPECTF--
+*** Testing opendir() : usage variation ***
+
+-- $path = './私はガラスを食べられますlevel_one': --
+bool(true)
+resource(%d) of type (stream)
+
+-- $path = '私はガラスを食べられますlevel_one/私はガラスを食べられますlevel_two': --
+bool(true)
+resource(%d) of type (stream)
+
+-- $path = '..': --
+resource(%d) of type (stream)
+
+-- $path = 'level_two', '.': --
+bool(true)
+resource(%d) of type (stream)
+
+-- $path = '../': --
+resource(%d) of type (stream)
+
+-- $path = './': --
+bool(true)
+resource(%d) of type (stream)
+
+-- $path = '../../'私はガラスを食べられますlevel_one': --
+bool(true)
+resource(%d) of type (stream)
+===DONE===
diff --git a/ext/standard/tests/dir/opendir_variation6-win32-mb.phpt b/ext/standard/tests/dir/opendir_variation6-win32-mb.phpt
new file mode 100644
index 0000000000..f54a2ad4ae
--- /dev/null
+++ b/ext/standard/tests/dir/opendir_variation6-win32-mb.phpt
@@ -0,0 +1,75 @@
+--TEST--
+Test opendir() function : usage variations - Different wildcards
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die("skip Valid only on Windows");
+}
+?>
+--FILE--
+<?php
+/* Prototype : mixed opendir(string $path[, resource $context])
+ * Description: Open a directory and return a dir_handle
+ * Source code: ext/standard/dir.c
+ */
+
+/*
+ * Pass paths containing wildcards to test if opendir() recognises them
+ */
+
+echo "*** Testing opendir() : usage variations ***\n";
+// create the temporary directories
+$file_path = dirname(__FILE__);
+$dir_path = $file_path . "/opendir_variation6";
+$sub_dir_path = $dir_path . "/sub_dir1";
+
+mkdir($dir_path);
+mkdir($sub_dir_path);
+
+// with different wildcard characters
+
+echo "\n-- Wildcard = '*' --\n";
+var_dump( opendir($file_path . "/opendir_var*") );
+var_dump( opendir($file_path . "/*") );
+
+echo "\n-- Wildcard = '?' --\n";
+var_dump( opendir($dir_path . "/sub_dir?") );
+var_dump( opendir($dir_path . "/sub?dir1") );
+
+?>
+===DONE===
+--CLEAN--
+<?php
+$dir_path = dirname(__FILE__) . "/opendir_variation6";
+$sub_dir_path = $dir_path . "/sub_dir1";
+
+rmdir($sub_dir_path);
+rmdir($dir_path);
+?>
+--EXPECTF--
+*** Testing opendir() : usage variations ***
+
+-- Wildcard = '*' --
+
+Warning: opendir(%s/opendir_var*,%s/opendir_var*): %s in %s on line %d
+
+Warning: opendir(%s/opendir_var*): failed to open dir: %s in %s on line %d
+bool(false)
+
+Warning: opendir(%s/*,%s/*): %s in %s on line %d
+
+Warning: opendir(%s/*): failed to open dir: %s in %s on line %d
+bool(false)
+
+-- Wildcard = '?' --
+
+Warning: opendir(%s/opendir_variation6/sub_dir?,%s/opendir_variation6/sub_dir?): %s in %s on line %d
+
+Warning: opendir(%s/opendir_variation6/sub_dir?): failed to open dir: %s in %s on line %d
+bool(false)
+
+Warning: opendir(%s/opendir_variation6/sub?dir1,%s/opendir_variation6/sub?dir1): %s in %s on line %d
+
+Warning: opendir(%s/opendir_variation6/sub?dir1): failed to open dir: %s in %s on line %d
+bool(false)
+===DONE===
diff --git a/ext/standard/tests/dir/readdir_basic-win32-mb.phpt b/ext/standard/tests/dir/readdir_basic-win32-mb.phpt
new file mode 100644
index 0000000000..90cb81a27d
--- /dev/null
+++ b/ext/standard/tests/dir/readdir_basic-win32-mb.phpt
@@ -0,0 +1,79 @@
+--TEST--
+Test readdir() function : basic functionality
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die("skip Valid only on Windows");
+}
+?>
+--FILE--
+<?php
+/* Prototype : string readdir([resource $dir_handle])
+ * Description: Read directory entry from dir_handle
+ * Source code: ext/standard/dir.C
+ */
+
+/*
+ * Test basic functionality of readdir()
+ */
+
+echo "*** Testing readdir() : basic functionality ***\n";
+
+// include the file.inc for Function: function create_files()
+chdir(dirname(__FILE__));
+include(dirname(__FILE__)."/../file/file.inc");
+
+$path = dirname(__FILE__) . '/私はガラスを食べられますreaddir_basic';
+mkdir($path);
+create_files($path, 3);
+
+echo "\n-- Call readdir() with \$path argument --\n";
+var_dump($dh = opendir($path));
+$a = array();
+while( FALSE !== ($file = readdir($dh)) ) {
+ $a[] = $file;
+}
+sort($a);
+foreach($a as $file) {
+ var_dump($file);
+}
+
+echo "\n-- Call readdir() without \$path argument --\n";
+var_dump($dh = opendir($path));
+$a = array();
+while( FALSE !== ( $file = readdir() ) ) {
+ $a[] = $file;
+}
+sort($a);
+foreach($a as $file) {
+ var_dump($file);
+}
+
+delete_files($path, 3);
+closedir($dh);
+?>
+===DONE===
+--CLEAN--
+<?php
+$path = dirname(__FILE__) . '/私はガラスを食べられますreaddir_basic';
+rmdir($path);
+?>
+--EXPECTF--
+*** Testing readdir() : basic functionality ***
+
+-- Call readdir() with $path argument --
+resource(%d) of type (stream)
+string(1) "."
+string(2) ".."
+string(9) "file1.tmp"
+string(9) "file2.tmp"
+string(9) "file3.tmp"
+
+-- Call readdir() without $path argument --
+resource(%d) of type (stream)
+string(1) "."
+string(2) ".."
+string(9) "file1.tmp"
+string(9) "file2.tmp"
+string(9) "file3.tmp"
+===DONE===
diff --git a/ext/standard/tests/dir/readdir_error-win32-mb.phpt b/ext/standard/tests/dir/readdir_error-win32-mb.phpt
new file mode 100644
index 0000000000..1388987259
--- /dev/null
+++ b/ext/standard/tests/dir/readdir_error-win32-mb.phpt
@@ -0,0 +1,49 @@
+--TEST--
+Test readdir() function : error conditions - Incorrect number of args
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die("skip Valid only on Windows");
+}
+?>
+--FILE--
+<?php
+/* Prototype : string readdir([resource $dir_handle])
+ * Description: Read directory entry from dir_handle
+ * Source code: ext/standard/dir.c
+ */
+
+/*
+ * Pass incorrect number of arguments to readdir() to test behaviour
+ */
+
+echo "*** Testing readdir() : error conditions ***\n";
+
+
+//Test readdir with one more than the expected number of arguments
+echo "\n-- Testing readdir() function with more than expected no. of arguments --\n";
+
+$path = dirname(__FILE__) . "/私はガラスを食べられますreaddir_error";
+mkdir($path);
+$dir_handle = opendir($path);
+$extra_arg = 10;
+
+var_dump( readdir($dir_handle, $extra_arg) );
+
+// close the handle so can remove dir in CLEAN section
+closedir($dir_handle);
+?>
+===DONE===
+--CLEAN--
+<?php
+$path = dirname(__FILE__) . "/私はガラスを食べられますreaddir_error";
+rmdir($path);
+?>
+--EXPECTF--
+*** Testing readdir() : error conditions ***
+
+-- Testing readdir() function with more than expected no. of arguments --
+
+Warning: readdir() expects at most 1 parameter, 2 given in %s on line %d
+NULL
+===DONE===
diff --git a/ext/standard/tests/dir/readdir_variation2-win32-mb.phpt b/ext/standard/tests/dir/readdir_variation2-win32-mb.phpt
new file mode 100644
index 0000000000..bde6d6f1c8
--- /dev/null
+++ b/ext/standard/tests/dir/readdir_variation2-win32-mb.phpt
@@ -0,0 +1,54 @@
+--TEST--
+Test readdir() function : usage variations - empty directories
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die("skip Valid only on Windows");
+}
+?>
+--FILE--
+<?php
+/* Prototype : string readdir([resource $dir_handle])
+ * Description: Read directory entry from dir_handle
+ * Source code: ext/standard/dir.c
+ */
+
+/*
+ * Pass readdir() a directory handle pointing to an empty directory to test behaviour
+ */
+
+echo "*** Testing readdir() : usage variations ***\n";
+
+$path = dirname(__FILE__) . '/私はガラスを食べられますreaddir_variation2';
+mkdir($path);
+$dir_handle = opendir($path);
+
+echo "\n-- Pass an empty directory to readdir() --\n";
+function mysort($a,$b) {
+ return strlen($a) > strlen($b) ? 1 : -1;
+}
+$entries = array();
+while(FALSE !== ($file = readdir($dir_handle))){
+ $entries[] = $file;
+}
+
+closedir($dir_handle);
+
+usort($entries, "mysort");
+foreach($entries as $entry) {
+ var_dump($entry);
+}
+?>
+===DONE===
+--CLEAN--
+<?php
+$path = dirname(__FILE__) . '/私はガラスを食べられますreaddir_variation2';
+rmdir($path);
+?>
+--EXPECTF--
+*** Testing readdir() : usage variations ***
+
+-- Pass an empty directory to readdir() --
+string(1) "."
+string(2) ".."
+===DONE===
diff --git a/ext/standard/tests/dir/readdir_variation3-win32-mb.phpt b/ext/standard/tests/dir/readdir_variation3-win32-mb.phpt
new file mode 100644
index 0000000000..a00e83769f
--- /dev/null
+++ b/ext/standard/tests/dir/readdir_variation3-win32-mb.phpt
@@ -0,0 +1,74 @@
+--TEST--
+Test readdir() function : usage variations - sub-directories
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die("skip Valid only on Windows");
+}
+?>
+--FILE--
+<?php
+/* Prototype : string readdir([resource $dir_handle])
+ * Description: Read directory entry from dir_handle
+ * Source code: ext/standard/dir.c
+ */
+
+/*
+ * Pass a directory handle pointing to a directory that has a sub-directory
+ * to test behaviour of readdir()
+ */
+
+echo "*** Testing readdir() : usage variations ***\n";
+
+// include the file.inc for Function: function create_files()
+chdir(dirname(__FILE__));
+include(dirname(__FILE__)."/../file/file.inc");
+
+$path_top = dirname(__FILE__) . '/readdir_variation3';
+$path_sub = $path_top . '/私はガラスを食べられますsub_folder';
+mkdir($path_top);
+mkdir($path_sub);
+
+create_files($path_top, 2);
+create_files($path_sub, 2);
+
+$dir_handle = opendir($path_top);
+while(FALSE !== ($file = readdir($dir_handle))) {
+
+ // different OS order files differently so will
+ // store file names into an array so can use sorted in expected output
+ $contents[] = $file;
+}
+
+// more important to check that all contents are present than order they are returned in
+sort($contents);
+var_dump($contents);
+
+delete_files($path_top, 2);
+delete_files($path_sub, 2);
+
+closedir($dir_handle);
+?>
+===DONE===
+--CLEAN--
+<?php
+$path_top = dirname(__FILE__) . '/readdir_variation3';
+$path_sub = $path_top . '/私はガラスを食べられますsub_folder';
+rmdir($path_sub);
+rmdir($path_top);
+?>
+--EXPECTF--
+*** Testing readdir() : usage variations ***
+array(5) {
+ [0]=>
+ string(1) "."
+ [1]=>
+ string(2) ".."
+ [2]=>
+ string(9) "file1.tmp"
+ [3]=>
+ string(9) "file2.tmp"
+ [4]=>
+ string(46) "私はガラスを食べられますsub_folder"
+}
+===DONE===
diff --git a/ext/standard/tests/dir/readdir_variation4-win32-mb.phpt b/ext/standard/tests/dir/readdir_variation4-win32-mb.phpt
new file mode 100644
index 0000000000..6540d8e571
--- /dev/null
+++ b/ext/standard/tests/dir/readdir_variation4-win32-mb.phpt
@@ -0,0 +1,184 @@
+--TEST--
+Test readdir() function : usage variations - different file names
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die("skip Valid only on Windows");
+}
+?>
+--FILE--
+<?php
+/* Prototype : string readdir([resource $dir_handle])
+ * Description: Read directory entry from dir_handle
+ * Source code: ext/standard/dir.c
+ */
+
+/*
+ * Pass a directory handle pointing to a directory that contains
+ * files with different file names to test how readdir() reads them
+ */
+
+echo "*** Testing readdir() : usage variations ***\n";
+
+$dir_path = dirname(__FILE__) . "/私はガラスを食べられますreaddir_variation4/";
+mkdir($dir_path);
+
+// heredoc string
+$heredoc = <<<EOT
+hd_file
+EOT;
+
+$inputs = array(
+
+ // int data
+/*1*/ 0,
+ 1,
+ 12345,
+ -2345,
+
+ // float data
+/*5*/ 10.5,
+ -10.5,
+ 12.3456789000e10,
+ 12.3456789000E-10,
+ .5,
+
+ // empty data
+/*10*/ "",
+ array(),
+
+ // string data
+/*12*/ "double_file",
+ 'single_file',
+ $heredoc,
+);
+
+$iterator = 1;
+foreach($inputs as $key => $input) {
+ echo "\n-- Iteration $iterator --\n";
+ $handle = "fp{$iterator}";
+ var_dump( $$handle = @fopen($dir_path . "私はガラスを食べられます$input.tmp", 'w') );
+ var_dump( fwrite($$handle, $key));
+ fclose($$handle);
+ $iterator++;
+};
+
+echo "\n-- Call to readdir() --\n";
+$dir_handle = opendir($dir_path);
+while(FALSE !== ($file = readdir($dir_handle))){
+
+ // different OS order files differently so will
+ // store file names into an array so can use sorted in expected output
+ $contents[] = $file;
+
+ // remove files while going through directory
+ @unlink($dir_path . $file);
+}
+
+// more important to check that all contents are present than order they are returned in
+sort($contents);
+var_dump($contents);
+
+closedir($dir_handle);
+?>
+===DONE===
+--CLEAN--
+<?php
+$dir_path = dirname(__FILE__) . "/私はガラスを食べられますreaddir_variation4/";
+rmdir($dir_path);
+?>
+--EXPECTF--
+*** Testing readdir() : usage variations ***
+
+-- Iteration 1 --
+resource(%d) of type (stream)
+int(1)
+
+-- Iteration 2 --
+resource(%d) of type (stream)
+int(1)
+
+-- Iteration 3 --
+resource(%d) of type (stream)
+int(1)
+
+-- Iteration 4 --
+resource(%d) of type (stream)
+int(1)
+
+-- Iteration 5 --
+resource(%d) of type (stream)
+int(1)
+
+-- Iteration 6 --
+resource(%d) of type (stream)
+int(1)
+
+-- Iteration 7 --
+resource(%d) of type (stream)
+int(1)
+
+-- Iteration 8 --
+resource(%d) of type (stream)
+int(1)
+
+-- Iteration 9 --
+resource(%d) of type (stream)
+int(1)
+
+-- Iteration 10 --
+resource(%d) of type (stream)
+int(1)
+
+-- Iteration 11 --
+resource(%d) of type (stream)
+int(2)
+
+-- Iteration 12 --
+resource(%d) of type (stream)
+int(2)
+
+-- Iteration 13 --
+resource(%d) of type (stream)
+int(2)
+
+-- Iteration 14 --
+resource(%d) of type (stream)
+int(2)
+
+-- Call to readdir() --
+array(16) {
+ [0]=>
+ string(1) "."
+ [1]=>
+ string(2) ".."
+ [2]=>
+ string(45) "私はガラスを食べられます-10.5.tmp"
+ [3]=>
+ string(45) "私はガラスを食べられます-2345.tmp"
+ [4]=>
+ string(40) "私はガラスを食べられます.tmp"
+ [5]=>
+ string(43) "私はガラスを食べられます0.5.tmp"
+ [6]=>
+ string(41) "私はガラスを食べられます0.tmp"
+ [7]=>
+ string(53) "私はガラスを食べられます1.23456789E-9.tmp"
+ [8]=>
+ string(41) "私はガラスを食べられます1.tmp"
+ [9]=>
+ string(44) "私はガラスを食べられます10.5.tmp"
+ [10]=>
+ string(45) "私はガラスを食べられます12345.tmp"
+ [11]=>
+ string(52) "私はガラスを食べられます123456789000.tmp"
+ [12]=>
+ string(45) "私はガラスを食べられますArray.tmp"
+ [13]=>
+ string(51) "私はガラスを食べられますdouble_file.tmp"
+ [14]=>
+ string(47) "私はガラスを食べられますhd_file.tmp"
+ [15]=>
+ string(51) "私はガラスを食べられますsingle_file.tmp"
+}
+===DONE===
diff --git a/ext/standard/tests/dir/readdir_variation6-win32-mb.phpt b/ext/standard/tests/dir/readdir_variation6-win32-mb.phpt
new file mode 100644
index 0000000000..db5de28302
--- /dev/null
+++ b/ext/standard/tests/dir/readdir_variation6-win32-mb.phpt
@@ -0,0 +1,86 @@
+--TEST--
+Test readdir() function : usage variations - operate on previously opened directory
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die("skip Valid only on Windows");
+}
+?>
+--FILE--
+<?php
+/* Prototype : string readdir([resource $dir_handle])
+ * Description: Read directory entry from dir_handle
+ * Source code: ext/standard/dir.c
+ */
+
+/*
+ * Open two directory handles on the same directory and pass both
+ * to readdir() to test behaviour
+ */
+
+echo "*** Testing readdir() : usage variations ***\n";
+
+// include the file.inc for Function: function create_files()
+include( dirname(__FILE__)."/../file/file.inc");
+
+// create the temporary directory
+$dir_path = dirname(__FILE__) . "/私はガラスを食べられますreaddir_variation6";
+mkdir($dir_path);
+
+// create files within the temporary directory
+create_files($dir_path, 3, "alphanumeric", 0755, 1, "w", "私はガラスを食べられますreaddir_variation6");
+
+// open the directory
+$dir_handle1 = opendir($dir_path);
+
+// open the same directory again without closing it
+opendir($dir_path);
+
+echo "\n-- Reading Directory Contents with Previous Handle --\n";
+$a = array();
+while (FALSE !== ($file = readdir($dir_handle1))) {
+ $a[] = $file;
+}
+sort($a);
+foreach ($a as $file) {
+ var_dump($file);
+}
+
+echo "\n-- Reading Directory Contents with Current Handle (no arguments supplied) --\n";
+$a = array();
+while (FALSE !== ($file = readdir())) {
+ $a[] = $file;
+}
+sort($a);
+foreach ($a as $file) {
+ var_dump($file);
+}
+
+// delete temporary files
+delete_files($dir_path, 3, "私はガラスを食べられますreaddir_variation6");
+closedir($dir_handle1);
+closedir();
+?>
+===DONE===
+--CLEAN--
+<?php
+$dir_path = dirname(__FILE__) . "/私はガラスを食べられますreaddir_variation6";
+rmdir($dir_path);
+?>
+--EXPECTF--
+*** Testing readdir() : usage variations ***
+
+-- Reading Directory Contents with Previous Handle --
+string(1) "."
+string(2) ".."
+string(59) "私はガラスを食べられますreaddir_variation61.tmp"
+string(59) "私はガラスを食べられますreaddir_variation62.tmp"
+string(59) "私はガラスを食べられますreaddir_variation63.tmp"
+
+-- Reading Directory Contents with Current Handle (no arguments supplied) --
+string(1) "."
+string(2) ".."
+string(59) "私はガラスを食べられますreaddir_variation61.tmp"
+string(59) "私はガラスを食べられますreaddir_variation62.tmp"
+string(59) "私はガラスを食べられますreaddir_variation63.tmp"
+===DONE===
diff --git a/ext/standard/tests/dir/rewinddir_basic-win32-mb.phpt b/ext/standard/tests/dir/rewinddir_basic-win32-mb.phpt
new file mode 100644
index 0000000000..bec721d3c5
--- /dev/null
+++ b/ext/standard/tests/dir/rewinddir_basic-win32-mb.phpt
@@ -0,0 +1,102 @@
+--TEST--
+Test rewinddir() function : basic functionality
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die("skip Valid only on Windows");
+}
+?>
+--FILE--
+<?php
+/* Prototype : void rewinddir([resource $dir_handle])
+ * Description: Rewind dir_handle back to the start
+ * Source code: ext/standard/dir.c
+ * Alias to functions: rewind
+ */
+
+/*
+ * Test basic functionality of rewinddir()
+ */
+
+echo "*** Testing rewinddir() : basic functionality ***\n";
+
+// include file.inc for create_files function
+include(dirname(__FILE__) . "/../file/file.inc");
+
+$dir_path1 = dirname(__FILE__) . "/私はガラスを食べられますrewinddir_basic_dir1";
+$dir_path2 = dirname(__FILE__) . "/私はガラスを食べられますrewinddir_basic_dir2";
+mkdir($dir_path1);
+mkdir($dir_path2);
+
+@create_files($dir_path1, 1, "numeric", 0755, 1, "w", "私はガラスを食べられますfile");
+@create_files($dir_path2, 1, 'numeric', 0755, 1, 'w', "私はガラスを食べられますfile", 2);
+var_dump($dh1 = opendir($dir_path1));
+var_dump($dh2 = opendir($dir_path2));
+
+$data = array();
+echo "\n-- Read and rewind first directory (argument supplied) --\n";
+while(FALSE !== $file1 = readdir($dh1)) {
+ $data[] = $file1;
+}
+$first = $data[0];
+sort($data);
+var_dump($data);
+
+var_dump(rewinddir($dh1));
+var_dump(readdir($dh1) == $first);
+
+$data = array();
+echo "\n-- Read and rewind second directory (no argument supplied) --\n";
+while(FALSE !== $file2 = readdir()) {
+ $data[] = $file2;
+}
+$first = $data[0];
+sort($data);
+var_dump($data);
+
+var_dump(rewinddir());
+var_dump(readdir() == $first);
+
+closedir($dh1);
+closedir($dh2);
+
+delete_files($dir_path1, 1, "私はガラスを食べられますfile");
+delete_files($dir_path2, 1, "私はガラスを食べられますfile", 2);
+?>
+===DONE===
+--CLEAN--
+<?php
+$dir_path1 = dirname(__FILE__) . "/私はガラスを食べられますrewinddir_basic_dir1";
+$dir_path2 = dirname(__FILE__) . "/私はガラスを食べられますrewinddir_basic_dir2";
+rmdir($dir_path1);
+rmdir($dir_path2);
+?>
+--EXPECTF--
+*** Testing rewinddir() : basic functionality ***
+resource(%d) of type (stream)
+resource(%d) of type (stream)
+
+-- Read and rewind first directory (argument supplied) --
+array(3) {
+ [0]=>
+ string(1) "."
+ [1]=>
+ string(2) ".."
+ [2]=>
+ string(45) "私はガラスを食べられますfile1.tmp"
+}
+NULL
+bool(true)
+
+-- Read and rewind second directory (no argument supplied) --
+array(3) {
+ [0]=>
+ string(1) "."
+ [1]=>
+ string(2) ".."
+ [2]=>
+ string(45) "私はガラスを食べられますfile2.tmp"
+}
+NULL
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/dir/rewinddir_error-win32-mb.phpt b/ext/standard/tests/dir/rewinddir_error-win32-mb.phpt
new file mode 100644
index 0000000000..bc107704a0
--- /dev/null
+++ b/ext/standard/tests/dir/rewinddir_error-win32-mb.phpt
@@ -0,0 +1,48 @@
+--TEST--
+Test rewinddir() function : error conditions - incorrect number of args
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die("skip Valid only on Windows");
+}
+?>
+--FILE--
+<?php
+/* Prototype : void rewinddir([resource $dir_handle])
+ * Description: Rewind dir_handle back to the start
+ * Source code: ext/standard/dir.c
+ * Alias to functions: rewind
+ */
+
+/*
+ * Pass incorrect number of arguments to rewinddir() to test behaviour
+ */
+
+echo "*** Testing rewinddir() : error conditions ***\n";
+
+
+//Test rewinddir with one more than the expected number of arguments
+echo "\n-- Testing rewinddir() function with more than expected no. of arguments --\n";
+
+$dir_path = dirname(__FILE__) . "/私はガラスを食べられますrewinddir_error";
+mkdir($dir_path);
+$dir_handle = opendir($dir_path);
+$extra_arg = 10;
+
+var_dump( rewinddir($dir_handle, $extra_arg) );
+closedir($dir_handle);
+?>
+===DONE===
+--CLEAN--
+<?php
+$dir_path = dirname(__FILE__) . "/私はガラスを食べられますrewinddir_error";
+rmdir($dir_path);
+?>
+--EXPECTF--
+*** Testing rewinddir() : error conditions ***
+
+-- Testing rewinddir() function with more than expected no. of arguments --
+
+Warning: rewinddir() expects at most 1 parameter, 2 given in %s on line %d
+NULL
+===DONE===
diff --git a/ext/standard/tests/dir/rewinddir_variation2-win32-mb.phpt b/ext/standard/tests/dir/rewinddir_variation2-win32-mb.phpt
new file mode 100644
index 0000000000..a3f5e8ecc9
--- /dev/null
+++ b/ext/standard/tests/dir/rewinddir_variation2-win32-mb.phpt
@@ -0,0 +1,51 @@
+--TEST--
+Test rewinddir() function : usage variations - operate on a closed directory
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die("skip Valid only on Windows");
+}
+?>
+--FILE--
+<?php
+/* Prototype : void rewinddir([resource $dir_handle])
+ * Description: Rewind dir_handle back to the start
+ * Source code: ext/standard/dir.c
+ * Alias to functions: rewind
+ */
+
+/*
+ * Open and close a directory handle then call rewinddir() to test behaviour
+ */
+
+echo "*** Testing rewinddir() : usage variations ***\n";
+
+$dir_path = dirname(__FILE__) . '/私はガラスを食べられますrewinddir_variation2';
+mkdir($dir_path);
+
+echo "\n-- Create the directory handle, read and close the directory --\n";
+var_dump($dir_handle = opendir($dir_path));
+var_dump(readdir($dir_handle));
+closedir($dir_handle);
+
+echo "\n-- Call to rewinddir() --\n";
+var_dump(rewinddir($dir_handle));
+?>
+===DONE===
+--CLEAN--
+<?php
+$dir_path = dirname(__FILE__) . '/私はガラスを食べられますrewinddir_variation2';
+rmdir($dir_path);
+?>
+--EXPECTF--
+*** Testing rewinddir() : usage variations ***
+
+-- Create the directory handle, read and close the directory --
+resource(%d) of type (stream)
+string(%d) "%s"
+
+-- Call to rewinddir() --
+
+Warning: rewinddir(): %s is not a valid Directory resource in %s on line %d
+bool(false)
+===DONE===
diff --git a/ext/standard/tests/dir/scandir_basic-win32-mb.phpt b/ext/standard/tests/dir/scandir_basic-win32-mb.phpt
new file mode 100644
index 0000000000..a089a0afa3
--- /dev/null
+++ b/ext/standard/tests/dir/scandir_basic-win32-mb.phpt
@@ -0,0 +1,76 @@
+--TEST--
+Test scandir() function : basic functionality
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die("skip Valid only on Windows");
+}
+?>
+--FILE--
+<?php
+/* Prototype : array scandir(string $dir [, int $sorting_order [, resource $context]])
+ * Description: List files & directories inside the specified path
+ * Source code: ext/standard/dir.c
+ */
+
+/*
+ * Test basic functionality of scandir()
+ */
+
+echo "*** Testing scandir() : basic functionality ***\n";
+
+// include file.inc for create_files function
+include (dirname(__FILE__) . '/../file/file.inc');
+
+// set up directory
+$directory = dirname(__FILE__) . '/私はガラスを食べられますscandir_basic';
+mkdir($directory);
+create_files($directory, 3, "numeric", 0755, 1, "w", "私はガラスを食べられますfile");
+
+echo "\n-- scandir() with mandatory arguments --\n";
+var_dump(scandir($directory));
+
+echo "\n-- scandir() with all arguments --\n";
+$sorting_order = SCANDIR_SORT_DESCENDING;
+$context = stream_context_create();
+var_dump(scandir($directory, $sorting_order, $context));
+
+delete_files($directory, 3, "私はガラスを食べられますfile");
+?>
+===DONE===
+--CLEAN--
+<?php
+$directory = dirname(__FILE__) . '/私はガラスを食べられますscandir_basic';
+rmdir($directory);
+?>
+--EXPECTF--
+*** Testing scandir() : basic functionality ***
+
+-- scandir() with mandatory arguments --
+array(5) {
+ [0]=>
+ string(1) "."
+ [1]=>
+ string(2) ".."
+ [2]=>
+ string(45) "私はガラスを食べられますfile1.tmp"
+ [3]=>
+ string(45) "私はガラスを食べられますfile2.tmp"
+ [4]=>
+ string(45) "私はガラスを食べられますfile3.tmp"
+}
+
+-- scandir() with all arguments --
+array(5) {
+ [0]=>
+ string(45) "私はガラスを食べられますfile3.tmp"
+ [1]=>
+ string(45) "私はガラスを食べられますfile2.tmp"
+ [2]=>
+ string(45) "私はガラスを食べられますfile1.tmp"
+ [3]=>
+ string(2) ".."
+ [4]=>
+ string(1) "."
+}
+===DONE===
diff --git a/ext/standard/tests/dir/scandir_error1-win32-mb.phpt b/ext/standard/tests/dir/scandir_error1-win32-mb.phpt
new file mode 100644
index 0000000000..6c1ce72a44
--- /dev/null
+++ b/ext/standard/tests/dir/scandir_error1-win32-mb.phpt
@@ -0,0 +1,53 @@
+--TEST--
+Test scandir() function : error conditions - Incorrect number of args
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die("skip Valid only on Windows");
+}
+?>
+--FILE--
+<?php
+/* Prototype : array scandir(string $dir [, int $sorting_order [, resource $context]])
+ * Description: List files & directories inside the specified path
+ * Source code: ext/standard/dir.c
+ */
+
+/*
+ * Pass incorrect number of arguments to scandir() to test behaviour
+ */
+
+echo "*** Testing scandir() : error conditions ***\n";
+
+// Zero arguments
+echo "\n-- Testing scandir() function with Zero arguments --\n";
+var_dump( scandir() );
+
+//Test scandir with one more than the expected number of arguments
+echo "\n-- Testing scandir() function with more than expected no. of arguments --\n";
+$dir = dirname(__FILE__) . '/私はガラスを食べられますscandir_error';
+mkdir($dir);
+$sorting_order = 10;
+$context = stream_context_create();
+$extra_arg = 10;
+var_dump( scandir($dir, $sorting_order, $context, $extra_arg) );
+?>
+===DONE===
+--CLEAN--
+<?php
+$directory = dirname(__FILE__) . '/私はガラスを食べられますscandir_error';
+rmdir($directory);
+?>
+--EXPECTF--
+*** Testing scandir() : error conditions ***
+
+-- Testing scandir() function with Zero arguments --
+
+Warning: scandir() expects at least 1 parameter, 0 given in %s on line %d
+NULL
+
+-- Testing scandir() function with more than expected no. of arguments --
+
+Warning: scandir() expects at most 3 parameters, 4 given in %s on line %d
+NULL
+===DONE===
diff --git a/ext/standard/tests/dir/scandir_variation10-win32-mb.phpt b/ext/standard/tests/dir/scandir_variation10-win32-mb.phpt
new file mode 100644
index 0000000000..b031a91505
--- /dev/null
+++ b/ext/standard/tests/dir/scandir_variation10-win32-mb.phpt
@@ -0,0 +1,85 @@
+--TEST--
+Test scandir() function : usage variations - different sorting constants
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die("skip Valid only on Windows");
+}
+?>
+--FILE--
+<?php
+/* Prototype : array scandir(string $dir [, int $sorting_order [, resource $context]])
+ * Description: List files & directories inside the specified path
+ * Source code: ext/standard/dir.c
+ */
+
+printf("SCANDIR_SORT_ASCENDING: %d\n", SCANDIR_SORT_ASCENDING);
+printf("SCANDIR_SORT_DESCENDING: %d\n", SCANDIR_SORT_DESCENDING);
+printf("SCANDIR_SORT_NONE: %d\n", SCANDIR_SORT_NONE);
+
+/*
+ * Pass different integers as $sorting_order argument to test how scandir()
+ * re-orders the array
+ */
+
+echo "*** Testing scandir() : usage variations ***\n";
+
+// include for create_files/delete_files functions
+include(dirname(__FILE__) . '/../file/file.inc');
+
+// create directory and files
+$dir = dirname(__FILE__) . '/私はガラスを食べられますscandir_variation10';
+mkdir($dir);
+@create_files($dir, 2, "numeric", 0755, 1, "w", "私はガラスを食べられますfile");
+
+// Deterministic tests.
+var_dump(scandir($dir, SCANDIR_SORT_ASCENDING));
+var_dump(scandir($dir, SCANDIR_SORT_DESCENDING));
+
+// Non-deterministic tests.
+$files = scandir($dir, SCANDIR_SORT_NONE);
+var_dump(count($files));
+var_dump(in_array('.', $files));
+var_dump(in_array('..', $files));
+var_dump(in_array('私はガラスを食べられますfile1.tmp', $files));
+var_dump(in_array('私はガラスを食べられますfile2.tmp', $files));
+
+delete_files($dir, 2, "私はガラスを食べられますfile");
+?>
+===DONE===
+--CLEAN--
+<?php
+$dir = dirname(__FILE__) . '/私はガラスを食べられますscandir_variation10';
+rmdir($dir);
+?>
+--EXPECTF--
+SCANDIR_SORT_ASCENDING: 0
+SCANDIR_SORT_DESCENDING: 1
+SCANDIR_SORT_NONE: 2
+*** Testing scandir() : usage variations ***
+array(4) {
+ [0]=>
+ string(1) "."
+ [1]=>
+ string(2) ".."
+ [2]=>
+ string(45) "私はガラスを食べられますfile1.tmp"
+ [3]=>
+ string(45) "私はガラスを食べられますfile2.tmp"
+}
+array(4) {
+ [0]=>
+ string(45) "私はガラスを食べられますfile2.tmp"
+ [1]=>
+ string(45) "私はガラスを食べられますfile1.tmp"
+ [2]=>
+ string(2) ".."
+ [3]=>
+ string(1) "."
+}
+int(4)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/dir/scandir_variation2-mb.phpt b/ext/standard/tests/dir/scandir_variation2-mb.phpt
new file mode 100644
index 0000000000..9e9df97802
--- /dev/null
+++ b/ext/standard/tests/dir/scandir_variation2-mb.phpt
@@ -0,0 +1,285 @@
+--TEST--
+Test scandir() function : usage variations - diff data types as $sorting_order arg
+--SKIPIF--
+<?php if (PHP_INT_SIZE != 8) die("skip this test is for 64-bit only");
+--FILE--
+<?php
+/* Prototype : array scandir(string $dir [, int $sorting_order [, resource $context]])
+ * Description: List files & directories inside the specified path
+ * Source code: ext/standard/dir.c
+ */
+
+/*
+ * Pass different data types as $sorting_order argument to test how scandir() behaves
+ */
+
+echo "*** Testing scandir() : usage variations ***\n";
+
+// Initialise function arguments not being substituted
+$dir = dirname(__FILE__) . '/私はガラスを食べられますscandir_variation2';
+mkdir($dir);
+
+//get an unset variable
+$unset_var = 10;
+unset ($unset_var);
+
+// get a class
+class classA
+{
+ public function __toString() {
+ return "Class A object";
+ }
+}
+
+// heredoc string
+$heredoc = <<<EOT
+hello world
+EOT;
+
+// get a resource variable
+$fp = fopen(__FILE__, "r");
+
+// unexpected values to be passed to $sorting_order argument
+$inputs = array(
+
+ // int data
+/*1*/ 0,
+ 1,
+ 12345,
+ -2345,
+
+ // float data
+/*5*/ 10.5,
+ -10.5,
+ 12.3456789000e10,
+ 12.3456789000E-10,
+ .5,
+
+ // null data
+/*10*/ NULL,
+ null,
+
+ // boolean data
+/*12*/ true,
+ false,
+ TRUE,
+ FALSE,
+
+ // empty data
+/*16*/ "",
+ '',
+ array(),
+
+ // string data
+/*19*/ "string",
+ 'string',
+ $heredoc,
+
+ // object data
+/*22*/ new classA(),
+
+ // undefined data
+/*23*/ @$undefined_var,
+
+ // unset data
+/*24*/ @$unset_var,
+
+ // resource variable
+/*25*/ $fp
+);
+
+// loop through each element of $inputs to check the behavior of scandir()
+$iterator = 1;
+foreach($inputs as $input) {
+ echo "\n-- Iteration $iterator --\n";
+ var_dump( scandir($dir, $input) );
+ $iterator++;
+};
+
+fclose($fp);
+?>
+===DONE===
+--CLEAN--
+<?php
+$dir = dirname(__FILE__) . '/私はガラスを食べられますscandir_variation2';
+rmdir($dir);
+?>
+--EXPECTF--
+*** Testing scandir() : usage variations ***
+
+-- Iteration 1 --
+array(2) {
+ [0]=>
+ string(1) "."
+ [1]=>
+ string(2) ".."
+}
+
+-- Iteration 2 --
+array(2) {
+ [0]=>
+ string(2) ".."
+ [1]=>
+ string(1) "."
+}
+
+-- Iteration 3 --
+array(2) {
+ [0]=>
+ string(2) ".."
+ [1]=>
+ string(1) "."
+}
+
+-- Iteration 4 --
+array(2) {
+ [0]=>
+ string(2) ".."
+ [1]=>
+ string(1) "."
+}
+
+-- Iteration 5 --
+array(2) {
+ [0]=>
+ string(2) ".."
+ [1]=>
+ string(1) "."
+}
+
+-- Iteration 6 --
+array(2) {
+ [0]=>
+ string(2) ".."
+ [1]=>
+ string(1) "."
+}
+
+-- Iteration 7 --
+array(2) {
+ [0]=>
+ string(2) ".."
+ [1]=>
+ string(1) "."
+}
+
+-- Iteration 8 --
+array(2) {
+ [0]=>
+ string(1) "."
+ [1]=>
+ string(2) ".."
+}
+
+-- Iteration 9 --
+array(2) {
+ [0]=>
+ string(1) "."
+ [1]=>
+ string(2) ".."
+}
+
+-- Iteration 10 --
+array(2) {
+ [0]=>
+ string(1) "."
+ [1]=>
+ string(2) ".."
+}
+
+-- Iteration 11 --
+array(2) {
+ [0]=>
+ string(1) "."
+ [1]=>
+ string(2) ".."
+}
+
+-- Iteration 12 --
+array(2) {
+ [0]=>
+ string(2) ".."
+ [1]=>
+ string(1) "."
+}
+
+-- Iteration 13 --
+array(2) {
+ [0]=>
+ string(1) "."
+ [1]=>
+ string(2) ".."
+}
+
+-- Iteration 14 --
+array(2) {
+ [0]=>
+ string(2) ".."
+ [1]=>
+ string(1) "."
+}
+
+-- Iteration 15 --
+array(2) {
+ [0]=>
+ string(1) "."
+ [1]=>
+ string(2) ".."
+}
+
+-- Iteration 16 --
+
+Warning: scandir() expects parameter 2 to be integer, string given in %s on line %d
+NULL
+
+-- Iteration 17 --
+
+Warning: scandir() expects parameter 2 to be integer, string given in %s on line %d
+NULL
+
+-- Iteration 18 --
+
+Warning: scandir() expects parameter 2 to be integer, array given in %s on line %d
+NULL
+
+-- Iteration 19 --
+
+Warning: scandir() expects parameter 2 to be integer, string given in %s on line %d
+NULL
+
+-- Iteration 20 --
+
+Warning: scandir() expects parameter 2 to be integer, string given in %s on line %d
+NULL
+
+-- Iteration 21 --
+
+Warning: scandir() expects parameter 2 to be integer, string given in %s on line %d
+NULL
+
+-- Iteration 22 --
+
+Warning: scandir() expects parameter 2 to be integer, object given in %s on line %d
+NULL
+
+-- Iteration 23 --
+array(2) {
+ [0]=>
+ string(1) "."
+ [1]=>
+ string(2) ".."
+}
+
+-- Iteration 24 --
+array(2) {
+ [0]=>
+ string(1) "."
+ [1]=>
+ string(2) ".."
+}
+
+-- Iteration 25 --
+
+Warning: scandir() expects parameter 2 to be integer, resource given in %s on line %d
+NULL
+===DONE===
diff --git a/ext/standard/tests/dir/scandir_variation3-win32-mb.phpt b/ext/standard/tests/dir/scandir_variation3-win32-mb.phpt
new file mode 100644
index 0000000000..c2b2ed5a17
--- /dev/null
+++ b/ext/standard/tests/dir/scandir_variation3-win32-mb.phpt
@@ -0,0 +1,244 @@
+--TEST--
+Test scandir() function : usage variations - diff data types as $context arg
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die("skip Valid only on Windows");
+}
+?>
+--FILE--
+<?php
+/* Prototype : array scandir(string $dir [, int $sorting_order [, resource $context]])
+ * Description: List files & directories inside the specified path
+ * Source code: ext/standard/dir.c
+ */
+
+/*
+ * Pass different data types as $context argument to test how scandir() behaves
+ */
+
+echo "*** Testing scandir() : usage variations ***\n";
+
+// Initialise function arguments not being substituted
+$dir = dirname(__FILE__) . '/私はガラスを食べられますscandir_variation3';
+mkdir($dir);
+$sorting_order = SCANDIR_SORT_ASCENDING;
+
+//get an unset variable
+$unset_var = 10;
+unset ($unset_var);
+
+// get a class
+class classA
+{
+ public function __toString() {
+ return "Class A object";
+ }
+}
+
+// heredoc string
+$heredoc = <<<EOT
+hello world
+EOT;
+
+// get a resource variable
+$fp = fopen(__FILE__, "r");
+
+// unexpected values to be passed to $context argument
+$inputs = array(
+
+ // int data
+/*1*/ 0,
+ 1,
+ 12345,
+ -2345,
+
+ // float data
+/*5*/ 10.5,
+ -10.5,
+ 12.3456789000e10,
+ 12.3456789000E-10,
+ .5,
+
+ // null data
+/*10*/ NULL,
+ null,
+
+ // boolean data
+/*12*/ true,
+ false,
+ TRUE,
+ FALSE,
+
+ // empty data
+/*16*/ "",
+ '',
+ array(),
+
+ // string data
+/*19*/ "string",
+ 'string',
+ $heredoc,
+
+ // object data
+/*22*/ new classA(),
+
+ // undefined data
+/*23*/ @$undefined_var,
+
+ // unset data
+/*24*/ @$unset_var,
+
+ // resource variable
+/*25*/ $fp
+);
+
+// loop through each element of $inputs to check the behavior of scandir()
+$iterator = 1;
+foreach($inputs as $input) {
+ echo "\n-- Iteration $iterator --\n";
+ var_dump( scandir($dir, $sorting_order, $input) );
+ $iterator++;
+};
+
+fclose($fp);
+?>
+===DONE===
+--CLEAN--
+<?php
+$dir = dirname(__FILE__) . '/私はガラスを食べられますscandir_variation3';
+rmdir($dir);
+?>
+--EXPECTF--
+*** Testing scandir() : usage variations ***
+
+-- Iteration 1 --
+
+Warning: scandir() expects parameter 3 to be resource, integer given in %s on line %d
+NULL
+
+-- Iteration 2 --
+
+Warning: scandir() expects parameter 3 to be resource, integer given in %s on line %d
+NULL
+
+-- Iteration 3 --
+
+Warning: scandir() expects parameter 3 to be resource, integer given in %s on line %d
+NULL
+
+-- Iteration 4 --
+
+Warning: scandir() expects parameter 3 to be resource, integer given in %s on line %d
+NULL
+
+-- Iteration 5 --
+
+Warning: scandir() expects parameter 3 to be resource, float given in %s on line %d
+NULL
+
+-- Iteration 6 --
+
+Warning: scandir() expects parameter 3 to be resource, float given in %s on line %d
+NULL
+
+-- Iteration 7 --
+
+Warning: scandir() expects parameter 3 to be resource, float given in %s on line %d
+NULL
+
+-- Iteration 8 --
+
+Warning: scandir() expects parameter 3 to be resource, float given in %s on line %d
+NULL
+
+-- Iteration 9 --
+
+Warning: scandir() expects parameter 3 to be resource, float given in %s on line %d
+NULL
+
+-- Iteration 10 --
+
+Warning: scandir() expects parameter 3 to be resource, null given in %s on line %d
+NULL
+
+-- Iteration 11 --
+
+Warning: scandir() expects parameter 3 to be resource, null given in %s on line %d
+NULL
+
+-- Iteration 12 --
+
+Warning: scandir() expects parameter 3 to be resource, boolean given in %s on line %d
+NULL
+
+-- Iteration 13 --
+
+Warning: scandir() expects parameter 3 to be resource, boolean given in %s on line %d
+NULL
+
+-- Iteration 14 --
+
+Warning: scandir() expects parameter 3 to be resource, boolean given in %s on line %d
+NULL
+
+-- Iteration 15 --
+
+Warning: scandir() expects parameter 3 to be resource, boolean given in %s on line %d
+NULL
+
+-- Iteration 16 --
+
+Warning: scandir() expects parameter 3 to be resource, string given in %s on line %d
+NULL
+
+-- Iteration 17 --
+
+Warning: scandir() expects parameter 3 to be resource, string given in %s on line %d
+NULL
+
+-- Iteration 18 --
+
+Warning: scandir() expects parameter 3 to be resource, array given in %s on line %d
+NULL
+
+-- Iteration 19 --
+
+Warning: scandir() expects parameter 3 to be resource, string given in %s on line %d
+NULL
+
+-- Iteration 20 --
+
+Warning: scandir() expects parameter 3 to be resource, string given in %s on line %d
+NULL
+
+-- Iteration 21 --
+
+Warning: scandir() expects parameter 3 to be resource, string given in %s on line %d
+NULL
+
+-- Iteration 22 --
+
+Warning: scandir() expects parameter 3 to be resource, object given in %s on line %d
+NULL
+
+-- Iteration 23 --
+
+Warning: scandir() expects parameter 3 to be resource, null given in %s on line %d
+NULL
+
+-- Iteration 24 --
+
+Warning: scandir() expects parameter 3 to be resource, null given in %s on line %d
+NULL
+
+-- Iteration 25 --
+
+Warning: scandir(): supplied resource is not a valid Stream-Context resource in %s on line %d
+array(2) {
+ [0]=>
+ string(1) "."
+ [1]=>
+ string(2) ".."
+}
+===DONE===
diff --git a/ext/standard/tests/dir/scandir_variation4-win32-mb.phpt b/ext/standard/tests/dir/scandir_variation4-win32-mb.phpt
new file mode 100644
index 0000000000..4c85dd75cf
--- /dev/null
+++ b/ext/standard/tests/dir/scandir_variation4-win32-mb.phpt
@@ -0,0 +1,175 @@
+--TEST--
+Test scandir() function : usage variations - different relative paths
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die("skip Valid only on Windows");
+}
+?>
+--FILE--
+<?php
+/* Prototype : array scandir(string $dir [, int $sorting_order [, resource $context]])
+ * Description: List files & directories inside the specified path
+ * Source code: ext/standard/dir.c
+ */
+
+/*
+ * Test scandir() with relative paths as $dir argument
+ */
+
+echo "*** Testing scandir() : usage variations ***\n";
+
+// include for create_files/delete_files functions
+include (dirname(__FILE__) . '/../file/file.inc');
+
+$base_dir_path = dirname(__FILE__);
+
+$level_one_dir_path = "$base_dir_path/私はガラスを食べられますlevel_one";
+$level_two_dir_path = "$level_one_dir_path/私はガラスを食べられますlevel_two";
+
+// create directories and files
+mkdir($level_one_dir_path);
+create_files($level_one_dir_path, 2, 'numeric', 0755, 1, 'w', '私はガラスを食べられますlevel_one', 1);
+mkdir($level_two_dir_path);
+create_files($level_two_dir_path, 2, 'numeric', 0755, 1, 'w', '私はガラスを食べられますlevel_two', 1);
+
+echo "\n-- \$path = './私はガラスを食べられますlevel_one': --\n";
+var_dump(chdir($base_dir_path));
+var_dump(scandir('./私はガラスを食べられますlevel_one'));
+
+echo "\n-- \$path = 'level_one/私はガラスを食べられますlevel_two': --\n";
+var_dump(chdir($base_dir_path));
+var_dump(scandir('私はガラスを食べられますlevel_one/私はガラスを食べられますlevel_two'));
+
+echo "\n-- \$path = '..': --\n";
+var_dump(chdir($level_two_dir_path));
+var_dump(scandir('..'));
+
+echo "\n-- \$path = '私はガラスを食べられますlevel_two', '.': --\n";
+var_dump(chdir($level_two_dir_path));
+var_dump(scandir('.'));
+
+echo "\n-- \$path = '../': --\n";
+var_dump(chdir($level_two_dir_path));
+var_dump(scandir('../'));
+
+echo "\n-- \$path = './': --\n";
+var_dump(chdir($level_two_dir_path));
+var_dump(scandir('./'));
+
+echo "\n-- \$path = '../../'私はガラスを食べられますlevel_one': --\n";
+var_dump(chdir($level_two_dir_path));
+var_dump(scandir('../../私はガラスを食べられますlevel_one'));
+
+@delete_files($level_one_dir_path, 2, '私はガラスを食べられますlevel_one');
+@delete_files($level_two_dir_path, 2, '私はガラスを食べられますlevel_two');
+?>
+===DONE===
+--CLEAN--
+<?php
+$dir_path = dirname(__FILE__);
+rmdir("$dir_path/私はガラスを食べられますlevel_one/私はガラスを食べられますlevel_two");
+rmdir("$dir_path/私はガラスを食べられますlevel_one");
+?>
+--EXPECTF--
+*** Testing scandir() : usage variations ***
+
+-- $path = './私はガラスを食べられますlevel_one': --
+bool(true)
+array(5) {
+ [0]=>
+ string(1) "."
+ [1]=>
+ string(2) ".."
+ [2]=>
+ string(50) "私はガラスを食べられますlevel_one1.tmp"
+ [3]=>
+ string(50) "私はガラスを食べられますlevel_one2.tmp"
+ [4]=>
+ string(45) "私はガラスを食べられますlevel_two"
+}
+
+-- $path = 'level_one/私はガラスを食べられますlevel_two': --
+bool(true)
+array(4) {
+ [0]=>
+ string(1) "."
+ [1]=>
+ string(2) ".."
+ [2]=>
+ string(50) "私はガラスを食べられますlevel_two1.tmp"
+ [3]=>
+ string(50) "私はガラスを食べられますlevel_two2.tmp"
+}
+
+-- $path = '..': --
+bool(true)
+array(5) {
+ [0]=>
+ string(1) "."
+ [1]=>
+ string(2) ".."
+ [2]=>
+ string(50) "私はガラスを食べられますlevel_one1.tmp"
+ [3]=>
+ string(50) "私はガラスを食べられますlevel_one2.tmp"
+ [4]=>
+ string(45) "私はガラスを食べられますlevel_two"
+}
+
+-- $path = '私はガラスを食べられますlevel_two', '.': --
+bool(true)
+array(4) {
+ [0]=>
+ string(1) "."
+ [1]=>
+ string(2) ".."
+ [2]=>
+ string(50) "私はガラスを食べられますlevel_two1.tmp"
+ [3]=>
+ string(50) "私はガラスを食べられますlevel_two2.tmp"
+}
+
+-- $path = '../': --
+bool(true)
+array(5) {
+ [0]=>
+ string(1) "."
+ [1]=>
+ string(2) ".."
+ [2]=>
+ string(50) "私はガラスを食べられますlevel_one1.tmp"
+ [3]=>
+ string(50) "私はガラスを食べられますlevel_one2.tmp"
+ [4]=>
+ string(45) "私はガラスを食べられますlevel_two"
+}
+
+-- $path = './': --
+bool(true)
+array(4) {
+ [0]=>
+ string(1) "."
+ [1]=>
+ string(2) ".."
+ [2]=>
+ string(50) "私はガラスを食べられますlevel_two1.tmp"
+ [3]=>
+ string(50) "私はガラスを食べられますlevel_two2.tmp"
+}
+
+-- $path = '../../'私はガラスを食べられますlevel_one': --
+bool(true)
+array(5) {
+ [0]=>
+ string(1) "."
+ [1]=>
+ string(2) ".."
+ [2]=>
+ string(50) "私はガラスを食べられますlevel_one1.tmp"
+ [3]=>
+ string(50) "私はガラスを食べられますlevel_one2.tmp"
+ [4]=>
+ string(45) "私はガラスを食べられますlevel_two"
+}
+===DONE===
diff --git a/ext/standard/tests/dir/scandir_variation8-win32-mb.phpt b/ext/standard/tests/dir/scandir_variation8-win32-mb.phpt
new file mode 100644
index 0000000000..f30645da45
--- /dev/null
+++ b/ext/standard/tests/dir/scandir_variation8-win32-mb.phpt
@@ -0,0 +1,160 @@
+--TEST--
+Test scandir() function : usage variations - different file names
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die("skip Valid only on Windows");
+}
+?>
+--FILE--
+<?php
+/* Prototype : array scandir(string $dir [, int $sorting_order [, resource $context]])
+ * Description: List files & directories inside the specified path
+ * Source code: ext/standard/dir.c
+ */
+
+/*
+ * Pass a directory containing files with different types of names to test how scandir()
+ * reads them
+ */
+
+echo "*** Testing scandir() : usage variations ***\n";
+
+$dir_path = dirname(__FILE__) . "/私はガラスを食べられますscandir_variation8/";
+mkdir($dir_path);
+
+// heredoc string
+$heredoc = <<<EOT
+hd_file
+EOT;
+
+$inputs = array(
+
+ // int data
+/*1*/ 0,
+ 1,
+ 12345,
+ -2345,
+
+ // float data
+/*5*/ 10.5,
+ -10.5,
+ 12.3456789000e10,
+ 12.3456789000E-10,
+ .5,
+
+ // empty data
+/*10*/ "",
+ array(),
+
+ // string data
+/*12*/ "double_file",
+ 'single_file',
+ $heredoc,
+);
+
+$iterator = 1;
+foreach($inputs as $key => $input) {
+ echo "\n-- Iteration $iterator --\n";
+ $handle = "fp{$iterator}";
+ var_dump( $$handle = @fopen($dir_path . "/私はガラスを食べられます$input.tmp", 'w') );
+ fclose($$handle);
+ $iterator++;
+};
+
+echo "\n-- Call to scandir() --\n";
+var_dump($content = scandir($dir_path));
+
+// remove all files in directory so can remove directory in CLEAN section
+foreach ($content as $file_name) {
+ // suppress errors as won't be able to remove "." and ".." entries
+ @unlink($dir_path . $file_name);
+}
+?>
+===DONE===
+--CLEAN--
+<?php
+$dir_path = dirname(__FILE__) . "/私はガラスを食べられますscandir_variation8";
+rmdir($dir_path);
+?>
+--EXPECTF--
+*** Testing scandir() : usage variations ***
+
+-- Iteration 1 --
+resource(%d) of type (stream)
+
+-- Iteration 2 --
+resource(%d) of type (stream)
+
+-- Iteration 3 --
+resource(%d) of type (stream)
+
+-- Iteration 4 --
+resource(%d) of type (stream)
+
+-- Iteration 5 --
+resource(%d) of type (stream)
+
+-- Iteration 6 --
+resource(%d) of type (stream)
+
+-- Iteration 7 --
+resource(%d) of type (stream)
+
+-- Iteration 8 --
+resource(%d) of type (stream)
+
+-- Iteration 9 --
+resource(%d) of type (stream)
+
+-- Iteration 10 --
+resource(%d) of type (stream)
+
+-- Iteration 11 --
+resource(%d) of type (stream)
+
+-- Iteration 12 --
+resource(%d) of type (stream)
+
+-- Iteration 13 --
+resource(%d) of type (stream)
+
+-- Iteration 14 --
+resource(%d) of type (stream)
+
+-- Call to scandir() --
+array(16) {
+ [0]=>
+ string(1) "."
+ [1]=>
+ string(2) ".."
+ [2]=>
+ string(45) "私はガラスを食べられます-10.5.tmp"
+ [3]=>
+ string(45) "私はガラスを食べられます-2345.tmp"
+ [4]=>
+ string(40) "私はガラスを食べられます.tmp"
+ [5]=>
+ string(43) "私はガラスを食べられます0.5.tmp"
+ [6]=>
+ string(41) "私はガラスを食べられます0.tmp"
+ [7]=>
+ string(53) "私はガラスを食べられます1.23456789E-9.tmp"
+ [8]=>
+ string(41) "私はガラスを食べられます1.tmp"
+ [9]=>
+ string(44) "私はガラスを食べられます10.5.tmp"
+ [10]=>
+ string(45) "私はガラスを食べられます12345.tmp"
+ [11]=>
+ string(52) "私はガラスを食べられます123456789000.tmp"
+ [12]=>
+ string(45) "私はガラスを食べられますArray.tmp"
+ [13]=>
+ string(51) "私はガラスを食べられますdouble_file.tmp"
+ [14]=>
+ string(47) "私はガラスを食べられますhd_file.tmp"
+ [15]=>
+ string(51) "私はガラスを食べられますsingle_file.tmp"
+}
+===DONE===
diff --git a/ext/standard/tests/dir/scandir_variation9-win32-mb.phpt b/ext/standard/tests/dir/scandir_variation9-win32-mb.phpt
new file mode 100644
index 0000000000..686eca43a6
--- /dev/null
+++ b/ext/standard/tests/dir/scandir_variation9-win32-mb.phpt
@@ -0,0 +1,78 @@
+--TEST--
+Test scandir() function : usage variations - different ints as $sorting_order arg
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die("skip Valid only on Windows");
+}
+?>
+--FILE--
+<?php
+/* Prototype : array scandir(string $dir [, int $sorting_order [, resource $context]])
+ * Description: List files & directories inside the specified path
+ * Source code: ext/standard/dir.c
+ */
+
+/*
+ * Pass different integers as $sorting_order argument to test how scandir()
+ * re-orders the array
+ */
+
+echo "*** Testing scandir() : usage variations ***\n";
+
+// include for create_files/delete_files functions
+include(dirname(__FILE__) . '/../file/file.inc');
+
+// create directory and files
+$dir = dirname(__FILE__) . '/私はガラスを食べられますscandir_variation9';
+mkdir($dir);
+@create_files($dir, 2, "numeric", 0755, 1, "w", "私はガラスを食べられますfile");
+
+// different ints to pass as $sorting_order argument
+$ints = array (PHP_INT_MAX, -PHP_INT_MAX, 0);
+
+foreach($ints as $sorting_order) {
+ var_dump( scandir($dir, $sorting_order) );
+}
+
+delete_files($dir, 2, "私はガラスを食べられますfile");
+?>
+===DONE===
+--CLEAN--
+<?php
+$dir = dirname(__FILE__) . '/私はガラスを食べられますscandir_variation9';
+rmdir($dir);
+?>
+--EXPECTF--
+*** Testing scandir() : usage variations ***
+array(4) {
+ [0]=>
+ string(45) "私はガラスを食べられますfile2.tmp"
+ [1]=>
+ string(45) "私はガラスを食べられますfile1.tmp"
+ [2]=>
+ string(2) ".."
+ [3]=>
+ string(1) "."
+}
+array(4) {
+ [0]=>
+ string(45) "私はガラスを食べられますfile2.tmp"
+ [1]=>
+ string(45) "私はガラスを食べられますfile1.tmp"
+ [2]=>
+ string(2) ".."
+ [3]=>
+ string(1) "."
+}
+array(4) {
+ [0]=>
+ string(1) "."
+ [1]=>
+ string(2) ".."
+ [2]=>
+ string(45) "私はガラスを食べられますfile1.tmp"
+ [3]=>
+ string(45) "私はガラスを食べられますfile2.tmp"
+}
+===DONE===
diff --git a/ext/standard/tests/directory/DirectoryClass_error_001-mb.phpt b/ext/standard/tests/directory/DirectoryClass_error_001-mb.phpt
new file mode 100644
index 0000000000..cba61d3fbf
--- /dev/null
+++ b/ext/standard/tests/directory/DirectoryClass_error_001-mb.phpt
@@ -0,0 +1,69 @@
+--TEST--
+Directory class behaviour.
+--FILE--
+<?php
+
+$d = getcwd().PATH_SEPARATOR."私はガラスを食べられます";
+
+mkdir($d);
+
+echo "\n--> Try all methods with bad handle:\n";
+$d = new Directory($d);
+$d->handle = "Havoc!";
+var_dump($d->read());
+var_dump($d->rewind());
+var_dump($d->close());
+
+echo "\n--> Try all methods with no handle:\n";
+$d = new Directory($d);
+unset($d->handle);
+var_dump($d->read());
+var_dump($d->rewind());
+var_dump($d->close());
+
+echo "\n--> Try all methods with wrong number of args:\n";
+$d = new Directory($d);
+var_dump($d->read(1,2));
+var_dump($d->rewind(1,2));
+var_dump($d->close(1,2));
+
+?>
+--CLEAN--
+<?php
+$d = getcwd().PATH_SEPARATOR."私はガラスを食べられます";
+rmdir($d);
+
+?>
+--EXPECTF--
+--> Try all methods with bad handle:
+
+Warning: Directory::read(): supplied argument is not a valid Directory resource in %s on line %d
+bool(false)
+
+Warning: Directory::rewind(): supplied argument is not a valid Directory resource in %s on line %d
+bool(false)
+
+Warning: Directory::close(): supplied argument is not a valid Directory resource in %s on line %d
+bool(false)
+
+--> Try all methods with no handle:
+
+Warning: Directory::read(): Unable to find my handle property in %s on line %d
+bool(false)
+
+Warning: Directory::rewind(): Unable to find my handle property in %s on line %d
+bool(false)
+
+Warning: Directory::close(): Unable to find my handle property in %s on line %d
+bool(false)
+
+--> Try all methods with wrong number of args:
+
+Warning: Directory::read() expects at most 1 parameter, 2 given in %s on line %d
+NULL
+
+Warning: Directory::rewind() expects at most 1 parameter, 2 given in %s on line %d
+NULL
+
+Warning: Directory::close() expects at most 1 parameter, 2 given in %s on line %d
+NULL
diff --git a/ext/standard/tests/directory/bug74589_utf8.phpt b/ext/standard/tests/directory/bug74589_utf8.phpt
new file mode 100644
index 0000000000..686e005b8a
--- /dev/null
+++ b/ext/standard/tests/directory/bug74589_utf8.phpt
@@ -0,0 +1,49 @@
+--TEST--
+Bug #74589 __DIR__ wrong for unicode character, UTF-8
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN' && PHP_OS != 'Linux') {
+ die('skip Linux or Windows only');
+}
+?>
+--INI--
+internal_encoding=utf-8
+--FILE--
+<?php
+/*
+#vim: set fileencoding=utf-8
+#vim: set encoding=utf-8
+*/
+
+$item = "bug74589_新建文件夹"; // utf-8 string
+$dir = dirname(__FILE__) . DIRECTORY_SEPARATOR . $item;
+$test_file = $dir . DIRECTORY_SEPARATOR . "test.php";
+
+mkdir($dir);
+
+file_put_contents($test_file,
+"<?php
+ var_dump(__DIR__);
+ var_dump(__FILE__);
+ var_dump(__DIR__ === dirname(__FILE__));");
+
+$php = getenv('TEST_PHP_EXECUTABLE');
+
+echo shell_exec("$php -n $test_file");
+
+?>
+===DONE===
+--EXPECTF--
+string(%d) "%sbug74589_新建文件夹"
+string(%d) "%sbug74589_新建文件夹%etest.php"
+bool(true)
+===DONE===
+--CLEAN--
+<?php
+ $item = "bug74589_新建文件夹"; // utf-8 string
+ $dir = dirname(__FILE__) . DIRECTORY_SEPARATOR . $item;
+ $test_file = $dir . DIRECTORY_SEPARATOR . "test.php";
+ unlink($test_file);
+ rmdir($dir);
+?>
+
diff --git a/ext/standard/tests/file/001-win32-mb.phpt b/ext/standard/tests/file/001-win32-mb.phpt
new file mode 100644
index 0000000000..eb3be23f83
--- /dev/null
+++ b/ext/standard/tests/file/001-win32-mb.phpt
@@ -0,0 +1,103 @@
+--TEST--
+File type functions
+--CREDITS--
+Dave Kelsey <d_kelsey@uk.ibm.com>
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die('skip only for Windows');
+}
+?>
+--FILE--
+<?php
+chdir(dirname(__FILE__));
+
+$fname = 'test_私はガラスを食べられます.file';
+
+@unlink($fname);
+if (file_exists($fname)) {
+ echo "$fname exists\n";
+} else {
+ echo "$fname does not exist\n";
+}
+fclose (fopen($fname, 'w'));
+chmod ($fname, 0744);
+if (file_exists($fname)) {
+ echo "$fname exists\n";
+} else {
+ echo "$fname does not exist\n";
+}
+if (is_link($fname)) {
+ echo "$fname is a symlink\n";
+} else {
+ echo "$fname is not a symlink\n";
+}
+if (file_exists($fname)) {
+ echo "$fname exists\n";
+} else {
+ echo "$fname does not exist\n";
+}
+$s = stat ($fname);
+$ls = lstat ($fname);
+for ($i = 0; $i <= 12; $i++) {
+ if ($ls[$i] != $s[$i]) {
+ echo "$fname lstat and stat differ at element $i\n";
+ }
+}
+echo "$fname is " . filetype($fname) . "\n";
+printf ("$fname permissions are 0%o\n", 0777 & fileperms($fname));
+echo "$fname size is " . filesize($fname) . "\n";
+if (is_writeable($fname)) {
+ echo "$fname is writeable\n";
+} else {
+ echo "$fname is not writeable\n";
+}
+if (is_readable($fname)) {
+ echo "$fname is readable\n";
+} else {
+ echo "$fname is not readable\n";
+}
+if (is_file($fname)) {
+ echo "$fname is a regular file\n";
+} else {
+ echo "$fname is not a regular file\n";
+}
+if (is_dir('../file')) {
+ echo "../file is a directory\n";
+} else {
+ echo "../file is not a directory\n";
+}
+if (is_dir($fname)) {
+ echo "$fname is a directory\n";
+} else {
+ echo "$fname is not a directory\n";
+}
+unlink($fname);
+if (file_exists($fname)) {
+ echo "$fname exists (cached)\n";
+} else {
+ echo "$fname does not exist\n";
+}
+clearstatcache();
+if (file_exists($fname)) {
+ echo "$fname exists\n";
+} else {
+ echo "$fname does not exist\n";
+}
+?>
+--EXPECT--
+test_私はガラスを食べられます.file does not exist
+test_私はガラスを食べられます.file exists
+test_私はガラスを食べられます.file is not a symlink
+test_私はガラスを食べられます.file exists
+test_私はガラスを食べられます.file is file
+test_私はガラスを食べられます.file permissions are 0666
+test_私はガラスを食べられます.file size is 0
+test_私はガラスを食べられます.file is writeable
+test_私はガラスを食べられます.file is readable
+test_私はガラスを食べられます.file is a regular file
+../file is a directory
+test_私はガラスを食べられます.file is not a directory
+test_私はガラスを食べられます.file does not exist
+test_私はガラスを食べられます.file does not exist
+
diff --git a/ext/standard/tests/file/007_variation11-win32-mb.phpt b/ext/standard/tests/file/007_variation11-win32-mb.phpt
new file mode 100644
index 0000000000..68a0e8ccd0
--- /dev/null
+++ b/ext/standard/tests/file/007_variation11-win32-mb.phpt
@@ -0,0 +1,78 @@
+--TEST--
+Test fopen and fclose() functions - usage variations - "wt" mode
+--SKIPIF--
+<?php
+if( substr(PHP_OS, 0, 3) != "WIN" )
+ die('skip Run only on Windows');
+?>
+--FILE--
+<?php
+/*
+ fopen() function:
+ Prototype: resource fopen(string $filename, string $mode
+ [, bool $use_include_path [, resource $context]] );
+ Description: Opens file or URL.
+*/
+/*
+ fclose() function:
+ Prototype: bool fclose ( resource $handle );
+ Description: Closes an open file pointer
+*/
+
+/* Test fopen() and fclose(): Opening the file in "wt" mode,
+ checking for the file creation, write & read operations,
+ checking for the file pointer position,
+ checking for the file truncation when trying to open an existing file in "wt" mode,
+ and fclose function
+*/
+$file_path = dirname(__FILE__);
+require($file_path."/file.inc");
+
+create_files($file_path, 1, "text_with_new_line", 0755, 20, "wt", "007_variation_私はガラスを食べられます", 11, "bytes");
+$file = $file_path."/007_variation_私はガラスを食べられます11.tmp";
+$string = "abcdefghij\nmnopqrst\tuvwxyz\n0123456789";
+
+echo "*** Test fopen() & fclose() functions: with 'wt' mode ***\n";
+$file_handle = fopen($file, "wt"); //opening the file "wt" mode
+var_dump($file_handle); //Check for the content of handle
+var_dump( get_resource_type($file_handle) ); //Check for the type of resource
+var_dump( ftell($file_handle) ); //Initial file pointer position, expected at the beginning of the file
+var_dump( fwrite($file_handle, $string) ); //Check for write operation; passes; expected:size of the $string
+var_dump( ftell($file_handle) ); //File pointer position after write operation, expected at the end of the file
+rewind($file_handle);
+var_dump( fread($file_handle, 100) ); //Check for read operation; fails; expected: empty string
+var_dump( ftell($file_handle) ); //File pointer position after read operation, expected at the beginning of the file
+var_dump( fclose($file_handle) ); //Check for close operation on the file handle
+var_dump( get_resource_type($file_handle) ); //Check whether resource is lost after close operation
+
+var_dump( filesize($file) ); //Check for size of existing data file before opening the file in "wt" mode again, expected: size of content
+clearstatcache();
+fclose( fopen($file, "wt") ); //Opening the existing data file again in "wt" mode
+var_dump( filesize($file) ); //Check for size of existing data file after opening the file in "wt" mode again, expected: 0 bytes
+clearstatcache();
+
+unlink($file); //Deleting the file
+fclose( fopen($file, "wt") ); //Opening the non-existing file in "wt" mode, which will be created
+var_dump( file_exists($file) ); //Check for the existence of file
+echo "*** Done ***\n";
+--CLEAN--
+<?php
+$file_path = dirname(__FILE__);
+$file = $file_path."/007_variation_私はガラスを食べられます11.tmp";
+unlink($file);
+?>
+--EXPECTF--
+*** Test fopen() & fclose() functions: with 'wt' mode ***
+resource(%d) of type (stream)
+string(6) "stream"
+int(0)
+int(37)
+int(37)
+string(0) ""
+int(0)
+bool(true)
+string(7) "Unknown"
+int(39)
+int(0)
+bool(true)
+*** Done ***
diff --git a/ext/standard/tests/file/bug38450.phpt b/ext/standard/tests/file/bug38450.phpt
index 07e413b92b..4a2953ea79 100644
--- a/ext/standard/tests/file/bug38450.phpt
+++ b/ext/standard/tests/file/bug38450.phpt
@@ -7,7 +7,7 @@ class VariableStream {
var $position;
var $varname;
- function __construct($var) {
+ function __construct($var=null) {
var_dump("constructor!");
}
@@ -102,7 +102,6 @@ var_dump($myvar);
echo "Done\n";
?>
--EXPECTF--
-Warning: Missing argument 1 for VariableStream::__construct() in %s on line %d
string(12) "constructor!"
line1
line2
diff --git a/ext/standard/tests/file/bug38450_1.phpt b/ext/standard/tests/file/bug38450_1.phpt
index 07e413b92b..d0682186f9 100644
--- a/ext/standard/tests/file/bug38450_1.phpt
+++ b/ext/standard/tests/file/bug38450_1.phpt
@@ -7,7 +7,7 @@ class VariableStream {
var $position;
var $varname;
- function __construct($var) {
+ function __construct($var = null) {
var_dump("constructor!");
}
@@ -102,7 +102,6 @@ var_dump($myvar);
echo "Done\n";
?>
--EXPECTF--
-Warning: Missing argument 1 for VariableStream::__construct() in %s on line %d
string(12) "constructor!"
line1
line2
diff --git a/ext/standard/tests/file/bug38450_2.phpt b/ext/standard/tests/file/bug38450_2.phpt
index 7934bb40f5..64c9f8d94a 100644
--- a/ext/standard/tests/file/bug38450_2.phpt
+++ b/ext/standard/tests/file/bug38450_2.phpt
@@ -7,7 +7,7 @@ class VariableStream {
var $position;
var $varname;
- function __construct($var) {
+ function __construct($var = null) {
throw new Exception("constructor");
}
@@ -102,7 +102,6 @@ var_dump($myvar);
echo "Done\n";
?>
--EXPECTF--
-Warning: Missing argument 1 for VariableStream::__construct() in %s on line %d
Warning: fopen(var://myvar): failed to open stream: "VariableStream::stream_open" call failed in %s on line %d
diff --git a/ext/standard/tests/file/bug38450_3.phpt b/ext/standard/tests/file/bug38450_3.phpt
index f2c4643ef3..a72a00310d 100644
--- a/ext/standard/tests/file/bug38450_3.phpt
+++ b/ext/standard/tests/file/bug38450_3.phpt
@@ -104,7 +104,7 @@ echo "Done\n";
--EXPECTF--
Warning: fopen(var://myvar): failed to open stream: "VariableStream::stream_open" call failed in %sbug38450_3.php on line %d
-Fatal error: Uncaught TypeError: Argument 1 passed to VariableStream::__construct() must be of the type array, none given in %s:%d
+Fatal error: Uncaught ArgumentCountError: Too few arguments to function VariableStream::__construct(), 0 passed and exactly 1 expected in %sbug38450_3.php:7
Stack trace:
#0 [internal function]: VariableStream->__construct()
#1 %s(%d): fopen('var://myvar', 'r+')
diff --git a/ext/standard/tests/file/bug39673.phpt b/ext/standard/tests/file/bug39673.phpt
index 3836f2103d..00c29b28da 100644
--- a/ext/standard/tests/file/bug39673.phpt
+++ b/ext/standard/tests/file/bug39673.phpt
@@ -22,14 +22,14 @@ $offsets = array(
foreach ($offsets as $offset) {
$r = file_get_contents($filename, false, null, $offset);
- var_dump(strlen($r));
+ if ($r !== false) var_dump(strlen($r));
}
@unlink($filename);
echo "Done\n";
?>
---EXPECTF--
-int(13824)
+--EXPECTF--
+int(1)
int(13824)
int(0)
int(1)
diff --git a/ext/standard/tests/file/bug43353-win32.phpt b/ext/standard/tests/file/bug43353-win32.phpt
index 0667f6984e..2faabb92c0 100644
--- a/ext/standard/tests/file/bug43353-win32.phpt
+++ b/ext/standard/tests/file/bug43353-win32.phpt
@@ -21,5 +21,5 @@ bool(false)
bool(false)
string(3) "foo"
-Warning: file_get_contents(datafoo:text/plain,foo): failed to open stream: Invalid argument in %s
+Warning: file_get_contents(datafoo:text/plain,foo): failed to open stream: No such file or directory in %s
bool(false)
diff --git a/ext/standard/tests/file/bug47517.phpt b/ext/standard/tests/file/bug47517.phpt
new file mode 100644
index 0000000000..f8c9e41446
--- /dev/null
+++ b/ext/standard/tests/file/bug47517.phpt
@@ -0,0 +1,21 @@
+--TEST--
+Bug #47517 test registry virtualization disabled
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die('skip only for Windows');
+}
+exec('net session 2>&1', $out, $status);
+if (!$status) {
+ die('skip test runs under an elevated user account');
+}
+?>
+--FILE--
+<?php
+/* This has to behave same way on both 64- and 32-bits. */
+file_put_contents('C:\Program Files\myfile.txt', 'hello');
+?>
+==DONE==
+--EXPECTF--
+Warning: file_put_contents(C:\Program Files\myfile.txt): failed to open stream: Permission denied in %sbug47517.php on line %d
+==DONE==
diff --git a/ext/standard/tests/file/bug52624.phpt b/ext/standard/tests/file/bug52624.phpt
index ee61eb90e6..c5e7a9cb0e 100644
--- a/ext/standard/tests/file/bug52624.phpt
+++ b/ext/standard/tests/file/bug52624.phpt
@@ -9,4 +9,6 @@ echo tempnam("directory_that_not_exists", "prefix_");
?>
--EXPECTF--
+Notice: tempnam(): file created in the system's temporary directory in %sbug52624.php on line %d
+
Warning: tempnam(): open_basedir restriction in effect. File(%s) is not within the allowed path(s): (%s) in %s on line %d
diff --git a/ext/standard/tests/file/bug71882.phpt b/ext/standard/tests/file/bug71882.phpt
index 3cb6d85240..c132aa95c7 100644
--- a/ext/standard/tests/file/bug71882.phpt
+++ b/ext/standard/tests/file/bug71882.phpt
@@ -7,5 +7,6 @@ var_dump(ftruncate($fd, -1));
?>
==DONE==
--EXPECTF--
+Warning: ftruncate(): Negative size is not supported in %s%ebug71882.php on line %d
bool(false)
==DONE==
diff --git a/ext/standard/tests/file/chmod_basic-win32-mb.phpt b/ext/standard/tests/file/chmod_basic-win32-mb.phpt
new file mode 100644
index 0000000000..3970504ddb
--- /dev/null
+++ b/ext/standard/tests/file/chmod_basic-win32-mb.phpt
@@ -0,0 +1,545 @@
+--TEST--
+chmod() basic functionality
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die('skip Windows only chmod test');
+}
+?>
+--FILE--
+<?php
+
+define("PERMISSIONS_MASK", 0777);
+
+$filename = __FILE__ . "私はガラスを食べられます.tmp";
+
+$fd = fopen($filename, "w+");
+fclose($fd);
+
+for ($perms_to_set = 0777; $perms_to_set >= 0; $perms_to_set--) {
+ chmod($filename, $perms_to_set);
+ $set_perms = (fileperms($filename) & PERMISSIONS_MASK);
+ clearstatcache();
+ printf("Setting mode %o gives mode %o\n", $perms_to_set, $set_perms);
+}
+var_dump(chmod($filename, 0777));
+
+unlink($filename);
+echo "done";
+
+?>
+--EXPECT--
+Setting mode 777 gives mode 666
+Setting mode 776 gives mode 666
+Setting mode 775 gives mode 666
+Setting mode 774 gives mode 666
+Setting mode 773 gives mode 666
+Setting mode 772 gives mode 666
+Setting mode 771 gives mode 666
+Setting mode 770 gives mode 666
+Setting mode 767 gives mode 666
+Setting mode 766 gives mode 666
+Setting mode 765 gives mode 666
+Setting mode 764 gives mode 666
+Setting mode 763 gives mode 666
+Setting mode 762 gives mode 666
+Setting mode 761 gives mode 666
+Setting mode 760 gives mode 666
+Setting mode 757 gives mode 666
+Setting mode 756 gives mode 666
+Setting mode 755 gives mode 666
+Setting mode 754 gives mode 666
+Setting mode 753 gives mode 666
+Setting mode 752 gives mode 666
+Setting mode 751 gives mode 666
+Setting mode 750 gives mode 666
+Setting mode 747 gives mode 666
+Setting mode 746 gives mode 666
+Setting mode 745 gives mode 666
+Setting mode 744 gives mode 666
+Setting mode 743 gives mode 666
+Setting mode 742 gives mode 666
+Setting mode 741 gives mode 666
+Setting mode 740 gives mode 666
+Setting mode 737 gives mode 666
+Setting mode 736 gives mode 666
+Setting mode 735 gives mode 666
+Setting mode 734 gives mode 666
+Setting mode 733 gives mode 666
+Setting mode 732 gives mode 666
+Setting mode 731 gives mode 666
+Setting mode 730 gives mode 666
+Setting mode 727 gives mode 666
+Setting mode 726 gives mode 666
+Setting mode 725 gives mode 666
+Setting mode 724 gives mode 666
+Setting mode 723 gives mode 666
+Setting mode 722 gives mode 666
+Setting mode 721 gives mode 666
+Setting mode 720 gives mode 666
+Setting mode 717 gives mode 666
+Setting mode 716 gives mode 666
+Setting mode 715 gives mode 666
+Setting mode 714 gives mode 666
+Setting mode 713 gives mode 666
+Setting mode 712 gives mode 666
+Setting mode 711 gives mode 666
+Setting mode 710 gives mode 666
+Setting mode 707 gives mode 666
+Setting mode 706 gives mode 666
+Setting mode 705 gives mode 666
+Setting mode 704 gives mode 666
+Setting mode 703 gives mode 666
+Setting mode 702 gives mode 666
+Setting mode 701 gives mode 666
+Setting mode 700 gives mode 666
+Setting mode 677 gives mode 666
+Setting mode 676 gives mode 666
+Setting mode 675 gives mode 666
+Setting mode 674 gives mode 666
+Setting mode 673 gives mode 666
+Setting mode 672 gives mode 666
+Setting mode 671 gives mode 666
+Setting mode 670 gives mode 666
+Setting mode 667 gives mode 666
+Setting mode 666 gives mode 666
+Setting mode 665 gives mode 666
+Setting mode 664 gives mode 666
+Setting mode 663 gives mode 666
+Setting mode 662 gives mode 666
+Setting mode 661 gives mode 666
+Setting mode 660 gives mode 666
+Setting mode 657 gives mode 666
+Setting mode 656 gives mode 666
+Setting mode 655 gives mode 666
+Setting mode 654 gives mode 666
+Setting mode 653 gives mode 666
+Setting mode 652 gives mode 666
+Setting mode 651 gives mode 666
+Setting mode 650 gives mode 666
+Setting mode 647 gives mode 666
+Setting mode 646 gives mode 666
+Setting mode 645 gives mode 666
+Setting mode 644 gives mode 666
+Setting mode 643 gives mode 666
+Setting mode 642 gives mode 666
+Setting mode 641 gives mode 666
+Setting mode 640 gives mode 666
+Setting mode 637 gives mode 666
+Setting mode 636 gives mode 666
+Setting mode 635 gives mode 666
+Setting mode 634 gives mode 666
+Setting mode 633 gives mode 666
+Setting mode 632 gives mode 666
+Setting mode 631 gives mode 666
+Setting mode 630 gives mode 666
+Setting mode 627 gives mode 666
+Setting mode 626 gives mode 666
+Setting mode 625 gives mode 666
+Setting mode 624 gives mode 666
+Setting mode 623 gives mode 666
+Setting mode 622 gives mode 666
+Setting mode 621 gives mode 666
+Setting mode 620 gives mode 666
+Setting mode 617 gives mode 666
+Setting mode 616 gives mode 666
+Setting mode 615 gives mode 666
+Setting mode 614 gives mode 666
+Setting mode 613 gives mode 666
+Setting mode 612 gives mode 666
+Setting mode 611 gives mode 666
+Setting mode 610 gives mode 666
+Setting mode 607 gives mode 666
+Setting mode 606 gives mode 666
+Setting mode 605 gives mode 666
+Setting mode 604 gives mode 666
+Setting mode 603 gives mode 666
+Setting mode 602 gives mode 666
+Setting mode 601 gives mode 666
+Setting mode 600 gives mode 666
+Setting mode 577 gives mode 444
+Setting mode 576 gives mode 444
+Setting mode 575 gives mode 444
+Setting mode 574 gives mode 444
+Setting mode 573 gives mode 444
+Setting mode 572 gives mode 444
+Setting mode 571 gives mode 444
+Setting mode 570 gives mode 444
+Setting mode 567 gives mode 444
+Setting mode 566 gives mode 444
+Setting mode 565 gives mode 444
+Setting mode 564 gives mode 444
+Setting mode 563 gives mode 444
+Setting mode 562 gives mode 444
+Setting mode 561 gives mode 444
+Setting mode 560 gives mode 444
+Setting mode 557 gives mode 444
+Setting mode 556 gives mode 444
+Setting mode 555 gives mode 444
+Setting mode 554 gives mode 444
+Setting mode 553 gives mode 444
+Setting mode 552 gives mode 444
+Setting mode 551 gives mode 444
+Setting mode 550 gives mode 444
+Setting mode 547 gives mode 444
+Setting mode 546 gives mode 444
+Setting mode 545 gives mode 444
+Setting mode 544 gives mode 444
+Setting mode 543 gives mode 444
+Setting mode 542 gives mode 444
+Setting mode 541 gives mode 444
+Setting mode 540 gives mode 444
+Setting mode 537 gives mode 444
+Setting mode 536 gives mode 444
+Setting mode 535 gives mode 444
+Setting mode 534 gives mode 444
+Setting mode 533 gives mode 444
+Setting mode 532 gives mode 444
+Setting mode 531 gives mode 444
+Setting mode 530 gives mode 444
+Setting mode 527 gives mode 444
+Setting mode 526 gives mode 444
+Setting mode 525 gives mode 444
+Setting mode 524 gives mode 444
+Setting mode 523 gives mode 444
+Setting mode 522 gives mode 444
+Setting mode 521 gives mode 444
+Setting mode 520 gives mode 444
+Setting mode 517 gives mode 444
+Setting mode 516 gives mode 444
+Setting mode 515 gives mode 444
+Setting mode 514 gives mode 444
+Setting mode 513 gives mode 444
+Setting mode 512 gives mode 444
+Setting mode 511 gives mode 444
+Setting mode 510 gives mode 444
+Setting mode 507 gives mode 444
+Setting mode 506 gives mode 444
+Setting mode 505 gives mode 444
+Setting mode 504 gives mode 444
+Setting mode 503 gives mode 444
+Setting mode 502 gives mode 444
+Setting mode 501 gives mode 444
+Setting mode 500 gives mode 444
+Setting mode 477 gives mode 444
+Setting mode 476 gives mode 444
+Setting mode 475 gives mode 444
+Setting mode 474 gives mode 444
+Setting mode 473 gives mode 444
+Setting mode 472 gives mode 444
+Setting mode 471 gives mode 444
+Setting mode 470 gives mode 444
+Setting mode 467 gives mode 444
+Setting mode 466 gives mode 444
+Setting mode 465 gives mode 444
+Setting mode 464 gives mode 444
+Setting mode 463 gives mode 444
+Setting mode 462 gives mode 444
+Setting mode 461 gives mode 444
+Setting mode 460 gives mode 444
+Setting mode 457 gives mode 444
+Setting mode 456 gives mode 444
+Setting mode 455 gives mode 444
+Setting mode 454 gives mode 444
+Setting mode 453 gives mode 444
+Setting mode 452 gives mode 444
+Setting mode 451 gives mode 444
+Setting mode 450 gives mode 444
+Setting mode 447 gives mode 444
+Setting mode 446 gives mode 444
+Setting mode 445 gives mode 444
+Setting mode 444 gives mode 444
+Setting mode 443 gives mode 444
+Setting mode 442 gives mode 444
+Setting mode 441 gives mode 444
+Setting mode 440 gives mode 444
+Setting mode 437 gives mode 444
+Setting mode 436 gives mode 444
+Setting mode 435 gives mode 444
+Setting mode 434 gives mode 444
+Setting mode 433 gives mode 444
+Setting mode 432 gives mode 444
+Setting mode 431 gives mode 444
+Setting mode 430 gives mode 444
+Setting mode 427 gives mode 444
+Setting mode 426 gives mode 444
+Setting mode 425 gives mode 444
+Setting mode 424 gives mode 444
+Setting mode 423 gives mode 444
+Setting mode 422 gives mode 444
+Setting mode 421 gives mode 444
+Setting mode 420 gives mode 444
+Setting mode 417 gives mode 444
+Setting mode 416 gives mode 444
+Setting mode 415 gives mode 444
+Setting mode 414 gives mode 444
+Setting mode 413 gives mode 444
+Setting mode 412 gives mode 444
+Setting mode 411 gives mode 444
+Setting mode 410 gives mode 444
+Setting mode 407 gives mode 444
+Setting mode 406 gives mode 444
+Setting mode 405 gives mode 444
+Setting mode 404 gives mode 444
+Setting mode 403 gives mode 444
+Setting mode 402 gives mode 444
+Setting mode 401 gives mode 444
+Setting mode 400 gives mode 444
+Setting mode 377 gives mode 666
+Setting mode 376 gives mode 666
+Setting mode 375 gives mode 666
+Setting mode 374 gives mode 666
+Setting mode 373 gives mode 666
+Setting mode 372 gives mode 666
+Setting mode 371 gives mode 666
+Setting mode 370 gives mode 666
+Setting mode 367 gives mode 666
+Setting mode 366 gives mode 666
+Setting mode 365 gives mode 666
+Setting mode 364 gives mode 666
+Setting mode 363 gives mode 666
+Setting mode 362 gives mode 666
+Setting mode 361 gives mode 666
+Setting mode 360 gives mode 666
+Setting mode 357 gives mode 666
+Setting mode 356 gives mode 666
+Setting mode 355 gives mode 666
+Setting mode 354 gives mode 666
+Setting mode 353 gives mode 666
+Setting mode 352 gives mode 666
+Setting mode 351 gives mode 666
+Setting mode 350 gives mode 666
+Setting mode 347 gives mode 666
+Setting mode 346 gives mode 666
+Setting mode 345 gives mode 666
+Setting mode 344 gives mode 666
+Setting mode 343 gives mode 666
+Setting mode 342 gives mode 666
+Setting mode 341 gives mode 666
+Setting mode 340 gives mode 666
+Setting mode 337 gives mode 666
+Setting mode 336 gives mode 666
+Setting mode 335 gives mode 666
+Setting mode 334 gives mode 666
+Setting mode 333 gives mode 666
+Setting mode 332 gives mode 666
+Setting mode 331 gives mode 666
+Setting mode 330 gives mode 666
+Setting mode 327 gives mode 666
+Setting mode 326 gives mode 666
+Setting mode 325 gives mode 666
+Setting mode 324 gives mode 666
+Setting mode 323 gives mode 666
+Setting mode 322 gives mode 666
+Setting mode 321 gives mode 666
+Setting mode 320 gives mode 666
+Setting mode 317 gives mode 666
+Setting mode 316 gives mode 666
+Setting mode 315 gives mode 666
+Setting mode 314 gives mode 666
+Setting mode 313 gives mode 666
+Setting mode 312 gives mode 666
+Setting mode 311 gives mode 666
+Setting mode 310 gives mode 666
+Setting mode 307 gives mode 666
+Setting mode 306 gives mode 666
+Setting mode 305 gives mode 666
+Setting mode 304 gives mode 666
+Setting mode 303 gives mode 666
+Setting mode 302 gives mode 666
+Setting mode 301 gives mode 666
+Setting mode 300 gives mode 666
+Setting mode 277 gives mode 666
+Setting mode 276 gives mode 666
+Setting mode 275 gives mode 666
+Setting mode 274 gives mode 666
+Setting mode 273 gives mode 666
+Setting mode 272 gives mode 666
+Setting mode 271 gives mode 666
+Setting mode 270 gives mode 666
+Setting mode 267 gives mode 666
+Setting mode 266 gives mode 666
+Setting mode 265 gives mode 666
+Setting mode 264 gives mode 666
+Setting mode 263 gives mode 666
+Setting mode 262 gives mode 666
+Setting mode 261 gives mode 666
+Setting mode 260 gives mode 666
+Setting mode 257 gives mode 666
+Setting mode 256 gives mode 666
+Setting mode 255 gives mode 666
+Setting mode 254 gives mode 666
+Setting mode 253 gives mode 666
+Setting mode 252 gives mode 666
+Setting mode 251 gives mode 666
+Setting mode 250 gives mode 666
+Setting mode 247 gives mode 666
+Setting mode 246 gives mode 666
+Setting mode 245 gives mode 666
+Setting mode 244 gives mode 666
+Setting mode 243 gives mode 666
+Setting mode 242 gives mode 666
+Setting mode 241 gives mode 666
+Setting mode 240 gives mode 666
+Setting mode 237 gives mode 666
+Setting mode 236 gives mode 666
+Setting mode 235 gives mode 666
+Setting mode 234 gives mode 666
+Setting mode 233 gives mode 666
+Setting mode 232 gives mode 666
+Setting mode 231 gives mode 666
+Setting mode 230 gives mode 666
+Setting mode 227 gives mode 666
+Setting mode 226 gives mode 666
+Setting mode 225 gives mode 666
+Setting mode 224 gives mode 666
+Setting mode 223 gives mode 666
+Setting mode 222 gives mode 666
+Setting mode 221 gives mode 666
+Setting mode 220 gives mode 666
+Setting mode 217 gives mode 666
+Setting mode 216 gives mode 666
+Setting mode 215 gives mode 666
+Setting mode 214 gives mode 666
+Setting mode 213 gives mode 666
+Setting mode 212 gives mode 666
+Setting mode 211 gives mode 666
+Setting mode 210 gives mode 666
+Setting mode 207 gives mode 666
+Setting mode 206 gives mode 666
+Setting mode 205 gives mode 666
+Setting mode 204 gives mode 666
+Setting mode 203 gives mode 666
+Setting mode 202 gives mode 666
+Setting mode 201 gives mode 666
+Setting mode 200 gives mode 666
+Setting mode 177 gives mode 444
+Setting mode 176 gives mode 444
+Setting mode 175 gives mode 444
+Setting mode 174 gives mode 444
+Setting mode 173 gives mode 444
+Setting mode 172 gives mode 444
+Setting mode 171 gives mode 444
+Setting mode 170 gives mode 444
+Setting mode 167 gives mode 444
+Setting mode 166 gives mode 444
+Setting mode 165 gives mode 444
+Setting mode 164 gives mode 444
+Setting mode 163 gives mode 444
+Setting mode 162 gives mode 444
+Setting mode 161 gives mode 444
+Setting mode 160 gives mode 444
+Setting mode 157 gives mode 444
+Setting mode 156 gives mode 444
+Setting mode 155 gives mode 444
+Setting mode 154 gives mode 444
+Setting mode 153 gives mode 444
+Setting mode 152 gives mode 444
+Setting mode 151 gives mode 444
+Setting mode 150 gives mode 444
+Setting mode 147 gives mode 444
+Setting mode 146 gives mode 444
+Setting mode 145 gives mode 444
+Setting mode 144 gives mode 444
+Setting mode 143 gives mode 444
+Setting mode 142 gives mode 444
+Setting mode 141 gives mode 444
+Setting mode 140 gives mode 444
+Setting mode 137 gives mode 444
+Setting mode 136 gives mode 444
+Setting mode 135 gives mode 444
+Setting mode 134 gives mode 444
+Setting mode 133 gives mode 444
+Setting mode 132 gives mode 444
+Setting mode 131 gives mode 444
+Setting mode 130 gives mode 444
+Setting mode 127 gives mode 444
+Setting mode 126 gives mode 444
+Setting mode 125 gives mode 444
+Setting mode 124 gives mode 444
+Setting mode 123 gives mode 444
+Setting mode 122 gives mode 444
+Setting mode 121 gives mode 444
+Setting mode 120 gives mode 444
+Setting mode 117 gives mode 444
+Setting mode 116 gives mode 444
+Setting mode 115 gives mode 444
+Setting mode 114 gives mode 444
+Setting mode 113 gives mode 444
+Setting mode 112 gives mode 444
+Setting mode 111 gives mode 444
+Setting mode 110 gives mode 444
+Setting mode 107 gives mode 444
+Setting mode 106 gives mode 444
+Setting mode 105 gives mode 444
+Setting mode 104 gives mode 444
+Setting mode 103 gives mode 444
+Setting mode 102 gives mode 444
+Setting mode 101 gives mode 444
+Setting mode 100 gives mode 444
+Setting mode 77 gives mode 444
+Setting mode 76 gives mode 444
+Setting mode 75 gives mode 444
+Setting mode 74 gives mode 444
+Setting mode 73 gives mode 444
+Setting mode 72 gives mode 444
+Setting mode 71 gives mode 444
+Setting mode 70 gives mode 444
+Setting mode 67 gives mode 444
+Setting mode 66 gives mode 444
+Setting mode 65 gives mode 444
+Setting mode 64 gives mode 444
+Setting mode 63 gives mode 444
+Setting mode 62 gives mode 444
+Setting mode 61 gives mode 444
+Setting mode 60 gives mode 444
+Setting mode 57 gives mode 444
+Setting mode 56 gives mode 444
+Setting mode 55 gives mode 444
+Setting mode 54 gives mode 444
+Setting mode 53 gives mode 444
+Setting mode 52 gives mode 444
+Setting mode 51 gives mode 444
+Setting mode 50 gives mode 444
+Setting mode 47 gives mode 444
+Setting mode 46 gives mode 444
+Setting mode 45 gives mode 444
+Setting mode 44 gives mode 444
+Setting mode 43 gives mode 444
+Setting mode 42 gives mode 444
+Setting mode 41 gives mode 444
+Setting mode 40 gives mode 444
+Setting mode 37 gives mode 444
+Setting mode 36 gives mode 444
+Setting mode 35 gives mode 444
+Setting mode 34 gives mode 444
+Setting mode 33 gives mode 444
+Setting mode 32 gives mode 444
+Setting mode 31 gives mode 444
+Setting mode 30 gives mode 444
+Setting mode 27 gives mode 444
+Setting mode 26 gives mode 444
+Setting mode 25 gives mode 444
+Setting mode 24 gives mode 444
+Setting mode 23 gives mode 444
+Setting mode 22 gives mode 444
+Setting mode 21 gives mode 444
+Setting mode 20 gives mode 444
+Setting mode 17 gives mode 444
+Setting mode 16 gives mode 444
+Setting mode 15 gives mode 444
+Setting mode 14 gives mode 444
+Setting mode 13 gives mode 444
+Setting mode 12 gives mode 444
+Setting mode 11 gives mode 444
+Setting mode 10 gives mode 444
+Setting mode 7 gives mode 444
+Setting mode 6 gives mode 444
+Setting mode 5 gives mode 444
+Setting mode 4 gives mode 444
+Setting mode 3 gives mode 444
+Setting mode 2 gives mode 444
+Setting mode 1 gives mode 444
+Setting mode 0 gives mode 444
+bool(true)
+done
diff --git a/ext/standard/tests/file/chmod_variation2-win32-mb.phpt b/ext/standard/tests/file/chmod_variation2-win32-mb.phpt
new file mode 100644
index 0000000000..abfb3e6f4c
--- /dev/null
+++ b/ext/standard/tests/file/chmod_variation2-win32-mb.phpt
@@ -0,0 +1,74 @@
+--TEST--
+chmod() with various paths
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die('skip Windows only chmod test');
+}
+?>
+--FILE--
+<?php
+
+define("PERMISSIONS_MASK", 0777);
+
+$script_directory = dirname(__FILE__);
+chdir($script_directory);
+$test_dirname = basename(__FILE__, ".php") . "testdir私はガラスを食べられます";
+mkdir($test_dirname);
+
+$filepath = __FILE__ . ".tmp";
+$filename = basename($filepath);
+$fd = fopen($filepath, "w+");
+fclose($fd);
+
+echo "chmod() on a path containing .. and .\n";
+var_dump(chmod("./$test_dirname/../$filename", 0777));
+var_dump(chmod("./$test_dirname/../$filename", 0755));
+clearstatcache();
+printf("%o\n", fileperms($filepath) & PERMISSIONS_MASK);
+
+echo "\nchmod() on a path containing .. with invalid directories\n";
+var_dump(chmod($filepath, 0777));
+var_dump(chmod("./$test_dirname/bad_dir/../../$filename", 0755));
+clearstatcache();
+printf("%o\n", fileperms($filepath) & PERMISSIONS_MASK);
+
+echo "\nchmod() on a relative path from a different working directory\n";
+chdir($test_dirname);
+var_dump(chmod("../$filename", 0777));
+var_dump(chmod("../$filename", 0755));
+clearstatcache();
+printf("%o\n", fileperms($filepath) & PERMISSIONS_MASK);
+chdir($script_directory);
+
+echo "\nchmod() on a directory with a trailing /\n";
+var_dump(chmod($test_dirname, 0777));
+var_dump(chmod("$test_dirname/", 0775));
+clearstatcache();
+printf("%o\n", fileperms($filepath) & PERMISSIONS_MASK);
+
+chdir($script_directory);
+rmdir($test_dirname);
+unlink($filepath);
+
+?>
+--EXPECTF--
+chmod() on a path containing .. and .
+bool(true)
+bool(true)
+666
+
+chmod() on a path containing .. with invalid directories
+bool(true)
+bool(true)
+666
+
+chmod() on a relative path from a different working directory
+bool(true)
+bool(true)
+666
+
+chmod() on a directory with a trailing /
+bool(true)
+bool(true)
+666
diff --git a/ext/standard/tests/file/copy_variation2-win32-mb.phpt b/ext/standard/tests/file/copy_variation2-win32-mb.phpt
new file mode 100644
index 0000000000..e818e294d8
--- /dev/null
+++ b/ext/standard/tests/file/copy_variation2-win32-mb.phpt
@@ -0,0 +1,218 @@
+--TEST--
+Test copy() function: usage variations - destination file names(special chars)
+--SKIPIF--
+<?php
+if(substr(PHP_OS, 0, 3) != "WIN")
+ die("skip only run on Windows");
+?>
+--FILE--
+<?php
+/* Prototype: bool copy ( string $source, string $dest );
+ Description: Makes a copy of the file source to dest.
+ Returns TRUE on success or FALSE on failure.
+*/
+
+/* Test copy() function: In creation of destination file names containing special characters
+ and checking the existence and size of destination files
+*/
+
+echo "*** Test copy() function: destination file names containing special characters ***\n";
+$file_path = dirname(__FILE__);
+$src_file_name = $file_path."/copy_variation2私はガラスを食べられます.tmp";
+$file_handle = fopen($src_file_name, "w");
+fwrite( $file_handle, str_repeat(b"Hello2World...\n", 100) );
+fclose($file_handle);
+
+/* array of destination file names */
+$dest_files = array(
+
+ /* File names containing special(non-alpha numeric) characters */
+ "_copy_variation2.tmp",
+ "@copy_variation2.tmp",
+ "#copy_variation2.tmp",
+ "+copy_variation2.tmp",
+ "?copy_variation2.tmp",
+ ">copy_variation2.tmp",
+ "!copy_variation2.tmp",
+ "&copy_variation2.tmp",
+ "(copy_variation2.tmp",
+ ":copy_variation2.tmp",
+ ";copy_variation2.tmp",
+ "=copy_variation2.tmp",
+ "[copy_variation2.tmp",
+ "^copy_variation2.tmp",
+ "{copy_variation2.tmp",
+ "|copy_variation2.tmp",
+ "~copy_variation2.tmp",
+ "\$copy_variation2.tmp"
+);
+
+echo "Size of the source file before copy operation => ";
+var_dump( filesize("$src_file_name") );
+clearstatcache();
+
+echo "\n--- Now applying copy() on source file to create copies ---";
+$count = 1;
+foreach($dest_files as $dest_file) {
+ echo "\n-- Iteration $count --\n";
+ $dest_file_name = $file_path."/$dest_file";
+
+ echo "Copy operation => ";
+ var_dump( copy($src_file_name, $dest_file_name) );
+
+ echo "Existence of destination file => ";
+ var_dump( file_exists($dest_file_name) );
+
+ if( file_exists($dest_file_name) ) {
+ echo "Destination file name => ";
+ print($dest_file_name);
+ echo "\n";
+
+ echo "Size of source file => ";
+ var_dump( filesize($src_file_name) );
+ clearstatcache();
+
+ echo "Size of destination file => ";
+ var_dump( filesize($dest_file_name) );
+ clearstatcache();
+
+ unlink($dest_file_name);
+ }
+ $count++;
+}
+
+echo "*** Done ***\n";
+?>
+
+--CLEAN--
+<?php
+unlink(dirname(__FILE__)."/copy_variation2私はガラスを食べられます.tmp");
+?>
+
+--EXPECTF--
+*** Test copy() function: destination file names containing special characters ***
+Size of the source file before copy operation => int(1500)
+
+--- Now applying copy() on source file to create copies ---
+-- Iteration 1 --
+Copy operation => bool(true)
+Existence of destination file => bool(true)
+Destination file name => %s/_copy_variation2.tmp
+Size of source file => int(1500)
+Size of destination file => int(1500)
+
+-- Iteration 2 --
+Copy operation => bool(true)
+Existence of destination file => bool(true)
+Destination file name => %s/@copy_variation2.tmp
+Size of source file => int(1500)
+Size of destination file => int(1500)
+
+-- Iteration 3 --
+Copy operation => bool(true)
+Existence of destination file => bool(true)
+Destination file name => %s/#copy_variation2.tmp
+Size of source file => int(1500)
+Size of destination file => int(1500)
+
+-- Iteration 4 --
+Copy operation => bool(true)
+Existence of destination file => bool(true)
+Destination file name => %s/+copy_variation2.tmp
+Size of source file => int(1500)
+Size of destination file => int(1500)
+
+-- Iteration 5 --
+Copy operation =>
+Warning: copy(%s): %s
+bool(false)
+Existence of destination file => bool(false)
+
+-- Iteration 6 --
+Copy operation =>
+Warning: copy(%s): %s
+bool(false)
+Existence of destination file => bool(false)
+
+-- Iteration 7 --
+Copy operation => bool(true)
+Existence of destination file => bool(true)
+Destination file name => %s/!copy_variation2.tmp
+Size of source file => int(1500)
+Size of destination file => int(1500)
+
+-- Iteration 8 --
+Copy operation => bool(true)
+Existence of destination file => bool(true)
+Destination file name => %s/&copy_variation2.tmp
+Size of source file => int(1500)
+Size of destination file => int(1500)
+
+-- Iteration 9 --
+Copy operation => bool(true)
+Existence of destination file => bool(true)
+Destination file name => %s/(copy_variation2.tmp
+Size of source file => int(1500)
+Size of destination file => int(1500)
+
+-- Iteration 10 --
+Copy operation =>
+Warning: copy(%s): %s
+bool(false)
+Existence of destination file => bool(false)
+
+-- Iteration 11 --
+Copy operation => bool(true)
+Existence of destination file => bool(true)
+Destination file name => %s/;copy_variation2.tmp
+Size of source file => int(1500)
+Size of destination file => int(1500)
+
+-- Iteration 12 --
+Copy operation => bool(true)
+Existence of destination file => bool(true)
+Destination file name => %s/=copy_variation2.tmp
+Size of source file => int(1500)
+Size of destination file => int(1500)
+
+-- Iteration 13 --
+Copy operation => bool(true)
+Existence of destination file => bool(true)
+Destination file name => %s/[copy_variation2.tmp
+Size of source file => int(1500)
+Size of destination file => int(1500)
+
+-- Iteration 14 --
+Copy operation => bool(true)
+Existence of destination file => bool(true)
+Destination file name => %s/^copy_variation2.tmp
+Size of source file => int(1500)
+Size of destination file => int(1500)
+
+-- Iteration 15 --
+Copy operation => bool(true)
+Existence of destination file => bool(true)
+Destination file name => %s/{copy_variation2.tmp
+Size of source file => int(1500)
+Size of destination file => int(1500)
+
+-- Iteration 16 --
+Copy operation =>
+Warning: copy(%s): %s
+bool(false)
+Existence of destination file => bool(false)
+
+-- Iteration 17 --
+Copy operation => bool(true)
+Existence of destination file => bool(true)
+Destination file name => %s/~copy_variation2.tmp
+Size of source file => int(1500)
+Size of destination file => int(1500)
+
+-- Iteration 18 --
+Copy operation => bool(true)
+Existence of destination file => bool(true)
+Destination file name => %s/$copy_variation2.tmp
+Size of source file => int(1500)
+Size of destination file => int(1500)
+*** Done ***
diff --git a/ext/standard/tests/file/dirname_no_path_normalization-win32.phpt b/ext/standard/tests/file/dirname_no_path_normalization-win32.phpt
new file mode 100644
index 0000000000..283834e8ce
--- /dev/null
+++ b/ext/standard/tests/file/dirname_no_path_normalization-win32.phpt
@@ -0,0 +1,27 @@
+--TEST--
+Test dirname() function : regression with path normalization
+--SKIPIF--
+<?php
+if(substr(PHP_OS, 0, 3) != "WIN")
+ die("skip Only valid for Windows");
+?>
+--FILE--
+<?php
+
+$s = '/php_sanity/sanity.php?';
+while (dirname($s) == "/php_sanity" && strlen($s) < 10000) {
+ $s .= str_repeat('X', 250);
+}
+
+if (strlen($s) >= 10000) {
+ echo "OK\n";
+} else {
+ print "ERROR: " . PHP_EOL;
+ var_dump(dirname($s));
+ var_dump(strlen($s));
+}
+?>
+===DONE===
+--EXPECT--
+OK
+===DONE===
diff --git a/ext/standard/tests/file/fflush_variation1-win32-mb.phpt b/ext/standard/tests/file/fflush_variation1-win32-mb.phpt
new file mode 100644
index 0000000000..80b8dedb65
--- /dev/null
+++ b/ext/standard/tests/file/fflush_variation1-win32-mb.phpt
@@ -0,0 +1,531 @@
+--TEST--
+Test fflush() function: usage variations - files in different modes
+--SKIPIF--
+<?php
+if( substr(PHP_OS, 0, 3) != "WIN" )
+ die("skip.. only for Windows");
+?>
+
+--FILE--
+<?php
+/* Prototype: bool fflush ( resource $handle );
+ Description: Flushes the output to a file
+*/
+
+/* test fflush() with handle to the files opened in different modes */
+
+$file_path = dirname(__FILE__);
+require $file_path.'/file.inc';
+
+echo "*** Testing fflush(): with various types of files ***\n";
+$file_types = array("empty", "numeric", "text", "text_with_new_line", "alphanumeric");
+$file_modes = array("w", "wb", "wt", "w+", "w+b", "w+t",
+ "a", "ab", "at", "a+","a+b", "a+t",
+ "x", "xb", "xt", "x+", "x+b", "x+t");
+
+$file_name = "$file_path/fflush_variation私はガラスを食べられます1.tmp";
+
+$count = 1;
+
+foreach( $file_types as $type ) {
+ echo "-- Iteration $count with file containing $type Data--\n";
+ foreach( $file_modes as $mode ) {
+ echo "-- File opened in $mode mode --\n";
+
+ // creating the file except for x mode
+ if( substr($mode, 0, 1) != "x" ) {
+ $file_handle = fopen($file_name, "w");
+ if($file_handle == false)
+ exit("Error:failed to open file $file_name");
+
+ // filling the file some data if mode is append mode
+ if( substr($mode, 0, 1) == "a")
+ fill_file($file_handle, $type, 10);
+ fclose($file_handle);
+ }
+
+ // opening the file in different modes
+ $file_handle = fopen($file_name, $mode);
+ if($file_handle == false)
+ exit("Error:failed to open file $file_name");
+
+ // writing data to the file
+ var_dump( fill_file($file_handle, $type, 50) );
+ var_dump( fflush($file_handle) );
+ fclose($file_handle);
+
+ // reading the contents of the file after flushing
+ var_dump( readfile($file_name) );
+ unlink($file_name);
+ }
+ $count++;
+}
+
+echo "\n*** Done ***";
+?>
+--EXPECTF--
+*** Testing fflush(): with various types of files ***
+-- Iteration 1 with file containing empty Data--
+-- File opened in w mode --
+bool(true)
+bool(true)
+int(0)
+-- File opened in wb mode --
+bool(true)
+bool(true)
+int(0)
+-- File opened in wt mode --
+bool(true)
+bool(true)
+int(0)
+-- File opened in w+ mode --
+bool(true)
+bool(true)
+int(0)
+-- File opened in w+b mode --
+bool(true)
+bool(true)
+int(0)
+-- File opened in w+t mode --
+bool(true)
+bool(true)
+int(0)
+-- File opened in a mode --
+bool(true)
+bool(true)
+int(0)
+-- File opened in ab mode --
+bool(true)
+bool(true)
+int(0)
+-- File opened in at mode --
+bool(true)
+bool(true)
+int(0)
+-- File opened in a+ mode --
+bool(true)
+bool(true)
+int(0)
+-- File opened in a+b mode --
+bool(true)
+bool(true)
+int(0)
+-- File opened in a+t mode --
+bool(true)
+bool(true)
+int(0)
+-- File opened in x mode --
+bool(true)
+bool(true)
+int(0)
+-- File opened in xb mode --
+bool(true)
+bool(true)
+int(0)
+-- File opened in xt mode --
+bool(true)
+bool(true)
+int(0)
+-- File opened in x+ mode --
+bool(true)
+bool(true)
+int(0)
+-- File opened in x+b mode --
+bool(true)
+bool(true)
+int(0)
+-- File opened in x+t mode --
+bool(true)
+bool(true)
+int(0)
+-- Iteration 2 with file containing numeric Data--
+-- File opened in w mode --
+bool(true)
+bool(true)
+22222222222222222222222222222222222222222222222222int(50)
+-- File opened in wb mode --
+bool(true)
+bool(true)
+22222222222222222222222222222222222222222222222222int(50)
+-- File opened in wt mode --
+bool(true)
+bool(true)
+22222222222222222222222222222222222222222222222222int(50)
+-- File opened in w+ mode --
+bool(true)
+bool(true)
+22222222222222222222222222222222222222222222222222int(50)
+-- File opened in w+b mode --
+bool(true)
+bool(true)
+22222222222222222222222222222222222222222222222222int(50)
+-- File opened in w+t mode --
+bool(true)
+bool(true)
+22222222222222222222222222222222222222222222222222int(50)
+-- File opened in a mode --
+bool(true)
+bool(true)
+222222222222222222222222222222222222222222222222222222222222int(60)
+-- File opened in ab mode --
+bool(true)
+bool(true)
+222222222222222222222222222222222222222222222222222222222222int(60)
+-- File opened in at mode --
+bool(true)
+bool(true)
+222222222222222222222222222222222222222222222222222222222222int(60)
+-- File opened in a+ mode --
+bool(true)
+bool(true)
+222222222222222222222222222222222222222222222222222222222222int(60)
+-- File opened in a+b mode --
+bool(true)
+bool(true)
+222222222222222222222222222222222222222222222222222222222222int(60)
+-- File opened in a+t mode --
+bool(true)
+bool(true)
+222222222222222222222222222222222222222222222222222222222222int(60)
+-- File opened in x mode --
+bool(true)
+bool(true)
+22222222222222222222222222222222222222222222222222int(50)
+-- File opened in xb mode --
+bool(true)
+bool(true)
+22222222222222222222222222222222222222222222222222int(50)
+-- File opened in xt mode --
+bool(true)
+bool(true)
+22222222222222222222222222222222222222222222222222int(50)
+-- File opened in x+ mode --
+bool(true)
+bool(true)
+22222222222222222222222222222222222222222222222222int(50)
+-- File opened in x+b mode --
+bool(true)
+bool(true)
+22222222222222222222222222222222222222222222222222int(50)
+-- File opened in x+t mode --
+bool(true)
+bool(true)
+22222222222222222222222222222222222222222222222222int(50)
+-- Iteration 3 with file containing text Data--
+-- File opened in w mode --
+bool(true)
+bool(true)
+text text text text text text text text text text int(50)
+-- File opened in wb mode --
+bool(true)
+bool(true)
+text text text text text text text text text text int(50)
+-- File opened in wt mode --
+bool(true)
+bool(true)
+text text text text text text text text text text int(50)
+-- File opened in w+ mode --
+bool(true)
+bool(true)
+text text text text text text text text text text int(50)
+-- File opened in w+b mode --
+bool(true)
+bool(true)
+text text text text text text text text text text int(50)
+-- File opened in w+t mode --
+bool(true)
+bool(true)
+text text text text text text text text text text int(50)
+-- File opened in a mode --
+bool(true)
+bool(true)
+text text text text text text text text text text text text int(60)
+-- File opened in ab mode --
+bool(true)
+bool(true)
+text text text text text text text text text text text text int(60)
+-- File opened in at mode --
+bool(true)
+bool(true)
+text text text text text text text text text text text text int(60)
+-- File opened in a+ mode --
+bool(true)
+bool(true)
+text text text text text text text text text text text text int(60)
+-- File opened in a+b mode --
+bool(true)
+bool(true)
+text text text text text text text text text text text text int(60)
+-- File opened in a+t mode --
+bool(true)
+bool(true)
+text text text text text text text text text text text text int(60)
+-- File opened in x mode --
+bool(true)
+bool(true)
+text text text text text text text text text text int(50)
+-- File opened in xb mode --
+bool(true)
+bool(true)
+text text text text text text text text text text int(50)
+-- File opened in xt mode --
+bool(true)
+bool(true)
+text text text text text text text text text text int(50)
+-- File opened in x+ mode --
+bool(true)
+bool(true)
+text text text text text text text text text text int(50)
+-- File opened in x+b mode --
+bool(true)
+bool(true)
+text text text text text text text text text text int(50)
+-- File opened in x+t mode --
+bool(true)
+bool(true)
+text text text text text text text text text text int(50)
+-- Iteration 4 with file containing text_with_new_line Data--
+-- File opened in w mode --
+bool(true)
+bool(true)
+line
+line of text
+line
+line of text
+line
+line of tint(50)
+-- File opened in wb mode --
+bool(true)
+bool(true)
+line
+line of text
+line
+line of text
+line
+line of tint(50)
+-- File opened in wt mode --
+bool(true)
+bool(true)
+line
+line of text
+line
+line of text
+line
+line of tint(55)
+-- File opened in w+ mode --
+bool(true)
+bool(true)
+line
+line of text
+line
+line of text
+line
+line of tint(50)
+-- File opened in w+b mode --
+bool(true)
+bool(true)
+line
+line of text
+line
+line of text
+line
+line of tint(50)
+-- File opened in w+t mode --
+bool(true)
+bool(true)
+line
+line of text
+line
+line of text
+line
+line of tint(55)
+-- File opened in a mode --
+bool(true)
+bool(true)
+line
+line line
+line of text
+line
+line of text
+line
+line of tint(60)
+-- File opened in ab mode --
+bool(true)
+bool(true)
+line
+line line
+line of text
+line
+line of text
+line
+line of tint(60)
+-- File opened in at mode --
+bool(true)
+bool(true)
+line
+line line
+line of text
+line
+line of text
+line
+line of tint(65)
+-- File opened in a+ mode --
+bool(true)
+bool(true)
+line
+line line
+line of text
+line
+line of text
+line
+line of tint(60)
+-- File opened in a+b mode --
+bool(true)
+bool(true)
+line
+line line
+line of text
+line
+line of text
+line
+line of tint(60)
+-- File opened in a+t mode --
+bool(true)
+bool(true)
+line
+line line
+line of text
+line
+line of text
+line
+line of tint(65)
+-- File opened in x mode --
+bool(true)
+bool(true)
+line
+line of text
+line
+line of text
+line
+line of tint(50)
+-- File opened in xb mode --
+bool(true)
+bool(true)
+line
+line of text
+line
+line of text
+line
+line of tint(50)
+-- File opened in xt mode --
+bool(true)
+bool(true)
+line
+line of text
+line
+line of text
+line
+line of tint(55)
+-- File opened in x+ mode --
+bool(true)
+bool(true)
+line
+line of text
+line
+line of text
+line
+line of tint(50)
+-- File opened in x+b mode --
+bool(true)
+bool(true)
+line
+line of text
+line
+line of text
+line
+line of tint(50)
+-- File opened in x+t mode --
+bool(true)
+bool(true)
+line
+line of text
+line
+line of text
+line
+line of tint(55)
+-- Iteration 5 with file containing alphanumeric Data--
+-- File opened in w mode --
+bool(true)
+bool(true)
+ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 int(50)
+-- File opened in wb mode --
+bool(true)
+bool(true)
+ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 int(50)
+-- File opened in wt mode --
+bool(true)
+bool(true)
+ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 int(50)
+-- File opened in w+ mode --
+bool(true)
+bool(true)
+ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 int(50)
+-- File opened in w+b mode --
+bool(true)
+bool(true)
+ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 int(50)
+-- File opened in w+t mode --
+bool(true)
+bool(true)
+ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 int(50)
+-- File opened in a mode --
+bool(true)
+bool(true)
+ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 int(60)
+-- File opened in ab mode --
+bool(true)
+bool(true)
+ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 int(60)
+-- File opened in at mode --
+bool(true)
+bool(true)
+ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 int(60)
+-- File opened in a+ mode --
+bool(true)
+bool(true)
+ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 int(60)
+-- File opened in a+b mode --
+bool(true)
+bool(true)
+ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 int(60)
+-- File opened in a+t mode --
+bool(true)
+bool(true)
+ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 int(60)
+-- File opened in x mode --
+bool(true)
+bool(true)
+ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 int(50)
+-- File opened in xb mode --
+bool(true)
+bool(true)
+ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 int(50)
+-- File opened in xt mode --
+bool(true)
+bool(true)
+ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 int(50)
+-- File opened in x+ mode --
+bool(true)
+bool(true)
+ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 int(50)
+-- File opened in x+b mode --
+bool(true)
+bool(true)
+ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 int(50)
+-- File opened in x+t mode --
+bool(true)
+bool(true)
+ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 int(50)
+
+*** Done ***
+
diff --git a/ext/standard/tests/file/fgets_variation4-win32-mb.phpt b/ext/standard/tests/file/fgets_variation4-win32-mb.phpt
new file mode 100644
index 0000000000..b5786df58c
--- /dev/null
+++ b/ext/standard/tests/file/fgets_variation4-win32-mb.phpt
@@ -0,0 +1,574 @@
+--TEST--
+Test fgets() function : usage variations - seek n read
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die('skip only valid for Windows');
+}
+?>
+--FILE--
+<?php
+/*
+ Prototype: string fgets ( resource $handle [, int $length] );
+ Description: Gets a line from file pointer
+*/
+
+// include the file.inc for common test funcitons
+include ("file.inc");
+
+$file_modes = array("w+", "w+b", "w+t",
+ "a+", "a+b", "a+t",
+ "x+", "x+b", "x+t");
+
+$file_content_types = array("numeric", "text", "text_with_new_line", "alphanumeric");
+
+echo "*** Testing fgets() : usage variations ***\n";
+
+$filename = dirname(__FILE__)."/fgets_variation4私はガラスを食べられます.tmp";
+
+foreach($file_modes as $file_mode) {
+ echo "\n-- Testing fgets() with file opened using mode $file_mode --\n";
+
+ foreach($file_content_types as $file_content_type) {
+ echo "-- File content type : $file_content_type --\n";
+
+ /* create files with $file_content_type */
+ $file_handle = fopen($filename, $file_mode);
+ $data = fill_file($file_handle, $file_content_type, 50);
+
+ if ( !$file_handle ) {
+ echo "Error: failed to open file $filename!";
+ exit();
+ }
+
+ echo "-- fgets() with location set by fseek() with default length --\n";
+ var_dump( fseek($file_handle, 5, SEEK_SET) );
+ var_dump( ftell($file_handle) );
+ var_dump( fgets($file_handle ) );
+ var_dump( ftell($file_handle) ); // ensure the file pointer position
+ var_dump( feof($file_handle) ); // enusre if eof set
+
+ echo "-- fgets() with location set by fseek() with length = 20 --\n";
+ var_dump( fseek($file_handle, 25, SEEK_SET) );
+ var_dump( ftell($file_handle) );
+ var_dump( fgets($file_handle, 20 ) ); // expected 19 chars
+ var_dump( ftell($file_handle) ); // ensure the file pointer position
+ var_dump( feof($file_handle) ); // enusre if eof set
+
+ //close file
+ fclose($file_handle);
+
+ // delete file
+ delete_file($filename);
+ } // file_content_type loop
+} // file_mode loop
+
+echo "Done\n";
+?>
+--EXPECTF--
+*** Testing fgets() : usage variations ***
+
+-- Testing fgets() with file opened using mode w+ --
+-- File content type : numeric --
+-- fgets() with location set by fseek() with default length --
+int(0)
+int(5)
+string(45) "222222222222222222222222222222222222222222222"
+int(50)
+bool(true)
+-- fgets() with location set by fseek() with length = 20 --
+int(0)
+int(25)
+string(19) "2222222222222222222"
+int(44)
+bool(false)
+-- File content type : text --
+-- fgets() with location set by fseek() with default length --
+int(0)
+int(5)
+string(45) "text text text text text text text text text "
+int(50)
+bool(true)
+-- fgets() with location set by fseek() with length = 20 --
+int(0)
+int(25)
+string(19) "text text text text"
+int(44)
+bool(false)
+-- File content type : text_with_new_line --
+-- fgets() with location set by fseek() with default length --
+int(0)
+int(5)
+string(13) "line of text
+"
+int(18)
+bool(false)
+-- fgets() with location set by fseek() with length = 20 --
+int(0)
+int(25)
+string(11) "ne of text
+"
+int(36)
+bool(false)
+-- File content type : alphanumeric --
+-- fgets() with location set by fseek() with default length --
+int(0)
+int(5)
+string(45) "ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 "
+int(50)
+bool(true)
+-- fgets() with location set by fseek() with length = 20 --
+int(0)
+int(25)
+string(19) "ab12 ab12 ab12 ab12"
+int(44)
+bool(false)
+
+-- Testing fgets() with file opened using mode w+b --
+-- File content type : numeric --
+-- fgets() with location set by fseek() with default length --
+int(0)
+int(5)
+string(45) "222222222222222222222222222222222222222222222"
+int(50)
+bool(true)
+-- fgets() with location set by fseek() with length = 20 --
+int(0)
+int(25)
+string(19) "2222222222222222222"
+int(44)
+bool(false)
+-- File content type : text --
+-- fgets() with location set by fseek() with default length --
+int(0)
+int(5)
+string(45) "text text text text text text text text text "
+int(50)
+bool(true)
+-- fgets() with location set by fseek() with length = 20 --
+int(0)
+int(25)
+string(19) "text text text text"
+int(44)
+bool(false)
+-- File content type : text_with_new_line --
+-- fgets() with location set by fseek() with default length --
+int(0)
+int(5)
+string(13) "line of text
+"
+int(18)
+bool(false)
+-- fgets() with location set by fseek() with length = 20 --
+int(0)
+int(25)
+string(11) "ne of text
+"
+int(36)
+bool(false)
+-- File content type : alphanumeric --
+-- fgets() with location set by fseek() with default length --
+int(0)
+int(5)
+string(45) "ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 "
+int(50)
+bool(true)
+-- fgets() with location set by fseek() with length = 20 --
+int(0)
+int(25)
+string(19) "ab12 ab12 ab12 ab12"
+int(44)
+bool(false)
+
+-- Testing fgets() with file opened using mode w+t --
+-- File content type : numeric --
+-- fgets() with location set by fseek() with default length --
+int(0)
+int(5)
+string(45) "222222222222222222222222222222222222222222222"
+int(50)
+bool(true)
+-- fgets() with location set by fseek() with length = 20 --
+int(0)
+int(25)
+string(19) "2222222222222222222"
+int(44)
+bool(false)
+-- File content type : text --
+-- fgets() with location set by fseek() with default length --
+int(0)
+int(5)
+string(45) "text text text text text text text text text "
+int(50)
+bool(true)
+-- fgets() with location set by fseek() with length = 20 --
+int(0)
+int(25)
+string(19) "text text text text"
+int(44)
+bool(false)
+-- File content type : text_with_new_line --
+-- fgets() with location set by fseek() with default length --
+int(0)
+int(5)
+string(1) "
+"
+int(6)
+bool(false)
+-- fgets() with location set by fseek() with length = 20 --
+int(0)
+int(25)
+string(12) "ine of text
+"
+int(37)
+bool(false)
+-- File content type : alphanumeric --
+-- fgets() with location set by fseek() with default length --
+int(0)
+int(5)
+string(45) "ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 "
+int(50)
+bool(true)
+-- fgets() with location set by fseek() with length = 20 --
+int(0)
+int(25)
+string(19) "ab12 ab12 ab12 ab12"
+int(44)
+bool(false)
+
+-- Testing fgets() with file opened using mode a+ --
+-- File content type : numeric --
+-- fgets() with location set by fseek() with default length --
+int(0)
+int(5)
+string(45) "222222222222222222222222222222222222222222222"
+int(50)
+bool(true)
+-- fgets() with location set by fseek() with length = 20 --
+int(0)
+int(25)
+string(19) "2222222222222222222"
+int(44)
+bool(false)
+-- File content type : text --
+-- fgets() with location set by fseek() with default length --
+int(0)
+int(5)
+string(45) "text text text text text text text text text "
+int(50)
+bool(true)
+-- fgets() with location set by fseek() with length = 20 --
+int(0)
+int(25)
+string(19) "text text text text"
+int(44)
+bool(false)
+-- File content type : text_with_new_line --
+-- fgets() with location set by fseek() with default length --
+int(0)
+int(5)
+string(13) "line of text
+"
+int(18)
+bool(false)
+-- fgets() with location set by fseek() with length = 20 --
+int(0)
+int(25)
+string(11) "ne of text
+"
+int(36)
+bool(false)
+-- File content type : alphanumeric --
+-- fgets() with location set by fseek() with default length --
+int(0)
+int(5)
+string(45) "ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 "
+int(50)
+bool(true)
+-- fgets() with location set by fseek() with length = 20 --
+int(0)
+int(25)
+string(19) "ab12 ab12 ab12 ab12"
+int(44)
+bool(false)
+
+-- Testing fgets() with file opened using mode a+b --
+-- File content type : numeric --
+-- fgets() with location set by fseek() with default length --
+int(0)
+int(5)
+string(45) "222222222222222222222222222222222222222222222"
+int(50)
+bool(true)
+-- fgets() with location set by fseek() with length = 20 --
+int(0)
+int(25)
+string(19) "2222222222222222222"
+int(44)
+bool(false)
+-- File content type : text --
+-- fgets() with location set by fseek() with default length --
+int(0)
+int(5)
+string(45) "text text text text text text text text text "
+int(50)
+bool(true)
+-- fgets() with location set by fseek() with length = 20 --
+int(0)
+int(25)
+string(19) "text text text text"
+int(44)
+bool(false)
+-- File content type : text_with_new_line --
+-- fgets() with location set by fseek() with default length --
+int(0)
+int(5)
+string(13) "line of text
+"
+int(18)
+bool(false)
+-- fgets() with location set by fseek() with length = 20 --
+int(0)
+int(25)
+string(11) "ne of text
+"
+int(36)
+bool(false)
+-- File content type : alphanumeric --
+-- fgets() with location set by fseek() with default length --
+int(0)
+int(5)
+string(45) "ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 "
+int(50)
+bool(true)
+-- fgets() with location set by fseek() with length = 20 --
+int(0)
+int(25)
+string(19) "ab12 ab12 ab12 ab12"
+int(44)
+bool(false)
+
+-- Testing fgets() with file opened using mode a+t --
+-- File content type : numeric --
+-- fgets() with location set by fseek() with default length --
+int(0)
+int(5)
+string(45) "222222222222222222222222222222222222222222222"
+int(50)
+bool(true)
+-- fgets() with location set by fseek() with length = 20 --
+int(0)
+int(25)
+string(19) "2222222222222222222"
+int(44)
+bool(false)
+-- File content type : text --
+-- fgets() with location set by fseek() with default length --
+int(0)
+int(5)
+string(45) "text text text text text text text text text "
+int(50)
+bool(true)
+-- fgets() with location set by fseek() with length = 20 --
+int(0)
+int(25)
+string(19) "text text text text"
+int(44)
+bool(false)
+-- File content type : text_with_new_line --
+-- fgets() with location set by fseek() with default length --
+int(0)
+int(5)
+string(1) "
+"
+int(6)
+bool(false)
+-- fgets() with location set by fseek() with length = 20 --
+int(0)
+int(25)
+string(12) "ine of text
+"
+int(37)
+bool(false)
+-- File content type : alphanumeric --
+-- fgets() with location set by fseek() with default length --
+int(0)
+int(5)
+string(45) "ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 "
+int(50)
+bool(true)
+-- fgets() with location set by fseek() with length = 20 --
+int(0)
+int(25)
+string(19) "ab12 ab12 ab12 ab12"
+int(44)
+bool(false)
+
+-- Testing fgets() with file opened using mode x+ --
+-- File content type : numeric --
+-- fgets() with location set by fseek() with default length --
+int(0)
+int(5)
+string(45) "222222222222222222222222222222222222222222222"
+int(50)
+bool(true)
+-- fgets() with location set by fseek() with length = 20 --
+int(0)
+int(25)
+string(19) "2222222222222222222"
+int(44)
+bool(false)
+-- File content type : text --
+-- fgets() with location set by fseek() with default length --
+int(0)
+int(5)
+string(45) "text text text text text text text text text "
+int(50)
+bool(true)
+-- fgets() with location set by fseek() with length = 20 --
+int(0)
+int(25)
+string(19) "text text text text"
+int(44)
+bool(false)
+-- File content type : text_with_new_line --
+-- fgets() with location set by fseek() with default length --
+int(0)
+int(5)
+string(13) "line of text
+"
+int(18)
+bool(false)
+-- fgets() with location set by fseek() with length = 20 --
+int(0)
+int(25)
+string(11) "ne of text
+"
+int(36)
+bool(false)
+-- File content type : alphanumeric --
+-- fgets() with location set by fseek() with default length --
+int(0)
+int(5)
+string(45) "ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 "
+int(50)
+bool(true)
+-- fgets() with location set by fseek() with length = 20 --
+int(0)
+int(25)
+string(19) "ab12 ab12 ab12 ab12"
+int(44)
+bool(false)
+
+-- Testing fgets() with file opened using mode x+b --
+-- File content type : numeric --
+-- fgets() with location set by fseek() with default length --
+int(0)
+int(5)
+string(45) "222222222222222222222222222222222222222222222"
+int(50)
+bool(true)
+-- fgets() with location set by fseek() with length = 20 --
+int(0)
+int(25)
+string(19) "2222222222222222222"
+int(44)
+bool(false)
+-- File content type : text --
+-- fgets() with location set by fseek() with default length --
+int(0)
+int(5)
+string(45) "text text text text text text text text text "
+int(50)
+bool(true)
+-- fgets() with location set by fseek() with length = 20 --
+int(0)
+int(25)
+string(19) "text text text text"
+int(44)
+bool(false)
+-- File content type : text_with_new_line --
+-- fgets() with location set by fseek() with default length --
+int(0)
+int(5)
+string(13) "line of text
+"
+int(18)
+bool(false)
+-- fgets() with location set by fseek() with length = 20 --
+int(0)
+int(25)
+string(11) "ne of text
+"
+int(36)
+bool(false)
+-- File content type : alphanumeric --
+-- fgets() with location set by fseek() with default length --
+int(0)
+int(5)
+string(45) "ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 "
+int(50)
+bool(true)
+-- fgets() with location set by fseek() with length = 20 --
+int(0)
+int(25)
+string(19) "ab12 ab12 ab12 ab12"
+int(44)
+bool(false)
+
+-- Testing fgets() with file opened using mode x+t --
+-- File content type : numeric --
+-- fgets() with location set by fseek() with default length --
+int(0)
+int(5)
+string(45) "222222222222222222222222222222222222222222222"
+int(50)
+bool(true)
+-- fgets() with location set by fseek() with length = 20 --
+int(0)
+int(25)
+string(19) "2222222222222222222"
+int(44)
+bool(false)
+-- File content type : text --
+-- fgets() with location set by fseek() with default length --
+int(0)
+int(5)
+string(45) "text text text text text text text text text "
+int(50)
+bool(true)
+-- fgets() with location set by fseek() with length = 20 --
+int(0)
+int(25)
+string(19) "text text text text"
+int(44)
+bool(false)
+-- File content type : text_with_new_line --
+-- fgets() with location set by fseek() with default length --
+int(0)
+int(5)
+string(1) "
+"
+int(6)
+bool(false)
+-- fgets() with location set by fseek() with length = 20 --
+int(0)
+int(25)
+string(12) "ine of text
+"
+int(37)
+bool(false)
+-- File content type : alphanumeric --
+-- fgets() with location set by fseek() with default length --
+int(0)
+int(5)
+string(45) "ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 ab12 "
+int(50)
+bool(true)
+-- fgets() with location set by fseek() with length = 20 --
+int(0)
+int(25)
+string(19) "ab12 ab12 ab12 ab12"
+int(44)
+bool(false)
+Done
diff --git a/ext/standard/tests/file/fgetss_basic2-win32-mb.phpt b/ext/standard/tests/file/fgetss_basic2-win32-mb.phpt
new file mode 100644
index 0000000000..7edf7bcbd1
--- /dev/null
+++ b/ext/standard/tests/file/fgetss_basic2-win32-mb.phpt
@@ -0,0 +1,216 @@
+--TEST--
+Test fgetss() function : Basic functionality - read/write modes
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die('skip.. only on Windows');
+}
+?>
+--FILE--
+<?php
+/*
+ Prototype: string fgetss ( resource $handle [, int $length [, string $allowable_tags]] );
+ Description: Gets line from file pointer and strip HTML tags
+*/
+
+/* try fgetss on files which are opened in read/write modes
+ w+, w+b, w+t,
+ a+, a+b, a+t,
+ x+, x+b, x+t
+*/
+
+
+echo "*** Testing fgetss() : basic operations ***\n";
+
+/* string with html and php tags */
+$string_with_tags = <<<EOT
+<test>Testing fgetss() functions</test>
+<?php echo "this string is within php tag"; ?> {;}<{> this
+is a heredoc string. <pg>ksklnm@@$$&$&^%&^%&^%&</pg>
+<html> html </html> <?php echo "php"; ?>
+EOT;
+if(substr(PHP_OS, 0, 3) == "WIN") {
+ $string_with_tags = str_replace("\r",'', $string_with_tags);
+}
+$filename = dirname(__FILE__)."/fgetss_basic2私はガラスを食べられます.tmp";
+
+/* try reading the file opened in different modes of reading */
+$file_modes = array("w+","w+b", "w+t","a+", "a+b", "a+t","x+","x+b","x+t");
+
+for($mode_counter = 0; $mode_counter < count($file_modes); $mode_counter++) {
+ echo "\n-- Testing fgetss() with file opened using $file_modes[$mode_counter] mode --\n";
+
+ /* create an empty file and write the strings with tags */
+ $file_handle = fopen($filename, $file_modes[$mode_counter]);
+ fwrite($file_handle,$string_with_tags); //writing data to the file
+ if(!$file_handle) {
+ echo "Error: failed to open file $filename!\n";
+ exit();
+ }
+
+ // rewind the file pointer to beginning of the file
+ var_dump( filesize($filename) );
+ var_dump( rewind($file_handle) );
+ var_dump( ftell($file_handle) );
+ var_dump( feof($file_handle) );
+
+ /* read entire file and strip tags */
+ echo "-- fgetss() with default length, file pointer at 0 --\n";
+ var_dump( fgetss($file_handle) ); // no length and allowable tags provided, reads entire file
+ var_dump( ftell($file_handle) );
+ var_dump( feof($file_handle) );
+
+ rewind($file_handle);
+ /* read entire file and strip tags tags */
+ echo "-- fgets() with length = 30, file pointer at 0 --\n";
+ var_dump( fgetss($file_handle ,30) ); // length parameter given,not reading entire file
+ var_dump( ftell($file_handle) ); // checking file pointer position initially
+ var_dump( feof($file_handle) ); // confirm file pointer is not at eof
+
+ // close the file
+ fclose($file_handle);
+
+ // delete the file
+ unlink($filename);
+} // end of for - mode_counter
+
+echo "Done\n";
+?>
+--EXPECTF--
+*** Testing fgetss() : basic operations ***
+
+-- Testing fgetss() with file opened using w+ mode --
+int(192)
+bool(true)
+int(0)
+bool(false)
+-- fgetss() with default length, file pointer at 0 --
+string(27) "Testing fgetss() functions
+"
+int(40)
+bool(false)
+-- fgets() with length = 30, file pointer at 0 --
+string(23) "Testing fgetss() functi"
+int(29)
+bool(false)
+
+-- Testing fgetss() with file opened using w+b mode --
+int(192)
+bool(true)
+int(0)
+bool(false)
+-- fgetss() with default length, file pointer at 0 --
+string(27) "Testing fgetss() functions
+"
+int(40)
+bool(false)
+-- fgets() with length = 30, file pointer at 0 --
+string(23) "Testing fgetss() functi"
+int(29)
+bool(false)
+
+-- Testing fgetss() with file opened using w+t mode --
+int(195)
+bool(true)
+int(0)
+bool(false)
+-- fgetss() with default length, file pointer at 0 --
+string(27) "Testing fgetss() functions
+"
+int(40)
+bool(false)
+-- fgets() with length = 30, file pointer at 0 --
+string(23) "Testing fgetss() functi"
+int(29)
+bool(false)
+
+-- Testing fgetss() with file opened using a+ mode --
+int(192)
+bool(true)
+int(0)
+bool(false)
+-- fgetss() with default length, file pointer at 0 --
+string(27) "Testing fgetss() functions
+"
+int(40)
+bool(false)
+-- fgets() with length = 30, file pointer at 0 --
+string(23) "Testing fgetss() functi"
+int(29)
+bool(false)
+
+-- Testing fgetss() with file opened using a+b mode --
+int(192)
+bool(true)
+int(0)
+bool(false)
+-- fgetss() with default length, file pointer at 0 --
+string(27) "Testing fgetss() functions
+"
+int(40)
+bool(false)
+-- fgets() with length = 30, file pointer at 0 --
+string(23) "Testing fgetss() functi"
+int(29)
+bool(false)
+
+-- Testing fgetss() with file opened using a+t mode --
+int(195)
+bool(true)
+int(0)
+bool(false)
+-- fgetss() with default length, file pointer at 0 --
+string(27) "Testing fgetss() functions
+"
+int(40)
+bool(false)
+-- fgets() with length = 30, file pointer at 0 --
+string(23) "Testing fgetss() functi"
+int(29)
+bool(false)
+
+-- Testing fgetss() with file opened using x+ mode --
+int(192)
+bool(true)
+int(0)
+bool(false)
+-- fgetss() with default length, file pointer at 0 --
+string(27) "Testing fgetss() functions
+"
+int(40)
+bool(false)
+-- fgets() with length = 30, file pointer at 0 --
+string(23) "Testing fgetss() functi"
+int(29)
+bool(false)
+
+-- Testing fgetss() with file opened using x+b mode --
+int(192)
+bool(true)
+int(0)
+bool(false)
+-- fgetss() with default length, file pointer at 0 --
+string(27) "Testing fgetss() functions
+"
+int(40)
+bool(false)
+-- fgets() with length = 30, file pointer at 0 --
+string(23) "Testing fgetss() functi"
+int(29)
+bool(false)
+
+-- Testing fgetss() with file opened using x+t mode --
+int(195)
+bool(true)
+int(0)
+bool(false)
+-- fgetss() with default length, file pointer at 0 --
+string(27) "Testing fgetss() functions
+"
+int(40)
+bool(false)
+-- fgets() with length = 30, file pointer at 0 --
+string(23) "Testing fgetss() functi"
+int(29)
+bool(false)
+Done
diff --git a/ext/standard/tests/file/file_get_contents_variation5_32bit.phpt b/ext/standard/tests/file/file_get_contents_variation5_32bit.phpt
new file mode 100644
index 0000000000..3afc3dc180
--- /dev/null
+++ b/ext/standard/tests/file/file_get_contents_variation5_32bit.phpt
@@ -0,0 +1,236 @@
+--TEST--
+Test file_get_contents() function : usage variation
+--CREDITS--
+Dave Kelsey <d_kelsey@uk.ibm.com>
+--SKIPIF--
+<?php if (PHP_INT_SIZE != 4) die("skip this test is for 32-bit only");
+--FILE--
+<?php
+/* Prototype : string file_get_contents(string filename [, bool use_include_path [, resource context [, long offset [, long maxlen]]]])
+ * Description: Read the entire file into a string
+ * Source code: ext/standard/file.c
+ * Alias to functions:
+ */
+
+echo "*** Testing file_get_contents() : usage variation ***\n";
+
+// Define error handler
+function test_error_handler($err_no, $err_msg, $filename, $linenum, $vars) {
+ if (error_reporting() != 0) {
+ // report non-silenced errors
+ echo "Error: $err_no - $err_msg, $filename($linenum)\n";
+ }
+}
+set_error_handler('test_error_handler');
+
+// Initialise function arguments not being substituted (if any)
+$filename = 'FileGetContentsVar5.tmp';
+$absFile = dirname(__FILE__).'/'.$filename;
+$h = fopen($absFile,"w");
+fwrite($h, b"contents read");
+fclose($h);
+
+
+//get an unset variable
+$unset_var = 10;
+unset ($unset_var);
+
+// define some classes
+class classWithToString
+{
+ public function __toString() {
+ return "Class A object";
+ }
+}
+
+class classWithoutToString
+{
+}
+
+// heredoc string
+$heredoc = <<<EOT
+hello world
+EOT;
+
+// add arrays
+$index_array = array (1, 2, 3);
+$assoc_array = array ('one' => 1, 'two' => 2);
+
+//array of values to iterate over
+$inputs = array(
+
+ // int data
+ 'int 0' => 0,
+ 'int 1' => 1,
+ 'int 12345' => 12345,
+ 'int -12345' => -12345,
+ 'int -10' => -10,
+
+ // float data
+ 'float 10.5' => 10.5,
+ 'float -10.5' => -10.5,
+ 'float -22.5' => -22.5,
+ 'float 12.3456789000e10' => 12.3456789000e10,
+ 'float -12.3456789000e10' => -12.3456789000e10,
+ 'float .5' => .5,
+
+ // array data
+ 'empty array' => array(),
+ 'int indexed array' => $index_array,
+ 'associative array' => $assoc_array,
+ 'nested arrays' => array('foo', $index_array, $assoc_array),
+
+ // null data
+ 'uppercase NULL' => NULL,
+ 'lowercase null' => null,
+
+ // boolean data
+ 'lowercase true' => true,
+ 'lowercase false' =>false,
+ 'uppercase TRUE' =>TRUE,
+ 'uppercase FALSE' =>FALSE,
+
+ // empty data
+ 'empty string DQ' => "",
+ 'empty string SQ' => '',
+
+ // string data
+ 'string DQ' => "string",
+ 'string SQ' => 'string',
+ 'mixed case string' => "sTrInG",
+ 'heredoc' => $heredoc,
+
+ // object data
+ 'instance of classWithToString' => new classWithToString(),
+ 'instance of classWithoutToString' => new classWithoutToString(),
+
+ // undefined data
+ 'undefined var' => @$undefined_var,
+
+ // unset data
+ 'unset var' => @$unset_var,
+);
+
+// loop through each element of the array for offset
+
+foreach($inputs as $key =>$value) {
+ echo "\n--$key--\n";
+ var_dump( file_get_contents($absFile, false, null, $value) );
+};
+
+unlink($absFile);
+
+?>
+===DONE===
+--EXPECTF--
+*** Testing file_get_contents() : usage variation ***
+
+--int 0--
+string(%d) "contents read"
+
+--int 1--
+string(%d) "ontents read"
+
+--int 12345--
+string(%d) ""
+
+--int -12345--
+Error: 2 - file_get_contents(): Failed to seek to position -12345 in the stream, %s(%d)
+bool(false)
+
+--int -10--
+string(10) "tents read"
+
+--float 10.5--
+string(3) "ead"
+
+--float -10.5--
+string(10) "tents read"
+
+--float -22.5--
+Error: 2 - file_get_contents(): Failed to seek to position -22 in the stream, %s(%d)
+bool(false)
+
+--float 12.3456789000e10--
+Error: 2 - file_get_contents() expects parameter 4 to be integer, float given, %s(%d)
+NULL
+
+--float -12.3456789000e10--
+Error: 2 - file_get_contents() expects parameter 4 to be integer, float given, %s(%d)
+NULL
+
+--float .5--
+string(%d) "contents read"
+
+--empty array--
+Error: 2 - file_get_contents() expects parameter 4 to be integer, array given, %s(%d)
+NULL
+
+--int indexed array--
+Error: 2 - file_get_contents() expects parameter 4 to be integer, array given, %s(%d)
+NULL
+
+--associative array--
+Error: 2 - file_get_contents() expects parameter 4 to be integer, array given, %s(%d)
+NULL
+
+--nested arrays--
+Error: 2 - file_get_contents() expects parameter 4 to be integer, array given, %s(%d)
+NULL
+
+--uppercase NULL--
+string(%d) "contents read"
+
+--lowercase null--
+string(%d) "contents read"
+
+--lowercase true--
+string(12) "ontents read"
+
+--lowercase false--
+string(%d) "contents read"
+
+--uppercase TRUE--
+string(12) "ontents read"
+
+--uppercase FALSE--
+string(%d) "contents read"
+
+--empty string DQ--
+Error: 2 - file_get_contents() expects parameter 4 to be integer, %unicode_string_optional% given, %s(%d)
+NULL
+
+--empty string SQ--
+Error: 2 - file_get_contents() expects parameter 4 to be integer, %unicode_string_optional% given, %s(%d)
+NULL
+
+--string DQ--
+Error: 2 - file_get_contents() expects parameter 4 to be integer, %unicode_string_optional% given, %s(%d)
+NULL
+
+--string SQ--
+Error: 2 - file_get_contents() expects parameter 4 to be integer, %unicode_string_optional% given, %s(%d)
+NULL
+
+--mixed case string--
+Error: 2 - file_get_contents() expects parameter 4 to be integer, %unicode_string_optional% given, %s(%d)
+NULL
+
+--heredoc--
+Error: 2 - file_get_contents() expects parameter 4 to be integer, %unicode_string_optional% given, %s(%d)
+NULL
+
+--instance of classWithToString--
+Error: 2 - file_get_contents() expects parameter 4 to be integer, object given, %s(%d)
+NULL
+
+--instance of classWithoutToString--
+Error: 2 - file_get_contents() expects parameter 4 to be integer, object given, %s(%d)
+NULL
+
+--undefined var--
+string(%d) "contents read"
+
+--unset var--
+string(%d) "contents read"
+===DONE===
diff --git a/ext/standard/tests/file/file_get_contents_variation5.phpt b/ext/standard/tests/file/file_get_contents_variation5_64bit.phpt
index aa13d69eb8..df33059f30 100644
--- a/ext/standard/tests/file/file_get_contents_variation5.phpt
+++ b/ext/standard/tests/file/file_get_contents_variation5_64bit.phpt
@@ -63,11 +63,13 @@ $inputs = array(
'int 0' => 0,
'int 1' => 1,
'int 12345' => 12345,
- 'int -12345' => -2345,
+ 'int -12345' => -12345,
+ 'int -10' => -10,
// float data
'float 10.5' => 10.5,
'float -10.5' => -10.5,
+ 'float -22.5' => -22.5,
'float 12.3456789000e10' => 12.3456789000e10,
'float -12.3456789000e10' => -12.3456789000e10,
'float .5' => .5,
@@ -133,19 +135,28 @@ string(%d) "ontents read"
string(%d) ""
--int -12345--
-string(%d) "contents read"
+Error: 2 - file_get_contents(): Failed to seek to position -12345 in the stream, %s(%d)
+bool(false)
+
+--int -10--
+string(10) "tents read"
--float 10.5--
string(3) "ead"
--float -10.5--
-string(%d) "contents read"
+string(10) "tents read"
+
+--float -22.5--
+Error: 2 - file_get_contents(): Failed to seek to position -22 in the stream, %s(%d)
+bool(false)
--float 12.3456789000e10--
string(%d) %s
--float -12.3456789000e10--
-string(%d) %s
+Error: 2 - file_get_contents(): Failed to seek to position -123456789000 in the stream, %s(%d)
+bool(false)
--float .5--
string(%d) "contents read"
diff --git a/ext/standard/tests/file/file_get_contents_variation7-win32-mb.phpt b/ext/standard/tests/file/file_get_contents_variation7-win32-mb.phpt
new file mode 100644
index 0000000000..cc8eb40f49
--- /dev/null
+++ b/ext/standard/tests/file/file_get_contents_variation7-win32-mb.phpt
@@ -0,0 +1,115 @@
+--TEST--
+Test file_get_contents() function : variation - various absolute and relative paths
+--CREDITS--
+Dave Kelsey <d_kelsey@uk.ibm.com>
+--SKIPIF--
+<?php
+if(substr(PHP_OS, 0, 3) != "WIN")
+ die("skip Only run on Windows");
+?>
+--FILE--
+<?php
+/* Prototype : string file_get_contents(string filename [, bool use_include_path [, resource context [, long offset [, long maxlen]]]])
+ * Description: Read the entire file into a string
+ * Source code: ext/standard/file.c
+ * Alias to functions:
+ */
+
+echo "*** Testing file_get_contents() : variation ***\n";
+$mainDir = "fileGetContentsVar7私はガラスを食べられます.dir";
+$subDir = "fileGetContentsVar7Sub私はガラスを食べられます";
+$absMainDir = dirname(__FILE__)."\\".$mainDir;
+mkdir($absMainDir);
+$absSubDir = $absMainDir."\\".$subDir;
+mkdir($absSubDir);
+
+$old_dir_path = getcwd();
+chdir(dirname(__FILE__));
+$unixifiedDir = '/'.substr(str_replace('\\','/',$absSubDir),3);
+
+$allDirs = array(
+ // absolute paths
+ "$absSubDir\\",
+ "$absSubDir\\..\\".$subDir,
+ "$absSubDir\\\\..\\.\\".$subDir,
+ "$absSubDir\\..\\..\\".$mainDir."\\.\\".$subDir,
+ "$absSubDir\\..\\\\\\".$subDir."\\\\..\\\\..\\".$subDir,
+ "$absSubDir\\BADDIR",
+
+ // relative paths
+ $mainDir."\\".$subDir,
+ $mainDir."\\\\".$subDir,
+ $mainDir."\\\\\\".$subDir,
+ ".\\".$mainDir."\\..\\".$mainDir."\\".$subDir,
+ "BADDIR",
+
+ // unixifed path
+ $unixifiedDir,
+);
+
+$filename = 'FileGetContentsVar7.tmp';
+$absFile = $absSubDir.'/'.$filename;
+$h = fopen($absFile,"w");
+fwrite($h, "contents read");
+fclose($h);
+
+for($i = 0; $i<count($allDirs); $i++) {
+ $j = $i+1;
+ $dir = $allDirs[$i];
+ echo "\n-- Iteration $j --\n";
+ var_dump(file_get_contents($dir."\\".$filename));
+}
+
+unlink($absFile);
+chdir($old_dir_path);
+rmdir($absSubDir);
+rmdir($absMainDir);
+
+echo "\n*** Done ***\n";
+?>
+--EXPECTF--
+*** Testing file_get_contents() : variation ***
+
+-- Iteration 1 --
+string(%d) "contents read"
+
+-- Iteration 2 --
+string(%d) "contents read"
+
+-- Iteration 3 --
+string(%d) "contents read"
+
+-- Iteration 4 --
+string(%d) "contents read"
+
+-- Iteration 5 --
+
+Warning: file_get_contents(%sfileGetContentsVar7私はガラスを食べられます.dir\fileGetContentsVar7Sub私はガラスを食べられます\..\\\fileGetContentsVar7Sub私はガラスを食べられます\\..\\..\fileGetContentsVar7Sub私はガラスを食べられます\FileGetContentsVar7.tmp): failed to open stream: No such file or directory in %s on line %d
+bool(false)
+
+-- Iteration 6 --
+
+Warning: file_get_contents(%sfileGetContentsVar7私はガラスを食べられます.dir\fileGetContentsVar7Sub私はガラスを食べられます\BADDIR\FileGetContentsVar7.tmp): failed to open stream: No such file or directory in %s on line %d
+bool(false)
+
+-- Iteration 7 --
+string(%d) "contents read"
+
+-- Iteration 8 --
+string(%d) "contents read"
+
+-- Iteration 9 --
+string(%d) "contents read"
+
+-- Iteration 10 --
+string(%d) "contents read"
+
+-- Iteration 11 --
+
+Warning: file_get_contents(BADDIR\FileGetContentsVar7.tmp): failed to open stream: No such file or directory in %s on line %d
+bool(false)
+
+-- Iteration 12 --
+string(%d) "contents read"
+
+*** Done ***
diff --git a/ext/standard/tests/file/file_put_contents_variation7-win32.phpt b/ext/standard/tests/file/file_put_contents_variation7-win32.phpt
index d7bfdf9233..e1a94a2043 100644
--- a/ext/standard/tests/file/file_put_contents_variation7-win32.phpt
+++ b/ext/standard/tests/file/file_put_contents_variation7-win32.phpt
@@ -59,10 +59,10 @@ for($i = 0; $i<count($allDirs); $i++) {
$j = $i+1;
$dir = $allDirs[$i];
echo "\n-- Iteration $j --\n";
- $res = file_put_contents($dir."\\".$filename, ($data + $i));
+ $res = file_put_contents($dir."\\".$filename, ($data . $i));
if ($res !== false) {
$in = file_get_contents($absFile);
- if ($in == ($data + $i)) {
+ if ($in == ($data . $i)) {
echo "Data written correctly\n";
}
else {
@@ -127,4 +127,4 @@ No data written
-- Iteration 12 --
Data written correctly
-*** Done *** \ No newline at end of file
+*** Done ***
diff --git a/ext/standard/tests/file/file_put_contents_variation7.phpt b/ext/standard/tests/file/file_put_contents_variation7.phpt
index 5c8e5f3602..b1b2face60 100644
--- a/ext/standard/tests/file/file_put_contents_variation7.phpt
+++ b/ext/standard/tests/file/file_put_contents_variation7.phpt
@@ -51,10 +51,10 @@ for($i = 0; $i<count($allDirs); $i++) {
$j = $i+1;
$dir = $allDirs[$i];
echo "\n-- Iteration $j --\n";
- $res = file_put_contents($dir."/".$filename, ($data + $i));
+ $res = file_put_contents($dir."/".$filename, ($data . $i));
if ($res !== false) {
$in = file_get_contents($absFile);
- if ($in == ($data + $i)) {
+ if ($in == ($data . $i)) {
echo "Data written correctly\n";
}
else {
@@ -116,4 +116,4 @@ Data written correctly
Warning: file_put_contents(BADDIR/FileGetContentsVar7.tmp): failed to open stream: %s in %s on line %d
No data written
-*** Done *** \ No newline at end of file
+*** Done ***
diff --git a/ext/standard/tests/file/file_variation5-win32-mb.phpt b/ext/standard/tests/file/file_variation5-win32-mb.phpt
new file mode 100644
index 0000000000..783efd6624
--- /dev/null
+++ b/ext/standard/tests/file/file_variation5-win32-mb.phpt
@@ -0,0 +1,74 @@
+--TEST--
+file() with various paths
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die('skip windows only test');
+}
+?>
+--FILE--
+<?php
+
+$script_directory = dirname(__FILE__);
+chdir($script_directory);
+$test_dirname = basename(__FILE__, ".php") . "私はガラスを食べられますtestdir";
+mkdir($test_dirname);
+
+$filepath = __FILE__ . ".tmp";
+$filename = basename($filepath);
+$fd = fopen($filepath, "w+");
+fwrite($fd, "Line 1\nLine 2\nLine 3");
+fclose($fd);
+
+echo "file() on a path containing .. and .\n";
+var_dump(file("./$test_dirname/../$filename"));
+
+echo "\nfile() on a path containing .. with invalid directories\n";
+var_dump(file("./$test_dirname/bad_dir/../../$filename"));
+
+echo "\nfile() on a relative path from a different working directory\n";
+chdir($test_dirname);
+var_dump(file("../$filename"));
+chdir($script_directory);
+
+chdir($script_directory);
+unlink($filepath);
+rmdir($test_dirname);
+
+?>
+--EXPECT--
+file() on a path containing .. and .
+array(3) {
+ [0]=>
+ string(7) "Line 1
+"
+ [1]=>
+ string(7) "Line 2
+"
+ [2]=>
+ string(6) "Line 3"
+}
+
+file() on a path containing .. with invalid directories
+array(3) {
+ [0]=>
+ string(7) "Line 1
+"
+ [1]=>
+ string(7) "Line 2
+"
+ [2]=>
+ string(6) "Line 3"
+}
+
+file() on a relative path from a different working directory
+array(3) {
+ [0]=>
+ string(7) "Line 1
+"
+ [1]=>
+ string(7) "Line 2
+"
+ [2]=>
+ string(6) "Line 3"
+}
diff --git a/ext/standard/tests/file/filesize_variation1-win32-mb.phpt b/ext/standard/tests/file/filesize_variation1-win32-mb.phpt
new file mode 100644
index 0000000000..14975feb49
--- /dev/null
+++ b/ext/standard/tests/file/filesize_variation1-win32-mb.phpt
@@ -0,0 +1,45 @@
+--TEST--
+Test filesize() function: usage variations - size of files
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die('skip only valid for Windows');
+}
+--FILE--
+<?php
+/*
+ Prototype : int filesize ( string $filename );
+ Description : Returns the size of the file in bytes, or FALSE
+ (and generates an error of level E_WARNING) in case of an error.
+*/
+
+$file_path = dirname(__FILE__);
+require($file_path."/file.inc");
+
+echo "*** Testing filesize(): usage variations ***\n";
+
+echo "*** Checking filesize() with different size of files ***\n";
+for($size = 1; $size <10000; $size = $size+1000)
+{
+ create_files($file_path, 1, "numeric", 0755, $size, "w", "私はガラスを食べられますfilesize_variation");
+ var_dump( filesize( $file_path."/私はガラスを食べられますfilesize_variation1.tmp") );
+ clearstatcache();
+ delete_files($file_path, 1, "私はガラスを食べられますfilesize_variation");
+}
+
+echo "*** Done ***\n";
+?>
+--EXPECTF--
+*** Testing filesize(): usage variations ***
+*** Checking filesize() with different size of files ***
+int(1024)
+int(1025024)
+int(2049024)
+int(3073024)
+int(4097024)
+int(5121024)
+int(6145024)
+int(7169024)
+int(8193024)
+int(9217024)
+*** Done ***
diff --git a/ext/standard/tests/file/filesize_variation3-win32.phpt b/ext/standard/tests/file/filesize_variation3-win32.phpt
index f46695954c..ab38c7d1c3 100644
--- a/ext/standard/tests/file/filesize_variation3-win32.phpt
+++ b/ext/standard/tests/file/filesize_variation3-win32.phpt
@@ -65,6 +65,8 @@ bool(true)
int(1200)
bool(true)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
*** Done ***
diff --git a/ext/standard/tests/file/filesize_variation3.phpt b/ext/standard/tests/file/filesize_variation3.phpt
index 3ae06fa871..67d46998d2 100644
--- a/ext/standard/tests/file/filesize_variation3.phpt
+++ b/ext/standard/tests/file/filesize_variation3.phpt
@@ -65,6 +65,8 @@ bool(true)
int(1200)
bool(true)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
*** Done ***
diff --git a/ext/standard/tests/file/fread_variation3-win32-mb.phpt b/ext/standard/tests/file/fread_variation3-win32-mb.phpt
new file mode 100644
index 0000000000..a39842ce5b
--- /dev/null
+++ b/ext/standard/tests/file/fread_variation3-win32-mb.phpt
@@ -0,0 +1,498 @@
+--TEST--
+Test fread() function : usage variations - read beyond file size, read/write mode
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die('skip...only valid for Windows');
+}
+?>
+--FILE--
+<?php
+/*
+ Prototype: string fread ( resource $handle [, int $length] );
+ Description: reads up to length bytes from the file pointer referenced by handle.
+ Reading stops when up to length bytes have been read, EOF (end of file) is
+ reached, (for network streams) when a packet becomes available, or (after
+ opening userspace stream) when 8192 bytes have been read whichever comes first.
+*/
+
+// include the file.inc for common functions for test
+include ("file.inc");
+
+/* Function : function check_read(resource $file_handle, int $read_size, int $expect_size)
+ Description : Read data from file of size $read_size and verifies that $expected_size no. of
+ bytes are read.
+ $file_handle : File Handle
+ $read_size : No. of bytes to be read.
+ $expect_size : Expected data length
+ Returns: returns the data read
+*/
+function check_read($file_handle, $read_size, $expect_size) {
+ // print file pointer position before read
+ var_dump( ftell($file_handle) );
+ var_dump( feof($file_handle) );
+
+ // read the data of size $read_size
+ echo "Reading $read_size bytes from file, expecting $expect_size bytes ... ";
+ $data_from_file = fread($file_handle, $read_size);
+
+ // check if data read is of expected size
+ if ( strlen($data_from_file) == $expect_size)
+ echo "OK\n";
+ else
+ echo "Error reading file, total number of bytes read = ".strlen($data_from_file)."\n";
+
+ // file pointer position after read
+ var_dump( ftell($file_handle) );
+ // check if file pointer at eof()
+ var_dump( feof($file_handle) );
+
+ return $data_from_file;
+}
+
+echo "*** Testing fread() : usage variations ***\n";
+
+$file_modes = array("a+","a+b","a+t",
+ "w+","w+b","w+t",
+ "x+","x+b","x+t");
+
+$file_content_types = array("numeric","text","text_with_new_line");
+
+foreach($file_content_types as $file_content_type) {
+ echo "\n-- Testing fread() with file having content of type ". $file_content_type ." --\n";
+
+ /* open the file using $files_modes and perform fread() on it */
+ foreach($file_modes as $file_mode) {
+ if(!strstr($file_mode,"x")){
+ /* create files with $file_content_type */
+ create_files ( dirname(__FILE__), 1, $file_content_type, 0755, 1, "w", "私はガラスを食べられますfread_variation", 3);
+ }
+
+ $filename = dirname(__FILE__)."/私はガラスを食べられますfread_variation3.tmp"; // this is name of the file created by create_files()
+ echo "-- File opened in mode ".$file_mode." --\n";
+ $file_handle = fopen($filename, $file_mode);
+ if (!$file_handle) {
+ echo "Error: failed to fopen() file: $filename!";
+ exit();
+ }
+
+ if(strstr($file_mode,"w") || strstr($file_mode,"x") ) {
+ $data_to_be_written="";
+ fill_file($file_handle, $file_content_type, 1024);
+ }
+
+ rewind($file_handle);
+
+ // read file by giving size more than its size
+ echo "-- Reading beyond filesize, expeceted : 1024 bytes --\n";
+ rewind($file_handle);
+ $data_from_file = check_read($file_handle, 1030, ( strstr($file_mode, "+") ? 1024 : 1024) );
+ if ( $data_from_file != false)
+ var_dump( md5($data_from_file) );
+
+ rewind($file_handle);
+ echo "-- Reading beyond filesize when file pointer pointing to EOF, expeceted : 0 bytes --\n";
+ // try fread when file pointer at end
+ fseek($file_handle, 0, SEEK_END);
+ //reading file when file pointer at end
+ $data_from_file = check_read($file_handle, 10, 0);
+ if ( $data_from_file != false)
+ var_dump( md5($data_from_file) );
+
+ // now close the file
+ fclose($file_handle);
+
+ // delete the file created
+ delete_file($filename); // delete file
+ } // end of inner foreach loop
+}// end of outer foreach loop
+
+echo"Done\n";
+?>
+--EXPECTF--
+*** Testing fread() : usage variations ***
+
+-- Testing fread() with file having content of type numeric --
+-- File opened in mode a+ --
+-- Reading beyond filesize, expeceted : 1024 bytes --
+int(0)
+bool(false)
+Reading 1030 bytes from file, expecting 1024 bytes ... OK
+int(1024)
+bool(true)
+string(32) "950b7457d1deb6332f2fc5d42f3129d6"
+-- Reading beyond filesize when file pointer pointing to EOF, expeceted : 0 bytes --
+int(1024)
+bool(false)
+Reading 10 bytes from file, expecting 0 bytes ... OK
+int(1024)
+bool(true)
+-- File opened in mode a+b --
+-- Reading beyond filesize, expeceted : 1024 bytes --
+int(0)
+bool(false)
+Reading 1030 bytes from file, expecting 1024 bytes ... OK
+int(1024)
+bool(true)
+string(32) "950b7457d1deb6332f2fc5d42f3129d6"
+-- Reading beyond filesize when file pointer pointing to EOF, expeceted : 0 bytes --
+int(1024)
+bool(false)
+Reading 10 bytes from file, expecting 0 bytes ... OK
+int(1024)
+bool(true)
+-- File opened in mode a+t --
+-- Reading beyond filesize, expeceted : 1024 bytes --
+int(0)
+bool(false)
+Reading 1030 bytes from file, expecting 1024 bytes ... OK
+int(1024)
+bool(true)
+string(32) "950b7457d1deb6332f2fc5d42f3129d6"
+-- Reading beyond filesize when file pointer pointing to EOF, expeceted : 0 bytes --
+int(1024)
+bool(false)
+Reading 10 bytes from file, expecting 0 bytes ... OK
+int(1024)
+bool(true)
+-- File opened in mode w+ --
+-- Reading beyond filesize, expeceted : 1024 bytes --
+int(0)
+bool(false)
+Reading 1030 bytes from file, expecting 1024 bytes ... OK
+int(1024)
+bool(true)
+string(32) "950b7457d1deb6332f2fc5d42f3129d6"
+-- Reading beyond filesize when file pointer pointing to EOF, expeceted : 0 bytes --
+int(1024)
+bool(false)
+Reading 10 bytes from file, expecting 0 bytes ... OK
+int(1024)
+bool(true)
+-- File opened in mode w+b --
+-- Reading beyond filesize, expeceted : 1024 bytes --
+int(0)
+bool(false)
+Reading 1030 bytes from file, expecting 1024 bytes ... OK
+int(1024)
+bool(true)
+string(32) "950b7457d1deb6332f2fc5d42f3129d6"
+-- Reading beyond filesize when file pointer pointing to EOF, expeceted : 0 bytes --
+int(1024)
+bool(false)
+Reading 10 bytes from file, expecting 0 bytes ... OK
+int(1024)
+bool(true)
+-- File opened in mode w+t --
+-- Reading beyond filesize, expeceted : 1024 bytes --
+int(0)
+bool(false)
+Reading 1030 bytes from file, expecting 1024 bytes ... OK
+int(1024)
+bool(true)
+string(32) "950b7457d1deb6332f2fc5d42f3129d6"
+-- Reading beyond filesize when file pointer pointing to EOF, expeceted : 0 bytes --
+int(1024)
+bool(false)
+Reading 10 bytes from file, expecting 0 bytes ... OK
+int(1024)
+bool(true)
+-- File opened in mode x+ --
+-- Reading beyond filesize, expeceted : 1024 bytes --
+int(0)
+bool(false)
+Reading 1030 bytes from file, expecting 1024 bytes ... OK
+int(1024)
+bool(true)
+string(32) "950b7457d1deb6332f2fc5d42f3129d6"
+-- Reading beyond filesize when file pointer pointing to EOF, expeceted : 0 bytes --
+int(1024)
+bool(false)
+Reading 10 bytes from file, expecting 0 bytes ... OK
+int(1024)
+bool(true)
+-- File opened in mode x+b --
+-- Reading beyond filesize, expeceted : 1024 bytes --
+int(0)
+bool(false)
+Reading 1030 bytes from file, expecting 1024 bytes ... OK
+int(1024)
+bool(true)
+string(32) "950b7457d1deb6332f2fc5d42f3129d6"
+-- Reading beyond filesize when file pointer pointing to EOF, expeceted : 0 bytes --
+int(1024)
+bool(false)
+Reading 10 bytes from file, expecting 0 bytes ... OK
+int(1024)
+bool(true)
+-- File opened in mode x+t --
+-- Reading beyond filesize, expeceted : 1024 bytes --
+int(0)
+bool(false)
+Reading 1030 bytes from file, expecting 1024 bytes ... OK
+int(1024)
+bool(true)
+string(32) "950b7457d1deb6332f2fc5d42f3129d6"
+-- Reading beyond filesize when file pointer pointing to EOF, expeceted : 0 bytes --
+int(1024)
+bool(false)
+Reading 10 bytes from file, expecting 0 bytes ... OK
+int(1024)
+bool(true)
+
+-- Testing fread() with file having content of type text --
+-- File opened in mode a+ --
+-- Reading beyond filesize, expeceted : 1024 bytes --
+int(0)
+bool(false)
+Reading 1030 bytes from file, expecting 1024 bytes ... OK
+int(1024)
+bool(true)
+string(32) "e486000c4c8452774f746a27658d87fa"
+-- Reading beyond filesize when file pointer pointing to EOF, expeceted : 0 bytes --
+int(1024)
+bool(false)
+Reading 10 bytes from file, expecting 0 bytes ... OK
+int(1024)
+bool(true)
+-- File opened in mode a+b --
+-- Reading beyond filesize, expeceted : 1024 bytes --
+int(0)
+bool(false)
+Reading 1030 bytes from file, expecting 1024 bytes ... OK
+int(1024)
+bool(true)
+string(32) "e486000c4c8452774f746a27658d87fa"
+-- Reading beyond filesize when file pointer pointing to EOF, expeceted : 0 bytes --
+int(1024)
+bool(false)
+Reading 10 bytes from file, expecting 0 bytes ... OK
+int(1024)
+bool(true)
+-- File opened in mode a+t --
+-- Reading beyond filesize, expeceted : 1024 bytes --
+int(0)
+bool(false)
+Reading 1030 bytes from file, expecting 1024 bytes ... OK
+int(1024)
+bool(true)
+string(32) "e486000c4c8452774f746a27658d87fa"
+-- Reading beyond filesize when file pointer pointing to EOF, expeceted : 0 bytes --
+int(1024)
+bool(false)
+Reading 10 bytes from file, expecting 0 bytes ... OK
+int(1024)
+bool(true)
+-- File opened in mode w+ --
+-- Reading beyond filesize, expeceted : 1024 bytes --
+int(0)
+bool(false)
+Reading 1030 bytes from file, expecting 1024 bytes ... OK
+int(1024)
+bool(true)
+string(32) "e486000c4c8452774f746a27658d87fa"
+-- Reading beyond filesize when file pointer pointing to EOF, expeceted : 0 bytes --
+int(1024)
+bool(false)
+Reading 10 bytes from file, expecting 0 bytes ... OK
+int(1024)
+bool(true)
+-- File opened in mode w+b --
+-- Reading beyond filesize, expeceted : 1024 bytes --
+int(0)
+bool(false)
+Reading 1030 bytes from file, expecting 1024 bytes ... OK
+int(1024)
+bool(true)
+string(32) "e486000c4c8452774f746a27658d87fa"
+-- Reading beyond filesize when file pointer pointing to EOF, expeceted : 0 bytes --
+int(1024)
+bool(false)
+Reading 10 bytes from file, expecting 0 bytes ... OK
+int(1024)
+bool(true)
+-- File opened in mode w+t --
+-- Reading beyond filesize, expeceted : 1024 bytes --
+int(0)
+bool(false)
+Reading 1030 bytes from file, expecting 1024 bytes ... OK
+int(1024)
+bool(true)
+string(32) "e486000c4c8452774f746a27658d87fa"
+-- Reading beyond filesize when file pointer pointing to EOF, expeceted : 0 bytes --
+int(1024)
+bool(false)
+Reading 10 bytes from file, expecting 0 bytes ... OK
+int(1024)
+bool(true)
+-- File opened in mode x+ --
+-- Reading beyond filesize, expeceted : 1024 bytes --
+int(0)
+bool(false)
+Reading 1030 bytes from file, expecting 1024 bytes ... OK
+int(1024)
+bool(true)
+string(32) "e486000c4c8452774f746a27658d87fa"
+-- Reading beyond filesize when file pointer pointing to EOF, expeceted : 0 bytes --
+int(1024)
+bool(false)
+Reading 10 bytes from file, expecting 0 bytes ... OK
+int(1024)
+bool(true)
+-- File opened in mode x+b --
+-- Reading beyond filesize, expeceted : 1024 bytes --
+int(0)
+bool(false)
+Reading 1030 bytes from file, expecting 1024 bytes ... OK
+int(1024)
+bool(true)
+string(32) "e486000c4c8452774f746a27658d87fa"
+-- Reading beyond filesize when file pointer pointing to EOF, expeceted : 0 bytes --
+int(1024)
+bool(false)
+Reading 10 bytes from file, expecting 0 bytes ... OK
+int(1024)
+bool(true)
+-- File opened in mode x+t --
+-- Reading beyond filesize, expeceted : 1024 bytes --
+int(0)
+bool(false)
+Reading 1030 bytes from file, expecting 1024 bytes ... OK
+int(1024)
+bool(true)
+string(32) "e486000c4c8452774f746a27658d87fa"
+-- Reading beyond filesize when file pointer pointing to EOF, expeceted : 0 bytes --
+int(1024)
+bool(false)
+Reading 10 bytes from file, expecting 0 bytes ... OK
+int(1024)
+bool(true)
+
+-- Testing fread() with file having content of type text_with_new_line --
+-- File opened in mode a+ --
+-- Reading beyond filesize, expeceted : 1024 bytes --
+int(0)
+bool(false)
+Reading 1030 bytes from file, expecting 1024 bytes ... OK
+int(1024)
+bool(true)
+string(32) "b09c8026a64a88d36d4c2f17983964bb"
+-- Reading beyond filesize when file pointer pointing to EOF, expeceted : 0 bytes --
+int(1024)
+bool(false)
+Reading 10 bytes from file, expecting 0 bytes ... OK
+int(1024)
+bool(true)
+-- File opened in mode a+b --
+-- Reading beyond filesize, expeceted : 1024 bytes --
+int(0)
+bool(false)
+Reading 1030 bytes from file, expecting 1024 bytes ... OK
+int(1024)
+bool(true)
+string(32) "b09c8026a64a88d36d4c2f17983964bb"
+-- Reading beyond filesize when file pointer pointing to EOF, expeceted : 0 bytes --
+int(1024)
+bool(false)
+Reading 10 bytes from file, expecting 0 bytes ... OK
+int(1024)
+bool(true)
+-- File opened in mode a+t --
+-- Reading beyond filesize, expeceted : 1024 bytes --
+int(0)
+bool(false)
+Reading 1030 bytes from file, expecting 1024 bytes ... OK
+int(1024)
+bool(true)
+string(32) "b09c8026a64a88d36d4c2f17983964bb"
+-- Reading beyond filesize when file pointer pointing to EOF, expeceted : 0 bytes --
+int(1024)
+bool(false)
+Reading 10 bytes from file, expecting 0 bytes ... OK
+int(1024)
+bool(true)
+-- File opened in mode w+ --
+-- Reading beyond filesize, expeceted : 1024 bytes --
+int(0)
+bool(false)
+Reading 1030 bytes from file, expecting 1024 bytes ... OK
+int(1024)
+bool(true)
+string(32) "b09c8026a64a88d36d4c2f17983964bb"
+-- Reading beyond filesize when file pointer pointing to EOF, expeceted : 0 bytes --
+int(1024)
+bool(false)
+Reading 10 bytes from file, expecting 0 bytes ... OK
+int(1024)
+bool(true)
+-- File opened in mode w+b --
+-- Reading beyond filesize, expeceted : 1024 bytes --
+int(0)
+bool(false)
+Reading 1030 bytes from file, expecting 1024 bytes ... OK
+int(1024)
+bool(true)
+string(32) "b09c8026a64a88d36d4c2f17983964bb"
+-- Reading beyond filesize when file pointer pointing to EOF, expeceted : 0 bytes --
+int(1024)
+bool(false)
+Reading 10 bytes from file, expecting 0 bytes ... OK
+int(1024)
+bool(true)
+-- File opened in mode w+t --
+-- Reading beyond filesize, expeceted : 1024 bytes --
+int(0)
+bool(false)
+Reading 1030 bytes from file, expecting 1024 bytes ... OK
+int(1024)
+bool(true)
+string(32) "b09c8026a64a88d36d4c2f17983964bb"
+-- Reading beyond filesize when file pointer pointing to EOF, expeceted : 0 bytes --
+int(1137)
+bool(false)
+Reading 10 bytes from file, expecting 0 bytes ... OK
+int(1137)
+bool(true)
+-- File opened in mode x+ --
+-- Reading beyond filesize, expeceted : 1024 bytes --
+int(0)
+bool(false)
+Reading 1030 bytes from file, expecting 1024 bytes ... OK
+int(1024)
+bool(true)
+string(32) "b09c8026a64a88d36d4c2f17983964bb"
+-- Reading beyond filesize when file pointer pointing to EOF, expeceted : 0 bytes --
+int(1024)
+bool(false)
+Reading 10 bytes from file, expecting 0 bytes ... OK
+int(1024)
+bool(true)
+-- File opened in mode x+b --
+-- Reading beyond filesize, expeceted : 1024 bytes --
+int(0)
+bool(false)
+Reading 1030 bytes from file, expecting 1024 bytes ... OK
+int(1024)
+bool(true)
+string(32) "b09c8026a64a88d36d4c2f17983964bb"
+-- Reading beyond filesize when file pointer pointing to EOF, expeceted : 0 bytes --
+int(1024)
+bool(false)
+Reading 10 bytes from file, expecting 0 bytes ... OK
+int(1024)
+bool(true)
+-- File opened in mode x+t --
+-- Reading beyond filesize, expeceted : 1024 bytes --
+int(0)
+bool(false)
+Reading 1030 bytes from file, expecting 1024 bytes ... OK
+int(1024)
+bool(true)
+string(32) "b09c8026a64a88d36d4c2f17983964bb"
+-- Reading beyond filesize when file pointer pointing to EOF, expeceted : 0 bytes --
+int(1137)
+bool(false)
+Reading 10 bytes from file, expecting 0 bytes ... OK
+int(1137)
+bool(true)
+Done
diff --git a/ext/standard/tests/file/fseek_ftell_rewind_basic2-win32-mb.phpt b/ext/standard/tests/file/fseek_ftell_rewind_basic2-win32-mb.phpt
new file mode 100644
index 0000000000..ab983a2680
--- /dev/null
+++ b/ext/standard/tests/file/fseek_ftell_rewind_basic2-win32-mb.phpt
@@ -0,0 +1,464 @@
+--TEST--
+Test fseek(), ftell() & rewind() functions : basic functionality - all w and x modes
+--SKIPIF--
+<?php
+if( substr(PHP_OS, 0, 3) != "WIN" )
+ die("skip.. only valid for Windows");
+?>
+
+--FILE--
+<?php
+/* Prototype: int fseek ( resource $handle, int $offset [, int $whence] );
+ Description: Seeks on a file pointer
+
+ Prototype: bool rewind ( resource $handle );
+ Description: Rewind the position of a file pointer
+
+ Prototype: int ftell ( resource $handle );
+ Description: Tells file pointer read/write position
+*/
+
+// include the file.inc for common functions for test
+include ("file.inc");
+
+/* Testing fseek(),ftell(),rewind() functions on all write and create with write modes */
+
+echo "*** Testing fseek(), ftell(), rewind() : basic operations ***\n";
+$file_modes = array( "w","wb","wt","w+","w+b","w+t",
+ "x","xb","xt","x+","x+b","x+t");
+
+$file_content_types = array("text_with_new_line","alphanumeric");
+
+$whence_set = array(SEEK_SET,SEEK_CUR,SEEK_END);
+$whence_string = array("SEEK_SET", "SEEK_CUR", "SEEK_END");
+
+$filename = dirname(__FILE__)."/fseek_ftell_rewind_basic2私はガラスを食べられます.tmp"; // this is name of the file created by create_files()
+
+foreach($file_content_types as $file_content_type){
+ echo "\n-- File having data of type ". $file_content_type ." --\n";
+
+ /* open the file using $files_modes and perform fseek(),ftell() and rewind() on it */
+ foreach($file_modes as $file_mode) {
+ echo "-- File opened in mode ".$file_mode." --\n";
+
+ $file_handle = fopen($filename, $file_mode);
+ if (!$file_handle) {
+ echo "Error: failed to fopen() file: $filename!";
+ exit();
+ }
+ $data_to_be_written="";
+ fill_buffer($data_to_be_written, $file_content_type, 512); //get the data of size 512
+ $data_to_be_written = $data_to_be_written;
+ fwrite($file_handle,(binary)$data_to_be_written);
+
+ // set file pointer to 0
+ var_dump( rewind($file_handle) ); // set to beginning of file
+ var_dump( ftell($file_handle) );
+
+ foreach($whence_set as $whence){
+ echo "-- Testing fseek() with whence = $whence_string[$whence] --\n";
+ var_dump( fseek($file_handle, 10, $whence) ); //expecting int(0)
+ var_dump( ftell($file_handle) ); // confirm the file pointer position
+ var_dump( feof($file_handle) ); //ensure that file pointer is not at end
+ } //end of whence loop
+
+ //close the file and check the size
+ fclose($file_handle);
+ var_dump( filesize($filename) );
+
+ delete_file($filename); // delete file with name
+ } //end of file_mode loop
+} //end of File content type loop
+echo "Done\n";
+?>
+--EXPECTF--
+*** Testing fseek(), ftell(), rewind() : basic operations ***
+
+-- File having data of type text_with_new_line --
+-- File opened in mode w --
+bool(true)
+int(0)
+-- Testing fseek() with whence = SEEK_SET --
+int(0)
+int(10)
+bool(false)
+-- Testing fseek() with whence = SEEK_CUR --
+int(0)
+int(20)
+bool(false)
+-- Testing fseek() with whence = SEEK_END --
+int(0)
+int(522)
+bool(false)
+int(512)
+-- File opened in mode wb --
+bool(true)
+int(0)
+-- Testing fseek() with whence = SEEK_SET --
+int(0)
+int(10)
+bool(false)
+-- Testing fseek() with whence = SEEK_CUR --
+int(0)
+int(20)
+bool(false)
+-- Testing fseek() with whence = SEEK_END --
+int(0)
+int(522)
+bool(false)
+int(512)
+-- File opened in mode wt --
+bool(true)
+int(0)
+-- Testing fseek() with whence = SEEK_SET --
+int(0)
+int(10)
+bool(false)
+-- Testing fseek() with whence = SEEK_CUR --
+int(0)
+int(20)
+bool(false)
+-- Testing fseek() with whence = SEEK_END --
+int(0)
+int(579)
+bool(false)
+int(569)
+-- File opened in mode w+ --
+bool(true)
+int(0)
+-- Testing fseek() with whence = SEEK_SET --
+int(0)
+int(10)
+bool(false)
+-- Testing fseek() with whence = SEEK_CUR --
+int(0)
+int(20)
+bool(false)
+-- Testing fseek() with whence = SEEK_END --
+int(0)
+int(522)
+bool(false)
+int(512)
+-- File opened in mode w+b --
+bool(true)
+int(0)
+-- Testing fseek() with whence = SEEK_SET --
+int(0)
+int(10)
+bool(false)
+-- Testing fseek() with whence = SEEK_CUR --
+int(0)
+int(20)
+bool(false)
+-- Testing fseek() with whence = SEEK_END --
+int(0)
+int(522)
+bool(false)
+int(512)
+-- File opened in mode w+t --
+bool(true)
+int(0)
+-- Testing fseek() with whence = SEEK_SET --
+int(0)
+int(10)
+bool(false)
+-- Testing fseek() with whence = SEEK_CUR --
+int(0)
+int(20)
+bool(false)
+-- Testing fseek() with whence = SEEK_END --
+int(0)
+int(579)
+bool(false)
+int(569)
+-- File opened in mode x --
+bool(true)
+int(0)
+-- Testing fseek() with whence = SEEK_SET --
+int(0)
+int(10)
+bool(false)
+-- Testing fseek() with whence = SEEK_CUR --
+int(0)
+int(20)
+bool(false)
+-- Testing fseek() with whence = SEEK_END --
+int(0)
+int(522)
+bool(false)
+int(512)
+-- File opened in mode xb --
+bool(true)
+int(0)
+-- Testing fseek() with whence = SEEK_SET --
+int(0)
+int(10)
+bool(false)
+-- Testing fseek() with whence = SEEK_CUR --
+int(0)
+int(20)
+bool(false)
+-- Testing fseek() with whence = SEEK_END --
+int(0)
+int(522)
+bool(false)
+int(512)
+-- File opened in mode xt --
+bool(true)
+int(0)
+-- Testing fseek() with whence = SEEK_SET --
+int(0)
+int(10)
+bool(false)
+-- Testing fseek() with whence = SEEK_CUR --
+int(0)
+int(20)
+bool(false)
+-- Testing fseek() with whence = SEEK_END --
+int(0)
+int(579)
+bool(false)
+int(569)
+-- File opened in mode x+ --
+bool(true)
+int(0)
+-- Testing fseek() with whence = SEEK_SET --
+int(0)
+int(10)
+bool(false)
+-- Testing fseek() with whence = SEEK_CUR --
+int(0)
+int(20)
+bool(false)
+-- Testing fseek() with whence = SEEK_END --
+int(0)
+int(522)
+bool(false)
+int(512)
+-- File opened in mode x+b --
+bool(true)
+int(0)
+-- Testing fseek() with whence = SEEK_SET --
+int(0)
+int(10)
+bool(false)
+-- Testing fseek() with whence = SEEK_CUR --
+int(0)
+int(20)
+bool(false)
+-- Testing fseek() with whence = SEEK_END --
+int(0)
+int(522)
+bool(false)
+int(512)
+-- File opened in mode x+t --
+bool(true)
+int(0)
+-- Testing fseek() with whence = SEEK_SET --
+int(0)
+int(10)
+bool(false)
+-- Testing fseek() with whence = SEEK_CUR --
+int(0)
+int(20)
+bool(false)
+-- Testing fseek() with whence = SEEK_END --
+int(0)
+int(579)
+bool(false)
+int(569)
+
+-- File having data of type alphanumeric --
+-- File opened in mode w --
+bool(true)
+int(0)
+-- Testing fseek() with whence = SEEK_SET --
+int(0)
+int(10)
+bool(false)
+-- Testing fseek() with whence = SEEK_CUR --
+int(0)
+int(20)
+bool(false)
+-- Testing fseek() with whence = SEEK_END --
+int(0)
+int(522)
+bool(false)
+int(512)
+-- File opened in mode wb --
+bool(true)
+int(0)
+-- Testing fseek() with whence = SEEK_SET --
+int(0)
+int(10)
+bool(false)
+-- Testing fseek() with whence = SEEK_CUR --
+int(0)
+int(20)
+bool(false)
+-- Testing fseek() with whence = SEEK_END --
+int(0)
+int(522)
+bool(false)
+int(512)
+-- File opened in mode wt --
+bool(true)
+int(0)
+-- Testing fseek() with whence = SEEK_SET --
+int(0)
+int(10)
+bool(false)
+-- Testing fseek() with whence = SEEK_CUR --
+int(0)
+int(20)
+bool(false)
+-- Testing fseek() with whence = SEEK_END --
+int(0)
+int(522)
+bool(false)
+int(512)
+-- File opened in mode w+ --
+bool(true)
+int(0)
+-- Testing fseek() with whence = SEEK_SET --
+int(0)
+int(10)
+bool(false)
+-- Testing fseek() with whence = SEEK_CUR --
+int(0)
+int(20)
+bool(false)
+-- Testing fseek() with whence = SEEK_END --
+int(0)
+int(522)
+bool(false)
+int(512)
+-- File opened in mode w+b --
+bool(true)
+int(0)
+-- Testing fseek() with whence = SEEK_SET --
+int(0)
+int(10)
+bool(false)
+-- Testing fseek() with whence = SEEK_CUR --
+int(0)
+int(20)
+bool(false)
+-- Testing fseek() with whence = SEEK_END --
+int(0)
+int(522)
+bool(false)
+int(512)
+-- File opened in mode w+t --
+bool(true)
+int(0)
+-- Testing fseek() with whence = SEEK_SET --
+int(0)
+int(10)
+bool(false)
+-- Testing fseek() with whence = SEEK_CUR --
+int(0)
+int(20)
+bool(false)
+-- Testing fseek() with whence = SEEK_END --
+int(0)
+int(522)
+bool(false)
+int(512)
+-- File opened in mode x --
+bool(true)
+int(0)
+-- Testing fseek() with whence = SEEK_SET --
+int(0)
+int(10)
+bool(false)
+-- Testing fseek() with whence = SEEK_CUR --
+int(0)
+int(20)
+bool(false)
+-- Testing fseek() with whence = SEEK_END --
+int(0)
+int(522)
+bool(false)
+int(512)
+-- File opened in mode xb --
+bool(true)
+int(0)
+-- Testing fseek() with whence = SEEK_SET --
+int(0)
+int(10)
+bool(false)
+-- Testing fseek() with whence = SEEK_CUR --
+int(0)
+int(20)
+bool(false)
+-- Testing fseek() with whence = SEEK_END --
+int(0)
+int(522)
+bool(false)
+int(512)
+-- File opened in mode xt --
+bool(true)
+int(0)
+-- Testing fseek() with whence = SEEK_SET --
+int(0)
+int(10)
+bool(false)
+-- Testing fseek() with whence = SEEK_CUR --
+int(0)
+int(20)
+bool(false)
+-- Testing fseek() with whence = SEEK_END --
+int(0)
+int(522)
+bool(false)
+int(512)
+-- File opened in mode x+ --
+bool(true)
+int(0)
+-- Testing fseek() with whence = SEEK_SET --
+int(0)
+int(10)
+bool(false)
+-- Testing fseek() with whence = SEEK_CUR --
+int(0)
+int(20)
+bool(false)
+-- Testing fseek() with whence = SEEK_END --
+int(0)
+int(522)
+bool(false)
+int(512)
+-- File opened in mode x+b --
+bool(true)
+int(0)
+-- Testing fseek() with whence = SEEK_SET --
+int(0)
+int(10)
+bool(false)
+-- Testing fseek() with whence = SEEK_CUR --
+int(0)
+int(20)
+bool(false)
+-- Testing fseek() with whence = SEEK_END --
+int(0)
+int(522)
+bool(false)
+int(512)
+-- File opened in mode x+t --
+bool(true)
+int(0)
+-- Testing fseek() with whence = SEEK_SET --
+int(0)
+int(10)
+bool(false)
+-- Testing fseek() with whence = SEEK_CUR --
+int(0)
+int(20)
+bool(false)
+-- Testing fseek() with whence = SEEK_END --
+int(0)
+int(522)
+bool(false)
+int(512)
+Done
diff --git a/ext/standard/tests/file/ftruncate.phpt b/ext/standard/tests/file/ftruncate.phpt
index 8954ef12f2..acc4e8afa3 100644
--- a/ext/standard/tests/file/ftruncate.phpt
+++ b/ext/standard/tests/file/ftruncate.phpt
Binary files differ
diff --git a/ext/standard/tests/file/ftruncate_variation1-win32-mb.phpt b/ext/standard/tests/file/ftruncate_variation1-win32-mb.phpt
new file mode 100644
index 0000000000..36994cb182
--- /dev/null
+++ b/ext/standard/tests/file/ftruncate_variation1-win32-mb.phpt
@@ -0,0 +1,461 @@
+--TEST--
+Test ftruncate() function : usage variations - truncate filesize to zero
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die('skip.. only valid for Windows');
+}
+?>
+--FILE--
+<?php
+/*
+ Prototype: bool ftruncate ( resource $handle, int $size );
+ Description: Truncates a file to a given length
+*/
+
+// include common file related test functions
+include ("file.inc");
+
+echo "*** Testing ftruncate() : usage variations ***\n";
+
+/* test ftruncate with file opened in different modes */
+$file_modes = array("r", "rb", "rt", "r+", "r+b", "r+t",
+ "w", "wb", "wt", "w+", "w+b", "w+t",
+ "x", "xb", "xt", "x+", "x+b", "x+t",
+ "a", "ab", "at", "a+", "a+b", "a+t");
+
+$file_content_types = array("numeric","text_with_new_line");
+
+foreach($file_content_types as $file_content_type) {
+ echo "\n-- Testing ftruncate() with file having data of type ". $file_content_type ." --\n";
+
+ for($mode_counter = 0; $mode_counter < count($file_modes); $mode_counter++) {
+ echo "-- Testing ftruncate() with file opening using $file_modes[$mode_counter] mode --\n";
+
+ // create 1 file with some contents
+ $filename = dirname(__FILE__)."/ftruncate_variation1私はガラスを食べられます1.tmp";
+ if( strstr($file_modes[$mode_counter], "x") || strstr($file_modes[$mode_counter], "w") ) {
+ // fopen the file using the $file_modes
+ $file_handle = fopen($filename, $file_modes[$mode_counter]);
+ fill_file($file_handle, $file_content_type, 1024);
+ } else {
+ create_files ( dirname(__FILE__), 1, $file_content_type, 0755, 1, "w", "ftruncate_variation1私はガラスを食べられます");
+ // fopen the file using the $file_modes
+ $file_handle = fopen($filename, $file_modes[$mode_counter]);
+ }
+ if (!$file_handle) {
+ echo "Error: failed to open file $filename!\n";
+ exit();
+ }
+
+ rewind($file_handle); // file pointer to 0
+
+ /* truncate it to size 0 */
+ echo "-- Testing ftruncate(): truncate file to size = 0 --\n";
+ $new_size = 0;
+ var_dump( filesize($filename) ); // check the current file size
+ var_dump( ftell($file_handle) );
+ var_dump( ftruncate($file_handle, $new_size) ); // truncate it
+ var_dump( ftell($file_handle) );
+ var_dump( feof($file_handle) );
+ fclose($file_handle);
+ clearstatcache(); // clear previous size value in cache
+ var_dump( filesize($filename) ); // check the file size, should be 0
+
+ //delete all files created
+ delete_file($filename);
+ } //end of inner for loop
+}//end of outer foreach loop
+echo "Done\n";
+?>
+--EXPECTF--
+*** Testing ftruncate() : usage variations ***
+
+-- Testing ftruncate() with file having data of type numeric --
+-- Testing ftruncate() with file opening using r mode --
+-- Testing ftruncate(): truncate file to size = 0 --
+int(1024)
+int(0)
+bool(false)
+int(0)
+bool(false)
+int(1024)
+-- Testing ftruncate() with file opening using rb mode --
+-- Testing ftruncate(): truncate file to size = 0 --
+int(1024)
+int(0)
+bool(false)
+int(0)
+bool(false)
+int(1024)
+-- Testing ftruncate() with file opening using rt mode --
+-- Testing ftruncate(): truncate file to size = 0 --
+int(1024)
+int(0)
+bool(false)
+int(0)
+bool(false)
+int(1024)
+-- Testing ftruncate() with file opening using r+ mode --
+-- Testing ftruncate(): truncate file to size = 0 --
+int(1024)
+int(0)
+bool(true)
+int(0)
+bool(false)
+int(0)
+-- Testing ftruncate() with file opening using r+b mode --
+-- Testing ftruncate(): truncate file to size = 0 --
+int(1024)
+int(0)
+bool(true)
+int(0)
+bool(false)
+int(0)
+-- Testing ftruncate() with file opening using r+t mode --
+-- Testing ftruncate(): truncate file to size = 0 --
+int(1024)
+int(0)
+bool(true)
+int(0)
+bool(false)
+int(0)
+-- Testing ftruncate() with file opening using w mode --
+-- Testing ftruncate(): truncate file to size = 0 --
+int(1024)
+int(0)
+bool(true)
+int(0)
+bool(false)
+int(0)
+-- Testing ftruncate() with file opening using wb mode --
+-- Testing ftruncate(): truncate file to size = 0 --
+int(1024)
+int(0)
+bool(true)
+int(0)
+bool(false)
+int(0)
+-- Testing ftruncate() with file opening using wt mode --
+-- Testing ftruncate(): truncate file to size = 0 --
+int(1024)
+int(0)
+bool(true)
+int(0)
+bool(false)
+int(0)
+-- Testing ftruncate() with file opening using w+ mode --
+-- Testing ftruncate(): truncate file to size = 0 --
+int(1024)
+int(0)
+bool(true)
+int(0)
+bool(false)
+int(0)
+-- Testing ftruncate() with file opening using w+b mode --
+-- Testing ftruncate(): truncate file to size = 0 --
+int(1024)
+int(0)
+bool(true)
+int(0)
+bool(false)
+int(0)
+-- Testing ftruncate() with file opening using w+t mode --
+-- Testing ftruncate(): truncate file to size = 0 --
+int(1024)
+int(0)
+bool(true)
+int(0)
+bool(false)
+int(0)
+-- Testing ftruncate() with file opening using x mode --
+-- Testing ftruncate(): truncate file to size = 0 --
+int(1024)
+int(0)
+bool(true)
+int(0)
+bool(false)
+int(0)
+-- Testing ftruncate() with file opening using xb mode --
+-- Testing ftruncate(): truncate file to size = 0 --
+int(1024)
+int(0)
+bool(true)
+int(0)
+bool(false)
+int(0)
+-- Testing ftruncate() with file opening using xt mode --
+-- Testing ftruncate(): truncate file to size = 0 --
+int(1024)
+int(0)
+bool(true)
+int(0)
+bool(false)
+int(0)
+-- Testing ftruncate() with file opening using x+ mode --
+-- Testing ftruncate(): truncate file to size = 0 --
+int(1024)
+int(0)
+bool(true)
+int(0)
+bool(false)
+int(0)
+-- Testing ftruncate() with file opening using x+b mode --
+-- Testing ftruncate(): truncate file to size = 0 --
+int(1024)
+int(0)
+bool(true)
+int(0)
+bool(false)
+int(0)
+-- Testing ftruncate() with file opening using x+t mode --
+-- Testing ftruncate(): truncate file to size = 0 --
+int(1024)
+int(0)
+bool(true)
+int(0)
+bool(false)
+int(0)
+-- Testing ftruncate() with file opening using a mode --
+-- Testing ftruncate(): truncate file to size = 0 --
+int(1024)
+int(0)
+bool(true)
+int(0)
+bool(false)
+int(0)
+-- Testing ftruncate() with file opening using ab mode --
+-- Testing ftruncate(): truncate file to size = 0 --
+int(1024)
+int(0)
+bool(true)
+int(0)
+bool(false)
+int(0)
+-- Testing ftruncate() with file opening using at mode --
+-- Testing ftruncate(): truncate file to size = 0 --
+int(1024)
+int(0)
+bool(true)
+int(0)
+bool(false)
+int(0)
+-- Testing ftruncate() with file opening using a+ mode --
+-- Testing ftruncate(): truncate file to size = 0 --
+int(1024)
+int(0)
+bool(true)
+int(0)
+bool(false)
+int(0)
+-- Testing ftruncate() with file opening using a+b mode --
+-- Testing ftruncate(): truncate file to size = 0 --
+int(1024)
+int(0)
+bool(true)
+int(0)
+bool(false)
+int(0)
+-- Testing ftruncate() with file opening using a+t mode --
+-- Testing ftruncate(): truncate file to size = 0 --
+int(1024)
+int(0)
+bool(true)
+int(0)
+bool(false)
+int(0)
+
+-- Testing ftruncate() with file having data of type text_with_new_line --
+-- Testing ftruncate() with file opening using r mode --
+-- Testing ftruncate(): truncate file to size = 0 --
+int(1024)
+int(0)
+bool(false)
+int(0)
+bool(false)
+int(1024)
+-- Testing ftruncate() with file opening using rb mode --
+-- Testing ftruncate(): truncate file to size = 0 --
+int(1024)
+int(0)
+bool(false)
+int(0)
+bool(false)
+int(1024)
+-- Testing ftruncate() with file opening using rt mode --
+-- Testing ftruncate(): truncate file to size = 0 --
+int(1024)
+int(0)
+bool(false)
+int(0)
+bool(false)
+int(1024)
+-- Testing ftruncate() with file opening using r+ mode --
+-- Testing ftruncate(): truncate file to size = 0 --
+int(1024)
+int(0)
+bool(true)
+int(0)
+bool(false)
+int(0)
+-- Testing ftruncate() with file opening using r+b mode --
+-- Testing ftruncate(): truncate file to size = 0 --
+int(1024)
+int(0)
+bool(true)
+int(0)
+bool(false)
+int(0)
+-- Testing ftruncate() with file opening using r+t mode --
+-- Testing ftruncate(): truncate file to size = 0 --
+int(1024)
+int(0)
+bool(true)
+int(0)
+bool(false)
+int(0)
+-- Testing ftruncate() with file opening using w mode --
+-- Testing ftruncate(): truncate file to size = 0 --
+int(1024)
+int(0)
+bool(true)
+int(0)
+bool(false)
+int(0)
+-- Testing ftruncate() with file opening using wb mode --
+-- Testing ftruncate(): truncate file to size = 0 --
+int(1024)
+int(0)
+bool(true)
+int(0)
+bool(false)
+int(0)
+-- Testing ftruncate() with file opening using wt mode --
+-- Testing ftruncate(): truncate file to size = 0 --
+int(1137)
+int(0)
+bool(true)
+int(0)
+bool(false)
+int(0)
+-- Testing ftruncate() with file opening using w+ mode --
+-- Testing ftruncate(): truncate file to size = 0 --
+int(1024)
+int(0)
+bool(true)
+int(0)
+bool(false)
+int(0)
+-- Testing ftruncate() with file opening using w+b mode --
+-- Testing ftruncate(): truncate file to size = 0 --
+int(1024)
+int(0)
+bool(true)
+int(0)
+bool(false)
+int(0)
+-- Testing ftruncate() with file opening using w+t mode --
+-- Testing ftruncate(): truncate file to size = 0 --
+int(1137)
+int(0)
+bool(true)
+int(0)
+bool(false)
+int(0)
+-- Testing ftruncate() with file opening using x mode --
+-- Testing ftruncate(): truncate file to size = 0 --
+int(1024)
+int(0)
+bool(true)
+int(0)
+bool(false)
+int(0)
+-- Testing ftruncate() with file opening using xb mode --
+-- Testing ftruncate(): truncate file to size = 0 --
+int(1024)
+int(0)
+bool(true)
+int(0)
+bool(false)
+int(0)
+-- Testing ftruncate() with file opening using xt mode --
+-- Testing ftruncate(): truncate file to size = 0 --
+int(1137)
+int(0)
+bool(true)
+int(0)
+bool(false)
+int(0)
+-- Testing ftruncate() with file opening using x+ mode --
+-- Testing ftruncate(): truncate file to size = 0 --
+int(1024)
+int(0)
+bool(true)
+int(0)
+bool(false)
+int(0)
+-- Testing ftruncate() with file opening using x+b mode --
+-- Testing ftruncate(): truncate file to size = 0 --
+int(1024)
+int(0)
+bool(true)
+int(0)
+bool(false)
+int(0)
+-- Testing ftruncate() with file opening using x+t mode --
+-- Testing ftruncate(): truncate file to size = 0 --
+int(1137)
+int(0)
+bool(true)
+int(0)
+bool(false)
+int(0)
+-- Testing ftruncate() with file opening using a mode --
+-- Testing ftruncate(): truncate file to size = 0 --
+int(1024)
+int(0)
+bool(true)
+int(0)
+bool(false)
+int(0)
+-- Testing ftruncate() with file opening using ab mode --
+-- Testing ftruncate(): truncate file to size = 0 --
+int(1024)
+int(0)
+bool(true)
+int(0)
+bool(false)
+int(0)
+-- Testing ftruncate() with file opening using at mode --
+-- Testing ftruncate(): truncate file to size = 0 --
+int(1024)
+int(0)
+bool(true)
+int(0)
+bool(false)
+int(0)
+-- Testing ftruncate() with file opening using a+ mode --
+-- Testing ftruncate(): truncate file to size = 0 --
+int(1024)
+int(0)
+bool(true)
+int(0)
+bool(false)
+int(0)
+-- Testing ftruncate() with file opening using a+b mode --
+-- Testing ftruncate(): truncate file to size = 0 --
+int(1024)
+int(0)
+bool(true)
+int(0)
+bool(false)
+int(0)
+-- Testing ftruncate() with file opening using a+t mode --
+-- Testing ftruncate(): truncate file to size = 0 --
+int(1024)
+int(0)
+bool(true)
+int(0)
+bool(false)
+int(0)
+Done
diff --git a/ext/standard/tests/file/ftruncate_variation4-win32.phpt b/ext/standard/tests/file/ftruncate_variation4-win32.phpt
index ae9e5a4743..5bf5f679b6 100644
--- a/ext/standard/tests/file/ftruncate_variation4-win32.phpt
+++ b/ext/standard/tests/file/ftruncate_variation4-win32.phpt
@@ -77,6 +77,8 @@ echo "Done\n";
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -85,6 +87,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -93,6 +97,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -101,6 +107,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -109,6 +117,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -117,6 +127,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -125,6 +137,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -133,6 +147,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -141,6 +157,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -149,6 +167,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -157,6 +177,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -165,6 +187,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -173,6 +197,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -181,6 +207,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -189,6 +217,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -197,6 +227,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -205,6 +237,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -213,6 +247,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -221,6 +257,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -229,6 +267,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -237,6 +277,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -245,6 +287,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -253,6 +297,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -261,6 +307,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -271,6 +319,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -279,6 +329,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -287,6 +339,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -295,6 +349,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -303,6 +359,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -311,6 +369,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -319,6 +379,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -327,6 +389,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -335,6 +399,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1137)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -343,6 +409,8 @@ int(1137)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -351,6 +419,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -359,6 +429,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1137)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -367,6 +439,8 @@ int(1137)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -375,6 +449,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -383,6 +459,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1137)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -391,6 +469,8 @@ int(1137)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -399,6 +479,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -407,6 +489,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1137)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -415,6 +499,8 @@ int(1137)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -423,6 +509,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -431,6 +519,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -439,6 +529,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -447,6 +539,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -455,6 +549,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
diff --git a/ext/standard/tests/file/ftruncate_variation4.phpt b/ext/standard/tests/file/ftruncate_variation4.phpt
index 4a5a36236c..ef0ee21996 100644
--- a/ext/standard/tests/file/ftruncate_variation4.phpt
+++ b/ext/standard/tests/file/ftruncate_variation4.phpt
@@ -77,6 +77,8 @@ echo "Done\n";
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -85,6 +87,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -93,6 +97,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -101,6 +107,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -109,6 +117,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -117,6 +127,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -125,6 +137,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -133,6 +147,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -141,6 +157,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -149,6 +167,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -157,6 +177,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -165,6 +187,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -173,6 +197,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -181,6 +207,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -189,6 +217,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -197,6 +227,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -205,6 +237,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -213,6 +247,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -221,6 +257,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -229,6 +267,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -237,6 +277,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -245,6 +287,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -253,6 +297,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -261,6 +307,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -271,6 +319,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -279,6 +329,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -287,6 +339,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -295,6 +349,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -303,6 +359,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -311,6 +369,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -319,6 +379,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -327,6 +389,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -335,6 +399,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -343,6 +409,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -351,6 +419,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -359,6 +429,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -367,6 +439,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -375,6 +449,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -383,6 +459,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -391,6 +469,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -399,6 +479,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -407,6 +489,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -415,6 +499,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -423,6 +509,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -431,6 +519,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -439,6 +529,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -447,6 +539,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
@@ -455,6 +549,8 @@ int(1024)
-- Testing ftruncate(): try truncating file to a negative size --
int(1024)
int(0)
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
int(0)
bool(false)
diff --git a/ext/standard/tests/file/fwrite_basic-win32-mb.phpt b/ext/standard/tests/file/fwrite_basic-win32-mb.phpt
new file mode 100644
index 0000000000..a65d81efb8
--- /dev/null
+++ b/ext/standard/tests/file/fwrite_basic-win32-mb.phpt
@@ -0,0 +1,424 @@
+--TEST--
+Test fwrite() function : basic functionality
+--SKIPIF--
+<?php
+if( substr(PHP_OS, 0, 3) != 'WIN' ) {
+ die('skip...Valid for Windows only');
+}
+?>
+--FILE--
+<?php
+/*
+ Prototype: int fwrite ( resource $handle,string string, [, int $length] );
+ Description: fwrite() writes the contents of string to the file stream pointed to by handle.
+ If the length arquement is given,writing will stop after length bytes have been
+ written or the end of string reached, whichever comes first.
+ fwrite() returns the number of bytes written or FALSE on error
+*/
+
+// include the file.inc for Function: function delete_file($filename)
+include ("file.inc");
+
+echo "*** Testing fwrite() basic operations ***\n";
+/*
+ test fwrite with file opened in mode : w,wb,wt,w+,w+b,w+t
+ File containing data of type, numeric, text, text_with_new_line, alphanumeric
+*/
+$file_modes = array( "w", "wb", "wt", "w+", "w+b", "w+t");
+$file_content_types = array("numeric","text","text_with_new_line","alphanumeric");
+
+foreach($file_content_types as $file_content_type) {
+ echo "\n-- Testing fwrite() with file having data of type ". $file_content_type ." --\n";
+ $filename = dirname(__FILE__)."/fwrite_basic-win32私はガラスを食べられます.tmp"; // this is name of the file
+
+ for($inner_loop_counter = 0;
+ $inner_loop_counter < count($file_modes);
+ $inner_loop_counter++) {
+ echo "-- File opened in mode : " . $file_modes[$inner_loop_counter]. " --\n";
+ /* open the file using $files_modes and perform fwrite() on it */
+ $file_handle = fopen($filename, $file_modes[$inner_loop_counter]);
+ if (!$file_handle) {
+ echo "Error: failed to fopen() file: $filename!";
+ exit();
+ }
+ $data_to_be_written="";
+ fill_buffer($data_to_be_written, $file_content_type, 1024); //get the data of size 1024
+
+ /* Write the data in to the file, verify the write by checking file pointer position,
+ eof position, and data. */
+ // writing 100 bytes
+ var_dump( ftell($file_handle) ); // Expecting 0
+ var_dump( fwrite($file_handle, $data_to_be_written, 100)); //int(100)
+ var_dump( feof($file_handle) ); // expected : false
+ var_dump( ftell($file_handle) ); //expected: 100
+
+ // trying to write more than the available data, available 1024 bytes but trying 2048
+ var_dump( fwrite($file_handle, $data_to_be_written, 2048)); //int(1024)
+ var_dump( feof($file_handle) ); // expected : false
+ var_dump( ftell($file_handle) ); // expected: 1124
+
+ // fwrite() without length parameter
+ var_dump( fwrite($file_handle, $data_to_be_written)); //int(1024)
+ var_dump( ftell($file_handle) ); // expected: 2148
+ var_dump( feof($file_handle) ); // expected: false
+
+ // close the file, get the size and content of the file.
+ var_dump( fclose($file_handle) ); //expected : true
+ clearstatcache();//clears file status cache
+ var_dump( filesize($filename) ); // expected: 2148
+ var_dump(md5(file_get_contents($filename))); // hash the output
+
+ } // end of inner for loop
+
+ // delete the file created : fwrite_basic.tmp
+ delete_file($filename);
+} // end of outer foreach loop
+echo "Done\n";
+?>
+--EXPECTF--
+*** Testing fwrite() basic operations ***
+
+-- Testing fwrite() with file having data of type numeric --
+-- File opened in mode : w --
+int(0)
+int(100)
+bool(false)
+int(100)
+int(1024)
+bool(false)
+int(1124)
+int(1024)
+int(2148)
+bool(false)
+bool(true)
+int(2148)
+string(32) "04db34906fe2c56dcfbd649b7d916974"
+-- File opened in mode : wb --
+int(0)
+int(100)
+bool(false)
+int(100)
+int(1024)
+bool(false)
+int(1124)
+int(1024)
+int(2148)
+bool(false)
+bool(true)
+int(2148)
+string(32) "04db34906fe2c56dcfbd649b7d916974"
+-- File opened in mode : wt --
+int(0)
+int(100)
+bool(false)
+int(100)
+int(1024)
+bool(false)
+int(1124)
+int(1024)
+int(2148)
+bool(false)
+bool(true)
+int(2148)
+string(32) "04db34906fe2c56dcfbd649b7d916974"
+-- File opened in mode : w+ --
+int(0)
+int(100)
+bool(false)
+int(100)
+int(1024)
+bool(false)
+int(1124)
+int(1024)
+int(2148)
+bool(false)
+bool(true)
+int(2148)
+string(32) "04db34906fe2c56dcfbd649b7d916974"
+-- File opened in mode : w+b --
+int(0)
+int(100)
+bool(false)
+int(100)
+int(1024)
+bool(false)
+int(1124)
+int(1024)
+int(2148)
+bool(false)
+bool(true)
+int(2148)
+string(32) "04db34906fe2c56dcfbd649b7d916974"
+-- File opened in mode : w+t --
+int(0)
+int(100)
+bool(false)
+int(100)
+int(1024)
+bool(false)
+int(1124)
+int(1024)
+int(2148)
+bool(false)
+bool(true)
+int(2148)
+string(32) "04db34906fe2c56dcfbd649b7d916974"
+
+-- Testing fwrite() with file having data of type text --
+-- File opened in mode : w --
+int(0)
+int(100)
+bool(false)
+int(100)
+int(1024)
+bool(false)
+int(1124)
+int(1024)
+int(2148)
+bool(false)
+bool(true)
+int(2148)
+string(32) "9c08ac77b7a93a84dd0b055900165e84"
+-- File opened in mode : wb --
+int(0)
+int(100)
+bool(false)
+int(100)
+int(1024)
+bool(false)
+int(1124)
+int(1024)
+int(2148)
+bool(false)
+bool(true)
+int(2148)
+string(32) "9c08ac77b7a93a84dd0b055900165e84"
+-- File opened in mode : wt --
+int(0)
+int(100)
+bool(false)
+int(100)
+int(1024)
+bool(false)
+int(1124)
+int(1024)
+int(2148)
+bool(false)
+bool(true)
+int(2148)
+string(32) "9c08ac77b7a93a84dd0b055900165e84"
+-- File opened in mode : w+ --
+int(0)
+int(100)
+bool(false)
+int(100)
+int(1024)
+bool(false)
+int(1124)
+int(1024)
+int(2148)
+bool(false)
+bool(true)
+int(2148)
+string(32) "9c08ac77b7a93a84dd0b055900165e84"
+-- File opened in mode : w+b --
+int(0)
+int(100)
+bool(false)
+int(100)
+int(1024)
+bool(false)
+int(1124)
+int(1024)
+int(2148)
+bool(false)
+bool(true)
+int(2148)
+string(32) "9c08ac77b7a93a84dd0b055900165e84"
+-- File opened in mode : w+t --
+int(0)
+int(100)
+bool(false)
+int(100)
+int(1024)
+bool(false)
+int(1124)
+int(1024)
+int(2148)
+bool(false)
+bool(true)
+int(2148)
+string(32) "9c08ac77b7a93a84dd0b055900165e84"
+
+-- Testing fwrite() with file having data of type text_with_new_line --
+-- File opened in mode : w --
+int(0)
+int(100)
+bool(false)
+int(100)
+int(1024)
+bool(false)
+int(1124)
+int(1024)
+int(2148)
+bool(false)
+bool(true)
+int(2148)
+string(32) "56a1963cc292d7f8245219116d9eca40"
+-- File opened in mode : wb --
+int(0)
+int(100)
+bool(false)
+int(100)
+int(1024)
+bool(false)
+int(1124)
+int(1024)
+int(2148)
+bool(false)
+bool(true)
+int(2148)
+string(32) "56a1963cc292d7f8245219116d9eca40"
+-- File opened in mode : wt --
+int(0)
+int(100)
+bool(false)
+int(100)
+int(1024)
+bool(false)
+int(1124)
+int(1024)
+int(2148)
+bool(false)
+bool(true)
+int(2385)
+string(32) "62b09dac6d598bf54de7b02e0e68e5c7"
+-- File opened in mode : w+ --
+int(0)
+int(100)
+bool(false)
+int(100)
+int(1024)
+bool(false)
+int(1124)
+int(1024)
+int(2148)
+bool(false)
+bool(true)
+int(2148)
+string(32) "56a1963cc292d7f8245219116d9eca40"
+-- File opened in mode : w+b --
+int(0)
+int(100)
+bool(false)
+int(100)
+int(1024)
+bool(false)
+int(1124)
+int(1024)
+int(2148)
+bool(false)
+bool(true)
+int(2148)
+string(32) "56a1963cc292d7f8245219116d9eca40"
+-- File opened in mode : w+t --
+int(0)
+int(100)
+bool(false)
+int(100)
+int(1024)
+bool(false)
+int(1124)
+int(1024)
+int(2148)
+bool(false)
+bool(true)
+int(2385)
+string(32) "62b09dac6d598bf54de7b02e0e68e5c7"
+
+-- Testing fwrite() with file having data of type alphanumeric --
+-- File opened in mode : w --
+int(0)
+int(100)
+bool(false)
+int(100)
+int(1024)
+bool(false)
+int(1124)
+int(1024)
+int(2148)
+bool(false)
+bool(true)
+int(2148)
+string(32) "719e3329c19218c12d232f2ee81e100f"
+-- File opened in mode : wb --
+int(0)
+int(100)
+bool(false)
+int(100)
+int(1024)
+bool(false)
+int(1124)
+int(1024)
+int(2148)
+bool(false)
+bool(true)
+int(2148)
+string(32) "719e3329c19218c12d232f2ee81e100f"
+-- File opened in mode : wt --
+int(0)
+int(100)
+bool(false)
+int(100)
+int(1024)
+bool(false)
+int(1124)
+int(1024)
+int(2148)
+bool(false)
+bool(true)
+int(2148)
+string(32) "719e3329c19218c12d232f2ee81e100f"
+-- File opened in mode : w+ --
+int(0)
+int(100)
+bool(false)
+int(100)
+int(1024)
+bool(false)
+int(1124)
+int(1024)
+int(2148)
+bool(false)
+bool(true)
+int(2148)
+string(32) "719e3329c19218c12d232f2ee81e100f"
+-- File opened in mode : w+b --
+int(0)
+int(100)
+bool(false)
+int(100)
+int(1024)
+bool(false)
+int(1124)
+int(1024)
+int(2148)
+bool(false)
+bool(true)
+int(2148)
+string(32) "719e3329c19218c12d232f2ee81e100f"
+-- File opened in mode : w+t --
+int(0)
+int(100)
+bool(false)
+int(100)
+int(1024)
+bool(false)
+int(1124)
+int(1024)
+int(2148)
+bool(false)
+bool(true)
+int(2148)
+string(32) "719e3329c19218c12d232f2ee81e100f"
+Done \ No newline at end of file
diff --git a/ext/standard/tests/file/fwrite_variation1-win32-mb.phpt b/ext/standard/tests/file/fwrite_variation1-win32-mb.phpt
new file mode 100644
index 0000000000..e43a04dfd6
--- /dev/null
+++ b/ext/standard/tests/file/fwrite_variation1-win32-mb.phpt
@@ -0,0 +1,235 @@
+--TEST--
+Test fwrite() function : usage variations - r, rb & rt modes
+--SKIPIF--
+<?php
+if( substr(PHP_OS, 0, 3) != 'WIN' ) {
+ die('skip...Not valid for Linux');
+}
+?>
+--FILE--
+<?php
+/*
+ Prototype: int fwrite ( resource $handle,string string, [, int $length] );
+ Description: fwrite() writes the contents of string to the file stream pointed to by handle.
+ If the length arquement is given,writing will stop after length bytes have been
+ written or the end of string reached, whichever comes first.
+ fwrite() returns the number of bytes written or FALSE on error
+*/
+
+
+echo "*** Testing fwrite() various operations ***\n";
+
+// include the file.inc for Function: function delete_file($filename)
+include ("file.inc");
+
+/*
+ Test fwrite with file opened in mode : r,rb,rt
+ File having content of type numeric, text,text_with_new_line & alphanumeric
+*/
+
+$file_modes = array("r","rb","rt");
+$file_content_types = array("numeric","text","text_with_new_line","alphanumeric");
+
+
+foreach($file_content_types as $file_content_type) {
+ echo "\n-- Testing fwrite() with file having content of type ". $file_content_type ." --\n";
+
+ /* open the file using $files_modes and perform fwrite() on it */
+ foreach($file_modes as $file_mode) {
+ echo "-- Opening file in $file_mode --\n";
+
+ // create the temp file with content of type $file_content_type
+ $filename = dirname(__FILE__)."/fwrite_variation1私はガラスを食べられます1.tmp"; // this is name of the file
+ create_files ( dirname(__FILE__), 1, $file_content_type, 0755, 1, "w", "fwrite_variation1私はガラスを食べられます");
+
+ $file_handle = fopen($filename, $file_mode);
+ if(!$file_handle) {
+ echo "Error: failed to fopen() file: $filename!";
+ exit();
+ }
+
+ $data_to_be_written="";
+ fill_buffer($data_to_be_written,$file_content_type,1024); //get the data of size 1024
+
+ /* Write the data into the file, verify it by checking the file pointer position, eof position,
+ filesize & by displaying the content */
+
+ var_dump( ftell($file_handle) ); // expected: 0
+ var_dump( fwrite($file_handle, $data_to_be_written ));
+ var_dump( ftell($file_handle) ); // expected: 0
+ var_dump( feof($file_handle) ); // expected: false
+
+ // move the file pointer to end of the file and try fwrite()
+ fseek($file_handle, SEEK_END, 0);
+ var_dump( ftell($file_handle) ); // expecting 1024
+ var_dump( fwrite($file_handle, $data_to_be_written) ); // fwrite to fail
+ var_dump( ftell($file_handle) ); //check that file pointer points at eof, expected: 1024
+ var_dump( feof($file_handle) ); // ensure that feof() points to eof, expected: true
+
+ // ensure that file content/size didn't change.
+ var_dump( fclose($file_handle) );
+ clearstatcache();//clears file status cache
+ var_dump( filesize($filename) ); // expected: 1024
+ var_dump(md5(file_get_contents($filename))); // hash the output
+ delete_file($filename); // delete file with name fwrite_variation1.tmp
+
+ } // end of inner foreach loop
+} // end of outer foreach loop
+
+echo "Done\n";
+?>
+--EXPECTF--
+*** Testing fwrite() various operations ***
+
+-- Testing fwrite() with file having content of type numeric --
+-- Opening file in r --
+int(0)
+int(0)
+int(0)
+bool(false)
+int(2)
+int(0)
+int(2)
+bool(false)
+bool(true)
+int(1024)
+string(32) "950b7457d1deb6332f2fc5d42f3129d6"
+-- Opening file in rb --
+int(0)
+int(0)
+int(0)
+bool(false)
+int(2)
+int(0)
+int(2)
+bool(false)
+bool(true)
+int(1024)
+string(32) "950b7457d1deb6332f2fc5d42f3129d6"
+-- Opening file in rt --
+int(0)
+int(0)
+int(0)
+bool(false)
+int(2)
+int(0)
+int(2)
+bool(false)
+bool(true)
+int(1024)
+string(32) "950b7457d1deb6332f2fc5d42f3129d6"
+
+-- Testing fwrite() with file having content of type text --
+-- Opening file in r --
+int(0)
+int(0)
+int(0)
+bool(false)
+int(2)
+int(0)
+int(2)
+bool(false)
+bool(true)
+int(1024)
+string(32) "e486000c4c8452774f746a27658d87fa"
+-- Opening file in rb --
+int(0)
+int(0)
+int(0)
+bool(false)
+int(2)
+int(0)
+int(2)
+bool(false)
+bool(true)
+int(1024)
+string(32) "e486000c4c8452774f746a27658d87fa"
+-- Opening file in rt --
+int(0)
+int(0)
+int(0)
+bool(false)
+int(2)
+int(0)
+int(2)
+bool(false)
+bool(true)
+int(1024)
+string(32) "e486000c4c8452774f746a27658d87fa"
+
+-- Testing fwrite() with file having content of type text_with_new_line --
+-- Opening file in r --
+int(0)
+int(0)
+int(0)
+bool(false)
+int(2)
+int(0)
+int(2)
+bool(false)
+bool(true)
+int(1024)
+string(32) "b09c8026a64a88d36d4c2f17983964bb"
+-- Opening file in rb --
+int(0)
+int(0)
+int(0)
+bool(false)
+int(2)
+int(0)
+int(2)
+bool(false)
+bool(true)
+int(1024)
+string(32) "b09c8026a64a88d36d4c2f17983964bb"
+-- Opening file in rt --
+int(0)
+int(0)
+int(0)
+bool(false)
+int(2)
+int(0)
+int(2)
+bool(false)
+bool(true)
+int(1024)
+string(32) "b09c8026a64a88d36d4c2f17983964bb"
+
+-- Testing fwrite() with file having content of type alphanumeric --
+-- Opening file in r --
+int(0)
+int(0)
+int(0)
+bool(false)
+int(2)
+int(0)
+int(2)
+bool(false)
+bool(true)
+int(1024)
+string(32) "3fabd48d8eaa65c14e0d93d6880c560c"
+-- Opening file in rb --
+int(0)
+int(0)
+int(0)
+bool(false)
+int(2)
+int(0)
+int(2)
+bool(false)
+bool(true)
+int(1024)
+string(32) "3fabd48d8eaa65c14e0d93d6880c560c"
+-- Opening file in rt --
+int(0)
+int(0)
+int(0)
+bool(false)
+int(2)
+int(0)
+int(2)
+bool(false)
+bool(true)
+int(1024)
+string(32) "3fabd48d8eaa65c14e0d93d6880c560c"
+Done
diff --git a/ext/standard/tests/file/glob_error_002-win32-mb.phpt b/ext/standard/tests/file/glob_error_002-win32-mb.phpt
new file mode 100644
index 0000000000..a61efd108c
--- /dev/null
+++ b/ext/standard/tests/file/glob_error_002-win32-mb.phpt
@@ -0,0 +1,27 @@
+--TEST--
+Test glob() function: error condition - pattern too long.
+--CREDITS--
+Dave Kelsey <d_kelsey@uk.ibm.com>
+--SKIPIF--
+<?php
+if(substr(PHP_OS, 0, 3) != "WIN")
+ die("skip Only valid for Windows");
+?>
+--FILE--
+<?php
+/* Prototype: array glob ( string $pattern [, int $flags] );
+ Description: Find pathnames matching a pattern
+*/
+
+echo "*** Testing glob() : error condition - pattern too long. ***\n";
+
+var_dump(glob(str_repeat('x', 3000)));
+
+echo "Done";
+?>
+--EXPECTF--
+*** Testing glob() : error condition - pattern too long. ***
+
+Warning: glob(): Pattern exceeds the maximum allowed length of %d characters in %s on line %d
+bool(false)
+Done
diff --git a/ext/standard/tests/file/glob_variation-win32-mb.phpt b/ext/standard/tests/file/glob_variation-win32-mb.phpt
new file mode 100644
index 0000000000..cba2e2b343
--- /dev/null
+++ b/ext/standard/tests/file/glob_variation-win32-mb.phpt
@@ -0,0 +1,466 @@
+--TEST--
+Test glob() function: usage variations
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die('skip.. only for Windows');
+}
+?>
+--FILE--
+<?php
+/* Prototype: array glob ( string $pattern [, int $flags] );
+ Description: Find pathnames matching a pattern
+*/
+
+echo "*** Testing glob() : usage variations ***\n";
+
+$file_path = dirname(__FILE__);
+
+// temp dir created
+//mkdir("$file_path/glob_variation");
+mkdir("$file_path/glob_variation私はガラスを食べられます");
+mkdir("$file_path/glob_variation私はガラスを食べられます/wonder");
+
+// temp files created
+$fp = fopen("$file_path/glob_variation私はガラスを食べられます/wonder12345", "w");
+fclose($fp);
+$fp = fopen("$file_path/glob_variation私はガラスを食べられます/wonder;123456", "w");
+fclose($fp);
+
+$patterns = array (
+ "$file_path/glob_variation私はガラスを食べられます/*der*",
+ "$file_path/glob_variation私はガラスを食べられます/?onder*",
+ "$file_path/glob_variation私はガラスを食べられます/w*der?*",
+ "$file_path/glob_variation私はガラスを食べられます/*der5",
+ "$file_path/glob_variation私はガラスを食べられます/??onder*",
+ "$file_path/glob_variation私はガラスを食べられます/***der***",
+ "$file_path/glob_variation私はガラスを食べられます/++onder*",
+ "$file_path/glob_variation私はガラスを食べられます/WONDER5\0",
+ '$file_path/glob_variation私はガラスを食べられます/wonder5',
+ "$file_path/glob_variation私はガラスを食べられます/?wonder?",
+ "$file_path/glob_variation私はガラスを食べられます/wonder?",
+ TRUE // boolean true
+);
+$counter = 1;
+/* loop through $patterns to match each $pattern with the files created
+ using glob() */
+foreach($patterns as $pattern) {
+ echo "\n-- Iteration $counter --\n";
+ var_dump( glob($pattern) ); // default arguments
+ var_dump( glob($pattern, GLOB_MARK) );
+ var_dump( glob($pattern, GLOB_NOSORT) );
+ var_dump( glob($pattern, GLOB_NOCHECK) );
+ var_dump( glob($pattern, GLOB_NOESCAPE) );
+ var_dump( glob($pattern, GLOB_ERR) );
+ $counter++;
+}
+
+echo "\n*** Testing glob() with pattern within braces ***\n";
+var_dump( glob("$file_path/glob_variation私はガラスを食べられます/*{5}", GLOB_BRACE) );
+
+// delete temp files and dir
+unlink("$file_path/glob_variation私はガラスを食べられます/wonder12345");
+unlink("$file_path/glob_variation私はガラスを食べられます/wonder;123456");
+rmdir("$file_path/glob_variation私はガラスを食べられます/wonder");
+rmdir("$file_path/glob_variation私はガラスを食べられます");
+
+echo "\n*** Testing glob() on directories ***\n";
+// temp dir created to check for pattern matching the sub dir created in it
+mkdir("$file_path/glob_variation私はガラスを食べられます/wonder1/wonder2", 0777, true);
+
+$counter = 1;
+/* loop through $patterns to match each $pattern with the directories created
+ using glob() */
+foreach($patterns as $pattern) {
+ echo "-- Iteration $counter --\n";
+ var_dump( glob($pattern, GLOB_ONLYDIR) );
+ $counter++;
+}
+
+echo "Done\n";
+?>
+--CLEAN--
+<?php
+$file_path = dirname(__FILE__);
+rmdir("$file_path/glob_variation私はガラスを食べられます/wonder1/wonder2");
+rmdir("$file_path/glob_variation私はガラスを食べられます/wonder1/");
+rmdir("$file_path/glob_variation私はガラスを食べられます/");
+?>
+--EXPECTF--
+*** Testing glob() : usage variations ***
+
+-- Iteration 1 --
+array(3) {
+ [0]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder"
+ [1]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder12345"
+ [2]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder;123456"
+}
+array(3) {
+ [0]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder12345"
+ [1]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder;123456"
+ [2]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder\"
+}
+array(3) {
+ [0]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder"
+ [1]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder12345"
+ [2]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder;123456"
+}
+array(3) {
+ [0]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder"
+ [1]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder12345"
+ [2]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder;123456"
+}
+array(3) {
+ [0]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder"
+ [1]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder12345"
+ [2]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder;123456"
+}
+array(3) {
+ [0]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder"
+ [1]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder12345"
+ [2]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder;123456"
+}
+
+-- Iteration 2 --
+array(3) {
+ [0]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder"
+ [1]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder12345"
+ [2]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder;123456"
+}
+array(3) {
+ [0]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder12345"
+ [1]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder;123456"
+ [2]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder\"
+}
+array(3) {
+ [0]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder"
+ [1]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder12345"
+ [2]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder;123456"
+}
+array(3) {
+ [0]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder"
+ [1]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder12345"
+ [2]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder;123456"
+}
+array(3) {
+ [0]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder"
+ [1]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder12345"
+ [2]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder;123456"
+}
+array(3) {
+ [0]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder"
+ [1]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder12345"
+ [2]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder;123456"
+}
+
+-- Iteration 3 --
+array(2) {
+ [0]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder12345"
+ [1]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder;123456"
+}
+array(2) {
+ [0]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder12345"
+ [1]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder;123456"
+}
+array(2) {
+ [0]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder12345"
+ [1]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder;123456"
+}
+array(2) {
+ [0]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder12345"
+ [1]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder;123456"
+}
+array(2) {
+ [0]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder12345"
+ [1]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder;123456"
+}
+array(2) {
+ [0]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder12345"
+ [1]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder;123456"
+}
+
+-- Iteration 4 --
+array(0) {
+}
+array(0) {
+}
+array(0) {
+}
+array(1) {
+ [0]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/*der5"
+}
+array(0) {
+}
+array(0) {
+}
+
+-- Iteration 5 --
+array(0) {
+}
+array(0) {
+}
+array(0) {
+}
+array(1) {
+ [0]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/??onder*"
+}
+array(0) {
+}
+array(0) {
+}
+
+-- Iteration 6 --
+array(3) {
+ [0]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder"
+ [1]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder12345"
+ [2]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder;123456"
+}
+array(3) {
+ [0]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder12345"
+ [1]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder;123456"
+ [2]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder\"
+}
+array(3) {
+ [0]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder"
+ [1]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder12345"
+ [2]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder;123456"
+}
+array(3) {
+ [0]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder"
+ [1]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder12345"
+ [2]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder;123456"
+}
+array(3) {
+ [0]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder"
+ [1]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder12345"
+ [2]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder;123456"
+}
+array(3) {
+ [0]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder"
+ [1]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder12345"
+ [2]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder;123456"
+}
+
+-- Iteration 7 --
+array(0) {
+}
+array(0) {
+}
+array(0) {
+}
+array(1) {
+ [0]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/++onder*"
+}
+array(0) {
+}
+array(0) {
+}
+
+-- Iteration 8 --
+
+Warning: glob() expects parameter 1 to be a valid path, string given %sglob_variation-win32-mb.php on line %d
+NULL
+
+Warning: glob() expects parameter 1 to be a valid path, string given %sglob_variation-win32-mb.php on line %d
+NULL
+
+Warning: glob() expects parameter 1 to be a valid path, string given %sglob_variation-win32-mb.php on line %d
+NULL
+
+Warning: glob() expects parameter 1 to be a valid path, string given %sglob_variation-win32-mb.php on line %d
+NULL
+
+Warning: glob() expects parameter 1 to be a valid path, string given %sglob_variation-win32-mb.php on line %d
+NULL
+
+Warning: glob() expects parameter 1 to be a valid path, string given %sglob_variation-win32-mb.php on line %d
+NULL
+
+-- Iteration 9 --
+array(0) {
+}
+array(0) {
+}
+array(0) {
+}
+array(1) {
+ [0]=>
+ string(%d) "$file_path/glob_variation私はガラスを食べられます/wonder5"
+}
+array(0) {
+}
+array(0) {
+}
+
+-- Iteration 10 --
+array(0) {
+}
+array(0) {
+}
+array(0) {
+}
+array(1) {
+ [0]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/?wonder?"
+}
+array(0) {
+}
+array(0) {
+}
+
+-- Iteration 11 --
+array(0) {
+}
+array(0) {
+}
+array(0) {
+}
+array(1) {
+ [0]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder?"
+}
+array(0) {
+}
+array(0) {
+}
+
+-- Iteration 12 --
+array(0) {
+}
+array(0) {
+}
+array(0) {
+}
+array(1) {
+ [0]=>
+ string(%d) "1"
+}
+array(0) {
+}
+array(0) {
+}
+
+*** Testing glob() with pattern within braces ***
+array(1) {
+ [0]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder12345"
+}
+
+*** Testing glob() on directories ***
+-- Iteration 1 --
+array(1) {
+ [0]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder1"
+}
+-- Iteration 2 --
+array(1) {
+ [0]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder1"
+}
+-- Iteration 3 --
+array(1) {
+ [0]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder1"
+}
+-- Iteration 4 --
+array(0) {
+}
+-- Iteration 5 --
+array(0) {
+}
+-- Iteration 6 --
+array(1) {
+ [0]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder1"
+}
+-- Iteration 7 --
+array(0) {
+}
+-- Iteration 8 --
+
+Warning: glob() expects parameter 1 to be a valid path, string given in %sglob_variation-win32-mb.php on line %d
+NULL
+-- Iteration 9 --
+array(0) {
+}
+-- Iteration 10 --
+array(0) {
+}
+-- Iteration 11 --
+array(1) {
+ [0]=>
+ string(%d) "%s/glob_variation私はガラスを食べられます/wonder1"
+}
+-- Iteration 12 --
+array(0) {
+}
+Done
diff --git a/ext/standard/tests/file/is_executable_basic-win32-mb.phpt b/ext/standard/tests/file/is_executable_basic-win32-mb.phpt
new file mode 100644
index 0000000000..eb71c63bd3
--- /dev/null
+++ b/ext/standard/tests/file/is_executable_basic-win32-mb.phpt
@@ -0,0 +1,1064 @@
+--TEST--
+Test is_executable() function: basic functionality
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die('skip.. only for Windows');
+}
+?>
+--FILE--
+<?php
+/* Prototype: bool is_executable ( string $filename );
+ Description: Tells whether the filename is executable
+*/
+require dirname(__FILE__).'/file.inc';
+
+echo "*** Testing is_executable(): basic functionality ***\n";
+
+// create a file
+$filename = dirname(__FILE__)."/私はガラスを食べられますis_executable.tmp";
+create_file($filename);
+
+$counter = 1;
+/* loop to check if the file with new mode is executable
+ using is_executable() */
+for($mode = 0000; $mode <= 0777; $mode++) {
+ echo "-- Changing mode of file to $mode --\n";
+ chmod($filename, $mode); // change mode of file
+ var_dump( is_executable($filename) );
+ $counter++;
+ clearstatcache();
+}
+
+// delete the temp file
+delete_file($filename);
+
+echo "Done\n";
+?>
+--EXPECTF--
+*** Testing is_executable(): basic functionality ***
+-- Changing mode of file to 0 --
+bool(false)
+-- Changing mode of file to 1 --
+bool(false)
+-- Changing mode of file to 2 --
+bool(false)
+-- Changing mode of file to 3 --
+bool(false)
+-- Changing mode of file to 4 --
+bool(false)
+-- Changing mode of file to 5 --
+bool(false)
+-- Changing mode of file to 6 --
+bool(false)
+-- Changing mode of file to 7 --
+bool(false)
+-- Changing mode of file to 8 --
+bool(false)
+-- Changing mode of file to 9 --
+bool(false)
+-- Changing mode of file to 10 --
+bool(false)
+-- Changing mode of file to 11 --
+bool(false)
+-- Changing mode of file to 12 --
+bool(false)
+-- Changing mode of file to 13 --
+bool(false)
+-- Changing mode of file to 14 --
+bool(false)
+-- Changing mode of file to 15 --
+bool(false)
+-- Changing mode of file to 16 --
+bool(false)
+-- Changing mode of file to 17 --
+bool(false)
+-- Changing mode of file to 18 --
+bool(false)
+-- Changing mode of file to 19 --
+bool(false)
+-- Changing mode of file to 20 --
+bool(false)
+-- Changing mode of file to 21 --
+bool(false)
+-- Changing mode of file to 22 --
+bool(false)
+-- Changing mode of file to 23 --
+bool(false)
+-- Changing mode of file to 24 --
+bool(false)
+-- Changing mode of file to 25 --
+bool(false)
+-- Changing mode of file to 26 --
+bool(false)
+-- Changing mode of file to 27 --
+bool(false)
+-- Changing mode of file to 28 --
+bool(false)
+-- Changing mode of file to 29 --
+bool(false)
+-- Changing mode of file to 30 --
+bool(false)
+-- Changing mode of file to 31 --
+bool(false)
+-- Changing mode of file to 32 --
+bool(false)
+-- Changing mode of file to 33 --
+bool(false)
+-- Changing mode of file to 34 --
+bool(false)
+-- Changing mode of file to 35 --
+bool(false)
+-- Changing mode of file to 36 --
+bool(false)
+-- Changing mode of file to 37 --
+bool(false)
+-- Changing mode of file to 38 --
+bool(false)
+-- Changing mode of file to 39 --
+bool(false)
+-- Changing mode of file to 40 --
+bool(false)
+-- Changing mode of file to 41 --
+bool(false)
+-- Changing mode of file to 42 --
+bool(false)
+-- Changing mode of file to 43 --
+bool(false)
+-- Changing mode of file to 44 --
+bool(false)
+-- Changing mode of file to 45 --
+bool(false)
+-- Changing mode of file to 46 --
+bool(false)
+-- Changing mode of file to 47 --
+bool(false)
+-- Changing mode of file to 48 --
+bool(false)
+-- Changing mode of file to 49 --
+bool(false)
+-- Changing mode of file to 50 --
+bool(false)
+-- Changing mode of file to 51 --
+bool(false)
+-- Changing mode of file to 52 --
+bool(false)
+-- Changing mode of file to 53 --
+bool(false)
+-- Changing mode of file to 54 --
+bool(false)
+-- Changing mode of file to 55 --
+bool(false)
+-- Changing mode of file to 56 --
+bool(false)
+-- Changing mode of file to 57 --
+bool(false)
+-- Changing mode of file to 58 --
+bool(false)
+-- Changing mode of file to 59 --
+bool(false)
+-- Changing mode of file to 60 --
+bool(false)
+-- Changing mode of file to 61 --
+bool(false)
+-- Changing mode of file to 62 --
+bool(false)
+-- Changing mode of file to 63 --
+bool(false)
+-- Changing mode of file to 64 --
+bool(false)
+-- Changing mode of file to 65 --
+bool(false)
+-- Changing mode of file to 66 --
+bool(false)
+-- Changing mode of file to 67 --
+bool(false)
+-- Changing mode of file to 68 --
+bool(false)
+-- Changing mode of file to 69 --
+bool(false)
+-- Changing mode of file to 70 --
+bool(false)
+-- Changing mode of file to 71 --
+bool(false)
+-- Changing mode of file to 72 --
+bool(false)
+-- Changing mode of file to 73 --
+bool(false)
+-- Changing mode of file to 74 --
+bool(false)
+-- Changing mode of file to 75 --
+bool(false)
+-- Changing mode of file to 76 --
+bool(false)
+-- Changing mode of file to 77 --
+bool(false)
+-- Changing mode of file to 78 --
+bool(false)
+-- Changing mode of file to 79 --
+bool(false)
+-- Changing mode of file to 80 --
+bool(false)
+-- Changing mode of file to 81 --
+bool(false)
+-- Changing mode of file to 82 --
+bool(false)
+-- Changing mode of file to 83 --
+bool(false)
+-- Changing mode of file to 84 --
+bool(false)
+-- Changing mode of file to 85 --
+bool(false)
+-- Changing mode of file to 86 --
+bool(false)
+-- Changing mode of file to 87 --
+bool(false)
+-- Changing mode of file to 88 --
+bool(false)
+-- Changing mode of file to 89 --
+bool(false)
+-- Changing mode of file to 90 --
+bool(false)
+-- Changing mode of file to 91 --
+bool(false)
+-- Changing mode of file to 92 --
+bool(false)
+-- Changing mode of file to 93 --
+bool(false)
+-- Changing mode of file to 94 --
+bool(false)
+-- Changing mode of file to 95 --
+bool(false)
+-- Changing mode of file to 96 --
+bool(false)
+-- Changing mode of file to 97 --
+bool(false)
+-- Changing mode of file to 98 --
+bool(false)
+-- Changing mode of file to 99 --
+bool(false)
+-- Changing mode of file to 100 --
+bool(false)
+-- Changing mode of file to 101 --
+bool(false)
+-- Changing mode of file to 102 --
+bool(false)
+-- Changing mode of file to 103 --
+bool(false)
+-- Changing mode of file to 104 --
+bool(false)
+-- Changing mode of file to 105 --
+bool(false)
+-- Changing mode of file to 106 --
+bool(false)
+-- Changing mode of file to 107 --
+bool(false)
+-- Changing mode of file to 108 --
+bool(false)
+-- Changing mode of file to 109 --
+bool(false)
+-- Changing mode of file to 110 --
+bool(false)
+-- Changing mode of file to 111 --
+bool(false)
+-- Changing mode of file to 112 --
+bool(false)
+-- Changing mode of file to 113 --
+bool(false)
+-- Changing mode of file to 114 --
+bool(false)
+-- Changing mode of file to 115 --
+bool(false)
+-- Changing mode of file to 116 --
+bool(false)
+-- Changing mode of file to 117 --
+bool(false)
+-- Changing mode of file to 118 --
+bool(false)
+-- Changing mode of file to 119 --
+bool(false)
+-- Changing mode of file to 120 --
+bool(false)
+-- Changing mode of file to 121 --
+bool(false)
+-- Changing mode of file to 122 --
+bool(false)
+-- Changing mode of file to 123 --
+bool(false)
+-- Changing mode of file to 124 --
+bool(false)
+-- Changing mode of file to 125 --
+bool(false)
+-- Changing mode of file to 126 --
+bool(false)
+-- Changing mode of file to 127 --
+bool(false)
+-- Changing mode of file to 128 --
+bool(false)
+-- Changing mode of file to 129 --
+bool(false)
+-- Changing mode of file to 130 --
+bool(false)
+-- Changing mode of file to 131 --
+bool(false)
+-- Changing mode of file to 132 --
+bool(false)
+-- Changing mode of file to 133 --
+bool(false)
+-- Changing mode of file to 134 --
+bool(false)
+-- Changing mode of file to 135 --
+bool(false)
+-- Changing mode of file to 136 --
+bool(false)
+-- Changing mode of file to 137 --
+bool(false)
+-- Changing mode of file to 138 --
+bool(false)
+-- Changing mode of file to 139 --
+bool(false)
+-- Changing mode of file to 140 --
+bool(false)
+-- Changing mode of file to 141 --
+bool(false)
+-- Changing mode of file to 142 --
+bool(false)
+-- Changing mode of file to 143 --
+bool(false)
+-- Changing mode of file to 144 --
+bool(false)
+-- Changing mode of file to 145 --
+bool(false)
+-- Changing mode of file to 146 --
+bool(false)
+-- Changing mode of file to 147 --
+bool(false)
+-- Changing mode of file to 148 --
+bool(false)
+-- Changing mode of file to 149 --
+bool(false)
+-- Changing mode of file to 150 --
+bool(false)
+-- Changing mode of file to 151 --
+bool(false)
+-- Changing mode of file to 152 --
+bool(false)
+-- Changing mode of file to 153 --
+bool(false)
+-- Changing mode of file to 154 --
+bool(false)
+-- Changing mode of file to 155 --
+bool(false)
+-- Changing mode of file to 156 --
+bool(false)
+-- Changing mode of file to 157 --
+bool(false)
+-- Changing mode of file to 158 --
+bool(false)
+-- Changing mode of file to 159 --
+bool(false)
+-- Changing mode of file to 160 --
+bool(false)
+-- Changing mode of file to 161 --
+bool(false)
+-- Changing mode of file to 162 --
+bool(false)
+-- Changing mode of file to 163 --
+bool(false)
+-- Changing mode of file to 164 --
+bool(false)
+-- Changing mode of file to 165 --
+bool(false)
+-- Changing mode of file to 166 --
+bool(false)
+-- Changing mode of file to 167 --
+bool(false)
+-- Changing mode of file to 168 --
+bool(false)
+-- Changing mode of file to 169 --
+bool(false)
+-- Changing mode of file to 170 --
+bool(false)
+-- Changing mode of file to 171 --
+bool(false)
+-- Changing mode of file to 172 --
+bool(false)
+-- Changing mode of file to 173 --
+bool(false)
+-- Changing mode of file to 174 --
+bool(false)
+-- Changing mode of file to 175 --
+bool(false)
+-- Changing mode of file to 176 --
+bool(false)
+-- Changing mode of file to 177 --
+bool(false)
+-- Changing mode of file to 178 --
+bool(false)
+-- Changing mode of file to 179 --
+bool(false)
+-- Changing mode of file to 180 --
+bool(false)
+-- Changing mode of file to 181 --
+bool(false)
+-- Changing mode of file to 182 --
+bool(false)
+-- Changing mode of file to 183 --
+bool(false)
+-- Changing mode of file to 184 --
+bool(false)
+-- Changing mode of file to 185 --
+bool(false)
+-- Changing mode of file to 186 --
+bool(false)
+-- Changing mode of file to 187 --
+bool(false)
+-- Changing mode of file to 188 --
+bool(false)
+-- Changing mode of file to 189 --
+bool(false)
+-- Changing mode of file to 190 --
+bool(false)
+-- Changing mode of file to 191 --
+bool(false)
+-- Changing mode of file to 192 --
+bool(false)
+-- Changing mode of file to 193 --
+bool(false)
+-- Changing mode of file to 194 --
+bool(false)
+-- Changing mode of file to 195 --
+bool(false)
+-- Changing mode of file to 196 --
+bool(false)
+-- Changing mode of file to 197 --
+bool(false)
+-- Changing mode of file to 198 --
+bool(false)
+-- Changing mode of file to 199 --
+bool(false)
+-- Changing mode of file to 200 --
+bool(false)
+-- Changing mode of file to 201 --
+bool(false)
+-- Changing mode of file to 202 --
+bool(false)
+-- Changing mode of file to 203 --
+bool(false)
+-- Changing mode of file to 204 --
+bool(false)
+-- Changing mode of file to 205 --
+bool(false)
+-- Changing mode of file to 206 --
+bool(false)
+-- Changing mode of file to 207 --
+bool(false)
+-- Changing mode of file to 208 --
+bool(false)
+-- Changing mode of file to 209 --
+bool(false)
+-- Changing mode of file to 210 --
+bool(false)
+-- Changing mode of file to 211 --
+bool(false)
+-- Changing mode of file to 212 --
+bool(false)
+-- Changing mode of file to 213 --
+bool(false)
+-- Changing mode of file to 214 --
+bool(false)
+-- Changing mode of file to 215 --
+bool(false)
+-- Changing mode of file to 216 --
+bool(false)
+-- Changing mode of file to 217 --
+bool(false)
+-- Changing mode of file to 218 --
+bool(false)
+-- Changing mode of file to 219 --
+bool(false)
+-- Changing mode of file to 220 --
+bool(false)
+-- Changing mode of file to 221 --
+bool(false)
+-- Changing mode of file to 222 --
+bool(false)
+-- Changing mode of file to 223 --
+bool(false)
+-- Changing mode of file to 224 --
+bool(false)
+-- Changing mode of file to 225 --
+bool(false)
+-- Changing mode of file to 226 --
+bool(false)
+-- Changing mode of file to 227 --
+bool(false)
+-- Changing mode of file to 228 --
+bool(false)
+-- Changing mode of file to 229 --
+bool(false)
+-- Changing mode of file to 230 --
+bool(false)
+-- Changing mode of file to 231 --
+bool(false)
+-- Changing mode of file to 232 --
+bool(false)
+-- Changing mode of file to 233 --
+bool(false)
+-- Changing mode of file to 234 --
+bool(false)
+-- Changing mode of file to 235 --
+bool(false)
+-- Changing mode of file to 236 --
+bool(false)
+-- Changing mode of file to 237 --
+bool(false)
+-- Changing mode of file to 238 --
+bool(false)
+-- Changing mode of file to 239 --
+bool(false)
+-- Changing mode of file to 240 --
+bool(false)
+-- Changing mode of file to 241 --
+bool(false)
+-- Changing mode of file to 242 --
+bool(false)
+-- Changing mode of file to 243 --
+bool(false)
+-- Changing mode of file to 244 --
+bool(false)
+-- Changing mode of file to 245 --
+bool(false)
+-- Changing mode of file to 246 --
+bool(false)
+-- Changing mode of file to 247 --
+bool(false)
+-- Changing mode of file to 248 --
+bool(false)
+-- Changing mode of file to 249 --
+bool(false)
+-- Changing mode of file to 250 --
+bool(false)
+-- Changing mode of file to 251 --
+bool(false)
+-- Changing mode of file to 252 --
+bool(false)
+-- Changing mode of file to 253 --
+bool(false)
+-- Changing mode of file to 254 --
+bool(false)
+-- Changing mode of file to 255 --
+bool(false)
+-- Changing mode of file to 256 --
+bool(false)
+-- Changing mode of file to 257 --
+bool(false)
+-- Changing mode of file to 258 --
+bool(false)
+-- Changing mode of file to 259 --
+bool(false)
+-- Changing mode of file to 260 --
+bool(false)
+-- Changing mode of file to 261 --
+bool(false)
+-- Changing mode of file to 262 --
+bool(false)
+-- Changing mode of file to 263 --
+bool(false)
+-- Changing mode of file to 264 --
+bool(false)
+-- Changing mode of file to 265 --
+bool(false)
+-- Changing mode of file to 266 --
+bool(false)
+-- Changing mode of file to 267 --
+bool(false)
+-- Changing mode of file to 268 --
+bool(false)
+-- Changing mode of file to 269 --
+bool(false)
+-- Changing mode of file to 270 --
+bool(false)
+-- Changing mode of file to 271 --
+bool(false)
+-- Changing mode of file to 272 --
+bool(false)
+-- Changing mode of file to 273 --
+bool(false)
+-- Changing mode of file to 274 --
+bool(false)
+-- Changing mode of file to 275 --
+bool(false)
+-- Changing mode of file to 276 --
+bool(false)
+-- Changing mode of file to 277 --
+bool(false)
+-- Changing mode of file to 278 --
+bool(false)
+-- Changing mode of file to 279 --
+bool(false)
+-- Changing mode of file to 280 --
+bool(false)
+-- Changing mode of file to 281 --
+bool(false)
+-- Changing mode of file to 282 --
+bool(false)
+-- Changing mode of file to 283 --
+bool(false)
+-- Changing mode of file to 284 --
+bool(false)
+-- Changing mode of file to 285 --
+bool(false)
+-- Changing mode of file to 286 --
+bool(false)
+-- Changing mode of file to 287 --
+bool(false)
+-- Changing mode of file to 288 --
+bool(false)
+-- Changing mode of file to 289 --
+bool(false)
+-- Changing mode of file to 290 --
+bool(false)
+-- Changing mode of file to 291 --
+bool(false)
+-- Changing mode of file to 292 --
+bool(false)
+-- Changing mode of file to 293 --
+bool(false)
+-- Changing mode of file to 294 --
+bool(false)
+-- Changing mode of file to 295 --
+bool(false)
+-- Changing mode of file to 296 --
+bool(false)
+-- Changing mode of file to 297 --
+bool(false)
+-- Changing mode of file to 298 --
+bool(false)
+-- Changing mode of file to 299 --
+bool(false)
+-- Changing mode of file to 300 --
+bool(false)
+-- Changing mode of file to 301 --
+bool(false)
+-- Changing mode of file to 302 --
+bool(false)
+-- Changing mode of file to 303 --
+bool(false)
+-- Changing mode of file to 304 --
+bool(false)
+-- Changing mode of file to 305 --
+bool(false)
+-- Changing mode of file to 306 --
+bool(false)
+-- Changing mode of file to 307 --
+bool(false)
+-- Changing mode of file to 308 --
+bool(false)
+-- Changing mode of file to 309 --
+bool(false)
+-- Changing mode of file to 310 --
+bool(false)
+-- Changing mode of file to 311 --
+bool(false)
+-- Changing mode of file to 312 --
+bool(false)
+-- Changing mode of file to 313 --
+bool(false)
+-- Changing mode of file to 314 --
+bool(false)
+-- Changing mode of file to 315 --
+bool(false)
+-- Changing mode of file to 316 --
+bool(false)
+-- Changing mode of file to 317 --
+bool(false)
+-- Changing mode of file to 318 --
+bool(false)
+-- Changing mode of file to 319 --
+bool(false)
+-- Changing mode of file to 320 --
+bool(false)
+-- Changing mode of file to 321 --
+bool(false)
+-- Changing mode of file to 322 --
+bool(false)
+-- Changing mode of file to 323 --
+bool(false)
+-- Changing mode of file to 324 --
+bool(false)
+-- Changing mode of file to 325 --
+bool(false)
+-- Changing mode of file to 326 --
+bool(false)
+-- Changing mode of file to 327 --
+bool(false)
+-- Changing mode of file to 328 --
+bool(false)
+-- Changing mode of file to 329 --
+bool(false)
+-- Changing mode of file to 330 --
+bool(false)
+-- Changing mode of file to 331 --
+bool(false)
+-- Changing mode of file to 332 --
+bool(false)
+-- Changing mode of file to 333 --
+bool(false)
+-- Changing mode of file to 334 --
+bool(false)
+-- Changing mode of file to 335 --
+bool(false)
+-- Changing mode of file to 336 --
+bool(false)
+-- Changing mode of file to 337 --
+bool(false)
+-- Changing mode of file to 338 --
+bool(false)
+-- Changing mode of file to 339 --
+bool(false)
+-- Changing mode of file to 340 --
+bool(false)
+-- Changing mode of file to 341 --
+bool(false)
+-- Changing mode of file to 342 --
+bool(false)
+-- Changing mode of file to 343 --
+bool(false)
+-- Changing mode of file to 344 --
+bool(false)
+-- Changing mode of file to 345 --
+bool(false)
+-- Changing mode of file to 346 --
+bool(false)
+-- Changing mode of file to 347 --
+bool(false)
+-- Changing mode of file to 348 --
+bool(false)
+-- Changing mode of file to 349 --
+bool(false)
+-- Changing mode of file to 350 --
+bool(false)
+-- Changing mode of file to 351 --
+bool(false)
+-- Changing mode of file to 352 --
+bool(false)
+-- Changing mode of file to 353 --
+bool(false)
+-- Changing mode of file to 354 --
+bool(false)
+-- Changing mode of file to 355 --
+bool(false)
+-- Changing mode of file to 356 --
+bool(false)
+-- Changing mode of file to 357 --
+bool(false)
+-- Changing mode of file to 358 --
+bool(false)
+-- Changing mode of file to 359 --
+bool(false)
+-- Changing mode of file to 360 --
+bool(false)
+-- Changing mode of file to 361 --
+bool(false)
+-- Changing mode of file to 362 --
+bool(false)
+-- Changing mode of file to 363 --
+bool(false)
+-- Changing mode of file to 364 --
+bool(false)
+-- Changing mode of file to 365 --
+bool(false)
+-- Changing mode of file to 366 --
+bool(false)
+-- Changing mode of file to 367 --
+bool(false)
+-- Changing mode of file to 368 --
+bool(false)
+-- Changing mode of file to 369 --
+bool(false)
+-- Changing mode of file to 370 --
+bool(false)
+-- Changing mode of file to 371 --
+bool(false)
+-- Changing mode of file to 372 --
+bool(false)
+-- Changing mode of file to 373 --
+bool(false)
+-- Changing mode of file to 374 --
+bool(false)
+-- Changing mode of file to 375 --
+bool(false)
+-- Changing mode of file to 376 --
+bool(false)
+-- Changing mode of file to 377 --
+bool(false)
+-- Changing mode of file to 378 --
+bool(false)
+-- Changing mode of file to 379 --
+bool(false)
+-- Changing mode of file to 380 --
+bool(false)
+-- Changing mode of file to 381 --
+bool(false)
+-- Changing mode of file to 382 --
+bool(false)
+-- Changing mode of file to 383 --
+bool(false)
+-- Changing mode of file to 384 --
+bool(false)
+-- Changing mode of file to 385 --
+bool(false)
+-- Changing mode of file to 386 --
+bool(false)
+-- Changing mode of file to 387 --
+bool(false)
+-- Changing mode of file to 388 --
+bool(false)
+-- Changing mode of file to 389 --
+bool(false)
+-- Changing mode of file to 390 --
+bool(false)
+-- Changing mode of file to 391 --
+bool(false)
+-- Changing mode of file to 392 --
+bool(false)
+-- Changing mode of file to 393 --
+bool(false)
+-- Changing mode of file to 394 --
+bool(false)
+-- Changing mode of file to 395 --
+bool(false)
+-- Changing mode of file to 396 --
+bool(false)
+-- Changing mode of file to 397 --
+bool(false)
+-- Changing mode of file to 398 --
+bool(false)
+-- Changing mode of file to 399 --
+bool(false)
+-- Changing mode of file to 400 --
+bool(false)
+-- Changing mode of file to 401 --
+bool(false)
+-- Changing mode of file to 402 --
+bool(false)
+-- Changing mode of file to 403 --
+bool(false)
+-- Changing mode of file to 404 --
+bool(false)
+-- Changing mode of file to 405 --
+bool(false)
+-- Changing mode of file to 406 --
+bool(false)
+-- Changing mode of file to 407 --
+bool(false)
+-- Changing mode of file to 408 --
+bool(false)
+-- Changing mode of file to 409 --
+bool(false)
+-- Changing mode of file to 410 --
+bool(false)
+-- Changing mode of file to 411 --
+bool(false)
+-- Changing mode of file to 412 --
+bool(false)
+-- Changing mode of file to 413 --
+bool(false)
+-- Changing mode of file to 414 --
+bool(false)
+-- Changing mode of file to 415 --
+bool(false)
+-- Changing mode of file to 416 --
+bool(false)
+-- Changing mode of file to 417 --
+bool(false)
+-- Changing mode of file to 418 --
+bool(false)
+-- Changing mode of file to 419 --
+bool(false)
+-- Changing mode of file to 420 --
+bool(false)
+-- Changing mode of file to 421 --
+bool(false)
+-- Changing mode of file to 422 --
+bool(false)
+-- Changing mode of file to 423 --
+bool(false)
+-- Changing mode of file to 424 --
+bool(false)
+-- Changing mode of file to 425 --
+bool(false)
+-- Changing mode of file to 426 --
+bool(false)
+-- Changing mode of file to 427 --
+bool(false)
+-- Changing mode of file to 428 --
+bool(false)
+-- Changing mode of file to 429 --
+bool(false)
+-- Changing mode of file to 430 --
+bool(false)
+-- Changing mode of file to 431 --
+bool(false)
+-- Changing mode of file to 432 --
+bool(false)
+-- Changing mode of file to 433 --
+bool(false)
+-- Changing mode of file to 434 --
+bool(false)
+-- Changing mode of file to 435 --
+bool(false)
+-- Changing mode of file to 436 --
+bool(false)
+-- Changing mode of file to 437 --
+bool(false)
+-- Changing mode of file to 438 --
+bool(false)
+-- Changing mode of file to 439 --
+bool(false)
+-- Changing mode of file to 440 --
+bool(false)
+-- Changing mode of file to 441 --
+bool(false)
+-- Changing mode of file to 442 --
+bool(false)
+-- Changing mode of file to 443 --
+bool(false)
+-- Changing mode of file to 444 --
+bool(false)
+-- Changing mode of file to 445 --
+bool(false)
+-- Changing mode of file to 446 --
+bool(false)
+-- Changing mode of file to 447 --
+bool(false)
+-- Changing mode of file to 448 --
+bool(false)
+-- Changing mode of file to 449 --
+bool(false)
+-- Changing mode of file to 450 --
+bool(false)
+-- Changing mode of file to 451 --
+bool(false)
+-- Changing mode of file to 452 --
+bool(false)
+-- Changing mode of file to 453 --
+bool(false)
+-- Changing mode of file to 454 --
+bool(false)
+-- Changing mode of file to 455 --
+bool(false)
+-- Changing mode of file to 456 --
+bool(false)
+-- Changing mode of file to 457 --
+bool(false)
+-- Changing mode of file to 458 --
+bool(false)
+-- Changing mode of file to 459 --
+bool(false)
+-- Changing mode of file to 460 --
+bool(false)
+-- Changing mode of file to 461 --
+bool(false)
+-- Changing mode of file to 462 --
+bool(false)
+-- Changing mode of file to 463 --
+bool(false)
+-- Changing mode of file to 464 --
+bool(false)
+-- Changing mode of file to 465 --
+bool(false)
+-- Changing mode of file to 466 --
+bool(false)
+-- Changing mode of file to 467 --
+bool(false)
+-- Changing mode of file to 468 --
+bool(false)
+-- Changing mode of file to 469 --
+bool(false)
+-- Changing mode of file to 470 --
+bool(false)
+-- Changing mode of file to 471 --
+bool(false)
+-- Changing mode of file to 472 --
+bool(false)
+-- Changing mode of file to 473 --
+bool(false)
+-- Changing mode of file to 474 --
+bool(false)
+-- Changing mode of file to 475 --
+bool(false)
+-- Changing mode of file to 476 --
+bool(false)
+-- Changing mode of file to 477 --
+bool(false)
+-- Changing mode of file to 478 --
+bool(false)
+-- Changing mode of file to 479 --
+bool(false)
+-- Changing mode of file to 480 --
+bool(false)
+-- Changing mode of file to 481 --
+bool(false)
+-- Changing mode of file to 482 --
+bool(false)
+-- Changing mode of file to 483 --
+bool(false)
+-- Changing mode of file to 484 --
+bool(false)
+-- Changing mode of file to 485 --
+bool(false)
+-- Changing mode of file to 486 --
+bool(false)
+-- Changing mode of file to 487 --
+bool(false)
+-- Changing mode of file to 488 --
+bool(false)
+-- Changing mode of file to 489 --
+bool(false)
+-- Changing mode of file to 490 --
+bool(false)
+-- Changing mode of file to 491 --
+bool(false)
+-- Changing mode of file to 492 --
+bool(false)
+-- Changing mode of file to 493 --
+bool(false)
+-- Changing mode of file to 494 --
+bool(false)
+-- Changing mode of file to 495 --
+bool(false)
+-- Changing mode of file to 496 --
+bool(false)
+-- Changing mode of file to 497 --
+bool(false)
+-- Changing mode of file to 498 --
+bool(false)
+-- Changing mode of file to 499 --
+bool(false)
+-- Changing mode of file to 500 --
+bool(false)
+-- Changing mode of file to 501 --
+bool(false)
+-- Changing mode of file to 502 --
+bool(false)
+-- Changing mode of file to 503 --
+bool(false)
+-- Changing mode of file to 504 --
+bool(false)
+-- Changing mode of file to 505 --
+bool(false)
+-- Changing mode of file to 506 --
+bool(false)
+-- Changing mode of file to 507 --
+bool(false)
+-- Changing mode of file to 508 --
+bool(false)
+-- Changing mode of file to 509 --
+bool(false)
+-- Changing mode of file to 510 --
+bool(false)
+-- Changing mode of file to 511 --
+bool(false)
+Done
diff --git a/ext/standard/tests/file/is_readable_basic-win32-mb.phpt b/ext/standard/tests/file/is_readable_basic-win32-mb.phpt
new file mode 100644
index 0000000000..2950dfa590
--- /dev/null
+++ b/ext/standard/tests/file/is_readable_basic-win32-mb.phpt
@@ -0,0 +1,1066 @@
+--TEST--
+Test is_readable() function: basic functionality
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die('skip only for Windows');
+}
+?>
+--FILE--
+<?php
+/* Prototype: bool is_readable ( string $filename );
+ Description: Tells whether the filename is readable
+*/
+
+// include common file test functions
+require dirname(__FILE__).'/file.inc';
+
+echo "*** Testing is_readable(): basic functionality ***\n";
+
+// create a file
+$filename = dirname(__FILE__)."/私はガラスを食べられますis_readable.tmp";
+create_file($filename);
+
+$counter = 1;
+/* loop to check if the file with new mode is readable
+ using is_readable() */
+for($mode = 0000; $mode <= 0777; $mode++) {
+ echo "-- Changing mode of file to $mode --\n";
+ chmod($filename, $mode); // change mode of file
+ var_dump( is_readable($filename) );
+ $counter++;
+ clearstatcache();
+}
+
+// delete the temp file
+delete_file($filename);
+
+echo "Done\n";
+?>
+--EXPECTF--
+*** Testing is_readable(): basic functionality ***
+-- Changing mode of file to 0 --
+bool(true)
+-- Changing mode of file to 1 --
+bool(true)
+-- Changing mode of file to 2 --
+bool(true)
+-- Changing mode of file to 3 --
+bool(true)
+-- Changing mode of file to 4 --
+bool(true)
+-- Changing mode of file to 5 --
+bool(true)
+-- Changing mode of file to 6 --
+bool(true)
+-- Changing mode of file to 7 --
+bool(true)
+-- Changing mode of file to 8 --
+bool(true)
+-- Changing mode of file to 9 --
+bool(true)
+-- Changing mode of file to 10 --
+bool(true)
+-- Changing mode of file to 11 --
+bool(true)
+-- Changing mode of file to 12 --
+bool(true)
+-- Changing mode of file to 13 --
+bool(true)
+-- Changing mode of file to 14 --
+bool(true)
+-- Changing mode of file to 15 --
+bool(true)
+-- Changing mode of file to 16 --
+bool(true)
+-- Changing mode of file to 17 --
+bool(true)
+-- Changing mode of file to 18 --
+bool(true)
+-- Changing mode of file to 19 --
+bool(true)
+-- Changing mode of file to 20 --
+bool(true)
+-- Changing mode of file to 21 --
+bool(true)
+-- Changing mode of file to 22 --
+bool(true)
+-- Changing mode of file to 23 --
+bool(true)
+-- Changing mode of file to 24 --
+bool(true)
+-- Changing mode of file to 25 --
+bool(true)
+-- Changing mode of file to 26 --
+bool(true)
+-- Changing mode of file to 27 --
+bool(true)
+-- Changing mode of file to 28 --
+bool(true)
+-- Changing mode of file to 29 --
+bool(true)
+-- Changing mode of file to 30 --
+bool(true)
+-- Changing mode of file to 31 --
+bool(true)
+-- Changing mode of file to 32 --
+bool(true)
+-- Changing mode of file to 33 --
+bool(true)
+-- Changing mode of file to 34 --
+bool(true)
+-- Changing mode of file to 35 --
+bool(true)
+-- Changing mode of file to 36 --
+bool(true)
+-- Changing mode of file to 37 --
+bool(true)
+-- Changing mode of file to 38 --
+bool(true)
+-- Changing mode of file to 39 --
+bool(true)
+-- Changing mode of file to 40 --
+bool(true)
+-- Changing mode of file to 41 --
+bool(true)
+-- Changing mode of file to 42 --
+bool(true)
+-- Changing mode of file to 43 --
+bool(true)
+-- Changing mode of file to 44 --
+bool(true)
+-- Changing mode of file to 45 --
+bool(true)
+-- Changing mode of file to 46 --
+bool(true)
+-- Changing mode of file to 47 --
+bool(true)
+-- Changing mode of file to 48 --
+bool(true)
+-- Changing mode of file to 49 --
+bool(true)
+-- Changing mode of file to 50 --
+bool(true)
+-- Changing mode of file to 51 --
+bool(true)
+-- Changing mode of file to 52 --
+bool(true)
+-- Changing mode of file to 53 --
+bool(true)
+-- Changing mode of file to 54 --
+bool(true)
+-- Changing mode of file to 55 --
+bool(true)
+-- Changing mode of file to 56 --
+bool(true)
+-- Changing mode of file to 57 --
+bool(true)
+-- Changing mode of file to 58 --
+bool(true)
+-- Changing mode of file to 59 --
+bool(true)
+-- Changing mode of file to 60 --
+bool(true)
+-- Changing mode of file to 61 --
+bool(true)
+-- Changing mode of file to 62 --
+bool(true)
+-- Changing mode of file to 63 --
+bool(true)
+-- Changing mode of file to 64 --
+bool(true)
+-- Changing mode of file to 65 --
+bool(true)
+-- Changing mode of file to 66 --
+bool(true)
+-- Changing mode of file to 67 --
+bool(true)
+-- Changing mode of file to 68 --
+bool(true)
+-- Changing mode of file to 69 --
+bool(true)
+-- Changing mode of file to 70 --
+bool(true)
+-- Changing mode of file to 71 --
+bool(true)
+-- Changing mode of file to 72 --
+bool(true)
+-- Changing mode of file to 73 --
+bool(true)
+-- Changing mode of file to 74 --
+bool(true)
+-- Changing mode of file to 75 --
+bool(true)
+-- Changing mode of file to 76 --
+bool(true)
+-- Changing mode of file to 77 --
+bool(true)
+-- Changing mode of file to 78 --
+bool(true)
+-- Changing mode of file to 79 --
+bool(true)
+-- Changing mode of file to 80 --
+bool(true)
+-- Changing mode of file to 81 --
+bool(true)
+-- Changing mode of file to 82 --
+bool(true)
+-- Changing mode of file to 83 --
+bool(true)
+-- Changing mode of file to 84 --
+bool(true)
+-- Changing mode of file to 85 --
+bool(true)
+-- Changing mode of file to 86 --
+bool(true)
+-- Changing mode of file to 87 --
+bool(true)
+-- Changing mode of file to 88 --
+bool(true)
+-- Changing mode of file to 89 --
+bool(true)
+-- Changing mode of file to 90 --
+bool(true)
+-- Changing mode of file to 91 --
+bool(true)
+-- Changing mode of file to 92 --
+bool(true)
+-- Changing mode of file to 93 --
+bool(true)
+-- Changing mode of file to 94 --
+bool(true)
+-- Changing mode of file to 95 --
+bool(true)
+-- Changing mode of file to 96 --
+bool(true)
+-- Changing mode of file to 97 --
+bool(true)
+-- Changing mode of file to 98 --
+bool(true)
+-- Changing mode of file to 99 --
+bool(true)
+-- Changing mode of file to 100 --
+bool(true)
+-- Changing mode of file to 101 --
+bool(true)
+-- Changing mode of file to 102 --
+bool(true)
+-- Changing mode of file to 103 --
+bool(true)
+-- Changing mode of file to 104 --
+bool(true)
+-- Changing mode of file to 105 --
+bool(true)
+-- Changing mode of file to 106 --
+bool(true)
+-- Changing mode of file to 107 --
+bool(true)
+-- Changing mode of file to 108 --
+bool(true)
+-- Changing mode of file to 109 --
+bool(true)
+-- Changing mode of file to 110 --
+bool(true)
+-- Changing mode of file to 111 --
+bool(true)
+-- Changing mode of file to 112 --
+bool(true)
+-- Changing mode of file to 113 --
+bool(true)
+-- Changing mode of file to 114 --
+bool(true)
+-- Changing mode of file to 115 --
+bool(true)
+-- Changing mode of file to 116 --
+bool(true)
+-- Changing mode of file to 117 --
+bool(true)
+-- Changing mode of file to 118 --
+bool(true)
+-- Changing mode of file to 119 --
+bool(true)
+-- Changing mode of file to 120 --
+bool(true)
+-- Changing mode of file to 121 --
+bool(true)
+-- Changing mode of file to 122 --
+bool(true)
+-- Changing mode of file to 123 --
+bool(true)
+-- Changing mode of file to 124 --
+bool(true)
+-- Changing mode of file to 125 --
+bool(true)
+-- Changing mode of file to 126 --
+bool(true)
+-- Changing mode of file to 127 --
+bool(true)
+-- Changing mode of file to 128 --
+bool(true)
+-- Changing mode of file to 129 --
+bool(true)
+-- Changing mode of file to 130 --
+bool(true)
+-- Changing mode of file to 131 --
+bool(true)
+-- Changing mode of file to 132 --
+bool(true)
+-- Changing mode of file to 133 --
+bool(true)
+-- Changing mode of file to 134 --
+bool(true)
+-- Changing mode of file to 135 --
+bool(true)
+-- Changing mode of file to 136 --
+bool(true)
+-- Changing mode of file to 137 --
+bool(true)
+-- Changing mode of file to 138 --
+bool(true)
+-- Changing mode of file to 139 --
+bool(true)
+-- Changing mode of file to 140 --
+bool(true)
+-- Changing mode of file to 141 --
+bool(true)
+-- Changing mode of file to 142 --
+bool(true)
+-- Changing mode of file to 143 --
+bool(true)
+-- Changing mode of file to 144 --
+bool(true)
+-- Changing mode of file to 145 --
+bool(true)
+-- Changing mode of file to 146 --
+bool(true)
+-- Changing mode of file to 147 --
+bool(true)
+-- Changing mode of file to 148 --
+bool(true)
+-- Changing mode of file to 149 --
+bool(true)
+-- Changing mode of file to 150 --
+bool(true)
+-- Changing mode of file to 151 --
+bool(true)
+-- Changing mode of file to 152 --
+bool(true)
+-- Changing mode of file to 153 --
+bool(true)
+-- Changing mode of file to 154 --
+bool(true)
+-- Changing mode of file to 155 --
+bool(true)
+-- Changing mode of file to 156 --
+bool(true)
+-- Changing mode of file to 157 --
+bool(true)
+-- Changing mode of file to 158 --
+bool(true)
+-- Changing mode of file to 159 --
+bool(true)
+-- Changing mode of file to 160 --
+bool(true)
+-- Changing mode of file to 161 --
+bool(true)
+-- Changing mode of file to 162 --
+bool(true)
+-- Changing mode of file to 163 --
+bool(true)
+-- Changing mode of file to 164 --
+bool(true)
+-- Changing mode of file to 165 --
+bool(true)
+-- Changing mode of file to 166 --
+bool(true)
+-- Changing mode of file to 167 --
+bool(true)
+-- Changing mode of file to 168 --
+bool(true)
+-- Changing mode of file to 169 --
+bool(true)
+-- Changing mode of file to 170 --
+bool(true)
+-- Changing mode of file to 171 --
+bool(true)
+-- Changing mode of file to 172 --
+bool(true)
+-- Changing mode of file to 173 --
+bool(true)
+-- Changing mode of file to 174 --
+bool(true)
+-- Changing mode of file to 175 --
+bool(true)
+-- Changing mode of file to 176 --
+bool(true)
+-- Changing mode of file to 177 --
+bool(true)
+-- Changing mode of file to 178 --
+bool(true)
+-- Changing mode of file to 179 --
+bool(true)
+-- Changing mode of file to 180 --
+bool(true)
+-- Changing mode of file to 181 --
+bool(true)
+-- Changing mode of file to 182 --
+bool(true)
+-- Changing mode of file to 183 --
+bool(true)
+-- Changing mode of file to 184 --
+bool(true)
+-- Changing mode of file to 185 --
+bool(true)
+-- Changing mode of file to 186 --
+bool(true)
+-- Changing mode of file to 187 --
+bool(true)
+-- Changing mode of file to 188 --
+bool(true)
+-- Changing mode of file to 189 --
+bool(true)
+-- Changing mode of file to 190 --
+bool(true)
+-- Changing mode of file to 191 --
+bool(true)
+-- Changing mode of file to 192 --
+bool(true)
+-- Changing mode of file to 193 --
+bool(true)
+-- Changing mode of file to 194 --
+bool(true)
+-- Changing mode of file to 195 --
+bool(true)
+-- Changing mode of file to 196 --
+bool(true)
+-- Changing mode of file to 197 --
+bool(true)
+-- Changing mode of file to 198 --
+bool(true)
+-- Changing mode of file to 199 --
+bool(true)
+-- Changing mode of file to 200 --
+bool(true)
+-- Changing mode of file to 201 --
+bool(true)
+-- Changing mode of file to 202 --
+bool(true)
+-- Changing mode of file to 203 --
+bool(true)
+-- Changing mode of file to 204 --
+bool(true)
+-- Changing mode of file to 205 --
+bool(true)
+-- Changing mode of file to 206 --
+bool(true)
+-- Changing mode of file to 207 --
+bool(true)
+-- Changing mode of file to 208 --
+bool(true)
+-- Changing mode of file to 209 --
+bool(true)
+-- Changing mode of file to 210 --
+bool(true)
+-- Changing mode of file to 211 --
+bool(true)
+-- Changing mode of file to 212 --
+bool(true)
+-- Changing mode of file to 213 --
+bool(true)
+-- Changing mode of file to 214 --
+bool(true)
+-- Changing mode of file to 215 --
+bool(true)
+-- Changing mode of file to 216 --
+bool(true)
+-- Changing mode of file to 217 --
+bool(true)
+-- Changing mode of file to 218 --
+bool(true)
+-- Changing mode of file to 219 --
+bool(true)
+-- Changing mode of file to 220 --
+bool(true)
+-- Changing mode of file to 221 --
+bool(true)
+-- Changing mode of file to 222 --
+bool(true)
+-- Changing mode of file to 223 --
+bool(true)
+-- Changing mode of file to 224 --
+bool(true)
+-- Changing mode of file to 225 --
+bool(true)
+-- Changing mode of file to 226 --
+bool(true)
+-- Changing mode of file to 227 --
+bool(true)
+-- Changing mode of file to 228 --
+bool(true)
+-- Changing mode of file to 229 --
+bool(true)
+-- Changing mode of file to 230 --
+bool(true)
+-- Changing mode of file to 231 --
+bool(true)
+-- Changing mode of file to 232 --
+bool(true)
+-- Changing mode of file to 233 --
+bool(true)
+-- Changing mode of file to 234 --
+bool(true)
+-- Changing mode of file to 235 --
+bool(true)
+-- Changing mode of file to 236 --
+bool(true)
+-- Changing mode of file to 237 --
+bool(true)
+-- Changing mode of file to 238 --
+bool(true)
+-- Changing mode of file to 239 --
+bool(true)
+-- Changing mode of file to 240 --
+bool(true)
+-- Changing mode of file to 241 --
+bool(true)
+-- Changing mode of file to 242 --
+bool(true)
+-- Changing mode of file to 243 --
+bool(true)
+-- Changing mode of file to 244 --
+bool(true)
+-- Changing mode of file to 245 --
+bool(true)
+-- Changing mode of file to 246 --
+bool(true)
+-- Changing mode of file to 247 --
+bool(true)
+-- Changing mode of file to 248 --
+bool(true)
+-- Changing mode of file to 249 --
+bool(true)
+-- Changing mode of file to 250 --
+bool(true)
+-- Changing mode of file to 251 --
+bool(true)
+-- Changing mode of file to 252 --
+bool(true)
+-- Changing mode of file to 253 --
+bool(true)
+-- Changing mode of file to 254 --
+bool(true)
+-- Changing mode of file to 255 --
+bool(true)
+-- Changing mode of file to 256 --
+bool(true)
+-- Changing mode of file to 257 --
+bool(true)
+-- Changing mode of file to 258 --
+bool(true)
+-- Changing mode of file to 259 --
+bool(true)
+-- Changing mode of file to 260 --
+bool(true)
+-- Changing mode of file to 261 --
+bool(true)
+-- Changing mode of file to 262 --
+bool(true)
+-- Changing mode of file to 263 --
+bool(true)
+-- Changing mode of file to 264 --
+bool(true)
+-- Changing mode of file to 265 --
+bool(true)
+-- Changing mode of file to 266 --
+bool(true)
+-- Changing mode of file to 267 --
+bool(true)
+-- Changing mode of file to 268 --
+bool(true)
+-- Changing mode of file to 269 --
+bool(true)
+-- Changing mode of file to 270 --
+bool(true)
+-- Changing mode of file to 271 --
+bool(true)
+-- Changing mode of file to 272 --
+bool(true)
+-- Changing mode of file to 273 --
+bool(true)
+-- Changing mode of file to 274 --
+bool(true)
+-- Changing mode of file to 275 --
+bool(true)
+-- Changing mode of file to 276 --
+bool(true)
+-- Changing mode of file to 277 --
+bool(true)
+-- Changing mode of file to 278 --
+bool(true)
+-- Changing mode of file to 279 --
+bool(true)
+-- Changing mode of file to 280 --
+bool(true)
+-- Changing mode of file to 281 --
+bool(true)
+-- Changing mode of file to 282 --
+bool(true)
+-- Changing mode of file to 283 --
+bool(true)
+-- Changing mode of file to 284 --
+bool(true)
+-- Changing mode of file to 285 --
+bool(true)
+-- Changing mode of file to 286 --
+bool(true)
+-- Changing mode of file to 287 --
+bool(true)
+-- Changing mode of file to 288 --
+bool(true)
+-- Changing mode of file to 289 --
+bool(true)
+-- Changing mode of file to 290 --
+bool(true)
+-- Changing mode of file to 291 --
+bool(true)
+-- Changing mode of file to 292 --
+bool(true)
+-- Changing mode of file to 293 --
+bool(true)
+-- Changing mode of file to 294 --
+bool(true)
+-- Changing mode of file to 295 --
+bool(true)
+-- Changing mode of file to 296 --
+bool(true)
+-- Changing mode of file to 297 --
+bool(true)
+-- Changing mode of file to 298 --
+bool(true)
+-- Changing mode of file to 299 --
+bool(true)
+-- Changing mode of file to 300 --
+bool(true)
+-- Changing mode of file to 301 --
+bool(true)
+-- Changing mode of file to 302 --
+bool(true)
+-- Changing mode of file to 303 --
+bool(true)
+-- Changing mode of file to 304 --
+bool(true)
+-- Changing mode of file to 305 --
+bool(true)
+-- Changing mode of file to 306 --
+bool(true)
+-- Changing mode of file to 307 --
+bool(true)
+-- Changing mode of file to 308 --
+bool(true)
+-- Changing mode of file to 309 --
+bool(true)
+-- Changing mode of file to 310 --
+bool(true)
+-- Changing mode of file to 311 --
+bool(true)
+-- Changing mode of file to 312 --
+bool(true)
+-- Changing mode of file to 313 --
+bool(true)
+-- Changing mode of file to 314 --
+bool(true)
+-- Changing mode of file to 315 --
+bool(true)
+-- Changing mode of file to 316 --
+bool(true)
+-- Changing mode of file to 317 --
+bool(true)
+-- Changing mode of file to 318 --
+bool(true)
+-- Changing mode of file to 319 --
+bool(true)
+-- Changing mode of file to 320 --
+bool(true)
+-- Changing mode of file to 321 --
+bool(true)
+-- Changing mode of file to 322 --
+bool(true)
+-- Changing mode of file to 323 --
+bool(true)
+-- Changing mode of file to 324 --
+bool(true)
+-- Changing mode of file to 325 --
+bool(true)
+-- Changing mode of file to 326 --
+bool(true)
+-- Changing mode of file to 327 --
+bool(true)
+-- Changing mode of file to 328 --
+bool(true)
+-- Changing mode of file to 329 --
+bool(true)
+-- Changing mode of file to 330 --
+bool(true)
+-- Changing mode of file to 331 --
+bool(true)
+-- Changing mode of file to 332 --
+bool(true)
+-- Changing mode of file to 333 --
+bool(true)
+-- Changing mode of file to 334 --
+bool(true)
+-- Changing mode of file to 335 --
+bool(true)
+-- Changing mode of file to 336 --
+bool(true)
+-- Changing mode of file to 337 --
+bool(true)
+-- Changing mode of file to 338 --
+bool(true)
+-- Changing mode of file to 339 --
+bool(true)
+-- Changing mode of file to 340 --
+bool(true)
+-- Changing mode of file to 341 --
+bool(true)
+-- Changing mode of file to 342 --
+bool(true)
+-- Changing mode of file to 343 --
+bool(true)
+-- Changing mode of file to 344 --
+bool(true)
+-- Changing mode of file to 345 --
+bool(true)
+-- Changing mode of file to 346 --
+bool(true)
+-- Changing mode of file to 347 --
+bool(true)
+-- Changing mode of file to 348 --
+bool(true)
+-- Changing mode of file to 349 --
+bool(true)
+-- Changing mode of file to 350 --
+bool(true)
+-- Changing mode of file to 351 --
+bool(true)
+-- Changing mode of file to 352 --
+bool(true)
+-- Changing mode of file to 353 --
+bool(true)
+-- Changing mode of file to 354 --
+bool(true)
+-- Changing mode of file to 355 --
+bool(true)
+-- Changing mode of file to 356 --
+bool(true)
+-- Changing mode of file to 357 --
+bool(true)
+-- Changing mode of file to 358 --
+bool(true)
+-- Changing mode of file to 359 --
+bool(true)
+-- Changing mode of file to 360 --
+bool(true)
+-- Changing mode of file to 361 --
+bool(true)
+-- Changing mode of file to 362 --
+bool(true)
+-- Changing mode of file to 363 --
+bool(true)
+-- Changing mode of file to 364 --
+bool(true)
+-- Changing mode of file to 365 --
+bool(true)
+-- Changing mode of file to 366 --
+bool(true)
+-- Changing mode of file to 367 --
+bool(true)
+-- Changing mode of file to 368 --
+bool(true)
+-- Changing mode of file to 369 --
+bool(true)
+-- Changing mode of file to 370 --
+bool(true)
+-- Changing mode of file to 371 --
+bool(true)
+-- Changing mode of file to 372 --
+bool(true)
+-- Changing mode of file to 373 --
+bool(true)
+-- Changing mode of file to 374 --
+bool(true)
+-- Changing mode of file to 375 --
+bool(true)
+-- Changing mode of file to 376 --
+bool(true)
+-- Changing mode of file to 377 --
+bool(true)
+-- Changing mode of file to 378 --
+bool(true)
+-- Changing mode of file to 379 --
+bool(true)
+-- Changing mode of file to 380 --
+bool(true)
+-- Changing mode of file to 381 --
+bool(true)
+-- Changing mode of file to 382 --
+bool(true)
+-- Changing mode of file to 383 --
+bool(true)
+-- Changing mode of file to 384 --
+bool(true)
+-- Changing mode of file to 385 --
+bool(true)
+-- Changing mode of file to 386 --
+bool(true)
+-- Changing mode of file to 387 --
+bool(true)
+-- Changing mode of file to 388 --
+bool(true)
+-- Changing mode of file to 389 --
+bool(true)
+-- Changing mode of file to 390 --
+bool(true)
+-- Changing mode of file to 391 --
+bool(true)
+-- Changing mode of file to 392 --
+bool(true)
+-- Changing mode of file to 393 --
+bool(true)
+-- Changing mode of file to 394 --
+bool(true)
+-- Changing mode of file to 395 --
+bool(true)
+-- Changing mode of file to 396 --
+bool(true)
+-- Changing mode of file to 397 --
+bool(true)
+-- Changing mode of file to 398 --
+bool(true)
+-- Changing mode of file to 399 --
+bool(true)
+-- Changing mode of file to 400 --
+bool(true)
+-- Changing mode of file to 401 --
+bool(true)
+-- Changing mode of file to 402 --
+bool(true)
+-- Changing mode of file to 403 --
+bool(true)
+-- Changing mode of file to 404 --
+bool(true)
+-- Changing mode of file to 405 --
+bool(true)
+-- Changing mode of file to 406 --
+bool(true)
+-- Changing mode of file to 407 --
+bool(true)
+-- Changing mode of file to 408 --
+bool(true)
+-- Changing mode of file to 409 --
+bool(true)
+-- Changing mode of file to 410 --
+bool(true)
+-- Changing mode of file to 411 --
+bool(true)
+-- Changing mode of file to 412 --
+bool(true)
+-- Changing mode of file to 413 --
+bool(true)
+-- Changing mode of file to 414 --
+bool(true)
+-- Changing mode of file to 415 --
+bool(true)
+-- Changing mode of file to 416 --
+bool(true)
+-- Changing mode of file to 417 --
+bool(true)
+-- Changing mode of file to 418 --
+bool(true)
+-- Changing mode of file to 419 --
+bool(true)
+-- Changing mode of file to 420 --
+bool(true)
+-- Changing mode of file to 421 --
+bool(true)
+-- Changing mode of file to 422 --
+bool(true)
+-- Changing mode of file to 423 --
+bool(true)
+-- Changing mode of file to 424 --
+bool(true)
+-- Changing mode of file to 425 --
+bool(true)
+-- Changing mode of file to 426 --
+bool(true)
+-- Changing mode of file to 427 --
+bool(true)
+-- Changing mode of file to 428 --
+bool(true)
+-- Changing mode of file to 429 --
+bool(true)
+-- Changing mode of file to 430 --
+bool(true)
+-- Changing mode of file to 431 --
+bool(true)
+-- Changing mode of file to 432 --
+bool(true)
+-- Changing mode of file to 433 --
+bool(true)
+-- Changing mode of file to 434 --
+bool(true)
+-- Changing mode of file to 435 --
+bool(true)
+-- Changing mode of file to 436 --
+bool(true)
+-- Changing mode of file to 437 --
+bool(true)
+-- Changing mode of file to 438 --
+bool(true)
+-- Changing mode of file to 439 --
+bool(true)
+-- Changing mode of file to 440 --
+bool(true)
+-- Changing mode of file to 441 --
+bool(true)
+-- Changing mode of file to 442 --
+bool(true)
+-- Changing mode of file to 443 --
+bool(true)
+-- Changing mode of file to 444 --
+bool(true)
+-- Changing mode of file to 445 --
+bool(true)
+-- Changing mode of file to 446 --
+bool(true)
+-- Changing mode of file to 447 --
+bool(true)
+-- Changing mode of file to 448 --
+bool(true)
+-- Changing mode of file to 449 --
+bool(true)
+-- Changing mode of file to 450 --
+bool(true)
+-- Changing mode of file to 451 --
+bool(true)
+-- Changing mode of file to 452 --
+bool(true)
+-- Changing mode of file to 453 --
+bool(true)
+-- Changing mode of file to 454 --
+bool(true)
+-- Changing mode of file to 455 --
+bool(true)
+-- Changing mode of file to 456 --
+bool(true)
+-- Changing mode of file to 457 --
+bool(true)
+-- Changing mode of file to 458 --
+bool(true)
+-- Changing mode of file to 459 --
+bool(true)
+-- Changing mode of file to 460 --
+bool(true)
+-- Changing mode of file to 461 --
+bool(true)
+-- Changing mode of file to 462 --
+bool(true)
+-- Changing mode of file to 463 --
+bool(true)
+-- Changing mode of file to 464 --
+bool(true)
+-- Changing mode of file to 465 --
+bool(true)
+-- Changing mode of file to 466 --
+bool(true)
+-- Changing mode of file to 467 --
+bool(true)
+-- Changing mode of file to 468 --
+bool(true)
+-- Changing mode of file to 469 --
+bool(true)
+-- Changing mode of file to 470 --
+bool(true)
+-- Changing mode of file to 471 --
+bool(true)
+-- Changing mode of file to 472 --
+bool(true)
+-- Changing mode of file to 473 --
+bool(true)
+-- Changing mode of file to 474 --
+bool(true)
+-- Changing mode of file to 475 --
+bool(true)
+-- Changing mode of file to 476 --
+bool(true)
+-- Changing mode of file to 477 --
+bool(true)
+-- Changing mode of file to 478 --
+bool(true)
+-- Changing mode of file to 479 --
+bool(true)
+-- Changing mode of file to 480 --
+bool(true)
+-- Changing mode of file to 481 --
+bool(true)
+-- Changing mode of file to 482 --
+bool(true)
+-- Changing mode of file to 483 --
+bool(true)
+-- Changing mode of file to 484 --
+bool(true)
+-- Changing mode of file to 485 --
+bool(true)
+-- Changing mode of file to 486 --
+bool(true)
+-- Changing mode of file to 487 --
+bool(true)
+-- Changing mode of file to 488 --
+bool(true)
+-- Changing mode of file to 489 --
+bool(true)
+-- Changing mode of file to 490 --
+bool(true)
+-- Changing mode of file to 491 --
+bool(true)
+-- Changing mode of file to 492 --
+bool(true)
+-- Changing mode of file to 493 --
+bool(true)
+-- Changing mode of file to 494 --
+bool(true)
+-- Changing mode of file to 495 --
+bool(true)
+-- Changing mode of file to 496 --
+bool(true)
+-- Changing mode of file to 497 --
+bool(true)
+-- Changing mode of file to 498 --
+bool(true)
+-- Changing mode of file to 499 --
+bool(true)
+-- Changing mode of file to 500 --
+bool(true)
+-- Changing mode of file to 501 --
+bool(true)
+-- Changing mode of file to 502 --
+bool(true)
+-- Changing mode of file to 503 --
+bool(true)
+-- Changing mode of file to 504 --
+bool(true)
+-- Changing mode of file to 505 --
+bool(true)
+-- Changing mode of file to 506 --
+bool(true)
+-- Changing mode of file to 507 --
+bool(true)
+-- Changing mode of file to 508 --
+bool(true)
+-- Changing mode of file to 509 --
+bool(true)
+-- Changing mode of file to 510 --
+bool(true)
+-- Changing mode of file to 511 --
+bool(true)
+Done
diff --git a/ext/standard/tests/file/mkdir_rmdir_variation-win32-mb.phpt b/ext/standard/tests/file/mkdir_rmdir_variation-win32-mb.phpt
new file mode 100644
index 0000000000..785644d8b0
--- /dev/null
+++ b/ext/standard/tests/file/mkdir_rmdir_variation-win32-mb.phpt
@@ -0,0 +1,1613 @@
+--TEST--
+Test mkdir() and rmdir() functions: usage variations
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die('skip only for Windows');
+}
+?>
+--FILE--
+<?php
+/* Prototype: bool mkdir ( string $pathname [, int $mode [, bool $recursive [, resource $context]]] );
+ Description: Makes directory
+*/
+
+echo "*** Testing mkdir() and rmdir() for different permissions ***\n";
+
+$context = stream_context_create();
+
+$file_path = dirname(__FILE__);
+$counter = 1;
+
+for($mode = 0000; $mode <= 0777; $mode++) {
+ echo "-- Changing mode of directory to $mode --\n";
+ var_dump( mkdir("$file_path/mkdir私はガラスを食べられます/", $mode, true) );
+ var_dump( rmdir("$file_path/mkdir私はガラスを食べられます/") );
+ $counter++;
+}
+
+echo "\n*** Testing mkdir() and rmdir() by giving stream context as fourth argument ***\n";
+var_dump( mkdir("$file_path/mkdir私はガラスを食べられます/test/", 0777, true, $context) );
+var_dump( rmdir("$file_path/mkdir私はガラスを食べられます/test/", $context) );
+
+echo "\n*** Testing rmdir() on a non-empty directory ***\n";
+var_dump( mkdir("$file_path/mkdir私はガラスを食べられます/test/", 0777, true) );
+var_dump( rmdir("$file_path/mkdir私はガラスを食べられます/") );
+
+echo "\n*** Testing mkdir() and rmdir() for binary safe functionality ***\n";
+var_dump( mkdir("$file_path/tempx000/") );
+var_dump( rmdir("$file_path/tempx000/") );
+
+echo "\n*** Testing mkdir() with miscelleneous input ***\n";
+/* changing mode of mkdir to prevent creating sub-directory under it */
+var_dump( chmod("$file_path/mkdir私はガラスを食べられます/", 0000) );
+/* creating sub-directory test1 under mkdir, expected: false */
+var_dump( mkdir("$file_path/mkdir私はガラスを食べられます/test1", 0777, true) );
+var_dump( chmod("$file_path/mkdir私はガラスを食べられます/", 0777) ); // chmod to enable removing test1 directory
+
+echo "Done\n";
+?>
+--CLEAN--
+<?php
+rmdir(dirname(__FILE__)."/mkdir私はガラスを食べられます/test/");
+rmdir(dirname(__FILE__)."/mkdir私はガラスを食べられます/test1/");
+rmdir(dirname(__FILE__)."/mkdir私はガラスを食べられます/");
+?>
+--EXPECTF--
+*** Testing mkdir() and rmdir() for different permissions ***
+-- Changing mode of directory to 0 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 1 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 2 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 3 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 4 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 5 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 6 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 7 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 8 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 9 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 10 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 11 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 12 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 13 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 14 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 15 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 16 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 17 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 18 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 19 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 20 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 21 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 22 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 23 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 24 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 25 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 26 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 27 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 28 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 29 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 30 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 31 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 32 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 33 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 34 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 35 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 36 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 37 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 38 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 39 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 40 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 41 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 42 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 43 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 44 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 45 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 46 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 47 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 48 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 49 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 50 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 51 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 52 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 53 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 54 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 55 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 56 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 57 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 58 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 59 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 60 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 61 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 62 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 63 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 64 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 65 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 66 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 67 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 68 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 69 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 70 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 71 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 72 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 73 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 74 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 75 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 76 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 77 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 78 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 79 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 80 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 81 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 82 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 83 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 84 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 85 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 86 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 87 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 88 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 89 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 90 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 91 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 92 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 93 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 94 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 95 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 96 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 97 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 98 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 99 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 100 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 101 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 102 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 103 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 104 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 105 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 106 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 107 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 108 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 109 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 110 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 111 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 112 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 113 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 114 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 115 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 116 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 117 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 118 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 119 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 120 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 121 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 122 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 123 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 124 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 125 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 126 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 127 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 128 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 129 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 130 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 131 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 132 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 133 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 134 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 135 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 136 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 137 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 138 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 139 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 140 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 141 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 142 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 143 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 144 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 145 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 146 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 147 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 148 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 149 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 150 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 151 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 152 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 153 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 154 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 155 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 156 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 157 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 158 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 159 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 160 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 161 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 162 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 163 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 164 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 165 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 166 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 167 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 168 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 169 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 170 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 171 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 172 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 173 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 174 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 175 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 176 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 177 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 178 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 179 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 180 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 181 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 182 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 183 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 184 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 185 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 186 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 187 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 188 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 189 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 190 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 191 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 192 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 193 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 194 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 195 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 196 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 197 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 198 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 199 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 200 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 201 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 202 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 203 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 204 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 205 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 206 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 207 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 208 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 209 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 210 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 211 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 212 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 213 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 214 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 215 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 216 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 217 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 218 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 219 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 220 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 221 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 222 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 223 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 224 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 225 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 226 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 227 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 228 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 229 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 230 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 231 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 232 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 233 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 234 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 235 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 236 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 237 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 238 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 239 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 240 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 241 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 242 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 243 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 244 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 245 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 246 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 247 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 248 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 249 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 250 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 251 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 252 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 253 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 254 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 255 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 256 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 257 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 258 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 259 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 260 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 261 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 262 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 263 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 264 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 265 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 266 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 267 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 268 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 269 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 270 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 271 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 272 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 273 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 274 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 275 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 276 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 277 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 278 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 279 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 280 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 281 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 282 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 283 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 284 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 285 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 286 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 287 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 288 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 289 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 290 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 291 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 292 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 293 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 294 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 295 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 296 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 297 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 298 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 299 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 300 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 301 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 302 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 303 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 304 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 305 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 306 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 307 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 308 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 309 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 310 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 311 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 312 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 313 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 314 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 315 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 316 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 317 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 318 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 319 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 320 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 321 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 322 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 323 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 324 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 325 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 326 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 327 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 328 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 329 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 330 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 331 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 332 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 333 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 334 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 335 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 336 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 337 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 338 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 339 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 340 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 341 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 342 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 343 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 344 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 345 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 346 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 347 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 348 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 349 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 350 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 351 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 352 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 353 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 354 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 355 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 356 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 357 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 358 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 359 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 360 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 361 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 362 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 363 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 364 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 365 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 366 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 367 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 368 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 369 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 370 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 371 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 372 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 373 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 374 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 375 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 376 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 377 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 378 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 379 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 380 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 381 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 382 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 383 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 384 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 385 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 386 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 387 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 388 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 389 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 390 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 391 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 392 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 393 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 394 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 395 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 396 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 397 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 398 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 399 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 400 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 401 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 402 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 403 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 404 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 405 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 406 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 407 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 408 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 409 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 410 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 411 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 412 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 413 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 414 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 415 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 416 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 417 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 418 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 419 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 420 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 421 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 422 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 423 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 424 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 425 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 426 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 427 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 428 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 429 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 430 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 431 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 432 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 433 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 434 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 435 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 436 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 437 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 438 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 439 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 440 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 441 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 442 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 443 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 444 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 445 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 446 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 447 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 448 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 449 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 450 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 451 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 452 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 453 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 454 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 455 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 456 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 457 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 458 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 459 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 460 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 461 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 462 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 463 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 464 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 465 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 466 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 467 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 468 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 469 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 470 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 471 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 472 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 473 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 474 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 475 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 476 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 477 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 478 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 479 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 480 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 481 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 482 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 483 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 484 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 485 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 486 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 487 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 488 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 489 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 490 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 491 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 492 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 493 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 494 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 495 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 496 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 497 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 498 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 499 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 500 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 501 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 502 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 503 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 504 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 505 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 506 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 507 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 508 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 509 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 510 --
+bool(true)
+bool(true)
+-- Changing mode of directory to 511 --
+bool(true)
+bool(true)
+
+*** Testing mkdir() and rmdir() by giving stream context as fourth argument ***
+bool(true)
+bool(true)
+
+*** Testing rmdir() on a non-empty directory ***
+bool(true)
+
+Warning: rmdir(%s/mkdir私はガラスを食べられます/): Directory not empty in %s on line %d
+bool(false)
+
+*** Testing mkdir() and rmdir() for binary safe functionality ***
+bool(true)
+bool(true)
+
+*** Testing mkdir() with miscelleneous input ***
+bool(true)
+bool(true)
+bool(true)
+Done
diff --git a/ext/standard/tests/file/parse_ini_file_variation6-win32-mb.phpt b/ext/standard/tests/file/parse_ini_file_variation6-win32-mb.phpt
new file mode 100644
index 0000000000..4e5d4347e4
--- /dev/null
+++ b/ext/standard/tests/file/parse_ini_file_variation6-win32-mb.phpt
@@ -0,0 +1,143 @@
+--TEST--
+Test parse_ini_file() function : variation - various absolute and relative paths
+--CREDITS--
+Dave Kelsey <d_kelsey@uk.ibm.com>
+--SKIPIF--
+<?php
+if(substr(PHP_OS, 0, 3) != "WIN")
+ die("skip Only run on Windows");
+?>
+--FILE--
+<?php
+/* Prototype : array parse_ini_file(string filename [, bool process_sections])
+ * Description: Parse configuration file
+ * Source code: ext/standard/basic_functions.c
+ * Alias to functions:
+ */
+
+echo "*** Testing parse_ini_file() : variation ***\n";
+$mainDir = "parseIniFileVar私はガラスを食べられます.dir";
+$subDir = "parseIniFileVar私はガラスを食べられますSub";
+$absMainDir = dirname(__FILE__)."\\".$mainDir;
+mkdir($absMainDir);
+$absSubDir = $absMainDir."\\".$subDir;
+mkdir($absSubDir);
+
+$old_dir_path = getcwd();
+chdir(dirname(__FILE__));
+$unixifiedDir = '/'.substr(str_replace('\\','/',$absSubDir),3);
+
+$allDirs = array(
+ // absolute paths
+ "$absSubDir\\",
+ "$absSubDir\\..\\".$subDir,
+ "$absSubDir\\\\..\\.\\".$subDir,
+ "$absSubDir\\..\\..\\".$mainDir."\\.\\".$subDir,
+ "$absSubDir\\..\\\\\\".$subDir."\\\\..\\\\..\\".$subDir,
+ "$absSubDir\\BADDIR",
+
+ // relative paths
+ $mainDir."\\".$subDir,
+ $mainDir."\\\\".$subDir,
+ $mainDir."\\\\\\".$subDir,
+ ".\\".$mainDir."\\..\\".$mainDir."\\".$subDir,
+ "BADDIR",
+
+ // unixifed path
+ $unixifiedDir,
+);
+
+$filename = 'parseIniFileVar私はガラスを食べられます.ini';
+$content="a=test";
+$absFile = $absSubDir.'/'.$filename;
+$h = fopen($absFile,"w");
+fwrite($h, $content);
+fclose($h);
+
+for($i = 0; $i<count($allDirs); $i++) {
+ $j = $i+1;
+ $dir = $allDirs[$i];
+ echo "\n-- Iteration $j --\n";
+ var_dump(parse_ini_file($dir."\\".$filename));
+}
+
+unlink($absFile);
+chdir($old_dir_path);
+rmdir($absSubDir);
+rmdir($absMainDir);
+
+echo "\n*** Done ***\n";
+?>
+--EXPECTF--
+*** Testing parse_ini_file() : variation ***
+
+-- Iteration 1 --
+array(1) {
+ ["a"]=>
+ string(4) "test"
+}
+
+-- Iteration 2 --
+array(1) {
+ ["a"]=>
+ string(4) "test"
+}
+
+-- Iteration 3 --
+array(1) {
+ ["a"]=>
+ string(4) "test"
+}
+
+-- Iteration 4 --
+array(1) {
+ ["a"]=>
+ string(4) "test"
+}
+
+-- Iteration 5 --
+
+Warning: parse_ini_file(%sparseIniFileVar私はガラスを食べられます.dir\parseIniFileVar私はガラスを食べられますSub\..\\\parseIniFileVar私はガラスを食べられますSub\\..\\..\parseIniFileVar私はガラスを食べられますSub\parseIniFileVar私はガラスを食べられます.ini): failed to open stream: No such file or directory in %s on line %d
+bool(false)
+
+-- Iteration 6 --
+
+Warning: parse_ini_file(%sparseIniFileVar私はガラスを食べられます.dir\parseIniFileVar私はガラスを食べられますSub\BADDIR\parseIniFileVar私はガラスを食べられます.ini): failed to open stream: No such file or directory in %s on line %d
+bool(false)
+
+-- Iteration 7 --
+array(1) {
+ ["a"]=>
+ string(4) "test"
+}
+
+-- Iteration 8 --
+array(1) {
+ ["a"]=>
+ string(4) "test"
+}
+
+-- Iteration 9 --
+array(1) {
+ ["a"]=>
+ string(4) "test"
+}
+
+-- Iteration 10 --
+array(1) {
+ ["a"]=>
+ string(4) "test"
+}
+
+-- Iteration 11 --
+
+Warning: parse_ini_file(BADDIR\parseIniFileVar私はガラスを食べられます.ini): failed to open stream: No such file or directory in %s on line %d
+bool(false)
+
+-- Iteration 12 --
+array(1) {
+ ["a"]=>
+ string(4) "test"
+}
+
+*** Done *** \ No newline at end of file
diff --git a/ext/standard/tests/file/popen_pclose_basic-win32-mb.phpt b/ext/standard/tests/file/popen_pclose_basic-win32-mb.phpt
new file mode 100644
index 0000000000..7e0a0e0c54
--- /dev/null
+++ b/ext/standard/tests/file/popen_pclose_basic-win32-mb.phpt
@@ -0,0 +1,75 @@
+--TEST--
+Test popen() and pclose function: basic functionality
+
+--SKIPIF--
+<?php
+if(substr(PHP_OS, 0, 3) != 'WIN' )
+ die("skip Not Valid for Linux");
+?>
+
+--FILE--
+<?php
+/*
+ * Prototype: resource popen ( string command, string mode )
+ * Description: Opens process file pointer.
+
+ * Prototype: int pclose ( resource handle );
+ * Description: Closes process file pointer.
+ */
+
+echo "*** Testing popen(): reading from the pipe ***\n";
+
+$file_path = dirname(__FILE__);
+
+$string = "Sample String 私はガラスを食べられます";
+$file_handle = popen(" echo $string", "r");
+fpassthru($file_handle);
+pclose($file_handle);
+
+echo "*** Testing popen(): writing to the pipe ***\n";
+$arr = array("ggg", "ddd", "aaa", "sss");
+// popen("sort", "w") fails if variables_order="GPCS"
+// this is set in the default INI file
+// it doesn't seem to be changeable in the --INI-- section
+// also, doing: ini_set('variables_order', ''); doesn't work!
+//
+// the only solution is to either put the absolute path here, or
+// remove variables_order= from PHP.ini (setting it in run-test's
+// default INI will fail too)
+//
+// since we can't depend on PHP.ini being set a certain way,
+// have to put the absolute path here.
+
+$sysroot = exec('echo %SYSTEMROOT%');
+
+$file_handle = popen("$sysroot/system32/sort", "w");
+$newline = "\n";
+foreach($arr as $str) {
+ fwrite($file_handle, (binary)$str);
+ fwrite($file_handle, (binary)(binary)(binary)(binary)(binary)(binary)(binary)(binary)(binary)$newline);
+}
+pclose($file_handle);
+
+echo "*** Testing popen() and pclose(): return type ***\n";
+$return_value_popen = popen("echo $string", "r");
+fpassthru($return_value_popen);
+var_dump( is_resource($return_value_popen) );
+$return_value_pclose = pclose($return_value_popen);
+var_dump( is_int($return_value_pclose) );
+
+echo "\n--- Done ---";
+?>
+--EXPECTF--
+*** Testing popen(): reading from the pipe ***
+Sample String 私はガラスを食べられます
+*** Testing popen(): writing to the pipe ***
+aaa
+ddd
+ggg
+sss
+*** Testing popen() and pclose(): return type ***
+Sample String 私はガラスを食べられます
+bool(true)
+bool(true)
+
+--- Done ---
diff --git a/ext/standard/tests/file/readfile_variation8-win32-mb.phpt b/ext/standard/tests/file/readfile_variation8-win32-mb.phpt
new file mode 100644
index 0000000000..e53f362367
--- /dev/null
+++ b/ext/standard/tests/file/readfile_variation8-win32-mb.phpt
@@ -0,0 +1,109 @@
+--TEST--
+Test readfile() function : variation
+--CREDITS--
+Dave Kelsey <d_kelsey@uk.ibm.com>
+--SKIPIF--
+<?php
+if(substr(PHP_OS, 0, 3) != "WIN")
+ die("skip Only valid for Windows");
+?>
+--FILE--
+<?php
+/* Prototype : int readfile(string filename [, bool use_include_path[, resource context]])
+ * Description: Output a file or a URL
+ * Source code: ext/standard/file.c
+ * Alias to functions:
+ */
+
+echo "*** Testing readfile() : variation ***\n";
+$mainDir = "readfileVar私はガラスを食べられます8";
+$subDir = "readfileVar私はガラスを食べられます8Sub";
+$absMainDir = dirname(__FILE__)."\\".$mainDir;
+mkdir($absMainDir);
+$absSubDir = $absMainDir."\\".$subDir;
+mkdir($absSubDir);
+
+$theFile = "fileToRead.tmp";
+$absFile = $absSubDir.'/'.$theFile;
+
+// create the file
+$h = fopen($absFile,"w");
+fwrite($h, "The File Contents");
+fclose($h);
+
+
+$old_dir_path = getcwd();
+chdir(dirname(__FILE__));
+$unixifiedDir = '/'.substr(str_replace('\\','/',$absSubDir),3);
+
+$allDirs = array(
+ // absolute paths
+ "$absSubDir\\",
+ "$absSubDir\\..\\".$subDir,
+ "$absSubDir\\\\..\\.\\".$subDir,
+ "$absSubDir\\..\\..\\".$mainDir."\\.\\".$subDir,
+ "$absSubDir\\..\\\\\\".$subDir."\\\\..\\\\..\\".$subDir,
+ "$absSubDir\\BADDIR",
+
+ // relative paths
+ $mainDir."\\".$subDir,
+ $mainDir."\\\\".$subDir,
+ $mainDir."\\\\\\".$subDir,
+ ".\\".$mainDir."\\..\\".$mainDir."\\".$subDir,
+ "BADDIR",
+
+ // unixifed path
+ $unixifiedDir,
+);
+
+for($i = 0; $i<count($allDirs); $i++) {
+ $j = $i+1;
+ $dir = $allDirs[$i];
+ echo "\n-- $dir --\n";
+ $ok = readfile($dir.'\\'.$theFile);
+ if ($ok === 1) {
+ echo "\n";
+ }
+}
+
+unlink($absFile);
+chdir($old_dir_path);
+rmdir($absSubDir);
+rmdir($absMainDir);
+
+echo "\n*** Done ***\n";
+?>
+--EXPECTF--
+*** Testing readfile() : variation ***
+
+-- %s\readfileVar私はガラスを食べられます8\readfileVar私はガラスを食べられます8Sub\ --
+The File Contents
+-- %s\readfileVar私はガラスを食べられます8\readfileVar私はガラスを食べられます8Sub\..\readfileVar私はガラスを食べられます8Sub --
+The File Contents
+-- %s\readfileVar私はガラスを食べられます8\readfileVar私はガラスを食べられます8Sub\\..\.\readfileVar私はガラスを食べられます8Sub --
+The File Contents
+-- %s\readfileVar私はガラスを食べられます8\readfileVar私はガラスを食べられます8Sub\..\..\readfileVar私はガラスを食べられます8\.\readfileVar私はガラスを食べられます8Sub --
+The File Contents
+-- %s\readfileVar私はガラスを食べられます8\readfileVar私はガラスを食べられます8Sub\..\\\readfileVar私はガラスを食べられます8Sub\\..\\..\readfileVar私はガラスを食べられます8Sub --
+
+Warning: readfile(%s\readfileVar私はガラスを食べられます8\readfileVar私はガラスを食べられます8Sub\..\\\readfileVar私はガラスを食べられます8Sub\\..\\..\readfileVar私はガラスを食べられます8Sub\fileToRead.tmp): failed to open stream: No such file or directory in %s on line %d
+
+-- %s\readfileVar私はガラスを食べられます8\readfileVar私はガラスを食べられます8Sub\BADDIR --
+
+Warning: readfile(%s\readfileVar私はガラスを食べられます8\readfileVar私はガラスを食べられます8Sub\BADDIR\fileToRead.tmp): failed to open stream: No such file or directory in %s on line %d
+
+-- readfileVar私はガラスを食べられます8\readfileVar私はガラスを食べられます8Sub --
+The File Contents
+-- readfileVar私はガラスを食べられます8\\readfileVar私はガラスを食べられます8Sub --
+The File Contents
+-- readfileVar私はガラスを食べられます8\\\readfileVar私はガラスを食べられます8Sub --
+The File Contents
+-- .\readfileVar私はガラスを食べられます8\..\readfileVar私はガラスを食べられます8\readfileVar私はガラスを食べられます8Sub --
+The File Contents
+-- BADDIR --
+
+Warning: readfile(BADDIR\fileToRead.tmp): failed to open stream: No such file or directory in %s on line %d
+
+-- /%s/readfileVar私はガラスを食べられます8/readfileVar私はガラスを食べられます8Sub --
+The File Contents
+*** Done *** \ No newline at end of file
diff --git a/ext/standard/tests/file/realpath_basic-win32-mb.phpt b/ext/standard/tests/file/realpath_basic-win32-mb.phpt
new file mode 100644
index 0000000000..c0f39b6054
--- /dev/null
+++ b/ext/standard/tests/file/realpath_basic-win32-mb.phpt
@@ -0,0 +1,89 @@
+--TEST--
+Test realpath() function: basic functionality
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die('skip only on Windows');
+}
+?>
+--FILE--
+<?php
+/* Prototype: string realpath ( string $path );
+ Description: Returns canonicalized absolute pathname
+*/
+
+echo "\n*** Testing basic functions of realpath() with files ***\n";
+
+/* creating directories and files */
+$file_path = dirname(__FILE__);
+mkdir("$file_path/realpath_私はガラスを食べられますbasic/home/test/", 0777, true);
+
+$file_handle1 = fopen("$file_path/realpath_私はガラスを食べられますbasic/home/test/realpath_私はガラスを食べられますbasic.tmp", "w");
+$file_handle2 = fopen("$file_path/realpath_私はガラスを食べられますbasic/home/realpath_私はガラスを食べられますbasic.tmp", "w");
+$file_handle3 = fopen("$file_path/realpath_私はガラスを食べられますbasic/realpath_私はガラスを食べられますbasic.tmp", "w");
+fclose($file_handle1);
+fclose($file_handle2);
+fclose($file_handle3);
+
+echo "\n*** Testing realpath() on filenames ***\n";
+$filenames = array (
+ /* filenames resulting in valid paths */
+ "$file_path/realpath_私はガラスを食べられますbasic/home/realpath_私はガラスを食べられますbasic.tmp",
+ "$file_path/realpath_私はガラスを食べられますbasic/realpath_私はガラスを食べられますbasic.tmp/",
+ "$file_path/realpath_私はガラスを食べられますbasic//home/test//../test/./realpath_私はガラスを食べられますbasic.tmp",
+ "$file_path/realpath_私はガラスを食べられますbasic/home//../././realpath_私はガラスを食べられますbasic.tmp//",
+
+ // checking for binary safe
+ b"$file_path/realpath_私はガラスを食べられますbasic/home/realpath_私はガラスを食べられますbasic.tmp",
+
+ /* filenames with invalid path */
+ "$file_path///realpath_私はガラスを食べられますbasic/home//..//././test//realpath_私はガラスを食べられますbasic.tmp",
+ "$file_path/realpath_私はガラスを食べられますbasic/home/../home/../test/../..realpath_私はガラスを食べられますbasic.tmp"
+);
+
+$counter = 1;
+/* loop through $files to read the filepath of $file in the above array */
+foreach($filenames as $file) {
+ echo "\n-- Iteration $counter --\n";
+ var_dump( realpath($file) );
+ $counter++;
+}
+
+echo "Done\n";
+?>
+--CLEAN--
+<?php
+$name_prefix = dirname(__FILE__)."/realpath_私はガラスを食べられますbasic";
+unlink("$name_prefix/home/test/realpath_私はガラスを食べられますbasic.tmp");
+unlink("$name_prefix/home/realpath_私はガラスを食べられますbasic.tmp");
+unlink("$name_prefix/realpath_私はガラスを食べられますbasic.tmp");
+rmdir("$name_prefix/home/test/");
+rmdir("$name_prefix/home/");
+rmdir("$name_prefix/");
+?>
+--EXPECTF--
+*** Testing basic functions of realpath() with files ***
+
+*** Testing realpath() on filenames ***
+
+-- Iteration 1 --
+string(%d) "%s\realpath_私はガラスを食べられますbasic\home\realpath_私はガラスを食べられますbasic.tmp"
+
+-- Iteration 2 --
+bool(false)
+
+-- Iteration 3 --
+string(%d) "%s\realpath_私はガラスを食べられますbasic\home\test\realpath_私はガラスを食べられますbasic.tmp"
+
+-- Iteration 4 --
+bool(false)
+
+-- Iteration 5 --
+string(%d) "%s\realpath_私はガラスを食べられますbasic\home\realpath_私はガラスを食べられますbasic.tmp"
+
+-- Iteration 6 --
+bool(false)
+
+-- Iteration 7 --
+bool(false)
+Done
diff --git a/ext/standard/tests/file/realpath_variation-win32-mb.phpt b/ext/standard/tests/file/realpath_variation-win32-mb.phpt
new file mode 100644
index 0000000000..66d7c8b0c7
--- /dev/null
+++ b/ext/standard/tests/file/realpath_variation-win32-mb.phpt
@@ -0,0 +1,102 @@
+--TEST--
+Test realpath() function: usage variation
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die('skip only on Windows');
+}
+?>
+--FILE--
+<?php
+/* Prototype: string realpath ( string $path );
+ Description: Returns canonicalized absolute pathname
+*/
+
+require dirname(__FILE__).'/file.inc';
+
+echo "*** Testing realpath(): usage variations ***\n";
+$name_prefix = dirname(__FILE__);
+$filename = "$name_prefix/realpath_variation_私はガラスを食べられます/home/tests/realpath_variation_私はガラスを食べられます.tmp";
+mkdir("$name_prefix/realpath_variation_私はガラスを食べられます/home/tests/", 0777, true);
+
+echo "\n*** Testing realpath() with filename stored inside a object ***\n";
+// create a temp file
+$file_handle = fopen($filename, "w");
+fclose($file_handle);
+
+// creating object with members as filename
+class object_temp {
+ public $filename;
+ function __construct($file) {
+ $this->filename = $file;
+ }
+}
+$obj1 = new object_temp("$name_prefix/realpath_variation_私はガラスを食べられます/../././realpath_variation_私はガラスを食べられます/home/tests/realpath_variation_私はガラスを食べられます.tmp");
+$obj2 = new object_temp("$name_prefix/realpath_variation_私はガラスを食べられます/home/..///realpath_variation_私はガラスを食べられます.tmp");
+
+var_dump( realpath($obj1->filename) );
+var_dump( realpath($obj2->filename) );
+
+echo "\n*** Testing realpath() with filename stored in an array ***\n";
+$file_arr = array (
+ "$name_prefix////realpath_variation_私はガラスを食べられます/home/tests/realpath_variation_私はガラスを食べられます.tmp",
+ "$name_prefix/./realpath_variation_私はガラスを食べられます/home/../home//tests//..//..//..//home//realpath_variation_私はガラスを食べられます.tmp/"
+);
+
+var_dump( realpath($file_arr[0]) );
+var_dump( realpath($file_arr[1]) );
+
+echo "\n*** Testing realpath() with filename as empty string, NULL and single space ***\n";
+$file_string = array (
+ /* filename as spaces */
+ " ",
+ ' ',
+
+ /* empty filename */
+ "",
+ '',
+ NULL,
+ null
+ );
+for($loop_counter = 0; $loop_counter < count($file_string); $loop_counter++) {
+ echo "-- Iteration";
+ echo $loop_counter + 1;
+ echo " --\n";
+ var_dump( realpath($file_string[$loop_counter]) );
+}
+
+echo "Done\n";
+?>
+--CLEAN--
+<?php
+$name_prefix = dirname(__FILE__)."/realpath_variation_私はガラスを食べられます";
+unlink("$name_prefix/home/tests/realpath_variation_私はガラスを食べられます.tmp");
+rmdir("$name_prefix/home/tests/");
+rmdir("$name_prefix/home/");
+rmdir("$name_prefix/");
+?>
+--EXPECTF--
+*** Testing realpath(): usage variations ***
+
+*** Testing realpath() with filename stored inside a object ***
+string(%d) "%s\realpath_variation_私はガラスを食べられます\home\tests\realpath_variation_私はガラスを食べられます.tmp"
+bool(false)
+
+*** Testing realpath() with filename stored in an array ***
+string(%d) "%s\realpath_variation_私はガラスを食べられます\home\tests\realpath_variation_私はガラスを食べられます.tmp"
+bool(false)
+
+*** Testing realpath() with filename as empty string, NULL and single space ***
+-- Iteration1 --
+bool(false)
+-- Iteration2 --
+bool(false)
+-- Iteration3 --
+string(%d) "%s"
+-- Iteration4 --
+string(%d) "%s"
+-- Iteration5 --
+string(%d) "%s"
+-- Iteration6 --
+string(%d) "%s"
+Done
diff --git a/ext/standard/tests/file/stat_basic-win32-mb.phpt b/ext/standard/tests/file/stat_basic-win32-mb.phpt
new file mode 100644
index 0000000000..334eb9ea65
--- /dev/null
+++ b/ext/standard/tests/file/stat_basic-win32-mb.phpt
@@ -0,0 +1,192 @@
+--TEST--
+Test stat() function: basic functionality
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die('skip.. valid only for Windows');
+}
+?>
+--FILE--
+<?php
+/*
+ Prototype: array stat ( string $filename );
+ Description: Gives information about a file
+*/
+
+$file_path = dirname(__FILE__);
+require("$file_path/file.inc");
+
+echo "*** Testing stat() : basic functionality ***\n";
+
+/* creating temp directory and file */
+
+// creating dir
+$dirname = "$file_path/stat_basic_私はガラスを食べられます";
+mkdir($dirname);
+// stat of the dir created
+$dir_stat = stat($dirname);
+clearstatcache();
+sleep(2);
+
+// creating file
+$filename = "$dirname/stat_basic_私はガラスを食べられます.tmp";
+$file_handle = fopen($filename, "w");
+fclose($file_handle);
+// stat of the file created
+$file_stat = stat($filename);
+sleep(2);
+
+// now new stat of the dir after file is created
+$new_dir_stat = stat($dirname);
+clearstatcache();
+
+// stat contains 13 different values stored twice, can be accessed using
+// numeric and named keys, compare them to see they are same
+echo "*** Testing stat(): validating the values stored in stat ***\n";
+// Initial stat values
+var_dump( compare_self_stat($file_stat) ); //expect true
+var_dump( compare_self_stat($dir_stat) ); //expect true
+
+// New stat values taken after creation of file
+var_dump( compare_self_stat($new_dir_stat) ); // expect true
+
+// compare the two stat values, initial stat and stat recorded after
+// creating file, also dump the value of stats
+echo "*** Testing stat(): comparing stats (recorded before and after file creation) ***\n";
+echo "-- comparing difference in dir stats before and after creating file in it --\n";
+$affected_elements = array( 9, 'mtime' );
+var_dump( compare_stats($dir_stat, $new_dir_stat, $affected_elements, '!=', true) ); // expect true
+
+echo "*** Testing stat(): for the return value ***\n";
+var_dump( is_array( stat($filename) ) );
+
+echo "\n---Done---";
+?>
+--CLEAN--
+<?php
+$file_path = dirname(__FILE__);
+unlink("$file_path/stat_basic_私はガラスを食べられます/stat_basic_私はガラスを食べられます.tmp");
+rmdir("$file_path/stat_basic_私はガラスを食べられます");
+?>
+--EXPECTF--
+*** Testing stat() : basic functionality ***
+*** Testing stat(): validating the values stored in stat ***
+bool(true)
+bool(true)
+bool(true)
+*** Testing stat(): comparing stats (recorded before and after file creation) ***
+-- comparing difference in dir stats before and after creating file in it --
+array(26) {
+ [0]=>
+ int(%d)
+ [1]=>
+ int(0)
+ [2]=>
+ int(%d)
+ [3]=>
+ int(%d)
+ [4]=>
+ int(0)
+ [5]=>
+ int(0)
+ [6]=>
+ int(%d)
+ [7]=>
+ int(%d)
+ [8]=>
+ int(%d)
+ [9]=>
+ int(%d)
+ [10]=>
+ int(%d)
+ [11]=>
+ int(-1)
+ [12]=>
+ int(-1)
+ ["dev"]=>
+ int(%d)
+ ["ino"]=>
+ int(0)
+ ["mode"]=>
+ int(%d)
+ ["nlink"]=>
+ int(%d)
+ ["uid"]=>
+ int(0)
+ ["gid"]=>
+ int(0)
+ ["rdev"]=>
+ int(%d)
+ ["size"]=>
+ int(%d)
+ ["atime"]=>
+ int(%d)
+ ["mtime"]=>
+ int(%d)
+ ["ctime"]=>
+ int(%d)
+ ["blksize"]=>
+ int(-1)
+ ["blocks"]=>
+ int(-1)
+}
+array(26) {
+ [0]=>
+ int(%d)
+ [1]=>
+ int(%d)
+ [2]=>
+ int(%d)
+ [3]=>
+ int(%d)
+ [4]=>
+ int(%d)
+ [5]=>
+ int(%d)
+ [6]=>
+ int(%d)
+ [7]=>
+ int(%d)
+ [8]=>
+ int(%d)
+ [9]=>
+ int(%d)
+ [10]=>
+ int(%d)
+ [11]=>
+ int(-1)
+ [12]=>
+ int(-1)
+ ["dev"]=>
+ int(%d)
+ ["ino"]=>
+ int(%d)
+ ["mode"]=>
+ int(%d)
+ ["nlink"]=>
+ int(%d)
+ ["uid"]=>
+ int(%d)
+ ["gid"]=>
+ int(%d)
+ ["rdev"]=>
+ int(%d)
+ ["size"]=>
+ int(%d)
+ ["atime"]=>
+ int(%d)
+ ["mtime"]=>
+ int(%d)
+ ["ctime"]=>
+ int(%d)
+ ["blksize"]=>
+ int(-1)
+ ["blocks"]=>
+ int(-1)
+}
+bool(true)
+*** Testing stat(): for the return value ***
+bool(true)
+
+---Done---
+
diff --git a/ext/standard/tests/file/stat_variation1-win32-mb.phpt b/ext/standard/tests/file/stat_variation1-win32-mb.phpt
new file mode 100644
index 0000000000..dcba28698f
--- /dev/null
+++ b/ext/standard/tests/file/stat_variation1-win32-mb.phpt
@@ -0,0 +1,94 @@
+--TEST--
+Test stat() functions: usage variations - effects of rename()
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die('skip.. only for Windows');
+}
+?>
+--FILE--
+<?php
+
+/*
+ * Prototype: array stat ( string $filename );
+ * Description: Gives information about a file
+ */
+
+/* test the effects of rename() on stats of dir/file */
+
+$file_path = dirname(__FILE__);
+require "$file_path/file.inc";
+
+
+/* create temp file and directory */
+mkdir("$file_path/stat_variation1_私はガラスを食べられます/"); // temp dir
+
+$file_handle = fopen("$file_path/stat_variation1_私はガラスを食べられます.tmp", "w"); // temp file
+fclose($file_handle);
+
+
+echo "*** Testing stat(): on file and directory ater renaming them ***\n";
+
+// renaming a file
+echo "-- Testing stat() for files after being renamed --\n";
+$old_filename = "$file_path/stat_variation1_私はガラスを食べられます.tmp";
+$new_filename = "$file_path/stat_variation1a_私はガラスを食べられます.tmp";
+$old_stat = stat($old_filename);
+clearstatcache();
+sleep(2);
+var_dump( rename($old_filename, $new_filename) );
+$new_stat = stat($new_filename);
+
+// compare the self stat
+var_dump( compare_self_stat($old_stat) );
+var_dump( compare_self_stat($new_stat) );
+
+// compare the two stats
+var_dump( compare_stats($old_stat, $old_stat, $all_stat_keys) );
+// clear the cache
+clearstatcache();
+
+// renaming a directory
+echo "-- Testing stat() for directory after being renamed --\n";
+$old_dirname = "$file_path/stat_variation1_私はガラスを食べられます";
+$new_dirname = "$file_path/stat_variation1a_私はガラスを食べられます";
+$old_stat = stat($old_dirname);
+clearstatcache();
+sleep(2);
+var_dump( rename($old_dirname, $new_dirname) );
+$new_stat = stat($new_dirname);
+
+// compare self stats
+var_dump( compare_self_stat($old_stat) );
+var_dump( compare_self_stat($new_stat) );
+
+// compare the two stats
+var_dump( compare_stats($old_stat, $new_stat, $all_stat_keys) );
+// clear the cache
+clearstatcache();
+
+
+echo "\n*** Done ***";
+?>
+
+--CLEAN--
+<?php
+$file_path = dirname(__FILE__);
+unlink("$file_path/stat_variation1a_私はガラスを食べられます.tmp");
+rmdir("$file_path/stat_variation1a_私はガラスを食べられます");
+?>
+--EXPECTF--
+*** Testing stat(): on file and directory ater renaming them ***
+-- Testing stat() for files after being renamed --
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+-- Testing stat() for directory after being renamed --
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+
+*** Done ***
+
diff --git a/ext/standard/tests/file/stream_rfc2397_006.phpt b/ext/standard/tests/file/stream_rfc2397_006.phpt
index e6694a2861..f6616a0c88 100644
--- a/ext/standard/tests/file/stream_rfc2397_006.phpt
+++ b/ext/standard/tests/file/stream_rfc2397_006.phpt
@@ -26,7 +26,9 @@ NULL
Warning: file_get_contents() expects parameter 1 to be a valid path, string given in %s line %d
NULL
-string(13) "foobar foobar"
+
+Warning: file_get_contents(data:;base64,#Zm9vYmFyIGZvb2Jhcg==): failed to open stream: rfc2397: unable to decode in %sstream_rfc2397_006.php on line %d
+bool(false)
Warning: file_get_contents(data:;base64,#Zm9vYmFyIGZvb2Jhc=): failed to open stream: rfc2397: unable to decode in %sstream_rfc2397_006.php on line %d
bool(false)
diff --git a/ext/standard/tests/file/tempnam_variation1-win32-mb.phpt b/ext/standard/tests/file/tempnam_variation1-win32-mb.phpt
new file mode 100644
index 0000000000..4d985410da
--- /dev/null
+++ b/ext/standard/tests/file/tempnam_variation1-win32-mb.phpt
@@ -0,0 +1,103 @@
+--TEST--
+Test tempnam() function: usage variations - creating files
+--SKIPIF--
+<?php
+if(substr(PHP_OS, 0, 3) != "WIN")
+ die("skip Only valid for Windows");
+?>
+--FILE--
+<?php
+/* Prototype: string tempnam ( string $dir, string $prefix );
+ Description: Create file with unique file name.
+*/
+
+/* Creating number of unique files by passing a file name as prefix */
+
+$file_path = dirname(__FILE__)."/tempnamVar1_私はガラスを食べられます";
+mkdir($file_path);
+
+echo "*** Testing tempnam() in creation of unique files ***\n";
+for($i=1; $i<=10; $i++) {
+ echo "-- Iteration $i --\n";
+ $files[$i] = tempnam("$file_path", "tempnam_variation1.tmp");
+
+ if( file_exists($files[$i]) ) {
+
+ echo "File name is => ";
+ print($files[$i]);
+ echo "\n";
+
+ echo "File permissions are => ";
+ printf("%o", fileperms($files[$i]) );
+ echo "\n";
+ clearstatcache();
+
+ echo "File created in => ";
+ $file_dir = dirname($files[$i]);
+
+ if (realpath($file_dir) == realpath(sys_get_temp_dir()) || realpath($file_dir."\\") == realpath(sys_get_temp_dir())) {
+ echo "temp dir\n";
+ }
+ else if (realpath($file_dir) == realpath($file_path) || realpath($file_dir."\\") == realpath($file_path)) {
+ echo "directory specified\n";
+ }
+ else {
+ echo "unknown location\n";
+ }
+ clearstatcache();
+ }
+ else {
+ print("- File is not created -");
+ }
+}
+for($i=1; $i<=10; $i++) {
+ unlink($files[$i]);
+}
+rmdir($file_path);
+
+
+echo "*** Done ***\n";
+?>
+--EXPECTF--
+*** Testing tempnam() in creation of unique files ***
+-- Iteration 1 --
+File name is => %s%et%s
+File permissions are => 100666
+File created in => directory specified
+-- Iteration 2 --
+File name is => %s%et%s
+File permissions are => 100666
+File created in => directory specified
+-- Iteration 3 --
+File name is => %s%et%s
+File permissions are => 100666
+File created in => directory specified
+-- Iteration 4 --
+File name is => %s%et%s
+File permissions are => 100666
+File created in => directory specified
+-- Iteration 5 --
+File name is => %s%et%s
+File permissions are => 100666
+File created in => directory specified
+-- Iteration 6 --
+File name is => %s%et%s
+File permissions are => 100666
+File created in => directory specified
+-- Iteration 7 --
+File name is => %s%et%s
+File permissions are => 100666
+File created in => directory specified
+-- Iteration 8 --
+File name is => %s%et%s
+File permissions are => 100666
+File created in => directory specified
+-- Iteration 9 --
+File name is => %s%et%s
+File permissions are => 100666
+File created in => directory specified
+-- Iteration 10 --
+File name is => %s%et%s
+File permissions are => 100666
+File created in => directory specified
+*** Done ***
diff --git a/ext/standard/tests/file/tempnam_variation2-win32.phpt b/ext/standard/tests/file/tempnam_variation2-win32.phpt
index 4224966daf..15d59614ff 100644
--- a/ext/standard/tests/file/tempnam_variation2-win32.phpt
+++ b/ext/standard/tests/file/tempnam_variation2-win32.phpt
@@ -121,6 +121,8 @@ File permissions are => 100666
File created in => directory specified
-- Iteration 7 --
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation2-win32.php on line %d
File name is => %s\t%s
File permissions are => 100666
File created in => temp dir
@@ -131,11 +133,15 @@ File permissions are => 100666
File created in => directory specified
-- Iteration 9 --
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation2-win32.php on line %d
File name is => %s\t%s
File permissions are => 100666
File created in => temp dir
-- Iteration 10 --
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation2-win32.php on line %d
File name is => %s\t%s
File permissions are => 100666
File created in => temp dir
@@ -156,8 +162,10 @@ File permissions are => 100666
File created in => directory specified
-- Iteration 14 --
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation2-win32.php on line %d
File name is => %s\t%s
File permissions are => 100666
File created in => temp dir
-*** Done *** \ No newline at end of file
+*** Done ***
diff --git a/ext/standard/tests/file/tempnam_variation2.phpt b/ext/standard/tests/file/tempnam_variation2.phpt
index b7e5cdc058..1a2de1885b 100644
--- a/ext/standard/tests/file/tempnam_variation2.phpt
+++ b/ext/standard/tests/file/tempnam_variation2.phpt
@@ -121,6 +121,8 @@ File permissions are => 100600
File created in => directory specified
-- Iteration 7 --
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation2.php on line %d
File name is => %s/tempnam_variation2.tmp%s
File permissions are => 100600
File created in => temp dir
@@ -131,11 +133,15 @@ File permissions are => 100600
File created in => directory specified
-- Iteration 9 --
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation2.php on line %d
File name is => %s/tempnam_variation2.tmp%s
File permissions are => 100600
File created in => temp dir
-- Iteration 10 --
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation2.php on line %d
File name is => %s/tempnam_variation2.tmp%s
File permissions are => 100600
File created in => temp dir
@@ -156,8 +162,10 @@ File permissions are => 100600
File created in => directory specified
-- Iteration 14 --
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation2.php on line %d
File name is => %s/tempnam_variation2.tmp%s
File permissions are => 100600
File created in => temp dir
-*** Done *** \ No newline at end of file
+*** Done ***
diff --git a/ext/standard/tests/file/tempnam_variation3-win32.phpt b/ext/standard/tests/file/tempnam_variation3-win32.phpt
index df0d88feda..6d5071b5e5 100644
--- a/ext/standard/tests/file/tempnam_variation3-win32.phpt
+++ b/ext/standard/tests/file/tempnam_variation3-win32.phpt
@@ -99,6 +99,8 @@ OK
-- Iteration 4 --
OK
-- Iteration 5 --
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation3-win32.php on line %d
Failed, not created in the correct directory %s vs %s
0
-- Iteration 6 --
diff --git a/ext/standard/tests/file/tempnam_variation4-0.phpt b/ext/standard/tests/file/tempnam_variation4-0.phpt
new file mode 100644
index 0000000000..e96846ff2c
--- /dev/null
+++ b/ext/standard/tests/file/tempnam_variation4-0.phpt
@@ -0,0 +1,638 @@
+--TEST--
+Test tempnam() function: usage variations - permissions(0000 to 0350) of dir
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) == 'WIN') {
+ die('skip Not valid for Windows');
+}
+// Skip if being run by root
+$filename = dirname(__FILE__)."/is_readable_root_check.tmp";
+$fp = fopen($filename, 'w');
+fclose($fp);
+if(fileowner($filename) == 0) {
+ unlink ($filename);
+ die('skip cannot be run as root');
+}
+unlink($filename);
+?>
+--FILE--
+<?php
+/* Prototype: string tempnam ( string $dir, string $prefix );
+ Description: Create file with unique file name.
+*/
+
+/* Trying to create the file in a dir with permissions from 0000 to 0350,
+ Allowable permissions: files are expected to be created in the input dir
+ Non-allowable permissions: files are expected to be created in '/tmp' dir
+*/
+
+echo "*** Testing tempnam() with dir of permissions from 0000 to 0350 ***\n";
+$file_path = dirname(__FILE__);
+$dir_name = $file_path."/tempnam_variation4";
+$prefix = "tempnamVar4.";
+
+mkdir($dir_name);
+
+for($mode = 0000; $mode <= 0350; $mode++) {
+ chmod($dir_name, $mode);
+ $file_name = tempnam($dir_name, $prefix);
+
+ if(file_exists($file_name) ) {
+ if (dirname($file_name) != $dir_name) {
+ /* Either there's a notice or error */
+ printf("%o\n", $mode);
+
+ if (realpath(dirname($file_name)) != realpath(sys_get_temp_dir())) {
+ echo " created in unexpected dir\n";
+ }
+ }
+ unlink($file_name);
+ }
+ else {
+ print("FAILED: File is not created\n");
+ }
+}
+
+rmdir($dir_name);
+
+echo "*** Done ***\n";
+?>
+--EXPECTF--
+*** Testing tempnam() with dir of permissions from 0000 to 0350 ***
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+0
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+1
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+2
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+3
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+4
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+5
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+6
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+7
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+10
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+11
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+12
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+13
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+14
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+15
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+16
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+17
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+20
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+21
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+22
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+23
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+24
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+25
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+26
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+27
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+30
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+31
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+32
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+33
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+34
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+35
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+36
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+37
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+40
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+41
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+42
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+43
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+44
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+45
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+46
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+47
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+50
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+51
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+52
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+53
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+54
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+55
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+56
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+57
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+60
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+61
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+62
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+63
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+64
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+65
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+66
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+67
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+70
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+71
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+72
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+73
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+74
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+75
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+76
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+77
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+100
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+101
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+102
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+103
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+104
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+105
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+106
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+107
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+110
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+111
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+112
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+113
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+114
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+115
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+116
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+117
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+120
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+121
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+122
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+123
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+124
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+125
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+126
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+127
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+130
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+131
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+132
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+133
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+134
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+135
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+136
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+137
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+140
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+141
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+142
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+143
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+144
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+145
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+146
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+147
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+150
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+151
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+152
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+153
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+154
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+155
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+156
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+157
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+160
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+161
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+162
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+163
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+164
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+165
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+166
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+167
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+170
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+171
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+172
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+173
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+174
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+175
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+176
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+177
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+200
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+201
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+202
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+203
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+204
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+205
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+206
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+207
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+210
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+211
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+212
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+213
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+214
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+215
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+216
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+217
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+220
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+221
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+222
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+223
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+224
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+225
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+226
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+227
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+230
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+231
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+232
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+233
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+234
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+235
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+236
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+237
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+240
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+241
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+242
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+243
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+244
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+245
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+246
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+247
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+250
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+251
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+252
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+253
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+254
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+255
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+256
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+257
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+260
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+261
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+262
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+263
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+264
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+265
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+266
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+267
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+270
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+271
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+272
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+273
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+274
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+275
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+276
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+277
+*** Done ***
diff --git a/ext/standard/tests/file/tempnam_variation4-1.phpt b/ext/standard/tests/file/tempnam_variation4-1.phpt
new file mode 100644
index 0000000000..667d3ecda6
--- /dev/null
+++ b/ext/standard/tests/file/tempnam_variation4-1.phpt
@@ -0,0 +1,638 @@
+--TEST--
+Test tempnam() function: usage variations - permissions(0351 to 0777) of dir
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) == 'WIN') {
+ die('skip Not valid for Windows');
+}
+// Skip if being run by root
+$filename = dirname(__FILE__)."/is_readable_root_check.tmp";
+$fp = fopen($filename, 'w');
+fclose($fp);
+if(fileowner($filename) == 0) {
+ unlink ($filename);
+ die('skip cannot be run as root');
+}
+unlink($filename);
+?>
+--FILE--
+<?php
+/* Prototype: string tempnam ( string $dir, string $prefix );
+ Description: Create file with unique file name.
+*/
+
+/* Trying to create the file in a dir with permissions from 0351 to 0777,
+ Allowable permissions: files are expected to be created in the input dir
+ Non-allowable permissions: files are expected to be created in '/tmp' dir
+*/
+
+echo "*** Testing tempnam() with dir of permissions from 0351 to 0777 ***\n";
+$file_path = dirname(__FILE__);
+$dir_name = $file_path."/tempnam_variation4";
+$prefix = "tempnamVar4.";
+
+mkdir($dir_name);
+
+for($mode = 0351; $mode <= 0777; $mode++) {
+ chmod($dir_name, $mode);
+ $file_name = tempnam($dir_name, $prefix);
+
+ if(file_exists($file_name) ) {
+ if (dirname($file_name) != $dir_name) {
+ /* Either there's a notice or error */
+ printf("%o\n", $mode);
+
+ if (realpath(dirname($file_name)) != realpath(sys_get_temp_dir())) {
+ echo " created in unexpected dir\n";
+ }
+ }
+ unlink($file_name);
+ }
+ else {
+ print("FAILED: File is not created\n");
+ }
+}
+
+rmdir($dir_name);
+
+echo "*** Done ***\n";
+?>
+--EXPECTF--
+*** Testing tempnam() with dir of permissions from 0351 to 0777 ***
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+400
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+401
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+402
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+403
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+404
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+405
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+406
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+407
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+410
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+411
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+412
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+413
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+414
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+415
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+416
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+417
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+420
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+421
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+422
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+423
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+424
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+425
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+426
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+427
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+430
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+431
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+432
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+433
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+434
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+435
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+436
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+437
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+440
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+441
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+442
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+443
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+444
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+445
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+446
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+447
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+450
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+451
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+452
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+453
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+454
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+455
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+456
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+457
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+460
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+461
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+462
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+463
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+464
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+465
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+466
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+467
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+470
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+471
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+472
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+473
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+474
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+475
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+476
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+477
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+500
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+501
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+502
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+503
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+504
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+505
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+506
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+507
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+510
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+511
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+512
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+513
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+514
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+515
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+516
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+517
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+520
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+521
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+522
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+523
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+524
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+525
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+526
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+527
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+530
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+531
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+532
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+533
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+534
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+535
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+536
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+537
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+540
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+541
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+542
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+543
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+544
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+545
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+546
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+547
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+550
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+551
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+552
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+553
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+554
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+555
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+556
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+557
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+560
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+561
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+562
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+563
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+564
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+565
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+566
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+567
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+570
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+571
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+572
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+573
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+574
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+575
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+576
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+577
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+600
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+601
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+602
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+603
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+604
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+605
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+606
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+607
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+610
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+611
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+612
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+613
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+614
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+615
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+616
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+617
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+620
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+621
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+622
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+623
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+624
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+625
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+626
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+627
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+630
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+631
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+632
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+633
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+634
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+635
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+636
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+637
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+640
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+641
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+642
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+643
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+644
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+645
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+646
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+647
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+650
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+651
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+652
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+653
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+654
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+655
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+656
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+657
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+660
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+661
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+662
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+663
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+664
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+665
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+666
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+667
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+670
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+671
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+672
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+673
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+674
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+675
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+676
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+677
+*** Done ***
diff --git a/ext/standard/tests/file/tempnam_variation4.phpt b/ext/standard/tests/file/tempnam_variation4.phpt
deleted file mode 100644
index 4ce1c7ec24..0000000000
--- a/ext/standard/tests/file/tempnam_variation4.phpt
+++ /dev/null
@@ -1,1092 +0,0 @@
---TEST--
-Test tempnam() function: usage variations - permissions(0000 to 0777) of dir
---SKIPIF--
-<?php
-if (substr(PHP_OS, 0, 3) == 'WIN') {
- die('skip Not valid for Windows');
-}
-// Skip if being run by root
-$filename = dirname(__FILE__)."/is_readable_root_check.tmp";
-$fp = fopen($filename, 'w');
-fclose($fp);
-if(fileowner($filename) == 0) {
- unlink ($filename);
- die('skip cannot be run as root');
-}
-unlink($filename);
-?>
---FILE--
-<?php
-/* Prototype: string tempnam ( string $dir, string $prefix );
- Description: Create file with unique file name.
-*/
-
-/* Trying to create the file in a dir with permissions from 0000 to 0777,
- Allowable permissions: files are expected to be created in the input dir
- Non-allowable permissions: files are expected to be created in '/tmp' dir
-*/
-
-echo "*** Testing tempnam() with dir of permissions from 0000 to 0777 ***\n";
-$file_path = dirname(__FILE__);
-$dir_name = $file_path."/tempnam_variation4";
-$prefix = "tempnamVar4.";
-
-mkdir($dir_name);
-
-for($mode = 0000; $mode <= 0777; $mode++) {
- echo "-- dir perms ";
- printf("%o", $mode);
- echo " --\n";
- chmod($dir_name, $mode);
- $file_name = tempnam($dir_name, $prefix);
-
- if(file_exists($file_name) ) {
- if (realpath(dirname($file_name)) == realpath(sys_get_temp_dir())) {
- $msg = " created in temp dir ";
- }
- else if (dirname($file_name) == $dir_name) {
- $msg = " created in requested dir";
- }
- else {
- $msg = " created in unexpected dir";
- }
-
- echo $msg."\n";
- unlink($file_name);
- }
- else {
- print("FAILED: File is not created\n");
- }
-}
-
-rmdir($dir_name);
-
-echo "*** Done ***\n";
-?>
---EXPECTF--
-*** Testing tempnam() with dir of permissions from 0000 to 0777 ***
--- dir perms 0 --
- created in temp dir
--- dir perms 1 --
- created in temp dir
--- dir perms 2 --
- created in temp dir
--- dir perms 3 --
- created in temp dir
--- dir perms 4 --
- created in temp dir
--- dir perms 5 --
- created in temp dir
--- dir perms 6 --
- created in temp dir
--- dir perms 7 --
- created in temp dir
--- dir perms 10 --
- created in temp dir
--- dir perms 11 --
- created in temp dir
--- dir perms 12 --
- created in temp dir
--- dir perms 13 --
- created in temp dir
--- dir perms 14 --
- created in temp dir
--- dir perms 15 --
- created in temp dir
--- dir perms 16 --
- created in temp dir
--- dir perms 17 --
- created in temp dir
--- dir perms 20 --
- created in temp dir
--- dir perms 21 --
- created in temp dir
--- dir perms 22 --
- created in temp dir
--- dir perms 23 --
- created in temp dir
--- dir perms 24 --
- created in temp dir
--- dir perms 25 --
- created in temp dir
--- dir perms 26 --
- created in temp dir
--- dir perms 27 --
- created in temp dir
--- dir perms 30 --
- created in temp dir
--- dir perms 31 --
- created in temp dir
--- dir perms 32 --
- created in temp dir
--- dir perms 33 --
- created in temp dir
--- dir perms 34 --
- created in temp dir
--- dir perms 35 --
- created in temp dir
--- dir perms 36 --
- created in temp dir
--- dir perms 37 --
- created in temp dir
--- dir perms 40 --
- created in temp dir
--- dir perms 41 --
- created in temp dir
--- dir perms 42 --
- created in temp dir
--- dir perms 43 --
- created in temp dir
--- dir perms 44 --
- created in temp dir
--- dir perms 45 --
- created in temp dir
--- dir perms 46 --
- created in temp dir
--- dir perms 47 --
- created in temp dir
--- dir perms 50 --
- created in temp dir
--- dir perms 51 --
- created in temp dir
--- dir perms 52 --
- created in temp dir
--- dir perms 53 --
- created in temp dir
--- dir perms 54 --
- created in temp dir
--- dir perms 55 --
- created in temp dir
--- dir perms 56 --
- created in temp dir
--- dir perms 57 --
- created in temp dir
--- dir perms 60 --
- created in temp dir
--- dir perms 61 --
- created in temp dir
--- dir perms 62 --
- created in temp dir
--- dir perms 63 --
- created in temp dir
--- dir perms 64 --
- created in temp dir
--- dir perms 65 --
- created in temp dir
--- dir perms 66 --
- created in temp dir
--- dir perms 67 --
- created in temp dir
--- dir perms 70 --
- created in temp dir
--- dir perms 71 --
- created in temp dir
--- dir perms 72 --
- created in temp dir
--- dir perms 73 --
- created in temp dir
--- dir perms 74 --
- created in temp dir
--- dir perms 75 --
- created in temp dir
--- dir perms 76 --
- created in temp dir
--- dir perms 77 --
- created in temp dir
--- dir perms 100 --
- created in temp dir
--- dir perms 101 --
- created in temp dir
--- dir perms 102 --
- created in temp dir
--- dir perms 103 --
- created in temp dir
--- dir perms 104 --
- created in temp dir
--- dir perms 105 --
- created in temp dir
--- dir perms 106 --
- created in temp dir
--- dir perms 107 --
- created in temp dir
--- dir perms 110 --
- created in temp dir
--- dir perms 111 --
- created in temp dir
--- dir perms 112 --
- created in temp dir
--- dir perms 113 --
- created in temp dir
--- dir perms 114 --
- created in temp dir
--- dir perms 115 --
- created in temp dir
--- dir perms 116 --
- created in temp dir
--- dir perms 117 --
- created in temp dir
--- dir perms 120 --
- created in temp dir
--- dir perms 121 --
- created in temp dir
--- dir perms 122 --
- created in temp dir
--- dir perms 123 --
- created in temp dir
--- dir perms 124 --
- created in temp dir
--- dir perms 125 --
- created in temp dir
--- dir perms 126 --
- created in temp dir
--- dir perms 127 --
- created in temp dir
--- dir perms 130 --
- created in temp dir
--- dir perms 131 --
- created in temp dir
--- dir perms 132 --
- created in temp dir
--- dir perms 133 --
- created in temp dir
--- dir perms 134 --
- created in temp dir
--- dir perms 135 --
- created in temp dir
--- dir perms 136 --
- created in temp dir
--- dir perms 137 --
- created in temp dir
--- dir perms 140 --
- created in temp dir
--- dir perms 141 --
- created in temp dir
--- dir perms 142 --
- created in temp dir
--- dir perms 143 --
- created in temp dir
--- dir perms 144 --
- created in temp dir
--- dir perms 145 --
- created in temp dir
--- dir perms 146 --
- created in temp dir
--- dir perms 147 --
- created in temp dir
--- dir perms 150 --
- created in temp dir
--- dir perms 151 --
- created in temp dir
--- dir perms 152 --
- created in temp dir
--- dir perms 153 --
- created in temp dir
--- dir perms 154 --
- created in temp dir
--- dir perms 155 --
- created in temp dir
--- dir perms 156 --
- created in temp dir
--- dir perms 157 --
- created in temp dir
--- dir perms 160 --
- created in temp dir
--- dir perms 161 --
- created in temp dir
--- dir perms 162 --
- created in temp dir
--- dir perms 163 --
- created in temp dir
--- dir perms 164 --
- created in temp dir
--- dir perms 165 --
- created in temp dir
--- dir perms 166 --
- created in temp dir
--- dir perms 167 --
- created in temp dir
--- dir perms 170 --
- created in temp dir
--- dir perms 171 --
- created in temp dir
--- dir perms 172 --
- created in temp dir
--- dir perms 173 --
- created in temp dir
--- dir perms 174 --
- created in temp dir
--- dir perms 175 --
- created in temp dir
--- dir perms 176 --
- created in temp dir
--- dir perms 177 --
- created in temp dir
--- dir perms 200 --
- created in temp dir
--- dir perms 201 --
- created in temp dir
--- dir perms 202 --
- created in temp dir
--- dir perms 203 --
- created in temp dir
--- dir perms 204 --
- created in temp dir
--- dir perms 205 --
- created in temp dir
--- dir perms 206 --
- created in temp dir
--- dir perms 207 --
- created in temp dir
--- dir perms 210 --
- created in temp dir
--- dir perms 211 --
- created in temp dir
--- dir perms 212 --
- created in temp dir
--- dir perms 213 --
- created in temp dir
--- dir perms 214 --
- created in temp dir
--- dir perms 215 --
- created in temp dir
--- dir perms 216 --
- created in temp dir
--- dir perms 217 --
- created in temp dir
--- dir perms 220 --
- created in temp dir
--- dir perms 221 --
- created in temp dir
--- dir perms 222 --
- created in temp dir
--- dir perms 223 --
- created in temp dir
--- dir perms 224 --
- created in temp dir
--- dir perms 225 --
- created in temp dir
--- dir perms 226 --
- created in temp dir
--- dir perms 227 --
- created in temp dir
--- dir perms 230 --
- created in temp dir
--- dir perms 231 --
- created in temp dir
--- dir perms 232 --
- created in temp dir
--- dir perms 233 --
- created in temp dir
--- dir perms 234 --
- created in temp dir
--- dir perms 235 --
- created in temp dir
--- dir perms 236 --
- created in temp dir
--- dir perms 237 --
- created in temp dir
--- dir perms 240 --
- created in temp dir
--- dir perms 241 --
- created in temp dir
--- dir perms 242 --
- created in temp dir
--- dir perms 243 --
- created in temp dir
--- dir perms 244 --
- created in temp dir
--- dir perms 245 --
- created in temp dir
--- dir perms 246 --
- created in temp dir
--- dir perms 247 --
- created in temp dir
--- dir perms 250 --
- created in temp dir
--- dir perms 251 --
- created in temp dir
--- dir perms 252 --
- created in temp dir
--- dir perms 253 --
- created in temp dir
--- dir perms 254 --
- created in temp dir
--- dir perms 255 --
- created in temp dir
--- dir perms 256 --
- created in temp dir
--- dir perms 257 --
- created in temp dir
--- dir perms 260 --
- created in temp dir
--- dir perms 261 --
- created in temp dir
--- dir perms 262 --
- created in temp dir
--- dir perms 263 --
- created in temp dir
--- dir perms 264 --
- created in temp dir
--- dir perms 265 --
- created in temp dir
--- dir perms 266 --
- created in temp dir
--- dir perms 267 --
- created in temp dir
--- dir perms 270 --
- created in temp dir
--- dir perms 271 --
- created in temp dir
--- dir perms 272 --
- created in temp dir
--- dir perms 273 --
- created in temp dir
--- dir perms 274 --
- created in temp dir
--- dir perms 275 --
- created in temp dir
--- dir perms 276 --
- created in temp dir
--- dir perms 277 --
- created in temp dir
--- dir perms 300 --
- created in requested dir
--- dir perms 301 --
- created in requested dir
--- dir perms 302 --
- created in requested dir
--- dir perms 303 --
- created in requested dir
--- dir perms 304 --
- created in requested dir
--- dir perms 305 --
- created in requested dir
--- dir perms 306 --
- created in requested dir
--- dir perms 307 --
- created in requested dir
--- dir perms 310 --
- created in requested dir
--- dir perms 311 --
- created in requested dir
--- dir perms 312 --
- created in requested dir
--- dir perms 313 --
- created in requested dir
--- dir perms 314 --
- created in requested dir
--- dir perms 315 --
- created in requested dir
--- dir perms 316 --
- created in requested dir
--- dir perms 317 --
- created in requested dir
--- dir perms 320 --
- created in requested dir
--- dir perms 321 --
- created in requested dir
--- dir perms 322 --
- created in requested dir
--- dir perms 323 --
- created in requested dir
--- dir perms 324 --
- created in requested dir
--- dir perms 325 --
- created in requested dir
--- dir perms 326 --
- created in requested dir
--- dir perms 327 --
- created in requested dir
--- dir perms 330 --
- created in requested dir
--- dir perms 331 --
- created in requested dir
--- dir perms 332 --
- created in requested dir
--- dir perms 333 --
- created in requested dir
--- dir perms 334 --
- created in requested dir
--- dir perms 335 --
- created in requested dir
--- dir perms 336 --
- created in requested dir
--- dir perms 337 --
- created in requested dir
--- dir perms 340 --
- created in requested dir
--- dir perms 341 --
- created in requested dir
--- dir perms 342 --
- created in requested dir
--- dir perms 343 --
- created in requested dir
--- dir perms 344 --
- created in requested dir
--- dir perms 345 --
- created in requested dir
--- dir perms 346 --
- created in requested dir
--- dir perms 347 --
- created in requested dir
--- dir perms 350 --
- created in requested dir
--- dir perms 351 --
- created in requested dir
--- dir perms 352 --
- created in requested dir
--- dir perms 353 --
- created in requested dir
--- dir perms 354 --
- created in requested dir
--- dir perms 355 --
- created in requested dir
--- dir perms 356 --
- created in requested dir
--- dir perms 357 --
- created in requested dir
--- dir perms 360 --
- created in requested dir
--- dir perms 361 --
- created in requested dir
--- dir perms 362 --
- created in requested dir
--- dir perms 363 --
- created in requested dir
--- dir perms 364 --
- created in requested dir
--- dir perms 365 --
- created in requested dir
--- dir perms 366 --
- created in requested dir
--- dir perms 367 --
- created in requested dir
--- dir perms 370 --
- created in requested dir
--- dir perms 371 --
- created in requested dir
--- dir perms 372 --
- created in requested dir
--- dir perms 373 --
- created in requested dir
--- dir perms 374 --
- created in requested dir
--- dir perms 375 --
- created in requested dir
--- dir perms 376 --
- created in requested dir
--- dir perms 377 --
- created in requested dir
--- dir perms 400 --
- created in temp dir
--- dir perms 401 --
- created in temp dir
--- dir perms 402 --
- created in temp dir
--- dir perms 403 --
- created in temp dir
--- dir perms 404 --
- created in temp dir
--- dir perms 405 --
- created in temp dir
--- dir perms 406 --
- created in temp dir
--- dir perms 407 --
- created in temp dir
--- dir perms 410 --
- created in temp dir
--- dir perms 411 --
- created in temp dir
--- dir perms 412 --
- created in temp dir
--- dir perms 413 --
- created in temp dir
--- dir perms 414 --
- created in temp dir
--- dir perms 415 --
- created in temp dir
--- dir perms 416 --
- created in temp dir
--- dir perms 417 --
- created in temp dir
--- dir perms 420 --
- created in temp dir
--- dir perms 421 --
- created in temp dir
--- dir perms 422 --
- created in temp dir
--- dir perms 423 --
- created in temp dir
--- dir perms 424 --
- created in temp dir
--- dir perms 425 --
- created in temp dir
--- dir perms 426 --
- created in temp dir
--- dir perms 427 --
- created in temp dir
--- dir perms 430 --
- created in temp dir
--- dir perms 431 --
- created in temp dir
--- dir perms 432 --
- created in temp dir
--- dir perms 433 --
- created in temp dir
--- dir perms 434 --
- created in temp dir
--- dir perms 435 --
- created in temp dir
--- dir perms 436 --
- created in temp dir
--- dir perms 437 --
- created in temp dir
--- dir perms 440 --
- created in temp dir
--- dir perms 441 --
- created in temp dir
--- dir perms 442 --
- created in temp dir
--- dir perms 443 --
- created in temp dir
--- dir perms 444 --
- created in temp dir
--- dir perms 445 --
- created in temp dir
--- dir perms 446 --
- created in temp dir
--- dir perms 447 --
- created in temp dir
--- dir perms 450 --
- created in temp dir
--- dir perms 451 --
- created in temp dir
--- dir perms 452 --
- created in temp dir
--- dir perms 453 --
- created in temp dir
--- dir perms 454 --
- created in temp dir
--- dir perms 455 --
- created in temp dir
--- dir perms 456 --
- created in temp dir
--- dir perms 457 --
- created in temp dir
--- dir perms 460 --
- created in temp dir
--- dir perms 461 --
- created in temp dir
--- dir perms 462 --
- created in temp dir
--- dir perms 463 --
- created in temp dir
--- dir perms 464 --
- created in temp dir
--- dir perms 465 --
- created in temp dir
--- dir perms 466 --
- created in temp dir
--- dir perms 467 --
- created in temp dir
--- dir perms 470 --
- created in temp dir
--- dir perms 471 --
- created in temp dir
--- dir perms 472 --
- created in temp dir
--- dir perms 473 --
- created in temp dir
--- dir perms 474 --
- created in temp dir
--- dir perms 475 --
- created in temp dir
--- dir perms 476 --
- created in temp dir
--- dir perms 477 --
- created in temp dir
--- dir perms 500 --
- created in temp dir
--- dir perms 501 --
- created in temp dir
--- dir perms 502 --
- created in temp dir
--- dir perms 503 --
- created in temp dir
--- dir perms 504 --
- created in temp dir
--- dir perms 505 --
- created in temp dir
--- dir perms 506 --
- created in temp dir
--- dir perms 507 --
- created in temp dir
--- dir perms 510 --
- created in temp dir
--- dir perms 511 --
- created in temp dir
--- dir perms 512 --
- created in temp dir
--- dir perms 513 --
- created in temp dir
--- dir perms 514 --
- created in temp dir
--- dir perms 515 --
- created in temp dir
--- dir perms 516 --
- created in temp dir
--- dir perms 517 --
- created in temp dir
--- dir perms 520 --
- created in temp dir
--- dir perms 521 --
- created in temp dir
--- dir perms 522 --
- created in temp dir
--- dir perms 523 --
- created in temp dir
--- dir perms 524 --
- created in temp dir
--- dir perms 525 --
- created in temp dir
--- dir perms 526 --
- created in temp dir
--- dir perms 527 --
- created in temp dir
--- dir perms 530 --
- created in temp dir
--- dir perms 531 --
- created in temp dir
--- dir perms 532 --
- created in temp dir
--- dir perms 533 --
- created in temp dir
--- dir perms 534 --
- created in temp dir
--- dir perms 535 --
- created in temp dir
--- dir perms 536 --
- created in temp dir
--- dir perms 537 --
- created in temp dir
--- dir perms 540 --
- created in temp dir
--- dir perms 541 --
- created in temp dir
--- dir perms 542 --
- created in temp dir
--- dir perms 543 --
- created in temp dir
--- dir perms 544 --
- created in temp dir
--- dir perms 545 --
- created in temp dir
--- dir perms 546 --
- created in temp dir
--- dir perms 547 --
- created in temp dir
--- dir perms 550 --
- created in temp dir
--- dir perms 551 --
- created in temp dir
--- dir perms 552 --
- created in temp dir
--- dir perms 553 --
- created in temp dir
--- dir perms 554 --
- created in temp dir
--- dir perms 555 --
- created in temp dir
--- dir perms 556 --
- created in temp dir
--- dir perms 557 --
- created in temp dir
--- dir perms 560 --
- created in temp dir
--- dir perms 561 --
- created in temp dir
--- dir perms 562 --
- created in temp dir
--- dir perms 563 --
- created in temp dir
--- dir perms 564 --
- created in temp dir
--- dir perms 565 --
- created in temp dir
--- dir perms 566 --
- created in temp dir
--- dir perms 567 --
- created in temp dir
--- dir perms 570 --
- created in temp dir
--- dir perms 571 --
- created in temp dir
--- dir perms 572 --
- created in temp dir
--- dir perms 573 --
- created in temp dir
--- dir perms 574 --
- created in temp dir
--- dir perms 575 --
- created in temp dir
--- dir perms 576 --
- created in temp dir
--- dir perms 577 --
- created in temp dir
--- dir perms 600 --
- created in temp dir
--- dir perms 601 --
- created in temp dir
--- dir perms 602 --
- created in temp dir
--- dir perms 603 --
- created in temp dir
--- dir perms 604 --
- created in temp dir
--- dir perms 605 --
- created in temp dir
--- dir perms 606 --
- created in temp dir
--- dir perms 607 --
- created in temp dir
--- dir perms 610 --
- created in temp dir
--- dir perms 611 --
- created in temp dir
--- dir perms 612 --
- created in temp dir
--- dir perms 613 --
- created in temp dir
--- dir perms 614 --
- created in temp dir
--- dir perms 615 --
- created in temp dir
--- dir perms 616 --
- created in temp dir
--- dir perms 617 --
- created in temp dir
--- dir perms 620 --
- created in temp dir
--- dir perms 621 --
- created in temp dir
--- dir perms 622 --
- created in temp dir
--- dir perms 623 --
- created in temp dir
--- dir perms 624 --
- created in temp dir
--- dir perms 625 --
- created in temp dir
--- dir perms 626 --
- created in temp dir
--- dir perms 627 --
- created in temp dir
--- dir perms 630 --
- created in temp dir
--- dir perms 631 --
- created in temp dir
--- dir perms 632 --
- created in temp dir
--- dir perms 633 --
- created in temp dir
--- dir perms 634 --
- created in temp dir
--- dir perms 635 --
- created in temp dir
--- dir perms 636 --
- created in temp dir
--- dir perms 637 --
- created in temp dir
--- dir perms 640 --
- created in temp dir
--- dir perms 641 --
- created in temp dir
--- dir perms 642 --
- created in temp dir
--- dir perms 643 --
- created in temp dir
--- dir perms 644 --
- created in temp dir
--- dir perms 645 --
- created in temp dir
--- dir perms 646 --
- created in temp dir
--- dir perms 647 --
- created in temp dir
--- dir perms 650 --
- created in temp dir
--- dir perms 651 --
- created in temp dir
--- dir perms 652 --
- created in temp dir
--- dir perms 653 --
- created in temp dir
--- dir perms 654 --
- created in temp dir
--- dir perms 655 --
- created in temp dir
--- dir perms 656 --
- created in temp dir
--- dir perms 657 --
- created in temp dir
--- dir perms 660 --
- created in temp dir
--- dir perms 661 --
- created in temp dir
--- dir perms 662 --
- created in temp dir
--- dir perms 663 --
- created in temp dir
--- dir perms 664 --
- created in temp dir
--- dir perms 665 --
- created in temp dir
--- dir perms 666 --
- created in temp dir
--- dir perms 667 --
- created in temp dir
--- dir perms 670 --
- created in temp dir
--- dir perms 671 --
- created in temp dir
--- dir perms 672 --
- created in temp dir
--- dir perms 673 --
- created in temp dir
--- dir perms 674 --
- created in temp dir
--- dir perms 675 --
- created in temp dir
--- dir perms 676 --
- created in temp dir
--- dir perms 677 --
- created in temp dir
--- dir perms 700 --
- created in requested dir
--- dir perms 701 --
- created in requested dir
--- dir perms 702 --
- created in requested dir
--- dir perms 703 --
- created in requested dir
--- dir perms 704 --
- created in requested dir
--- dir perms 705 --
- created in requested dir
--- dir perms 706 --
- created in requested dir
--- dir perms 707 --
- created in requested dir
--- dir perms 710 --
- created in requested dir
--- dir perms 711 --
- created in requested dir
--- dir perms 712 --
- created in requested dir
--- dir perms 713 --
- created in requested dir
--- dir perms 714 --
- created in requested dir
--- dir perms 715 --
- created in requested dir
--- dir perms 716 --
- created in requested dir
--- dir perms 717 --
- created in requested dir
--- dir perms 720 --
- created in requested dir
--- dir perms 721 --
- created in requested dir
--- dir perms 722 --
- created in requested dir
--- dir perms 723 --
- created in requested dir
--- dir perms 724 --
- created in requested dir
--- dir perms 725 --
- created in requested dir
--- dir perms 726 --
- created in requested dir
--- dir perms 727 --
- created in requested dir
--- dir perms 730 --
- created in requested dir
--- dir perms 731 --
- created in requested dir
--- dir perms 732 --
- created in requested dir
--- dir perms 733 --
- created in requested dir
--- dir perms 734 --
- created in requested dir
--- dir perms 735 --
- created in requested dir
--- dir perms 736 --
- created in requested dir
--- dir perms 737 --
- created in requested dir
--- dir perms 740 --
- created in requested dir
--- dir perms 741 --
- created in requested dir
--- dir perms 742 --
- created in requested dir
--- dir perms 743 --
- created in requested dir
--- dir perms 744 --
- created in requested dir
--- dir perms 745 --
- created in requested dir
--- dir perms 746 --
- created in requested dir
--- dir perms 747 --
- created in requested dir
--- dir perms 750 --
- created in requested dir
--- dir perms 751 --
- created in requested dir
--- dir perms 752 --
- created in requested dir
--- dir perms 753 --
- created in requested dir
--- dir perms 754 --
- created in requested dir
--- dir perms 755 --
- created in requested dir
--- dir perms 756 --
- created in requested dir
--- dir perms 757 --
- created in requested dir
--- dir perms 760 --
- created in requested dir
--- dir perms 761 --
- created in requested dir
--- dir perms 762 --
- created in requested dir
--- dir perms 763 --
- created in requested dir
--- dir perms 764 --
- created in requested dir
--- dir perms 765 --
- created in requested dir
--- dir perms 766 --
- created in requested dir
--- dir perms 767 --
- created in requested dir
--- dir perms 770 --
- created in requested dir
--- dir perms 771 --
- created in requested dir
--- dir perms 772 --
- created in requested dir
--- dir perms 773 --
- created in requested dir
--- dir perms 774 --
- created in requested dir
--- dir perms 775 --
- created in requested dir
--- dir perms 776 --
- created in requested dir
--- dir perms 777 --
- created in requested dir
-*** Done ***
diff --git a/ext/standard/tests/file/tempnam_variation7-win32.phpt b/ext/standard/tests/file/tempnam_variation7-win32.phpt
index 0c8caca95c..918ef36588 100644
--- a/ext/standard/tests/file/tempnam_variation7-win32.phpt
+++ b/ext/standard/tests/file/tempnam_variation7-win32.phpt
@@ -65,10 +65,14 @@ echo "\n*** Done ***\n";
--EXPECTF--
*** Testing tempnam() with invalid/non-existing directory names ***
-- Iteration 0 --
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation7-win32.php on line %d
File name is => %s%et%s
File permissions are => 100666
File created in => temp dir
-- Iteration 1 --
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation7-win32.php on line %d
File name is => %s%et%s
File permissions are => 100666
File created in => temp dir
@@ -85,6 +89,8 @@ File name is => %s%et%s
File permissions are => 100666
File created in => temp dir
-- Iteration 5 --
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation7-win32.php on line %d
File name is => %s%et%s
File permissions are => 100666
File created in => temp dir
@@ -101,10 +107,14 @@ Warning: tempnam() expects parameter 1 to be a valid path, array given in %s on
Warning: unlink(): %r(Invalid argument|No such file or directory)%r in %s on line %d
-- Iteration 8 --
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation7-win32.php on line %d
File name is => %s%et%s
File permissions are => 100666
File created in => temp dir
-- Iteration 9 --
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation7-win32.php on line %d
File name is => %s%et%s
File permissions are => 100666
File created in => temp dir
diff --git a/ext/standard/tests/file/tempnam_variation7.phpt b/ext/standard/tests/file/tempnam_variation7.phpt
index b6f81caabc..d24c1d8974 100644
--- a/ext/standard/tests/file/tempnam_variation7.phpt
+++ b/ext/standard/tests/file/tempnam_variation7.phpt
@@ -70,10 +70,14 @@ echo "\n*** Done ***\n";
--EXPECTF--
*** Testing tempnam() with invalid/non-existing directory names ***
-- Iteration 0 --
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation7.php on line %d
File name is => %s%etempnam_variation3.tmp%s
File permissions are => 100600
File created in => temp dir
-- Iteration 1 --
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation7.php on line %d
File name is => %s%etempnam_variation3.tmp%s
File permissions are => 100600
File created in => temp dir
@@ -90,6 +94,8 @@ File name is => %s%etempnam_variation3.tmp%s
File permissions are => 100600
File created in => temp dir
-- Iteration 5 --
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation7.php on line %d
File name is => %s%etempnam_variation3.tmp%s
File permissions are => 100600
File created in => temp dir
@@ -106,10 +112,14 @@ Warning: tempnam() expects parameter 1 to be a valid path, array given in %s on
Warning: unlink(): %s in %s on line %d
-- Iteration 8 --
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation7.php on line %d
File name is => %s/tempnam_variation3.tmp%s
File permissions are => 100600
File created in => temp dir
-- Iteration 9 --
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation7.php on line %d
File name is => %s/tempnam_variation3.tmp%s
File permissions are => 100600
File created in => temp dir
diff --git a/ext/standard/tests/file/tempnam_variation8-win32.phpt b/ext/standard/tests/file/tempnam_variation8-win32.phpt
index 8df67b609e..52ff7b9daa 100644
--- a/ext/standard/tests/file/tempnam_variation8-win32.phpt
+++ b/ext/standard/tests/file/tempnam_variation8-win32.phpt
@@ -115,11 +115,15 @@ File permissions are => 100666
File created in => directory specified
-- Iteration 6 --
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation8-win32.php on line %d
File name is => %s\t%s
File permissions are => 100666
File created in => temp dir
-- Iteration 7 --
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation8-win32.php on line %d
File name is => %s\t%s
File permissions are => 100666
File created in => temp dir
@@ -140,6 +144,8 @@ File permissions are => 100666
File created in => directory specified
-- Iteration 11 --
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation8-win32.php on line %d
File name is => %s\t%s
File permissions are => 100666
File created in => temp dir
diff --git a/ext/standard/tests/file/touch_basic-win32-mb.phpt b/ext/standard/tests/file/touch_basic-win32-mb.phpt
new file mode 100644
index 0000000000..d721edc84c
--- /dev/null
+++ b/ext/standard/tests/file/touch_basic-win32-mb.phpt
@@ -0,0 +1,96 @@
+--TEST--
+Test touch() function : basic functionality
+--CREDITS--
+Dave Kelsey <d_kelsey@uk.ibm.com>
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die('skip.. only for Windows');
+}
+?>
+--FILE--
+<?php
+/* Prototype : proto bool touch(string filename [, int time [, int atime]])
+ * Description: Set modification time of file
+ * Source code: ext/standard/filestat.c
+ * Alias to functions:
+ */
+
+echo "*** Testing touch() : basic functionality ***\n";
+
+$filename = dirname(__FILE__)."/touch_私はガラスを食べられます.dat";
+
+echo "\n--- testing touch creates a file ---\n";
+@unlink($filename);
+if (file_exists($filename)) {
+ die("touch_basic failed");
+}
+var_dump( touch($filename) );
+if (file_exists($filename) == false) {
+ die("touch_basic failed");
+}
+
+echo "\n --- testing touch doesn't alter file contents ---\n";
+$testln = "Here is a test line";
+$h = fopen($filename, "wb");
+fwrite($h, $testln);
+fclose($h);
+touch($filename);
+$h = fopen($filename, "rb");
+echo fgets($h);
+fclose($h);
+
+echo "\n\n --- testing touch alters the correct file metadata ---\n";
+$init_meta = stat($filename);
+clearstatcache();
+sleep(1);
+touch($filename);
+$next_meta = stat($filename);
+$type = array("dev", "ino", "mode", "nlink", "uid", "gid",
+ "rdev", "size", "atime", "mtime", "ctime",
+ "blksize", "blocks");
+
+for ($i = 0; $i < count($type); $i++) {
+ if ($init_meta[$i] != $next_meta[$i]) {
+ echo "stat data differs at $type[$i]\n";
+ }
+}
+
+
+// Initialise all required variables
+$time = 10000;
+$atime = 20470;
+
+// Calling touch() with all possible arguments
+echo "\n --- testing touch using all parameters ---\n";
+var_dump( touch($filename, $time, $atime) );
+clearstatcache();
+$init_meta = stat($filename);
+echo "ctime=".$init_meta['ctime']."\n";
+echo "mtime=".$init_meta['mtime']."\n";
+echo "atime=".$init_meta['atime']."\n";
+
+unlink($filename);
+
+echo "Done";
+?>
+--EXPECTF--
+*** Testing touch() : basic functionality ***
+
+--- testing touch creates a file ---
+bool(true)
+
+ --- testing touch doesn't alter file contents ---
+Here is a test line
+
+ --- testing touch alters the correct file metadata ---
+stat data differs at atime
+stat data differs at mtime
+
+ --- testing touch using all parameters ---
+bool(true)
+ctime=%d
+mtime=10000
+atime=20470
+Done
+
diff --git a/ext/standard/tests/file/touch_variation3-win32-mb.phpt b/ext/standard/tests/file/touch_variation3-win32-mb.phpt
new file mode 100644
index 0000000000..445ef3fb1c
--- /dev/null
+++ b/ext/standard/tests/file/touch_variation3-win32-mb.phpt
@@ -0,0 +1,200 @@
+--TEST--
+Test touch() function : usage variation - different types for time
+--CREDITS--
+Dave Kelsey <d_kelsey@uk.ibm.com>
+--SKIPIF--
+<?php
+if (PHP_INT_SIZE != 8) die("skip this test is for 64-bit only");
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die('skip.. only for Windows');
+}
+?>
+--FILE--
+<?php
+/* Prototype : bool touch(string filename [, int time [, int atime]])
+ * Description: Set modification time of file
+ * Source code: ext/standard/filestat.c
+ * Alias to functions:
+ */
+
+echo "*** Testing touch() : usage variation ***\n";
+
+// Define error handler
+function test_error_handler($err_no, $err_msg, $filename, $linenum, $vars) {
+ if (error_reporting() != 0) {
+ // report non-silenced errors
+ echo "Error: $err_no - $err_msg, $filename($linenum)\n";
+ }
+}
+set_error_handler('test_error_handler');
+
+// Initialise function arguments not being substituted (if any)
+$filename = 'touchVar2_私はガラスを食べられます.tmp';
+$atime = 10;
+
+//get an unset variable
+$unset_var = 10;
+unset ($unset_var);
+
+// define some classes
+class classWithToString
+{
+ public function __toString() {
+ return "Class A object";
+ }
+}
+
+class classWithoutToString
+{
+}
+
+// heredoc string
+$heredoc = <<<EOT
+hello world
+EOT;
+
+// add arrays
+$index_array = array (1, 2, 3);
+$assoc_array = array ('one' => 1, 'two' => 2);
+
+//array of values to iterate over
+$inputs = array(
+
+ // float data
+ 'float 10.5' => 10.5,
+ 'float 12.3456789000e10' => 12.3456789000e10,
+ 'float .5' => .5,
+
+ // array data
+ 'empty array' => array(),
+ 'int indexed array' => $index_array,
+ 'associative array' => $assoc_array,
+ 'nested arrays' => array('foo', $index_array, $assoc_array),
+
+ // null data
+ 'uppercase NULL' => NULL,
+ 'lowercase null' => null,
+
+ // boolean data
+ 'lowercase true' => true,
+ 'lowercase false' =>false,
+ 'uppercase TRUE' =>TRUE,
+ 'uppercase FALSE' =>FALSE,
+
+ // empty data
+ 'empty string DQ' => "",
+ 'empty string SQ' => '',
+
+ // string data
+ 'string DQ' => "string",
+ 'string SQ' => 'string',
+ 'mixed case string' => "sTrInG",
+ 'heredoc' => $heredoc,
+
+ // object data
+ 'instance of classWithToString' => new classWithToString(),
+ 'instance of classWithoutToString' => new classWithoutToString(),
+
+ // undefined data
+ 'undefined var' => @$undefined_var,
+
+ // unset data
+ 'unset var' => @$unset_var,
+);
+
+// loop through each element of the array for time
+
+foreach($inputs as $key =>$value) {
+ echo "\n--$key--\n";
+ var_dump( touch($filename, $value, $atime) );
+};
+
+unlink($filename);
+
+?>
+===DONE===
+--EXPECTF--
+*** Testing touch() : usage variation ***
+
+--float 10.5--
+bool(true)
+
+--float 12.3456789000e10--
+bool(true)
+
+--float .5--
+bool(true)
+
+--empty array--
+Error: 2 - touch() expects parameter 2 to be integer, array given, %s(%d)
+NULL
+
+--int indexed array--
+Error: 2 - touch() expects parameter 2 to be integer, array given, %s(%d)
+NULL
+
+--associative array--
+Error: 2 - touch() expects parameter 2 to be integer, array given, %s(%d)
+NULL
+
+--nested arrays--
+Error: 2 - touch() expects parameter 2 to be integer, array given, %s(%d)
+NULL
+
+--uppercase NULL--
+bool(true)
+
+--lowercase null--
+bool(true)
+
+--lowercase true--
+bool(true)
+
+--lowercase false--
+bool(true)
+
+--uppercase TRUE--
+bool(true)
+
+--uppercase FALSE--
+bool(true)
+
+--empty string DQ--
+Error: 2 - touch() expects parameter 2 to be integer, string given, %s(%d)
+NULL
+
+--empty string SQ--
+Error: 2 - touch() expects parameter 2 to be integer, string given, %s(%d)
+NULL
+
+--string DQ--
+Error: 2 - touch() expects parameter 2 to be integer, string given, %s(%d)
+NULL
+
+--string SQ--
+Error: 2 - touch() expects parameter 2 to be integer, string given, %s(%d)
+NULL
+
+--mixed case string--
+Error: 2 - touch() expects parameter 2 to be integer, string given, %s(%d)
+NULL
+
+--heredoc--
+Error: 2 - touch() expects parameter 2 to be integer, string given, %s(%d)
+NULL
+
+--instance of classWithToString--
+Error: 2 - touch() expects parameter 2 to be integer, object given, %s(%d)
+NULL
+
+--instance of classWithoutToString--
+Error: 2 - touch() expects parameter 2 to be integer, object given, %s(%d)
+NULL
+
+--undefined var--
+bool(true)
+
+--unset var--
+bool(true)
+===DONE===
+
diff --git a/ext/standard/tests/file/unlink_error-win32-mb.phpt b/ext/standard/tests/file/unlink_error-win32-mb.phpt
new file mode 100644
index 0000000000..5111f34b76
--- /dev/null
+++ b/ext/standard/tests/file/unlink_error-win32-mb.phpt
@@ -0,0 +1,113 @@
+--TEST--
+Testing unlink() function : error conditions
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die('skip.. only on Windows');
+}
+?>
+--FILE--
+<?php
+/* Prototype : bool unlink ( string $filename [, resource $context] );
+ Description : Deletes filename
+*/
+
+$file_path = dirname(__FILE__).DIRECTORY_SEPARATOR."私はガラスを食べられます";
+
+@mkdir($file_path);
+
+$filename = "$file_path/unlink_error.tmp"; // temp file name used here
+$fp = fopen($filename, "w"); // create file
+fclose($fp);
+
+// creating a context
+$context = stream_context_create();
+
+echo "*** Testing unlink() : error conditions ***\n";
+
+echo "-- Testing unlink() on unexpected no. of arguments --\n";
+// arg < expected
+var_dump( unlink() );
+// args > expected
+var_dump( unlink($filename, $context, true) );
+var_dump( file_exists($filename) ); // expected: true
+
+echo "\n-- Testing unlink() on invalid arguments --\n";
+// invalid arguments
+var_dump( unlink('') ); // $filename as empty string
+var_dump( file_exists('') ); // confirm file doesnt exist
+
+var_dump( unlink(NULL) ); // $filename as NULL
+var_dump( file_exists(NULL) ); // confirm file doesnt exist
+
+var_dump( unlink(false) ); // $filename as boolean false
+var_dump( file_exists(false) ); // confirm file doesnt exist
+
+var_dump( unlink($filename, '') ); // $context as empty string
+var_dump( unlink($filename, false) ); // $context as boolean false
+var_dump( unlink($filename, NULL) ); // $context as NULL
+
+
+echo "\n-- Testing unlink() on non-existent file --\n";
+var_dump( unlink(dirname(__FILE__)."/non_existent_file.tmp") );
+
+echo "\n-- Testing unlink() on directory --\n";
+// temp directory used here
+$dirname = "$file_path/unlink_error";
+// create temp dir
+mkdir($dirname);
+// unlinking directory
+var_dump( unlink($dirname) ); // expected: false as unlink() does not work on dir
+
+echo "Done\n";
+?>
+--CLEAN--
+<?php
+unlink(dirname(__FILE__)."/私はガラスを食べられます/unlink_error.tmp");
+rmdir(dirname(__FILE__)."/私はガラスを食べられます/unlink_error");
+rmdir(dirname(__FILE__)."/私はガラスを食べられます");
+?>
+--EXPECTF--
+*** Testing unlink() : error conditions ***
+-- Testing unlink() on unexpected no. of arguments --
+
+Warning: unlink() expects at least 1 parameter, 0 given in %s on line %d
+bool(false)
+
+Warning: unlink() expects at most 2 parameters, 3 given in %s on line %d
+bool(false)
+bool(true)
+
+-- Testing unlink() on invalid arguments --
+
+Warning: unlink(): %s in %s on line %d
+bool(false)
+bool(false)
+
+Warning: unlink(): %s in %s on line %d
+bool(false)
+bool(false)
+
+Warning: unlink(): %s in %s on line %d
+bool(false)
+bool(false)
+
+Warning: unlink() expects parameter 2 to be resource, string given in %s on line %d
+bool(false)
+
+Warning: unlink() expects parameter 2 to be resource, boolean given in %s on line %d
+bool(false)
+
+Warning: unlink() expects parameter 2 to be resource, null given in %s on line %d
+bool(false)
+
+-- Testing unlink() on non-existent file --
+
+Warning: unlink(%s/non_existent_file.tmp): No such file or directory in %s on line %d
+bool(false)
+
+-- Testing unlink() on directory --
+
+Warning: unlink(%s/unlink_error): Permission denied in %s on line %d
+bool(false)
+Done
diff --git a/ext/standard/tests/file/unlink_variation1-win32-mb.phpt b/ext/standard/tests/file/unlink_variation1-win32-mb.phpt
new file mode 100644
index 0000000000..adfb10edfe
--- /dev/null
+++ b/ext/standard/tests/file/unlink_variation1-win32-mb.phpt
@@ -0,0 +1,87 @@
+--TEST--
+Test unlink() function : usage variations - unlinking file in a directory
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die('skip only on Windows');
+}
+?>
+--FILE--
+<?php
+/* Prototype : bool unlink ( string $filename [, resource $context] );
+ Description : Deletes filename
+*/
+
+/* Delete file having default permission but its dir having readonly permission
+ Delete file having readonly permission but dir having default permission
+*/
+
+
+$file_path = dirname(__FILE__).DIRECTORY_SEPARATOR."私はガラスを食べられます";
+@mkdir($file_path);
+
+// temp dir name used here
+$dirname = "$file_path/unlink_variation1";
+// temp filename used here
+$filename = "$dirname/unlink_variation1-win32.tmp";
+
+echo "\n*** Testing unlink() on file inside a directory ***\n";
+// create temp dir
+mkdir($dirname);
+// create temp file inside $dirname
+$fp = fopen($filename, "w");
+fclose($fp);
+
+echo "-- Unlink file having default permission and its dir having read only permission --\n";
+// remove write permission of $dirname
+// on windows dir permission is not respected
+var_dump( chmod($dirname, 0444) );
+// now try deleting $filename
+var_dump( unlink($filename) ); // expected: true
+var_dump( file_exists($filename) ); // confirm file is deleted
+
+// remove the dir
+var_dump( chmod($dirname, 0777) );
+rmdir($dirname);
+
+echo "\n-- Unlinking file without write permission, its dir having default permission --\n";
+// create the temp dir
+mkdir($dirname);
+
+// create the temp file
+$fp = fopen($filename, "w");
+fclose($fp);
+
+// remove write permission from file
+var_dump( chmod($filename, 0444) );
+
+// now try deleting temp file inside $dirname
+var_dump( unlink($filename) ); // expected: false
+
+// reassign write permission to temp file
+chmod($filename, 0777);
+// delete temp file
+var_dump( unlink($filename) );
+var_dump( file_exists($filename) ); // confirm file is deleted
+// remove temp dir
+rmdir($dirname);
+rmdir($file_path);
+
+echo "Done\n";
+?>
+--EXPECTF--
+*** Testing unlink() on file inside a directory ***
+-- Unlink file having default permission and its dir having read only permission --
+bool(true)
+bool(true)
+bool(false)
+bool(true)
+
+-- Unlinking file without write permission, its dir having default permission --
+bool(true)
+
+Warning: unlink(%s/unlink_variation1/unlink_variation1-win32.tmp): Permission denied in %s on line %d
+bool(false)
+bool(true)
+bool(false)
+Done
diff --git a/ext/standard/tests/file/unlink_variation8-win32.phpt b/ext/standard/tests/file/unlink_variation8-win32.phpt
index 3ad7ff2c64..8c9fbd895c 100644
--- a/ext/standard/tests/file/unlink_variation8-win32.phpt
+++ b/ext/standard/tests/file/unlink_variation8-win32.phpt
@@ -102,10 +102,10 @@ file removed
Warning: unlink(%s/BADDIR/file.tmp): No such file or directory in %s on line %d
-- removing unlinkVar8.tmp/file.tmp/ --
-Warning: unlink(unlinkVar8.tmp/file.tmp/): Invalid argument in %s on line %d
+Warning: unlink(unlinkVar8.tmp/file.tmp/): No such file or directory in %s on line %d
-- removing %s/unlinkVar8.tmp/file.tmp/ --
-Warning: unlink(%s/unlinkVar8.tmp/file.tmp/): Invalid argument in %s on line %d
+Warning: unlink(%s/unlinkVar8.tmp/file.tmp/): No such file or directory in %s on line %d
-- removing unlinkVar8.tmp//file.tmp --
file removed
-- removing %s//unlinkVar8.tmp//file.tmp --
diff --git a/ext/standard/tests/file/unlink_variation9-win32.phpt b/ext/standard/tests/file/unlink_variation9-win32.phpt
index acc2ce6a83..0eaed2e4ca 100644
--- a/ext/standard/tests/file/unlink_variation9-win32.phpt
+++ b/ext/standard/tests/file/unlink_variation9-win32.phpt
@@ -104,10 +104,10 @@ file removed
Warning: unlink(%s\BADDIR\file.tmp): No such file or directory in %s on line %d
-- removing unlinkVar9.tmp\file.tmp\ --
-Warning: unlink(unlinkVar9.tmp\file.tmp\): Invalid argument in %s on line %d
+Warning: unlink(unlinkVar9.tmp\file.tmp\): No such file or directory in %s on line %d
-- removing %s\unlinkVar9.tmp\file.tmp\ --
-Warning: unlink(%s\unlinkVar9.tmp\file.tmp\): Invalid argument in %s on line %d
+Warning: unlink(%s\unlinkVar9.tmp\file.tmp\): No such file or directory in %s on line %d
-- removing unlinkVar9.tmp\\file.tmp --
file removed
-- removing %s\\unlinkVar9.tmp\\file.tmp --
diff --git a/ext/standard/tests/file/userstreams_005.phpt b/ext/standard/tests/file/userstreams_005.phpt
index 2d39be25d6..a2af1b4086 100644
--- a/ext/standard/tests/file/userstreams_005.phpt
+++ b/ext/standard/tests/file/userstreams_005.phpt
@@ -55,6 +55,8 @@ bool(true)
truncation with new_size=10
bool(true)
------ stream_truncate negative size: -------
+
+Warning: ftruncate(): Negative size is not supported in %s on line %d
bool(false)
------ stream_truncate bad return: -------
truncation with new_size=0
diff --git a/ext/standard/tests/file/windows_links/bug73962.phpt b/ext/standard/tests/file/windows_links/bug73962.phpt
new file mode 100644
index 0000000000..6d578386e6
--- /dev/null
+++ b/ext/standard/tests/file/windows_links/bug73962.phpt
@@ -0,0 +1,77 @@
+--TEST--
+Bug #73962 bug with symlink related to cyrillic directory
+--SKIPIF--
+<?php
+if(substr(PHP_OS, 0, 3) != 'WIN' ) {
+ die('skip windows only test');
+}
+include_once __DIR__ . '/common.inc';
+$ret = exec('mklink bug48746_tmp.lnk ' . __FILE__ .' 2>&1', $out);
+if (strpos($ret, 'privilege')) {
+ die('skip. SeCreateSymbolicLinkPrivilege not enable for this user.');
+}
+unlink('bug48746_tmp.lnk');
+?>
+--FILE--
+<?php
+include_once __DIR__ . '/common.inc';
+$mountvol = get_mountvol();
+$old_dir = __DIR__;
+$dirname = '"' . __DIR__ . "\\mnt\\test\\новая папка" . '"';
+exec("mkdir " . $dirname, $output, $ret_val);
+chdir(__DIR__ . "\\mnt\\test");
+$drive = substr(__DIR__, 0, 2);
+$pathwithoutdrive = substr(__DIR__, 2);
+$ret = exec($mountvol . " " . $drive . " /L", $output, $ret_val);
+exec("mklink /d mounted_volume " . $ret, $output, $ret_val);
+$fullpath = "mounted_volume" . $pathwithoutdrive;
+exec("mklink /d mklink_symlink \"новая папка\"", $output, $ret_val);
+file_put_contents("mklink_symlink\\a.php", "<?php echo \"I am included.\n\" ?>");
+file_put_contents("$fullpath\\mnt\\test\\новая папка\\b.php", "<?php echo \"I am included.\n\" ?>");
+var_dump(scandir("mklink_symlink"));
+var_dump(scandir("$fullpath\\mnt\\test\\новая папка"));
+var_dump(scandir("$fullpath\\mnt\\test\\mklink_symlink"));
+var_dump(is_readable("$fullpath\\mnt\\test\\mklink_symlink\b.php"));
+unlink("$fullpath\\mnt\\test\\новая папка\\b.php");
+unlink("mklink_symlink\\a.php");
+chdir($old_dir);
+rmdir(__DIR__ . "\\mnt\\test\\новая папка");
+rmdir(__DIR__ . "\\mnt\\test\\mklink_symlink");
+rmdir(__DIR__ . "\\mnt\\test\\mounted_volume");
+rmdir(__DIR__ . "\\mnt\\test");
+rmdir(__DIR__ . "\\mnt");
+
+?>
+--EXPECT--
+array(4) {
+ [0]=>
+ string(1) "."
+ [1]=>
+ string(2) ".."
+ [2]=>
+ string(5) "a.php"
+ [3]=>
+ string(5) "b.php"
+}
+array(4) {
+ [0]=>
+ string(1) "."
+ [1]=>
+ string(2) ".."
+ [2]=>
+ string(5) "a.php"
+ [3]=>
+ string(5) "b.php"
+}
+array(4) {
+ [0]=>
+ string(1) "."
+ [1]=>
+ string(2) ".."
+ [2]=>
+ string(5) "a.php"
+ [3]=>
+ string(5) "b.php"
+}
+bool(true)
+
diff --git a/ext/standard/tests/file/windows_mb_path/bug54028.phpt b/ext/standard/tests/file/windows_mb_path/bug54028.phpt
new file mode 100644
index 0000000000..7dc6d0e8b8
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/bug54028.phpt
@@ -0,0 +1,69 @@
+--TEST--
+Bug #54028 Directory::read() cannot handle non-unicode chars properly
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts("mbstring");
+
+?>
+--FILE--
+<?php
+
+/* This file is in UTF-8. */
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$prefix = dirname(__FILE__) . DIRECTORY_SEPARATOR . "testBug54028" . DIRECTORY_SEPARATOR;
+
+$dirs = array("a", "ソ", "ゾ", "şŞıİğĞ", "多国語", "王", "汚れて掘る");
+
+mkdir($prefix);
+foreach ($dirs as $d) {
+ mkdir($prefix . $d);
+}
+
+$directory = dir($prefix);
+while (false !== ($content = $directory->read())) {
+ if ("." == $content || ".." == $content) continue;
+
+ printf("Returned (%s)\n", $content);
+ printf("Encoding: %s\n", mb_detect_encoding($content));
+ if ($content != get_basename_with_cp($prefix . $content, 65001, false)) {
+ echo "Verification failed!\n";
+ }
+ echo "\n";
+}
+
+foreach ($dirs as $d) {
+ rmdir($prefix . $d);
+}
+rmdir($prefix);
+
+?>
+===DONE===
+--EXPECT--
+Returned (a)
+Encoding: ASCII
+
+Returned (şŞıİğĞ)
+Encoding: UTF-8
+
+Returned (ソ)
+Encoding: UTF-8
+
+Returned (ゾ)
+Encoding: UTF-8
+
+Returned (多国語)
+Encoding: UTF-8
+
+Returned (汚れて掘る)
+Encoding: UTF-8
+
+Returned (王)
+Encoding: UTF-8
+
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/bug54028_2.phpt b/ext/standard/tests/file/windows_mb_path/bug54028_2.phpt
new file mode 100644
index 0000000000..fef7394c24
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/bug54028_2.phpt
@@ -0,0 +1,66 @@
+--TEST--
+Bug #54028 realpath(".") return false
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+
+?>
+--FILE--
+<?php
+
+/* This file is in UTF-8. */
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$prefix = dirname(__FILE__) . DIRECTORY_SEPARATOR . "testBug54028" . DIRECTORY_SEPARATOR;
+
+$dirs = array("a", "ソ", "ゾ", "şŞıİğĞ", "多国語", "王", "汚れて掘る");
+
+mkdir($prefix);
+foreach ($dirs as $d) {
+ mkdir($prefix . $d);
+}
+
+$old_cwd = getcwd();
+foreach ($dirs as $d) {
+ $now = $prefix . $d;
+ var_dump(chdir($now));
+ var_dump($dn = realpath("."));
+ var_dump($d == get_basename_with_cp($dn, 65001, false));
+}
+chdir($old_cwd);
+
+foreach ($dirs as $d) {
+ rmdir($prefix . $d);
+}
+rmdir($prefix);
+
+?>
+===DONE===
+--EXPECTF--
+bool(true)
+string(%d) "%sa"
+bool(true)
+bool(true)
+string(%d) "%sソ"
+bool(true)
+bool(true)
+string(%d) "%sゾ"
+bool(true)
+bool(true)
+string(%d) "%sşŞıİğĞ"
+bool(true)
+bool(true)
+string(%d) "%s多国語"
+bool(true)
+bool(true)
+string(%d) "%s王"
+bool(true)
+bool(true)
+string(%d) "%s汚れて掘る"
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/bug54977.phpt b/ext/standard/tests/file/windows_mb_path/bug54977.phpt
new file mode 100644
index 0000000000..d5b184158e
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/bug54977.phpt
@@ -0,0 +1,52 @@
+--TEST--
+Bug #54977 UTF-8 files and folder are not shown
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+
+?>
+--FILE--
+<?php
+
+/* This file is in UTF-8. */
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$prefix = dirname(__FILE__) . DIRECTORY_SEPARATOR . "testBug54977" . DIRECTORY_SEPARATOR;
+
+$paths = array("多国語", "王", "汚れて掘る");
+
+mkdir($prefix);
+foreach ($paths as $d) {
+ mkdir($prefix . $d);
+ file_put_contents($prefix . $d . ".test", $d);
+}
+
+$myDirectory = opendir($prefix);
+while($entryName = readdir($myDirectory)) {
+ echo get_basename_with_cp($prefix . $entryName, 65001, false) . "\n";
+}
+closedir($myDirectory);
+
+foreach ($paths as $d) {
+ rmdir($prefix . $d);
+ unlink($prefix . $d . ".test");
+}
+rmdir($prefix);
+
+?>
+===DONE===
+--EXPECT--
+testBug54977
+windows_mb_path
+多国語
+多国語.test
+汚れて掘る
+汚れて掘る.test
+王
+王.test
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/bug61315.phpt b/ext/standard/tests/file/windows_mb_path/bug61315.phpt
new file mode 100644
index 0000000000..6ce056a250
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/bug61315.phpt
@@ -0,0 +1,63 @@
+--TEST--
+Bug #61315 stat() fails with specific DBCS characters
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+
+?>
+--FILE--
+<?php
+
+/* This file is in UTF-8. */
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$prefix = dirname(__FILE__) . DIRECTORY_SEPARATOR . "testBug61315" . DIRECTORY_SEPARATOR;
+
+$d0 = $prefix . "ソフト";
+$d1 = $prefix . "フォルダ";
+
+mkdir($prefix);
+
+mkdir($d0);
+mkdir($d1);
+
+get_basename_with_cp($d0, 65001);
+get_basename_with_cp($d1, 65001);
+
+touch("$d0\\test0.txt");
+touch("$d1\\test1.txt");
+
+var_dump(count(stat("$d0\\test0.txt")) > 0);
+var_dump(count(stat("$d0\\test0.txt")) > 0);
+
+unlink("$d0\\test0.txt");
+unlink("$d1\\test1.txt");
+
+rmdir($d0);
+rmdir($d1);
+
+rmdir($prefix);
+
+?>
+===DONE===
+--EXPECTF--
+Active code page: 65001
+getting basename of %s\ソフト
+string(9) "ソフト"
+bool(true)
+string(%d) "%s\ソフト"
+Active code page: %d
+Active code page: 65001
+getting basename of %s\フォルダ
+string(12) "フォルダ"
+bool(true)
+string(%d) "%s\フォルダ"
+Active code page: %d
+bool(true)
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/bug64506.phpt b/ext/standard/tests/file/windows_mb_path/bug64506.phpt
new file mode 100644
index 0000000000..784cffadb7
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/bug64506.phpt
@@ -0,0 +1,51 @@
+--TEST--Bug #64506
+PHP can not read or write file correctly if file name have special char like š
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+
+?>
+--FILE--
+<?php
+/*
+#vim: set fileencoding=UTF-8
+#vim: set encoding=UTF-8
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$fnw = dirname(__FILE__) . DIRECTORY_SEPARATOR . "š.txt"; // UTF-8
+
+$f = fopen($fnw, 'w');
+if ($f) {
+ var_dump($f, fwrite($f, "writing to an mb filename"));
+} else {
+ echo "open utf8 failed\n";
+}
+var_dump(fclose($f));
+
+var_dump(file_get_contents($fnw));
+
+get_basename_with_cp($fnw, 65001);
+
+var_dump(unlink($fnw));
+
+?>
+===DONE===
+--EXPECTF--
+resource(%d) of type (stream)
+int(25)
+bool(true)
+string(25) "writing to an mb filename"
+Active code page: 65001
+getting basename of %s\š.txt
+string(6) "š.txt"
+bool(true)
+string(%d) "%s\š.txt"
+Active code page: %d
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/bug64699.phpt b/ext/standard/tests/file/windows_mb_path/bug64699.phpt
new file mode 100644
index 0000000000..3ad000a3b4
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/bug64699.phpt
@@ -0,0 +1,63 @@
+--TEST--
+Bug #64699 is_dir() is inaccurate result on Windows with japanese locale.
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+
+?>
+--FILE--
+<?php
+
+/* This file is in UTF-8. */
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$old_cp = get_active_cp();
+set_active_cp(65001);
+
+$prefix = dirname(__FILE__) . DIRECTORY_SEPARATOR . "testBug64699" . DIRECTORY_SEPARATOR;
+
+$dirs = array("a", "ソ", "ゾ", "şŞıİğĞ", "多国語", "表");
+
+mkdir($prefix);
+foreach ($dirs as $d) {
+ mkdir($prefix . $d);
+}
+
+$dir = $prefix;
+if ($dh = opendir($dir)) {
+ while (($file = readdir($dh)) !== false) {
+ $path = $dir . $file;
+ $type = filetype($path);
+ $type2= is_dir($path) ? 'dir' : 'file';
+ $comp = $type == $type2 ? 'OK' : 'NG';
+ echo "filetype()[".str_pad($type, 4)."] == is_dir()[".str_pad($type2, 4)."] -> $comp: {$file}\n";
+ }
+ closedir($dh);
+}
+
+foreach ($dirs as $d) {
+ rmdir($prefix . $d);
+}
+rmdir($prefix);
+
+set_active_cp($old_cp);
+
+?>
+===DONE===
+--EXPECTF--
+Active code page: 65001
+filetype()[dir ] == is_dir()[dir ] -> OK: .
+filetype()[dir ] == is_dir()[dir ] -> OK: ..
+filetype()[dir ] == is_dir()[dir ] -> OK: a
+filetype()[dir ] == is_dir()[dir ] -> OK: şŞıİğĞ
+filetype()[dir ] == is_dir()[dir ] -> OK: ソ
+filetype()[dir ] == is_dir()[dir ] -> OK: ゾ
+filetype()[dir ] == is_dir()[dir ] -> OK: 多国語
+filetype()[dir ] == is_dir()[dir ] -> OK: 表
+Active code page: %d
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/bug70903.phpt b/ext/standard/tests/file/windows_mb_path/bug70903.phpt
new file mode 100644
index 0000000000..863fd4ab2e
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/bug70903.phpt
@@ -0,0 +1,49 @@
+--TEST--
+Bug #70903 scandir wrongly interprets the Turkish "ı" character
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+
+?>
+--FILE--
+<?php
+
+/* This file is in UTF-8. */
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$prefix = dirname(__FILE__) . DIRECTORY_SEPARATOR . "testBug70903" . DIRECTORY_SEPARATOR;
+
+$d0 = $prefix . "ı";
+
+mkdir($prefix);
+
+mkdir($d0);
+
+get_basename_with_cp($d0, 65001);
+
+touch("$d0\\ı.txt");
+
+var_dump(count(stat("$d0\\ı.txt")) > 0);
+
+unlink("$d0\\ı.txt");
+
+rmdir($d0);
+
+rmdir($prefix);
+
+?>
+===DONE===
+--EXPECTF--
+Active code page: 65001
+getting basename of %s\ı
+string(2) "ı"
+bool(true)
+string(%d) "%s\ı"
+Active code page: %d
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/bug71509.phpt b/ext/standard/tests/file/windows_mb_path/bug71509.phpt
new file mode 100644
index 0000000000..2d74bfc8b3
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/bug71509.phpt
@@ -0,0 +1,44 @@
+--TEST--
+Bug #71509 Zip problem with swedish letters in filename.
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts("zip");
+
+?>
+--FILE--
+<?PHP
+//
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = iconv("ISO-8859-1", "UTF-8", "Rd_Statistics"); // cp1252
+$prefix = create_data("bug71509", "$item.txt");
+$testfile_zip = $prefix . DIRECTORY_SEPARATOR . "$item.txt";
+$outputfile_zip = $prefix . DIRECTORY_SEPARATOR . "$item.zip";
+
+var_dump(file_exists($testfile_zip));
+
+$zipfile = new ZipArchive;
+
+$return_code = $zipfile->open($outputfile_zip, ZipArchive::CREATE);
+if ($return_code != true) die("Failed to open file: " . $return_code);
+
+$return_code = $zipfile->addfile($testfile_zip, basename($testfile_zip));
+if ($return_code != true) print("Failed to add file: " . $zipfile->getStatusString());
+
+$return_code = $zipfile->close();
+if ($return_code != true) die("Failed to close archive: " . $zipfile->getStatusString());
+
+var_dump(file_exists($outputfile_zip));
+
+remove_data("bug71509");
+?>
+===DONE===
+--EXPECT--
+bool(true)
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/bug74923.phpt b/ext/standard/tests/file/windows_mb_path/bug74923.phpt
new file mode 100644
index 0000000000..3660202a4c
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/bug74923.phpt
@@ -0,0 +1,26 @@
+--TEST--
+Bug #74923 Crash when crawling through network share
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+
+?>
+--FILE--
+<?php
+
+/* No way to affect timeout here. On different systems this might take some
+ dozens of seconds to complete. */
+
+$s = '\\\\hello.com' . str_repeat('\\', 260);
+
+var_dump($s, @stat($s));
+
+?>
+===DONE===
+--EXPECTF--
+string(271) "%s"
+bool(false)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_big5_0.phpt b/ext/standard/tests/file/windows_mb_path/test_big5_0.phpt
new file mode 100644
index 0000000000..e291dd0855
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_big5_0.phpt
@@ -0,0 +1,45 @@
+--TEST--
+Test fopen() for reading big5 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+skip_if_wrong_cp(950, "ansi");
+
+?>
+--INI--
+defalut_charset=big5
+--FILE--
+<?php
+/*
+#vim: set fileencoding=big5
+#vim: set encoding=big5
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = "զhr`|"; // BIG5 string
+$prefix = create_data("file_big5", $item, 950);
+$fn = $prefix . DIRECTORY_SEPARATOR . "$item";
+
+$f = fopen($fn, 'r');
+if ($f) {
+ var_dump($f, fread($f, 42));
+ var_dump(fclose($f));
+} else {
+ echo "open utf8 failed\n";
+}
+
+remove_data("file_big5");
+
+?>
+===DONE===
+--EXPECTF--
+resource(%d) of type (stream)
+string(%d) "reading file wihh multibyte filename
+"
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_big5_1.phpt b/ext/standard/tests/file/windows_mb_path/test_big5_1.phpt
new file mode 100644
index 0000000000..74202fa966
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_big5_1.phpt
@@ -0,0 +1,54 @@
+--TEST--
+Test mkdir/rmdir big5 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+skip_if_wrong_cp(950, "ansi");
+
+?>
+--INI--
+internal_encoding=big5
+--FILE--
+<?php
+/*
+#vim: set fileencoding=big5
+#vim: set encoding=big5
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = "զhr`|"; // BIG5 string
+$prefix = create_data("dir_big5", $item . "5", 950);
+$path = $prefix . DIRECTORY_SEPARATOR . "${item}5";
+
+$subpath = $path . DIRECTORY_SEPARATOR . "${item}4";
+
+/* The mb dirname exists*/
+var_dump(file_exists($path));
+
+var_dump(mkdir($subpath));
+var_dump(file_exists($subpath));
+
+get_basename_with_cp($subpath, 950);
+
+var_dump(rmdir($subpath));
+remove_data("dir_big5");
+
+?>
+===DONE===
+--EXPECTF--
+bool(true)
+bool(true)
+bool(true)
+Active code page: 950
+getting basename of %sզhr`|5\զhr`|4
+string(%d) "զhr`|4"
+bool(true)
+string(%d) "%sզhr`|5\զhr`|4"
+Active code page: %d
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_big5_2.phpt b/ext/standard/tests/file/windows_mb_path/test_big5_2.phpt
new file mode 100644
index 0000000000..3be40bf61e
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_big5_2.phpt
@@ -0,0 +1,58 @@
+--TEST--
+Test fopen() for write big5 to UTF-8 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+skip_if_wrong_cp(950, "ansi");
+
+?>
+--INI--
+defalut_charset=cp950
+--FILE--
+<?php
+/*
+#vim: set fileencoding=big5
+#vim: set encoding=big5
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = "զhr`|"; // BIG5 string
+$prefix = create_data("file_big5", $item . "25", 950);
+$fn = $prefix . DIRECTORY_SEPARATOR . "{$item}25";
+
+$f = fopen($fn, 'w');
+if ($f) {
+ var_dump($f, fwrite($f, "writing to an mb filename"));
+ var_dump(fclose($f));
+} else {
+ echo "open utf8 failed\n";
+}
+
+var_dump(file_get_contents($fn));
+
+get_basename_with_cp($fn, 950);
+
+var_dump(unlink($fn));
+
+remove_data("file_big5");
+
+?>
+===DONE===
+--EXPECTF--
+resource(%d) of type (stream)
+int(25)
+bool(true)
+string(25) "writing to an mb filename"
+Active code page: 950
+getting basename of %sզhr`|25
+string(%d) "զhr`|25"
+bool(true)
+string(%d) "%s測試多字節路徑25"
+Active code page: %d
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_big5_to_utf8_0.phpt b/ext/standard/tests/file/windows_mb_path/test_big5_to_utf8_0.phpt
new file mode 100644
index 0000000000..3002b50c22
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_big5_to_utf8_0.phpt
@@ -0,0 +1,42 @@
+--TEST--
+Test fopen() for reading big5 to UTF-8 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+
+?>
+--FILE--
+<?php
+/*
+#vim: set fileencoding=big5
+#vim: set encoding=big5
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = iconv('big5', 'utf-8', "զhr`|"); // BIG5 string
+$prefix = create_data("file_big5", $item);
+$fn = $prefix . DIRECTORY_SEPARATOR . "$item";
+
+$f = fopen($fn, 'r');
+if ($f) {
+ var_dump($f, fread($f, 42));
+ var_dump(fclose($f));
+} else {
+ echo "open utf8 failed\n";
+}
+
+remove_data("file_big5");
+
+?>
+===DONE===
+--EXPECTF--
+resource(%d) of type (stream)
+string(%d) "reading file wihh multibyte filename
+"
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_big5_to_utf8_1.phpt b/ext/standard/tests/file/windows_mb_path/test_big5_to_utf8_1.phpt
new file mode 100644
index 0000000000..abf3a76cc2
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_big5_to_utf8_1.phpt
@@ -0,0 +1,51 @@
+--TEST--
+Test mkdir/rmdir big5 to UTF-8 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+
+?>
+--FILE--
+<?php
+/*
+#vim: set fileencoding=big5
+#vim: set encoding=big5
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = iconv('big5', 'utf-8', "զhr`|"); // BIG5 string
+$prefix = create_data("dir_big5", $item . "5");
+$path = $prefix . DIRECTORY_SEPARATOR . "${item}5";
+
+$subpath = $path . DIRECTORY_SEPARATOR . "${item}4";
+
+/* The mb dirname exists*/
+var_dump(file_exists($path));
+
+var_dump(mkdir($subpath));
+var_dump(file_exists($subpath));
+
+get_basename_with_cp($subpath, 65001);
+
+var_dump(rmdir($subpath));
+remove_data("dir_big5");
+
+?>
+===DONE===
+--EXPECTF--
+bool(true)
+bool(true)
+bool(true)
+Active code page: 65001
+getting basename of %s\測試多字節路徑5\測試多字節路徑4
+string(22) "測試多字節路徑4"
+bool(true)
+string(%d) "%s\測試多字節路徑5\測試多字節路徑4"
+Active code page: %d
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_big5_to_utf8_2.phpt b/ext/standard/tests/file/windows_mb_path/test_big5_to_utf8_2.phpt
new file mode 100644
index 0000000000..f204790e8e
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_big5_to_utf8_2.phpt
@@ -0,0 +1,55 @@
+--TEST--
+Test fopen() for write big5 to UTF-8 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+
+?>
+--FILE--
+<?php
+/*
+#vim: set fileencoding=big5
+#vim: set encoding=big5
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = iconv('big5', 'utf-8', "զhr`|"); // BIG5 string
+$prefix = create_data("file_big5", $item . "25");
+$fn = $prefix . DIRECTORY_SEPARATOR . "{$item}25";
+
+$f = fopen($fn, 'w');
+if ($f) {
+ var_dump($f, fwrite($f, "writing to an mb filename"));
+ var_dump(fclose($f));
+} else {
+ echo "open utf8 failed\n";
+}
+
+var_dump(file_get_contents($fn));
+
+get_basename_with_cp($fn, 65001);
+
+var_dump(unlink($fn));
+
+remove_data("file_big5");
+
+?>
+===DONE===
+--EXPECTF--
+resource(%d) of type (stream)
+int(25)
+bool(true)
+string(25) "writing to an mb filename"
+Active code page: 65001
+getting basename of %s\測試多字節路徑25
+string(23) "測試多字節路徑25"
+bool(true)
+string(%d) "%s\測試多字節路徑25"
+Active code page: %d
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1250_to_utf8_0.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1250_to_utf8_0.phpt
new file mode 100644
index 0000000000..7a6496a619
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp1250_to_utf8_0.phpt
@@ -0,0 +1,42 @@
+--TEST--
+Test fopen() for reading UTF-8 path with cp1250 specific symbols
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+
+?>
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp1250
+#vim: set encoding=cp1250
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = "šđčćž_ŠĐČĆŽ"; // cp1250 specific chars
+$prefix = create_data("file_cp1250", $item);
+$fn = $prefix . DIRECTORY_SEPARATOR . $item;
+
+$f = fopen($fn, 'r');
+if ($f) {
+ var_dump($f, fread($f, 42));
+ var_dump(fclose($f));
+} else {
+ echo "open utf8 failed\n";
+}
+
+remove_data("file_cp1250");
+
+?>
+===DONE===
+--EXPECTF--
+resource(%d) of type (stream)
+string(37) "reading file wihh multibyte filename
+"
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1250_to_utf8_1.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1250_to_utf8_1.phpt
new file mode 100644
index 0000000000..bd4421e7a2
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp1250_to_utf8_1.phpt
@@ -0,0 +1,51 @@
+--TEST--
+Test mkdir/rmdir UTF-8 path with cp1250 specific symbols
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+
+?>
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp1250
+#vim: set encoding=cp1250
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = "šđčćž_ŠĐČĆŽ"; // cp1250 specific chars
+$prefix = create_data("dir_cp1250", "${item}42");
+$path = $prefix . DIRECTORY_SEPARATOR . "${item}42";
+
+$subpath = $path . DIRECTORY_SEPARATOR . "${item}4";
+
+/* The mb dirname exists*/
+var_dump(file_exists($path));
+
+var_dump(mkdir($subpath));
+var_dump(file_exists($subpath));
+
+get_basename_with_cp($subpath, 65001);
+
+var_dump(rmdir($subpath));
+remove_data("dir_cp1250");
+
+?>
+===DONE===
+--EXPECTF--
+bool(true)
+bool(true)
+bool(true)
+Active code page: 65001
+getting basename of %s\šđčćž_ŠĐČĆŽ42\šđčćž_ŠĐČĆŽ4
+string(22) "šđčćž_ŠĐČĆŽ4"
+bool(true)
+string(%d) "%s\šđčćž_ŠĐČĆŽ42\šđčćž_ŠĐČĆŽ4"
+Active code page: %d
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1250_to_utf8_2.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1250_to_utf8_2.phpt
new file mode 100644
index 0000000000..488343d8d1
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp1250_to_utf8_2.phpt
@@ -0,0 +1,52 @@
+--TEST--
+Test fopen() for write to UTF-8 path with cp1250 specific symbols
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+
+?>
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp1250
+#vim: set encoding=cp1250
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = "šđčćž_ŠĐČĆŽ"; // cp1250 specific chars
+$prefix = create_data("dir_cp1250", "${item}42}");
+$fn = $prefix . DIRECTORY_SEPARATOR . "${item}33";
+
+$f = fopen($fn, 'w');
+if ($f) {
+ var_dump($f, fwrite($f, "writing to an mb filename"));
+ var_dump(fclose($f));
+} else {
+ echo "open utf8 failed\n";
+}
+
+var_dump(file_get_contents($fn));
+
+get_basename_with_cp($fn, 65001);
+
+remove_data("dir_cp1250");
+
+?>
+===DONE===
+--EXPECTF--
+resource(%d) of type (stream)
+int(25)
+bool(true)
+string(25) "writing to an mb filename"
+Active code page: 65001
+getting basename of %s\šđčćž_ŠĐČĆŽ33
+string(23) "šđčćž_ŠĐČĆŽ33"
+bool(true)
+string(%d) "%s\šđčćž_ŠĐČĆŽ33"
+Active code page: %d
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1250_to_utf8_3.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1250_to_utf8_3.phpt
new file mode 100644
index 0000000000..84b05b71d9
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp1250_to_utf8_3.phpt
@@ -0,0 +1,42 @@
+--TEST--
+Test fopen() for reading UTF-8 path with cp1250 specific symbols
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+
+?>
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp1250
+#vim: set encoding=cp1250
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = "Árvíztűrő tükörfúrógép"; // cp1250 specific chars
+$prefix = create_data("file_cp1250", $item);
+$fn = $prefix . DIRECTORY_SEPARATOR . $item;
+
+$f = fopen($fn, 'r');
+if ($f) {
+ var_dump($f, fread($f, 42));
+ var_dump(fclose($f));
+} else {
+ echo "open utf8 failed\n";
+}
+
+remove_data("file_cp1250");
+
+?>
+===DONE===
+--EXPECTF--
+resource(%d) of type (stream)
+string(37) "reading file wihh multibyte filename
+"
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1250_to_utf8_4.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1250_to_utf8_4.phpt
new file mode 100644
index 0000000000..3759daa71d
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp1250_to_utf8_4.phpt
@@ -0,0 +1,51 @@
+--TEST--
+Test mkdir/rmdir UTF-8 path with cp1250 specific symbols
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+
+?>
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp1250
+#vim: set encoding=cp1250
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = "Árvíztűrő tükörfúrógép"; // cp1250 specific chars
+$prefix = create_data("dir_cp1250", "${item}42");
+$path = $prefix . DIRECTORY_SEPARATOR . "${item}42";
+
+$subpath = $path . DIRECTORY_SEPARATOR . "${item}4";
+
+/* The mb dirname exists*/
+var_dump(file_exists($path));
+
+var_dump(mkdir($subpath));
+var_dump(file_exists($subpath));
+
+get_basename_with_cp($subpath, 65001);
+
+var_dump(rmdir($subpath));
+remove_data("dir_cp1250");
+
+?>
+===DONE===
+--EXPECTF--
+bool(true)
+bool(true)
+bool(true)
+Active code page: 65001
+getting basename of %s\Árvíztűrő tükörfúrógép42\Árvíztűrő tükörfúrógép4
+string(32) "Árvíztűrő tükörfúrógép4"
+bool(true)
+string(%d) "%s\Árvíztűrő tükörfúrógép42\Árvíztűrő tükörfúrógép4"
+Active code page: %d
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1250_to_utf8_5.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1250_to_utf8_5.phpt
new file mode 100644
index 0000000000..7e1998ba2a
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp1250_to_utf8_5.phpt
@@ -0,0 +1,52 @@
+--TEST--
+Test fopen() for write to UTF-8 path with cp1250 specific symbols
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+
+?>
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp1250
+#vim: set encoding=cp1250
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = "Árvíztűrő tükörfúrógép"; // cp1250 specific chars
+$prefix = create_data("dir_cp1250", "${item}42}");
+$fn = $prefix . DIRECTORY_SEPARATOR . "${item}33";
+
+$f = fopen($fn, 'w');
+if ($f) {
+ var_dump($f, fwrite($f, "writing to an mb filename"));
+ var_dump(fclose($f));
+} else {
+ echo "open utf8 failed\n";
+}
+
+var_dump(file_get_contents($fn));
+
+get_basename_with_cp($fn, 65001);
+
+remove_data("dir_cp1250");
+
+?>
+===DONE===
+--EXPECTF--
+resource(%d) of type (stream)
+int(25)
+bool(true)
+string(25) "writing to an mb filename"
+Active code page: 65001
+getting basename of %s\Árvíztűrő tükörfúrógép33
+string(33) "Árvíztűrő tükörfúrógép33"
+bool(true)
+string(%d) "%s\Árvíztűrő tükörfúrógép33"
+Active code page: %d
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1251_0.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1251_0.phpt
new file mode 100644
index 0000000000..7dfd7eadbf
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp1251_0.phpt
@@ -0,0 +1,44 @@
+--TEST--
+Test fopen() for reading CP1251 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+skip_if_wrong_cp(1251, "ansi");
+
+?>
+--INI--
+default_charset=cp1251
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp1251
+#vim: set encoding=cp1251
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = ""; // cp1251 string
+$prefix = create_data("file_cp1251", $item, 1251);
+$fn = $prefix . DIRECTORY_SEPARATOR . $item;
+
+$f = fopen($fn, 'r');
+if ($f) {
+ var_dump($f, fread($f, 42));
+ var_dump(fclose($f));
+} else {
+ echo "open utf8 failed\n";
+}
+
+remove_data("file_cp1251");
+
+?>
+===DONE===
+--EXPECTF--
+resource(%d) of type (stream)
+string(35) "opened an utf8 filename for reading"
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1251_1.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1251_1.phpt
new file mode 100644
index 0000000000..3abce0d0d5
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp1251_1.phpt
@@ -0,0 +1,54 @@
+--TEST--
+Test mkdir/rmdir CP1251 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+skip_if_wrong_cp(1251, "ansi");
+
+?>
+--INI--
+default_charset=cp1251
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp1251
+#vim: set encoding=cp1251
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = ""; // cp1251 string
+$prefix = create_data("dir_cp1251", $item . "3", 1251);
+$path = $prefix . DIRECTORY_SEPARATOR . $item . "3";
+
+$subpath = $path . DIRECTORY_SEPARATOR . "${item}4";
+
+/* The mb dirname exists*/
+var_dump(file_exists($path));
+
+var_dump(mkdir($subpath));
+var_dump(file_exists($subpath));
+
+get_basename_with_cp($subpath, 1251);
+
+var_dump(rmdir($subpath));
+remove_data("dir_cp1251");
+
+?>
+===DONE===
+--EXPECTF--
+bool(true)
+bool(true)
+bool(true)
+Active code page: 1251
+getting basename of %s\3\4
+string(%d) "4"
+bool(true)
+string(%d) "%s\3\4"
+Active code page: %d
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1251_2.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1251_2.phpt
new file mode 100644
index 0000000000..2a3b7390b1
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp1251_2.phpt
@@ -0,0 +1,57 @@
+--TEST--
+Test fopen() for write CP1251 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+skip_if_wrong_cp(1251, "ansi");
+
+?>
+--INI--
+internal_encoding=cp1251
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp1251
+#vim: set encoding=cp1251
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = ""; // cp1251 string
+$prefix = create_data("file_cp1251", $item . "7", 1251);
+$fn = $prefix . DIRECTORY_SEPARATOR . "${item}7";
+
+$f = fopen($fn, 'w');
+if ($f) {
+ var_dump($f, fwrite($f, "writing to an mb filename"));
+ var_dump(fclose($f));
+} else {
+ echo "open utf8 failed\n";
+}
+
+var_dump(file_get_contents($fn));
+
+get_basename_with_cp($fn, 1251);
+
+var_dump(unlink($fn));
+remove_data("file_cp1251");
+
+?>
+===DONE===
+--EXPECTF--
+resource(%d) of type (stream)
+int(25)
+bool(true)
+string(25) "writing to an mb filename"
+Active code page: 1251
+getting basename of %s\7
+string(%d) "7"
+bool(true)
+string(%d) "%s\7"
+Active code page: %d
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1251_to_utf8_0.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1251_to_utf8_0.phpt
new file mode 100644
index 0000000000..13395d1017
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp1251_to_utf8_0.phpt
@@ -0,0 +1,41 @@
+--TEST--
+Test fopen() for reading CP1251 to UTF-8 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+
+?>
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp1251
+#vim: set encoding=cp1251
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = iconv('cp1251', 'utf-8', ""); // cp1251 string
+$prefix = create_data("file_cp1251", $item);
+$fn = $prefix . DIRECTORY_SEPARATOR . $item;
+
+$f = fopen($fn, 'r');
+if ($f) {
+ var_dump($f, fread($f, 42));
+ var_dump(fclose($f));
+} else {
+ echo "open utf8 failed\n";
+}
+
+remove_data("file_cp1251");
+
+?>
+===DONE===
+--EXPECTF--
+resource(%d) of type (stream)
+string(35) "opened an utf8 filename for reading"
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1251_to_utf8_1.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1251_to_utf8_1.phpt
new file mode 100644
index 0000000000..193e2fdc74
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp1251_to_utf8_1.phpt
@@ -0,0 +1,51 @@
+--TEST--
+Test mkdir/rmdir CP1251 to UTF-8 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+
+?>
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp1251
+#vim: set encoding=cp1251
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = iconv('cp1251', 'utf-8', ""); // cp1251 string
+$prefix = create_data("dir_cp1251", $item . "3");
+$path = $prefix . DIRECTORY_SEPARATOR . $item . "3";
+
+$subpath = $path . DIRECTORY_SEPARATOR . "${item}4";
+
+/* The mb dirname exists*/
+var_dump(file_exists($path));
+
+var_dump(mkdir($subpath));
+var_dump(file_exists($subpath));
+
+get_basename_with_cp($subpath, 65001);
+
+var_dump(rmdir($subpath));
+remove_data("dir_cp1251");
+
+?>
+===DONE===
+--EXPECTF--
+bool(true)
+bool(true)
+bool(true)
+Active code page: 65001
+getting basename of %s\привет3\привет4
+string(13) "привет4"
+bool(true)
+string(%d) "%s\привет3\привет4"
+Active code page: %d
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1251_to_utf8_2.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1251_to_utf8_2.phpt
new file mode 100644
index 0000000000..e552464214
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp1251_to_utf8_2.phpt
@@ -0,0 +1,54 @@
+--TEST--
+Test fopen() for write CP1251 to UTF-8 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+
+?>
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp1251
+#vim: set encoding=cp1251
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = iconv('cp1251', 'utf-8', ""); // cp1251 string
+$prefix = create_data("file_cp1251", $item . "7");
+$fn = $prefix . DIRECTORY_SEPARATOR . "${item}7";
+
+$f = fopen($fn, 'w');
+if ($f) {
+ var_dump($f, fwrite($f, "writing to an mb filename"));
+ var_dump(fclose($f));
+} else {
+ echo "open utf8 failed\n";
+}
+
+var_dump(file_get_contents($fn));
+
+get_basename_with_cp($fn, 65001);
+
+var_dump(unlink($fn));
+remove_data("file_cp1251");
+
+?>
+===DONE===
+--EXPECTF--
+resource(%d) of type (stream)
+int(25)
+bool(true)
+string(25) "writing to an mb filename"
+Active code page: 65001
+getting basename of %s\привет7
+string(13) "привет7"
+bool(true)
+string(%d) "%s\привет7"
+Active code page: %d
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1251_zend_multibyte_0.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1251_zend_multibyte_0.phpt
new file mode 100644
index 0000000000..b638eb4822
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp1251_zend_multibyte_0.phpt
@@ -0,0 +1,44 @@
+--TEST--
+Test fopen() for reading CP1251 with zend.multibyte
+--INI--
+zend.multibyte=1
+zend.script_encoding=cp1251
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts("mbstring");
+
+?>
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp1251
+#vim: set encoding=cp1251
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = ""; // cp1251 string
+$prefix = create_data("file_cp1251", $item);
+$fn = $prefix . DIRECTORY_SEPARATOR . $item;
+
+$f = fopen($fn, 'r');
+if ($f) {
+ var_dump($f, fread($f, 42));
+ var_dump(fclose($f));
+} else {
+ echo "open failed\n";
+}
+
+remove_data("file_cp1251");
+
+?>
+===DONE===
+--EXPECTF--
+resource(%d) of type (stream)
+string(35) "opened an utf8 filename for reading"
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1251_zend_multibyte_1.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1251_zend_multibyte_1.phpt
new file mode 100644
index 0000000000..e44849b061
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp1251_zend_multibyte_1.phpt
@@ -0,0 +1,54 @@
+--TEST--
+Test mkdir/rmdir CP1251 with zend.multibyte
+--INI--
+zend.multibyte=1
+zend.script_encoding=cp1251
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts("mbstring");
+
+?>
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp1251
+#vim: set encoding=cp1251
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = ""; // cp1251 string
+$prefix = create_data("dir_cp1251", $item . "3");
+$path = $prefix . DIRECTORY_SEPARATOR . "${item}3";
+
+$subpath = $path . DIRECTORY_SEPARATOR . "${item}4";
+
+/* The mb dirname exists*/
+var_dump(file_exists($path));
+
+var_dump(mkdir($subpath));
+var_dump(file_exists($subpath));
+
+get_basename_with_cp($subpath, 65001);
+
+var_dump(rmdir($subpath));
+remove_data("dir_cp1251");
+
+?>
+===DONE===
+--EXPECTF--
+bool(true)
+bool(true)
+bool(true)
+Active code page: 65001
+getting basename of %s\привет3\привет4
+string(13) "привет4"
+bool(true)
+string(%d) "%s\привет3\привет4"
+Active code page: %d
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1251_zend_multibyte_2.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1251_zend_multibyte_2.phpt
new file mode 100644
index 0000000000..c0b685c55b
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp1251_zend_multibyte_2.phpt
@@ -0,0 +1,57 @@
+--TEST--
+Test fopen() for write CP1251 with zend.multibyte
+--INI--
+zend.multibyte=1
+zend.script_encoding=cp1251
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts("mbstring");
+
+?>
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp1251
+#vim: set encoding=cp1251
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = "7"; // cp1251 string
+$prefix = create_data("file_cp1251", $item);
+$fn = $prefix . DIRECTORY_SEPARATOR . $item;
+
+$f = fopen($fn, 'w');
+if ($f) {
+ var_dump($f, fwrite($f, "writing to an mb filename"));
+ var_dump(fclose($f));
+} else {
+ echo "open failed\n";
+}
+
+var_dump(file_get_contents($fn));
+
+get_basename_with_cp($fn, 65001);
+
+var_dump(unlink($fn));
+remove_data("file_cp1251");
+
+?>
+===DONE===
+--EXPECTF--
+resource(%d) of type (stream)
+int(25)
+bool(true)
+string(25) "writing to an mb filename"
+Active code page: 65001
+getting basename of %s\привет7
+string(13) "привет7"
+bool(true)
+string(%d) "%s\привет7"
+Active code page: %d
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1252_0.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1252_0.phpt
new file mode 100644
index 0000000000..85a1c41f07
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp1252_0.phpt
@@ -0,0 +1,42 @@
+--TEST--
+cp1252 cmd test
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+skip_if_wrong_cp(437, "oem");
+
+?>
+--INI--
+internal_encoding=cp1252
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp1252
+#vim: set encoding=cp1252
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = "gef";
+$prefix = create_data("file", $item, 1252);
+$fn = $prefix . DIRECTORY_SEPARATOR . $item;
+
+var_dump($fn);
+var_dump(touch($fn));
+var_dump(file_exists($fn));
+system("dir /b " . $fn);
+
+remove_data("file");
+
+?>
+===DONE===
+--EXPECTF--
+string(%d) "%s\gef"
+bool(true)
+bool(true)
+gef
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1252_to_utf8_0.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1252_to_utf8_0.phpt
new file mode 100644
index 0000000000..bdb1a7a878
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp1252_to_utf8_0.phpt
@@ -0,0 +1,42 @@
+--TEST--
+Test fopen() for reading cp1252 to UTF-8 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+
+?>
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp1252
+#vim: set encoding=cp1252
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = iconv('cp1252', 'utf-8', "tsch"); // cp1252 string
+$prefix = create_data("file_cp1252", $item);
+$fn = $prefix . DIRECTORY_SEPARATOR . $item;
+
+$f = fopen($fn, 'r');
+if ($f) {
+ var_dump($f, fread($f, 42));
+ var_dump(fclose($f));
+} else {
+ echo "open utf8 failed\n";
+}
+
+remove_data("file_cp1252");
+
+?>
+===DONE===
+--EXPECTF--
+resource(%d) of type (stream)
+string(%d) "hallo
+"
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1252_to_utf8_1.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1252_to_utf8_1.phpt
new file mode 100644
index 0000000000..059ab8dd8f
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp1252_to_utf8_1.phpt
@@ -0,0 +1,51 @@
+--TEST--
+Test mkdir/rmdir cp1252 to UTF-8 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+
+?>
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp1252
+#vim: set encoding=cp1252
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = iconv('cp1252', 'utf-8', "tsch"); // cp1252 string
+$prefix = create_data("dir_cp1252", "${item}3");
+$path = $prefix . DIRECTORY_SEPARATOR . "${item}3";
+
+$subpath = $path . DIRECTORY_SEPARATOR . "${item}4";
+
+/* The mb dirname exists*/
+var_dump(file_exists($path));
+
+var_dump(mkdir($subpath));
+var_dump(file_exists($subpath));
+
+get_basename_with_cp($subpath, 65001);
+
+var_dump(rmdir($subpath));
+remove_data("dir_cp1252");
+
+?>
+===DONE===
+--EXPECTF--
+bool(true)
+bool(true)
+bool(true)
+Active code page: 65001
+getting basename of %s\tschüß3\tschüß4
+string(9) "tschüß4"
+bool(true)
+string(%d) "%s\tschüß3\tschüß4"
+Active code page: %d
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1252_to_utf8_2.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1252_to_utf8_2.phpt
new file mode 100644
index 0000000000..9ea2485eeb
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp1252_to_utf8_2.phpt
@@ -0,0 +1,54 @@
+--TEST--
+Test fopen() for write cp1252 to UTF-8 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+
+?>
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp1252
+#vim: set encoding=cp1252
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = iconv('cp1252', 'utf-8', "tsch"); // cp1252 string
+$prefix = create_data("dir_cp1252", "${item}3");
+$fn = $prefix . DIRECTORY_SEPARATOR . "${item}7";
+
+$f = fopen($fn, 'w');
+if ($f) {
+ var_dump($f, fwrite($f, "writing to an mb filename"));
+ var_dump(fclose($f));
+} else {
+ echo "open utf8 failed\n";
+}
+
+var_dump(file_get_contents($fn));
+
+get_basename_with_cp($fn, 65001);
+
+var_dump(unlink($fn));
+remove_data("dir_cp1252");
+
+?>
+===DONE===
+--EXPECTF--
+resource(%d) of type (stream)
+int(25)
+bool(true)
+string(25) "writing to an mb filename"
+Active code page: 65001
+getting basename of %s\tschüß7
+string(9) "tschüß7"
+bool(true)
+string(%d) "%s\tschüß7"
+Active code page: %d
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1252_to_utf8_3.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1252_to_utf8_3.phpt
new file mode 100644
index 0000000000..cae3426e75
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp1252_to_utf8_3.phpt
@@ -0,0 +1,41 @@
+--TEST--
+Test fopen() for reading cp1252 to UTF-8 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+
+?>
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp1252
+#vim: set encoding=cp1252
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = iconv('cp1252', 'utf-8', "Volao"); // cp1252 string
+$prefix = create_data("file2_cp1252", $item);
+$fn = $prefix . DIRECTORY_SEPARATOR . $item;
+
+$f = fopen($fn, 'r');
+if ($f) {
+ var_dump($f, fread($f, 42));
+ var_dump(fclose($f));
+} else {
+ echo "open utf8 failed\n";
+}
+
+remove_data("file2_cp1252");
+
+?>
+===DONE===
+--EXPECTF--
+resource(%d) of type (stream)
+string(4) "hola"
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1252_to_utf8_4.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1252_to_utf8_4.phpt
new file mode 100644
index 0000000000..cacaf5d36d
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp1252_to_utf8_4.phpt
@@ -0,0 +1,51 @@
+--TEST--
+Test mkdir/rmdir cp1252 to UTF-8 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+
+?>
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp1252
+#vim: set encoding=cp1252
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = iconv('cp1252', 'utf-8', "Volao"); // cp1252 string
+$prefix = create_data("dir2_cp1252", "${item}3");
+$path = $prefix . DIRECTORY_SEPARATOR . "${item}3";
+
+$subpath = $path . DIRECTORY_SEPARATOR . "${item}4";
+
+/* The mb dirname exists*/
+var_dump(file_exists($path));
+
+var_dump(mkdir($subpath));
+var_dump(file_exists($subpath));
+
+get_basename_with_cp($subpath, 65001);
+
+var_dump(rmdir($subpath));
+remove_data("dir2_cp1252");
+
+?>
+===DONE===
+--EXPECTF--
+bool(true)
+bool(true)
+bool(true)
+Active code page: 65001
+getting basename of %s\Voláçao3\Voláçao4
+string(10) "Voláçao4"
+bool(true)
+string(%d) "%s\Voláçao3\Voláçao4"
+Active code page: %d
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1252_to_utf8_5.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1252_to_utf8_5.phpt
new file mode 100644
index 0000000000..3ac634134a
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp1252_to_utf8_5.phpt
@@ -0,0 +1,54 @@
+--TEST--
+Test fopen() for write cp1252 to UTF-8 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+
+?>
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp1252
+#vim: set encoding=cp1252
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = iconv('cp1252', 'utf-8', "Volao"); // cp1252 string
+$prefix = create_data("dir2_cp1252", "${item}3");
+$fn = $prefix . DIRECTORY_SEPARATOR . "${item}7";
+
+$f = fopen($fn, 'w');
+if ($f) {
+ var_dump($f, fwrite($f, "writing to an mb filename"));
+ var_dump(fclose($f));
+} else {
+ echo "open utf8 failed\n";
+}
+
+var_dump(file_get_contents($fn));
+
+get_basename_with_cp($fn, 65001);
+
+var_dump(unlink($fn));
+remove_data("dir2_cp1252");
+
+?>
+===DONE===
+--EXPECTF--
+resource(%d) of type (stream)
+int(25)
+bool(true)
+string(25) "writing to an mb filename"
+Active code page: 65001
+getting basename of %s\Voláçao7
+string(10) "Voláçao7"
+bool(true)
+string(%d) "%s\Voláçao7"
+Active code page: %d
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1253_0.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1253_0.phpt
new file mode 100644
index 0000000000..12ae1ac7cd
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp1253_0.phpt
@@ -0,0 +1,45 @@
+--TEST--
+Test fopen() for reading cp1253 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+skip_if_wrong_cp(1253, "ansi");
+
+?>
+--INI--
+internal_encoding=cp1253
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp1253
+#vim: set encoding=cp1253
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = " ";
+$prefix = create_data("file_cp1253", $item, 1253);
+$fn = $prefix . DIRECTORY_SEPARATOR . $item;
+
+$f = fopen($fn, 'r');
+if ($f) {
+ var_dump($f, fread($f, 42));
+ var_dump(fclose($f));
+} else {
+ echo "open utf8 failed\n";
+}
+
+remove_data("file_cp1253");
+
+?>
+===DONE===
+--EXPECTF--
+resource(%d) of type (stream)
+string(37) "reading file wihh multibyte filename
+"
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1253_1.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1253_1.phpt
new file mode 100644
index 0000000000..9049a65ee1
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp1253_1.phpt
@@ -0,0 +1,54 @@
+--TEST--
+Test mkdir/rmdir cp1253 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+skip_if_wrong_cp(1253, "ansi");
+
+?>
+--INI--
+default_charset=cp1253
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp1253
+#vim: set encoding=cp1253
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = " ";
+$prefix = create_data("dir_cp1253", "${item}42", 1253);
+$path = $prefix . DIRECTORY_SEPARATOR . "${item}42";
+
+$subpath = $path . DIRECTORY_SEPARATOR . "${item}4";
+
+/* The mb dirname exists*/
+var_dump(file_exists($path));
+
+var_dump(mkdir($subpath));
+var_dump(file_exists($subpath));
+
+get_basename_with_cp($subpath, 1253);
+
+var_dump(rmdir($subpath));
+remove_data("dir_cp1253");
+
+?>
+===DONE===
+--EXPECTF--
+bool(true)
+bool(true)
+bool(true)
+Active code page: 1253
+getting basename of %s\ 42\ 4
+string(%d) " 4"
+bool(true)
+string(%d) "%s\ 42\ 4"
+Active code page: %d
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1253_2.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1253_2.phpt
new file mode 100644
index 0000000000..4c39fb5bda
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp1253_2.phpt
@@ -0,0 +1,55 @@
+--TEST--
+Test fopen() for write cp1253
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+skip_if_wrong_cp(1253, "ansi");
+
+?>
+--INI--
+internal_encoding=cp1253
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp1253
+#vim: set encoding=cp1253
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = " ";
+$prefix = create_data("dir_cp1253", "${item}42}", 1253);
+$fn = $prefix . DIRECTORY_SEPARATOR . "${item}33";
+
+$f = fopen($fn, 'w');
+if ($f) {
+ var_dump($f, fwrite($f, "writing to an mb filename"));
+ var_dump(fclose($f));
+} else {
+ echo "open utf8 failed\n";
+}
+
+var_dump(file_get_contents($fn));
+
+get_basename_with_cp($fn, 1253);
+
+remove_data("dir_cp1253");
+
+?>
+===DONE===
+--EXPECTF--
+resource(%d) of type (stream)
+int(25)
+bool(true)
+string(25) "writing to an mb filename"
+Active code page: 1253
+getting basename of %s\ 33
+string(%d) " 33"
+bool(true)
+string(%d) "%s\ 33"
+Active code page: %d
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1253_to_utf8_0.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1253_to_utf8_0.phpt
new file mode 100644
index 0000000000..0fbc78fc12
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp1253_to_utf8_0.phpt
@@ -0,0 +1,42 @@
+--TEST--
+Test fopen() for reading cp1253 to UTF-8 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+
+?>
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp1253
+#vim: set encoding=cp1253
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = "διαδρομή δοκιμής";
+$prefix = create_data("file_cp1253", $item);
+$fn = $prefix . DIRECTORY_SEPARATOR . $item;
+
+$f = fopen($fn, 'r');
+if ($f) {
+ var_dump($f, fread($f, 42));
+ var_dump(fclose($f));
+} else {
+ echo "open utf8 failed\n";
+}
+
+remove_data("file_cp1253");
+
+?>
+===DONE===
+--EXPECTF--
+resource(%d) of type (stream)
+string(37) "reading file wihh multibyte filename
+"
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1253_to_utf8_1.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1253_to_utf8_1.phpt
new file mode 100644
index 0000000000..ddc79b1204
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp1253_to_utf8_1.phpt
@@ -0,0 +1,51 @@
+--TEST--
+Test mkdir/rmdir cp1253 to UTF-8 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+
+?>
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp1253
+#vim: set encoding=cp1253
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = "διαδρομή δοκιμής";
+$prefix = create_data("dir_cp1253", "${item}42");
+$path = $prefix . DIRECTORY_SEPARATOR . "${item}42";
+
+$subpath = $path . DIRECTORY_SEPARATOR . "${item}4";
+
+/* The mb dirname exists*/
+var_dump(file_exists($path));
+
+var_dump(mkdir($subpath));
+var_dump(file_exists($subpath));
+
+get_basename_with_cp($subpath, 65001);
+
+var_dump(rmdir($subpath));
+remove_data("dir_cp1253");
+
+?>
+===DONE===
+--EXPECTF--
+bool(true)
+bool(true)
+bool(true)
+Active code page: 65001
+getting basename of %s\διαδρομή δοκιμής42\διαδρομή δοκιμής4
+string(32) "διαδρομή δοκιμής4"
+bool(true)
+string(%d) "%s\διαδρομή δοκιμής42\διαδρομή δοκιμής4"
+Active code page: %d
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1253_to_utf8_2.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1253_to_utf8_2.phpt
new file mode 100644
index 0000000000..6e28c89e0c
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp1253_to_utf8_2.phpt
@@ -0,0 +1,52 @@
+--TEST--
+Test fopen() for write cp1253 to UTF-8 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+
+?>
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp1253
+#vim: set encoding=cp1253
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = "διαδρομή δοκιμής";
+$prefix = create_data("dir_cp1253", "${item}42}");
+$fn = $prefix . DIRECTORY_SEPARATOR . "${item}33";
+
+$f = fopen($fn, 'w');
+if ($f) {
+ var_dump($f, fwrite($f, "writing to an mb filename"));
+ var_dump(fclose($f));
+} else {
+ echo "open utf8 failed\n";
+}
+
+var_dump(file_get_contents($fn));
+
+get_basename_with_cp($fn, 65001);
+
+remove_data("dir_cp1253");
+
+?>
+===DONE===
+--EXPECTF--
+resource(%d) of type (stream)
+int(25)
+bool(true)
+string(25) "writing to an mb filename"
+Active code page: 65001
+getting basename of %s\διαδρομή δοκιμής33
+string(33) "διαδρομή δοκιμής33"
+bool(true)
+string(%d) "%s\διαδρομή δοκιμής33"
+Active code page: %d
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1254_0.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1254_0.phpt
new file mode 100644
index 0000000000..5fbcb74602
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp1254_0.phpt
@@ -0,0 +1,45 @@
+--TEST--
+Test fopen() for reading cp1254 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+skip_if_wrong_cp(857, "oem");
+
+?>
+--INI--
+default_charset=cp1254
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp1254
+#vim: set encoding=cp1254
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = "okbaytl ileri";
+$prefix = create_data("file_cp1254", $item, 1254);
+$fn = $prefix . DIRECTORY_SEPARATOR . $item;
+
+$f = fopen($fn, 'r');
+if ($f) {
+ var_dump($f, fread($f, 42));
+ var_dump(fclose($f));
+} else {
+ echo "open utf8 failed\n";
+}
+
+remove_data("file_cp1254");
+
+?>
+===DONE===
+--EXPECTF--
+resource(%d) of type (stream)
+string(37) "reading file wihh multibyte filename
+"
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1254_1.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1254_1.phpt
new file mode 100644
index 0000000000..314f78ecb8
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp1254_1.phpt
@@ -0,0 +1,54 @@
+--TEST--
+Test mkdir/rmdir cp1254 to UTF-8 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+skip_if_wrong_cp(857, "oem");
+
+?>
+--INI--
+internal_encoding=cp1254
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp1254
+#vim: set encoding=cp1254
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = "okbaytl ileri";
+$prefix = create_data("dir_cp1254", "${item}42", 1254);
+$path = $prefix . DIRECTORY_SEPARATOR . "${item}42";
+
+$subpath = $path . DIRECTORY_SEPARATOR . "${item}4";
+
+/* The mb dirname exists*/
+var_dump(file_exists($path));
+
+var_dump(mkdir($subpath));
+var_dump(file_exists($subpath));
+
+get_basename_with_cp($subpath, 1254);
+
+var_dump(rmdir($subpath));
+remove_data("dir_cp1254");
+
+?>
+===DONE===
+--EXPECTF--
+bool(true)
+bool(true)
+bool(true)
+Active code page: 1254
+getting basename of %s\okbaytl ileri42\okbaytl ileri4
+string(%d) "okbaytl ileri4"
+bool(true)
+string(%d) "%s\okbaytl ileri42\okbaytl ileri4"
+Active code page: %d
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1254_2.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1254_2.phpt
new file mode 100644
index 0000000000..d2a04d905a
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp1254_2.phpt
@@ -0,0 +1,55 @@
+--TEST--
+Test fopen() for write cp1254 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+skip_if_wrong_cp(857, "oem");
+
+?>
+--INI--
+internal_encoding=cp1254
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp1254
+#vim: set encoding=cp1254
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = "okbaytl ileri";
+$prefix = create_data("dir_cp1254", "${item}42}", 1254);
+$fn = $prefix . DIRECTORY_SEPARATOR . "${item}33";
+
+$f = fopen($fn, 'w');
+if ($f) {
+ var_dump($f, fwrite($f, "writing to an mb filename"));
+ var_dump(fclose($f));
+} else {
+ echo "open utf8 failed\n";
+}
+
+var_dump(file_get_contents($fn));
+
+get_basename_with_cp($fn, 1254);
+
+remove_data("dir_cp1254");
+
+?>
+===DONE===
+--EXPECTF--
+resource(%d) of type (stream)
+int(25)
+bool(true)
+string(25) "writing to an mb filename"
+Active code page: 1254
+getting basename of %s\okbaytl ileri33
+string(%d) "okbaytl ileri33"
+bool(true)
+string(%d) "%s\okbaytl ileri33"
+Active code page: %d
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1254_3.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1254_3.phpt
new file mode 100644
index 0000000000..13357217c8
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp1254_3.phpt
@@ -0,0 +1,43 @@
+--TEST--
+cp1254 cmd test
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+skip_if_wrong_cp(1254, "ansi");
+
+?>
+--INI--
+default_charset=cp1254
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp1254
+#vim: set encoding=cp1254
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+
+$item = "okbaytl ileri";
+$prefix = create_data("file_cp1254", $item, 1254);
+$fn = $prefix . DIRECTORY_SEPARATOR . $item;
+
+var_dump($fn);
+var_dump(touch($fn));
+var_dump(file_exists($fn));
+system("dir /b \"" . $fn . "\"");
+
+remove_data("file_cp1254");
+
+?>
+===DONE===
+--EXPECTF--
+string(%d) "%s\okbaytl ileri"
+bool(true)
+bool(true)
+okbaytl ileri
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1254_to_utf8_0.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1254_to_utf8_0.phpt
new file mode 100644
index 0000000000..9a92099cd0
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp1254_to_utf8_0.phpt
@@ -0,0 +1,42 @@
+--TEST--
+Test fopen() for reading cp1254 to UTF-8 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+
+?>
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp1254
+#vim: set encoding=cp1254
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = "çokbaytlı işleri";
+$prefix = create_data("file_cp1254", $item);
+$fn = $prefix . DIRECTORY_SEPARATOR . $item;
+
+$f = fopen($fn, 'r');
+if ($f) {
+ var_dump($f, fread($f, 42));
+ var_dump(fclose($f));
+} else {
+ echo "open utf8 failed\n";
+}
+
+remove_data("file_cp1254");
+
+?>
+===DONE===
+--EXPECTF--
+resource(%d) of type (stream)
+string(37) "reading file wihh multibyte filename
+"
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1254_to_utf8_1.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1254_to_utf8_1.phpt
new file mode 100644
index 0000000000..551a0f2235
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp1254_to_utf8_1.phpt
@@ -0,0 +1,51 @@
+--TEST--
+Test mkdir/rmdir cp1254 to UTF-8 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+
+?>
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp1254
+#vim: set encoding=cp1254
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = "çokbaytlı işleri";
+$prefix = create_data("dir_cp1254", "${item}42");
+$path = $prefix . DIRECTORY_SEPARATOR . "${item}42";
+
+$subpath = $path . DIRECTORY_SEPARATOR . "${item}4";
+
+/* The mb dirname exists*/
+var_dump(file_exists($path));
+
+var_dump(mkdir($subpath));
+var_dump(file_exists($subpath));
+
+get_basename_with_cp($subpath, 65001);
+
+var_dump(rmdir($subpath));
+remove_data("dir_cp1254");
+
+?>
+===DONE===
+--EXPECTF--
+bool(true)
+bool(true)
+bool(true)
+Active code page: 65001
+getting basename of %s\çokbaytlı işleri42\çokbaytlı işleri4
+string(20) "çokbaytlı işleri4"
+bool(true)
+string(%d) "%s\çokbaytlı işleri42\çokbaytlı işleri4"
+Active code page: %d
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1254_to_utf8_2.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1254_to_utf8_2.phpt
new file mode 100644
index 0000000000..12df56b11e
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp1254_to_utf8_2.phpt
@@ -0,0 +1,52 @@
+--TEST--
+Test fopen() for write cp1254 to UTF-8 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+
+?>
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp1254
+#vim: set encoding=cp1254
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = "çokbaytlı işleri";
+$prefix = create_data("dir_cp1254", "${item}42}");
+$fn = $prefix . DIRECTORY_SEPARATOR . "${item}33";
+
+$f = fopen($fn, 'w');
+if ($f) {
+ var_dump($f, fwrite($f, "writing to an mb filename"));
+ var_dump(fclose($f));
+} else {
+ echo "open utf8 failed\n";
+}
+
+var_dump(file_get_contents($fn));
+
+get_basename_with_cp($fn, 65001);
+
+remove_data("dir_cp1254");
+
+?>
+===DONE===
+--EXPECTF--
+resource(%d) of type (stream)
+int(25)
+bool(true)
+string(25) "writing to an mb filename"
+Active code page: 65001
+getting basename of %s\çokbaytlı işleri33
+string(%d) "çokbaytlı işleri33"
+bool(true)
+string(%d) "%s\çokbaytlı işleri33"
+Active code page: %d
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1254_to_utf8_3.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1254_to_utf8_3.phpt
new file mode 100644
index 0000000000..cbaba44e4e
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp1254_to_utf8_3.phpt
@@ -0,0 +1,40 @@
+--TEST--
+cp1254 cmd test
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+
+?>
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp1254
+#vim: set encoding=cp1254
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+
+$item = "çokbaytlı işleri";
+$prefix = create_data("file_cp1254", $item);
+$fn = $prefix . DIRECTORY_SEPARATOR . $item;
+
+var_dump($fn);
+var_dump(touch($fn));
+var_dump(file_exists($fn));
+system("dir /b \"" . $fn . "\"");
+
+remove_data("file_cp1254");
+
+?>
+===DONE===
+--EXPECTF--
+string(%d) "%s\çokbaytlı işleri"
+bool(true)
+bool(true)
+çokbaytlı işleri
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1255_0.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1255_0.phpt
new file mode 100644
index 0000000000..8a685b68c4
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp1255_0.phpt
@@ -0,0 +1,45 @@
+--TEST--
+Test fopen() for reading cp1255 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+skip_if_wrong_cp(1255, "ansi");
+
+?>
+--INI--
+internal_encoding=cp1255
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp1255
+#vim: set encoding=cp1255
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = " ";
+$prefix = create_data("file_cp1255", $item, 1255);
+$fn = $prefix . DIRECTORY_SEPARATOR . $item;
+
+$f = fopen($fn, 'r');
+if ($f) {
+ var_dump($f, fread($f, 42));
+ var_dump(fclose($f));
+} else {
+ echo "open utf8 failed\n";
+}
+
+remove_data("file_cp1255");
+
+?>
+===DONE===
+--EXPECTF--
+resource(%d) of type (stream)
+string(37) "reading file wihh multibyte filename
+"
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1255_1.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1255_1.phpt
new file mode 100644
index 0000000000..391a282a69
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp1255_1.phpt
@@ -0,0 +1,54 @@
+--TEST--
+Test mkdir/rmdir cp1255 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+skip_if_wrong_cp(1255, "ansi");
+
+?>
+--INI--
+default_charset=cp1255
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp1255
+#vim: set encoding=cp1255
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = " ";
+$prefix = create_data("dir_cp1255", "${item}42", 1255);
+$path = $prefix . DIRECTORY_SEPARATOR . "${item}42";
+
+$subpath = $path . DIRECTORY_SEPARATOR . "${item}4";
+
+/* The mb dirname exists*/
+var_dump(file_exists($path));
+
+var_dump(mkdir($subpath));
+var_dump(file_exists($subpath));
+
+get_basename_with_cp($subpath, 1255);
+
+var_dump(rmdir($subpath));
+remove_data("dir_cp1255");
+
+?>
+===DONE===
+--EXPECTF--
+bool(true)
+bool(true)
+bool(true)
+Active code page: 1255
+getting basename of %s\ 42\ 4
+string(%d) " 4"
+bool(true)
+string(%d) "%s\ 42\ 4"
+Active code page: %d
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1255_2.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1255_2.phpt
new file mode 100644
index 0000000000..2f38398bb5
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp1255_2.phpt
@@ -0,0 +1,55 @@
+--TEST--
+Test fopen() for write cp1255 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+skip_if_wrong_cp(1255, "ansi");
+
+?>
+--INI--
+internal_encoding=cp1255
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp1255
+#vim: set encoding=cp1255
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = " ";
+$prefix = create_data("dir_cp1255", "${item}42}", 1255);
+$fn = $prefix . DIRECTORY_SEPARATOR . "${item}33";
+
+$f = fopen($fn, 'w');
+if ($f) {
+ var_dump($f, fwrite($f, "writing to an mb filename"));
+ var_dump(fclose($f));
+} else {
+ echo "open utf8 failed\n";
+}
+
+var_dump(file_get_contents($fn));
+
+get_basename_with_cp($fn, 1255);
+
+remove_data("dir_cp1255");
+
+?>
+===DONE===
+--EXPECTF--
+resource(%d) of type (stream)
+int(25)
+bool(true)
+string(25) "writing to an mb filename"
+Active code page: 1255
+getting basename of %s\ 33
+string(%d) " 33"
+bool(true)
+string(%d) "%s\ 33"
+Active code page: %d
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1255_to_utf8_0.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1255_to_utf8_0.phpt
new file mode 100644
index 0000000000..31a48dbc0c
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp1255_to_utf8_0.phpt
@@ -0,0 +1,42 @@
+--TEST--
+Test fopen() for reading cp1255 to UTF-8 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+
+?>
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp1255
+#vim: set encoding=cp1255
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = "כללים מרובים";
+$prefix = create_data("file_cp1255", $item);
+$fn = $prefix . DIRECTORY_SEPARATOR . $item;
+
+$f = fopen($fn, 'r');
+if ($f) {
+ var_dump($f, fread($f, 42));
+ var_dump(fclose($f));
+} else {
+ echo "open utf8 failed\n";
+}
+
+remove_data("file_cp1255");
+
+?>
+===DONE===
+--EXPECTF--
+resource(%d) of type (stream)
+string(37) "reading file wihh multibyte filename
+"
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1255_to_utf8_1.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1255_to_utf8_1.phpt
new file mode 100644
index 0000000000..b6a59e4be3
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp1255_to_utf8_1.phpt
@@ -0,0 +1,51 @@
+--TEST--
+Test mkdir/rmdir cp1255 to UTF-8 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+
+?>
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp1255
+#vim: set encoding=cp1255
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = "כללים מרובים";
+$prefix = create_data("dir_cp1255", "${item}42");
+$path = $prefix . DIRECTORY_SEPARATOR . "${item}42";
+
+$subpath = $path . DIRECTORY_SEPARATOR . "${item}4";
+
+/* The mb dirname exists*/
+var_dump(file_exists($path));
+
+var_dump(mkdir($subpath));
+var_dump(file_exists($subpath));
+
+get_basename_with_cp($subpath, 65001);
+
+var_dump(rmdir($subpath));
+remove_data("dir_cp1255");
+
+?>
+===DONE===
+--EXPECTF--
+bool(true)
+bool(true)
+bool(true)
+Active code page: 65001
+getting basename of %s\כללים מרובים42\כללים מרובים4
+string(24) "כללים מרובים4"
+bool(true)
+string(%s) "%s\כללים מרובים42\כללים מרובים4"
+Active code page: %d
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1255_to_utf8_2.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1255_to_utf8_2.phpt
new file mode 100644
index 0000000000..53f83b3a0e
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp1255_to_utf8_2.phpt
@@ -0,0 +1,52 @@
+--TEST--
+Test fopen() for write cp1255 to UTF-8 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+
+?>
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp1255
+#vim: set encoding=cp1255
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = "כללים מרובים";
+$prefix = create_data("dir_cp1255", "${item}42}");
+$fn = $prefix . DIRECTORY_SEPARATOR . "${item}33";
+
+$f = fopen($fn, 'w');
+if ($f) {
+ var_dump($f, fwrite($f, "writing to an mb filename"));
+ var_dump(fclose($f));
+} else {
+ echo "open utf8 failed\n";
+}
+
+var_dump(file_get_contents($fn));
+
+get_basename_with_cp($fn, 65001);
+
+remove_data("dir_cp1255");
+
+?>
+===DONE===
+--EXPECTF--
+resource(%d) of type (stream)
+int(25)
+bool(true)
+string(25) "writing to an mb filename"
+Active code page: 65001
+getting basename of %s\כללים מרובים33
+string(%s) "כללים מרובים33"
+bool(true)
+string(%d) "%s\כללים מרובים33"
+Active code page: %d
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1256_0.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1256_0.phpt
new file mode 100644
index 0000000000..6b473abb54
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp1256_0.phpt
@@ -0,0 +1,45 @@
+--TEST--
+Test fopen() for reading cp1256 to UTF-8 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+skip_if_wrong_cp(1256, "ansi");
+
+?>
+--INI--
+internal_encoding=cp1256
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp1256
+#vim: set encoding=cp1256
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = " "; // cp1256 string
+$prefix = create_data("file_cp1256", $item, 1256);
+$fn = $prefix . DIRECTORY_SEPARATOR . $item;
+
+$f = fopen($fn, 'r');
+if ($f) {
+ var_dump($f, fread($f, 42));
+ var_dump(fclose($f));
+} else {
+ echo "open utf8 failed\n";
+}
+
+remove_data("file_cp1256");
+
+?>
+===DONE===
+--EXPECTF--
+resource(%d) of type (stream)
+string(37) "reading file wihh multibyte filename
+"
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1256_1.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1256_1.phpt
new file mode 100644
index 0000000000..1a4233f2c2
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp1256_1.phpt
@@ -0,0 +1,54 @@
+--TEST--
+Test mkdir/rmdir cp1256 to UTF-8 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+skip_if_wrong_cp(1256, "ansi");
+
+?>
+--INI--
+internal_encoding=cp1256
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp1256
+#vim: set encoding=cp1256
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = " ";
+$prefix = create_data("dir_cp1256", "${item}42", 1256);
+$path = $prefix . DIRECTORY_SEPARATOR . "${item}42";
+
+$subpath = $path . DIRECTORY_SEPARATOR . "${item}4";
+
+/* The mb dirname exists*/
+var_dump(file_exists($path));
+
+var_dump(mkdir($subpath));
+var_dump(file_exists($subpath));
+
+get_basename_with_cp($subpath, 1256);
+
+var_dump(rmdir($subpath));
+remove_data("dir_cp1256");
+
+?>
+===DONE===
+--EXPECTF--
+bool(true)
+bool(true)
+bool(true)
+Active code page: 1256
+getting basename of %s\ 42\ 4
+string(%d) " 4"
+bool(true)
+string(%d) "%s\ 42\ 4"
+Active code page: %d
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1256_2.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1256_2.phpt
new file mode 100644
index 0000000000..5c25d60fc8
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp1256_2.phpt
@@ -0,0 +1,55 @@
+--TEST--
+Test fopen() for write cp1256 to UTF-8 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+skip_if_wrong_cp(1256, "ansi");
+
+?>
+--INI--
+default_charset=cp1256
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp1256
+#vim: set encoding=cp1256
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = " ";
+$prefix = create_data("dir_cp1256", "${item}42}", 1256);
+$fn = $prefix . DIRECTORY_SEPARATOR . "${item}33";
+
+$f = fopen($fn, 'w');
+if ($f) {
+ var_dump($f, fwrite($f, "writing to an mb filename"));
+ var_dump(fclose($f));
+} else {
+ echo "open utf8 failed\n";
+}
+
+var_dump(file_get_contents($fn));
+
+get_basename_with_cp($fn, 1256);
+
+remove_data("dir_cp1256");
+
+?>
+===DONE===
+--EXPECTF--
+resource(%d) of type (stream)
+int(25)
+bool(true)
+string(25) "writing to an mb filename"
+Active code page: 1256
+getting basename of %s\ 33
+string(%d) " 33"
+bool(true)
+string(%d) "%s\ 33"
+Active code page: %s
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1256_to_utf8_0.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1256_to_utf8_0.phpt
new file mode 100644
index 0000000000..b021df7fa4
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp1256_to_utf8_0.phpt
@@ -0,0 +1,42 @@
+--TEST--
+Test fopen() for reading cp1256 to UTF-8 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+
+?>
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp1256
+#vim: set encoding=cp1256
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = "مسار متعدد البايت اختبار";
+$prefix = create_data("file_cp1256", $item);
+$fn = $prefix . DIRECTORY_SEPARATOR . $item;
+
+$f = fopen($fn, 'r');
+if ($f) {
+ var_dump($f, fread($f, 42));
+ var_dump(fclose($f));
+} else {
+ echo "open utf8 failed\n";
+}
+
+remove_data("file_cp1256");
+
+?>
+===DONE===
+--EXPECTF--
+resource(%d) of type (stream)
+string(37) "reading file wihh multibyte filename
+"
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1256_to_utf8_1.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1256_to_utf8_1.phpt
new file mode 100644
index 0000000000..2fb10a97a5
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp1256_to_utf8_1.phpt
@@ -0,0 +1,51 @@
+--TEST--
+Test mkdir/rmdir cp1256 to UTF-8 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+
+?>
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp1256
+#vim: set encoding=cp1256
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = "مسار متعدد البايت اختبار";
+$prefix = create_data("dir_cp1256", "${item}42");
+$path = $prefix . DIRECTORY_SEPARATOR . "${item}42";
+
+$subpath = $path . DIRECTORY_SEPARATOR . "${item}4";
+
+/* The mb dirname exists*/
+var_dump(file_exists($path));
+
+var_dump(mkdir($subpath));
+var_dump(file_exists($subpath));
+
+get_basename_with_cp($subpath, 65001);
+
+var_dump(rmdir($subpath));
+remove_data("dir_cp1256");
+
+?>
+===DONE===
+--EXPECTF--
+bool(true)
+bool(true)
+bool(true)
+Active code page: 65001
+getting basename of %s\مسار متعدد البايت اختبار42\مسار متعدد البايت اختبار4
+string(46) "مسار متعدد البايت اختبار4"
+bool(true)
+string(%d) "%s\مسار متعدد البايت اختبار42\مسار متعدد البايت اختبار4"
+Active code page: %d
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1256_to_utf8_2.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1256_to_utf8_2.phpt
new file mode 100644
index 0000000000..ba70e1a77e
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp1256_to_utf8_2.phpt
@@ -0,0 +1,52 @@
+--TEST--
+Test fopen() for write cp1256 to UTF-8 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+
+?>
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp1256
+#vim: set encoding=cp1256
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = "مسار متعدد البايت اختبار";
+$prefix = create_data("dir_cp1256", "${item}42}");
+$fn = $prefix . DIRECTORY_SEPARATOR . "${item}33";
+
+$f = fopen($fn, 'w');
+if ($f) {
+ var_dump($f, fwrite($f, "writing to an mb filename"));
+ var_dump(fclose($f));
+} else {
+ echo "open utf8 failed\n";
+}
+
+var_dump(file_get_contents($fn));
+
+get_basename_with_cp($fn, 65001);
+
+remove_data("dir_cp1256");
+
+?>
+===DONE===
+--EXPECTF--
+resource(%d) of type (stream)
+int(25)
+bool(true)
+string(25) "writing to an mb filename"
+Active code page: 65001
+getting basename of %s\مسار متعدد البايت اختبار33
+string(47) "مسار متعدد البايت اختبار33"
+bool(true)
+string(%d) "%s\مسار متعدد البايت اختبار33"
+Active code page: %s
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp874_0.phpt b/ext/standard/tests/file/windows_mb_path/test_cp874_0.phpt
new file mode 100644
index 0000000000..52f080e2c2
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp874_0.phpt
@@ -0,0 +1,114 @@
+--TEST--
+Thai cp874 basic test
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+skip_if_wrong_cp(874, "oem");
+
+?>
+--INI--
+default_charset=cp874
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp874
+#vim: set encoding=cp874
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$names = array( /* cp874 */
+ "跴ͺ1",
+ "跴ͺ2",
+ "跴ͺ3",
+ "跴ͺ4",
+ "跴ͺ5",
+ "跴ͺ6",
+ "跴ͺ7",
+ "跴ͺ8",
+ "跴ͺ8 10",
+);
+
+$i = 0;
+foreach ($names as $name) {
+ $path = dirname(__FILE__) . DIRECTORY_SEPARATOR . $name . ".txt";
+
+ file_put_contents($path, "hello" . $i++);
+
+ get_basename_with_cp($path, 874);
+ var_dump(file_get_contents($path));
+
+ unlink($path);
+}
+
+?>
+===DONE===
+--EXPECTF--
+Active code page: %d
+getting basename of %s跴ͺ1.txt
+string(%d) "跴ͺ1.txt"
+bool(true)
+string(%d) "%s跴ͺ1.txt"
+Active code page: %d
+string(6) "hello0"
+Active code page: %d
+getting basename of %s跴ͺ2.txt
+string(%d) "跴ͺ2.txt"
+bool(true)
+string(%d) "%s跴ͺ2.txt"
+Active code page: %d
+string(6) "hello1"
+Active code page: %d
+getting basename of %s跴ͺ3.txt
+string(%d) "跴ͺ3.txt"
+bool(true)
+string(%d) "%s跴ͺ3.txt"
+Active code page: %d
+string(6) "hello2"
+Active code page: %d
+getting basename of %s跴ͺ4.txt
+string(%d) "跴ͺ4.txt"
+bool(true)
+string(%d) "%s跴ͺ4.txt"
+Active code page: %d
+string(6) "hello3"
+Active code page: %d
+getting basename of %s跴ͺ5.txt
+string(%d) "跴ͺ5.txt"
+bool(true)
+string(%d) "%s跴ͺ5.txt"
+Active code page: %d
+string(6) "hello4"
+Active code page: %d
+getting basename of %s跴ͺ6.txt
+string(%d) "跴ͺ6.txt"
+bool(true)
+string(%d) "%s跴ͺ6.txt"
+Active code page: %d
+string(6) "hello5"
+Active code page: %d
+getting basename of %s跴ͺ7.txt
+string(%d) "跴ͺ7.txt"
+bool(true)
+string(%d) "%s跴ͺ7.txt"
+Active code page: %d
+string(6) "hello6"
+Active code page: %d
+getting basename of %s跴ͺ8.txt
+string(%d) "跴ͺ8.txt"
+bool(true)
+string(%d) "%s跴ͺ8.txt"
+Active code page: %d
+string(6) "hello7"
+Active code page: %d
+getting basename of %s跴ͺ8 10.txt
+string(%d) "跴ͺ8 10.txt"
+bool(true)
+string(%d) "%s跴ͺ8 10.txt"
+Active code page: %d
+string(6) "hello8"
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp874_1.phpt b/ext/standard/tests/file/windows_mb_path/test_cp874_1.phpt
new file mode 100644
index 0000000000..6cb2fb7eee
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp874_1.phpt
@@ -0,0 +1,43 @@
+--TEST--
+Thai cp874 cmd test
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+skip_if_wrong_cp(874, "ansi");
+
+?>
+--INI--
+internal_encoding=cp874
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp874
+#vim: set encoding=cp874
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+
+$item = "跴ͺ11";
+$prefix = create_data("file_cp874", $item, 874);
+$fn = dirname(__FILE__) . DIRECTORY_SEPARATOR . $item;
+
+var_dump($fn);
+var_dump(touch($fn));
+var_dump(file_exists($fn));
+system("dir /b " . $fn);
+
+remove_data("file_cp874");
+
+?>
+===DONE===
+--EXPECTF--
+string(%d) "%s\跴ͺ11"
+bool(true)
+bool(true)
+跴ͺ11
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp874_to_utf8_0.phpt b/ext/standard/tests/file/windows_mb_path/test_cp874_to_utf8_0.phpt
new file mode 100644
index 0000000000..8d32f88a8c
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp874_to_utf8_0.phpt
@@ -0,0 +1,111 @@
+--TEST--
+Thai UTF-8 basic test
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+
+?>
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp874
+#vim: set encoding=cp874
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$names = array( /* cp874 */
+ "跴ͺ1",
+ "跴ͺ2",
+ "跴ͺ3",
+ "跴ͺ4",
+ "跴ͺ5",
+ "跴ͺ6",
+ "跴ͺ7",
+ "跴ͺ8",
+ "跴ͺ8 10",
+);
+
+$i = 0;
+foreach ($names as $name) {
+ $pathw = dirname(__FILE__) . DIRECTORY_SEPARATOR . iconv('cp874', 'utf-8', $name) . ".txt";
+
+ file_put_contents($pathw, "hello" . $i++);
+
+ get_basename_with_cp($pathw, 65001);
+ var_dump(file_get_contents($pathw));
+
+ unlink($pathw);
+}
+
+?>
+===DONE===
+--EXPECTF--
+Active code page: %d
+getting basename of %sเป็นแฟ้มที่ทดสอบ1.txt
+string(%d) "เป็นแฟ้มที่ทดสอบ1.txt"
+bool(true)
+string(%d) "%sเป็นแฟ้มที่ทดสอบ1.txt"
+Active code page: %d
+string(6) "hello0"
+Active code page: %d
+getting basename of %sเป็นแฟ้มที่ทดสอบ2.txt
+string(%d) "เป็นแฟ้มที่ทดสอบ2.txt"
+bool(true)
+string(%d) "%sเป็นแฟ้มที่ทดสอบ2.txt"
+Active code page: %d
+string(6) "hello1"
+Active code page: %d
+getting basename of %sเป็นแฟ้มที่ทดสอบ3.txt
+string(%d) "เป็นแฟ้มที่ทดสอบ3.txt"
+bool(true)
+string(%d) "%sเป็นแฟ้มที่ทดสอบ3.txt"
+Active code page: %d
+string(6) "hello2"
+Active code page: %d
+getting basename of %sเป็นแฟ้มที่ทดสอบ4.txt
+string(%d) "เป็นแฟ้มที่ทดสอบ4.txt"
+bool(true)
+string(%d) "%sเป็นแฟ้มที่ทดสอบ4.txt"
+Active code page: %d
+string(6) "hello3"
+Active code page: %d
+getting basename of %sเป็นแฟ้มที่ทดสอบ5.txt
+string(%d) "เป็นแฟ้มที่ทดสอบ5.txt"
+bool(true)
+string(%d) "%sเป็นแฟ้มที่ทดสอบ5.txt"
+Active code page: %d
+string(6) "hello4"
+Active code page: %d
+getting basename of %sเป็นแฟ้มที่ทดสอบ6.txt
+string(%d) "เป็นแฟ้มที่ทดสอบ6.txt"
+bool(true)
+string(%d) "%sเป็นแฟ้มที่ทดสอบ6.txt"
+Active code page: %d
+string(6) "hello5"
+Active code page: %d
+getting basename of %sเป็นแฟ้มที่ทดสอบ7.txt
+string(%d) "เป็นแฟ้มที่ทดสอบ7.txt"
+bool(true)
+string(%d) "%sเป็นแฟ้มที่ทดสอบ7.txt"
+Active code page: %d
+string(6) "hello6"
+Active code page: %d
+getting basename of %sเป็นแฟ้มที่ทดสอบ8.txt
+string(%d) "เป็นแฟ้มที่ทดสอบ8.txt"
+bool(true)
+string(%d) "%sเป็นแฟ้มที่ทดสอบ8.txt"
+Active code page: %d
+string(6) "hello7"
+Active code page: %d
+getting basename of %sเป็นแฟ้มที่ทดสอบ8 10.txt
+string(%d) "เป็นแฟ้มที่ทดสอบ8 10.txt"
+bool(true)
+string(%d) "%sเป็นแฟ้มที่ทดสอบ8 10.txt"
+Active code page: %d
+string(6) "hello8"
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp874_to_utf8_1.phpt b/ext/standard/tests/file/windows_mb_path/test_cp874_to_utf8_1.phpt
new file mode 100644
index 0000000000..843c66f4a2
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp874_to_utf8_1.phpt
@@ -0,0 +1,40 @@
+--TEST--
+Thai UTF-8 cmd test
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+
+?>
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp874
+#vim: set encoding=cp874
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+
+$item = "เป็นแฟ้มที่ทดสอบ11";
+$prefix = create_data("file_cp874", $item);
+$fn = $prefix . DIRECTORY_SEPARATOR . $item;
+
+var_dump($fn);
+var_dump(touch($fn));
+var_dump(file_exists($fn));
+system("dir /b " . $fn);
+
+remove_data("file_cp874");
+
+?>
+===DONE===
+--EXPECTF--
+string(%d) "%s\เป็นแฟ้มที่ทดสอบ11"
+bool(true)
+bool(true)
+เป็นแฟ้มที่ทดสอบ11
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp932_0.phpt b/ext/standard/tests/file/windows_mb_path/test_cp932_0.phpt
new file mode 100644
index 0000000000..1835f0d360
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp932_0.phpt
@@ -0,0 +1,45 @@
+--TEST--
+Test fopen() for reading cp932 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+skip_if_wrong_cp(932, "ansi");
+
+?>
+--INI--
+default_charset=cp932
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp932
+#vim: set encoding=cp932
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = "eXg}`oCgEpX"; // cp932 string
+$prefix = create_data("file_cp932", $item, 932);
+$fn = $prefix . DIRECTORY_SEPARATOR . $item;
+
+$f = fopen($fn, 'r');
+if ($f) {
+ var_dump($f, fread($f, 42));
+ var_dump(fclose($f));
+} else {
+ echo "open utf8 failed\n";
+}
+
+remove_data("file_cp932");
+
+?>
+===DONE===
+--EXPECTF--
+resource(%d) of type (stream)
+string(37) "reading file wihh multibyte filename
+"
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp932_1.phpt b/ext/standard/tests/file/windows_mb_path/test_cp932_1.phpt
new file mode 100644
index 0000000000..f7f24c045d
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp932_1.phpt
@@ -0,0 +1,54 @@
+--TEST--
+Test mkdir/rmdir cp932
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+skip_if_wrong_cp(932, "oem");
+
+?>
+--INI--
+default_charset=cp932
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp932
+#vim: set encoding=cp932
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = "eXg}`oCgEpX"; // cp932 string
+$prefix = create_data("dir_cp932", "${item}42", 932);
+$path = $prefix . DIRECTORY_SEPARATOR . "${item}42";
+
+$subpath = $path . DIRECTORY_SEPARATOR . "${item}4";
+
+/* The mb dirname exists*/
+var_dump(file_exists($path));
+
+var_dump(mkdir($subpath));
+var_dump(file_exists($subpath));
+
+get_basename_with_cp($subpath, 932);
+
+var_dump(rmdir($subpath));
+remove_data("dir_cp932");
+
+?>
+===DONE===
+--EXPECTF--
+bool(true)
+bool(true)
+bool(true)
+Active code page: 932
+getting basename of %s\eXg}`oCgEpX42\eXg}`oCgEpX4
+string(%d) "eXg}`oCgEpX4"
+bool(true)
+string(%d) "%s\eXg}`oCgEpX42\eXg}`oCgEpX4"
+Active code page: %d
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp932_2.phpt b/ext/standard/tests/file/windows_mb_path/test_cp932_2.phpt
new file mode 100644
index 0000000000..d529f6a484
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp932_2.phpt
@@ -0,0 +1,55 @@
+--TEST--
+Test fopen() for write to cp932 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+skip_if_wrong_cp(932, "oem");
+
+?>
+--INI--
+internal_encoding=cp932
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp932
+#vim: set encoding=cp932
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = "eXg}`oCgEpX"; // cp932 string
+$prefix = create_data("dir_cp932", "${item}42}", 932);
+$fn = $prefix . DIRECTORY_SEPARATOR . "${item}33";
+
+$f = fopen($fn, 'w');
+if ($f) {
+ var_dump($f, fwrite($f, "writing to an mb filename"));
+ var_dump(fclose($f));
+} else {
+ echo "open utf8 failed\n";
+}
+
+var_dump(file_get_contents($fn));
+
+get_basename_with_cp($fn, 932);
+
+remove_data("dir_cp932");
+
+?>
+===DONE===
+--EXPECTF--
+resource(%d) of type (stream)
+int(25)
+bool(true)
+string(25) "writing to an mb filename"
+Active code page: 932
+getting basename of %s\eXg}`oCgEpX33
+string(%d) "eXg}`oCgEpX33"
+bool(true)
+string(%d) "%s\eXg}`oCgEpX33"
+Active code page: %d
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp932_3.phpt b/ext/standard/tests/file/windows_mb_path/test_cp932_3.phpt
new file mode 100644
index 0000000000..db31c1ad7a
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp932_3.phpt
@@ -0,0 +1,43 @@
+--TEST--
+cp932 cmd test
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+skip_if_wrong_cp(932, "ansi");
+
+?>
+--INI--
+internal_encoding=cp932
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp932
+#vim: set encoding=cp932
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+
+$item = "eXg}`oCgEpX77"; // cp932 string
+$prefix = create_data("file_cp932", $item, 932);
+$fn = $prefix . DIRECTORY_SEPARATOR . $item;
+
+var_dump($fn);
+var_dump(touch($fn));
+var_dump(file_exists($fn));
+system("dir /b " . $fn);
+
+remove_data("file_cp932");
+
+?>
+===DONE===
+--EXPECTF--
+string(%d) "%s\eXg}`oCgEpX77"
+bool(true)
+bool(true)
+eXg}`oCgEpX77
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp932_to_utf8_0.phpt b/ext/standard/tests/file/windows_mb_path/test_cp932_to_utf8_0.phpt
new file mode 100644
index 0000000000..a04c14d276
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp932_to_utf8_0.phpt
@@ -0,0 +1,42 @@
+--TEST--
+Test fopen() for reading cp932 to UTF-8 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+
+?>
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp932
+#vim: set encoding=cp932
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = iconv('cp932', 'utf-8', "eXg}`oCgEpX"); // cp932 string
+$prefix = create_data("file_cp932", $item);
+$fn = $prefix . DIRECTORY_SEPARATOR . $item;
+
+$f = fopen($fn, 'r');
+if ($f) {
+ var_dump($f, fread($f, 42));
+ var_dump(fclose($f));
+} else {
+ echo "open utf8 failed\n";
+}
+
+remove_data("file_cp932");
+
+?>
+===DONE===
+--EXPECTF--
+resource(%d) of type (stream)
+string(37) "reading file wihh multibyte filename
+"
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp932_to_utf8_1.phpt b/ext/standard/tests/file/windows_mb_path/test_cp932_to_utf8_1.phpt
new file mode 100644
index 0000000000..9b13922a7a
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp932_to_utf8_1.phpt
@@ -0,0 +1,51 @@
+--TEST--
+Test mkdir/rmdir cp932 to UTF-8 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+
+?>
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp932
+#vim: set encoding=cp932
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = iconv('cp932', 'utf-8', "eXg}`oCgEpX"); // cp932 string
+$prefix = create_data("dir_cp932", "${item}42");
+$path = $prefix . DIRECTORY_SEPARATOR . "${item}42";
+
+$subpath = $path . DIRECTORY_SEPARATOR . "${item}4";
+
+/* The mb dirname exists*/
+var_dump(file_exists($path));
+
+var_dump(mkdir($subpath));
+var_dump(file_exists($subpath));
+
+get_basename_with_cp($subpath, 65001);
+
+var_dump(rmdir($subpath));
+remove_data("dir_cp932");
+
+?>
+===DONE===
+--EXPECTF--
+bool(true)
+bool(true)
+bool(true)
+Active code page: 65001
+getting basename of %s\テストマルチバイト・パス42\テストマルチバイト・パス4
+string(37) "テストマルチバイト・パス4"
+bool(true)
+string(%d) "%s\テストマルチバイト・パス42\テストマルチバイト・パス4"
+Active code page: %d
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp932_to_utf8_2.phpt b/ext/standard/tests/file/windows_mb_path/test_cp932_to_utf8_2.phpt
new file mode 100644
index 0000000000..fbe290468c
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp932_to_utf8_2.phpt
@@ -0,0 +1,52 @@
+--TEST--
+Test fopen() for write cp932 to UTF-8 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+
+?>
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp932
+#vim: set encoding=cp932
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = iconv('cp932', 'utf-8', "eXg}`oCgEpX"); // cp932 string
+$prefix = create_data("dir_cp932", "${item}42}");
+$fn = $prefix . DIRECTORY_SEPARATOR . "${item}33";
+
+$f = fopen($fn, 'w');
+if ($f) {
+ var_dump($f, fwrite($f, "writing to an mb filename"));
+ var_dump(fclose($f));
+} else {
+ echo "open utf8 failed\n";
+}
+
+var_dump(file_get_contents($fn));
+
+get_basename_with_cp($fn, 65001);
+
+remove_data("dir_cp932");
+
+?>
+===DONE===
+--EXPECTF--
+resource(%d) of type (stream)
+int(25)
+bool(true)
+string(25) "writing to an mb filename"
+Active code page: 65001
+getting basename of %s\テストマルチバイト・パス33
+string(38) "テストマルチバイト・パス33"
+bool(true)
+string(%d) "%s\テストマルチバイト・パス33"
+Active code page: %d
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp936_0.phpt b/ext/standard/tests/file/windows_mb_path/test_cp936_0.phpt
new file mode 100644
index 0000000000..bcaa31353f
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp936_0.phpt
@@ -0,0 +1,45 @@
+--TEST--
+Test fopen() for reading cp936 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+skip_if_wrong_cp(936, "ansi");
+
+?>
+--INI--
+internal_encoding=cp936
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp936
+#vim: set encoding=cp936
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = "yԇֹ·"; // cp936 string
+$prefix = create_data("file_cp936", $item, 936);
+$fn = $prefix . DIRECTORY_SEPARATOR . "$item";
+
+$f = fopen($fn, 'r');
+if ($f) {
+ var_dump($f, fread($f, 42));
+ var_dump(fclose($f));
+} else {
+ echo "open utf8 failed\n";
+}
+
+remove_data("file_cp936");
+
+?>
+===DONE===
+--EXPECTF--
+resource(%d) of type (stream)
+string(%d) "reading file wihh multibyte filename
+"
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp936_1.phpt b/ext/standard/tests/file/windows_mb_path/test_cp936_1.phpt
new file mode 100644
index 0000000000..9edb6d1301
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp936_1.phpt
@@ -0,0 +1,54 @@
+--TEST--
+Test mkdir/rmdir cp936 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+skip_if_wrong_cp(936, "oem");
+
+?>
+--INI--
+internal_encoding=cp936
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp936
+#vim: set encoding=cp936
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = "yԇֹ·"; // cp936 string
+$prefix = create_data("dir_cp936", $item . "5", 936);
+$path = $prefix . DIRECTORY_SEPARATOR . "${item}5";
+
+$subpath = $path . DIRECTORY_SEPARATOR . "${item}4";
+
+/* The mb dirname exists*/
+var_dump(file_exists($path));
+
+var_dump(mkdir($subpath));
+var_dump(file_exists($subpath));
+
+get_basename_with_cp($subpath, 936);
+
+var_dump(rmdir($subpath));
+remove_data("dir_cp936");
+
+?>
+===DONE===
+--EXPECTF--
+bool(true)
+bool(true)
+bool(true)
+Active code page: 936
+getting basename of %s\yԇֹ·5\yԇֹ·4
+string(15) "yԇֹ·4"
+bool(true)
+string(%d) "%s\yԇֹ·5\yԇֹ·4"
+Active code page: %d
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp936_2.phpt b/ext/standard/tests/file/windows_mb_path/test_cp936_2.phpt
new file mode 100644
index 0000000000..2c05c313e9
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp936_2.phpt
@@ -0,0 +1,58 @@
+--TEST--
+Test fopen() for write cp936 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+skip_if_wrong_cp(936, "oem");
+
+?>
+--INI--
+default_charset=cp936
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp936
+#vim: set encoding=cp936
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = "yԇֹ·"; // cp936 string
+$prefix = create_data("file_cp936", $item . "25", 936);
+$fn = $prefix . DIRECTORY_SEPARATOR . "{$item}25";
+
+$f = fopen($fn, 'w');
+if ($f) {
+ var_dump($f, fwrite($f, "writing to an mb filename"));
+ var_dump(fclose($f));
+} else {
+ echo "open utf8 failed\n";
+}
+
+var_dump(file_get_contents($fn));
+
+get_basename_with_cp($fn, 936);
+
+var_dump(unlink($fn));
+
+remove_data("file_cp936");
+
+?>
+===DONE===
+--EXPECTF--
+resource(%d) of type (stream)
+int(25)
+bool(true)
+string(25) "writing to an mb filename"
+Active code page: 936
+getting basename of %s\yԇֹ·25
+string(%d) "yԇֹ·25"
+bool(true)
+string(%d) "%s\yԇֹ·25"
+Active code page: %d
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp936_to_utf8_0.phpt b/ext/standard/tests/file/windows_mb_path/test_cp936_to_utf8_0.phpt
new file mode 100644
index 0000000000..554a05d94c
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp936_to_utf8_0.phpt
@@ -0,0 +1,42 @@
+--TEST--
+Test fopen() for reading cp936 to UTF-8 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+
+?>
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp936
+#vim: set encoding=cp936
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = iconv('cp936', 'utf-8', "yԇֹ·"); // cp936 string
+$prefix = create_data("file_cp936", $item);
+$fn = $prefix . DIRECTORY_SEPARATOR . "$item";
+
+$f = fopen($fn, 'r');
+if ($f) {
+ var_dump($f, fread($f, 42));
+ var_dump(fclose($f));
+} else {
+ echo "open utf8 failed\n";
+}
+
+remove_data("file_cp936");
+
+?>
+===DONE===
+--EXPECTF--
+resource(%d) of type (stream)
+string(%d) "reading file wihh multibyte filename
+"
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp936_to_utf8_1.phpt b/ext/standard/tests/file/windows_mb_path/test_cp936_to_utf8_1.phpt
new file mode 100644
index 0000000000..79bdfa4283
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp936_to_utf8_1.phpt
@@ -0,0 +1,51 @@
+--TEST--
+Test mkdir/rmdir cp936 to UTF-8 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+
+?>
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp936
+#vim: set encoding=cp936
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = iconv('cp936', 'utf-8', "yԇֹ·"); // cp936 string
+$prefix = create_data("dir_cp936", $item . "5");
+$path = $prefix . DIRECTORY_SEPARATOR . "${item}5";
+
+$subpath = $path . DIRECTORY_SEPARATOR . "${item}4";
+
+/* The mb dirname exists*/
+var_dump(file_exists($path));
+
+var_dump(mkdir($subpath));
+var_dump(file_exists($subpath));
+
+get_basename_with_cp($subpath, 65001);
+
+var_dump(rmdir($subpath));
+remove_data("dir_cp936");
+
+?>
+===DONE===
+--EXPECTF--
+bool(true)
+bool(true)
+bool(true)
+Active code page: 65001
+getting basename of %s\測試多字節路徑5\測試多字節路徑4
+string(22) "測試多字節路徑4"
+bool(true)
+string(%d) "%s\測試多字節路徑5\測試多字節路徑4"
+Active code page: %d
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cp936_to_utf8_2.phpt b/ext/standard/tests/file/windows_mb_path/test_cp936_to_utf8_2.phpt
new file mode 100644
index 0000000000..22b64c31f9
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cp936_to_utf8_2.phpt
@@ -0,0 +1,55 @@
+--TEST--
+Test fopen() for write cp936 to UTF-8 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+
+?>
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp936
+#vim: set encoding=cp936
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = iconv('cp936', 'utf-8', "yԇֹ·"); // cp936 string
+$prefix = create_data("file_cp936", $item . "25");
+$fn = $prefix . DIRECTORY_SEPARATOR . "{$item}25";
+
+$f = fopen($fn, 'w');
+if ($f) {
+ var_dump($f, fwrite($f, "writing to an mb filename"));
+ var_dump(fclose($f));
+} else {
+ echo "open utf8 failed\n";
+}
+
+var_dump(file_get_contents($fn));
+
+get_basename_with_cp($fn, 65001);
+
+var_dump(unlink($fn));
+
+remove_data("file_cp936");
+
+?>
+===DONE===
+--EXPECTF--
+resource(%d) of type (stream)
+int(25)
+bool(true)
+string(25) "writing to an mb filename"
+Active code page: 65001
+getting basename of %s\測試多字節路徑25
+string(23) "測試多字節路徑25"
+bool(true)
+string(%d) "%s\測試多字節路徑25"
+Active code page: %d
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_cwd_mb_names.phpt b/ext/standard/tests/file/windows_mb_path/test_cwd_mb_names.phpt
new file mode 100644
index 0000000000..d41a215b68
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_cwd_mb_names.phpt
@@ -0,0 +1,52 @@
+--TEST--
+Test chdir()/getcwd() with a dir for multibyte filenames
+
+--SKIPIF--
+
+<?php
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+
+
+?>
+
+--FILE--
+
+<?php
+
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$prefix = create_data("dir_mb");
+$dirw = $prefix . DIRECTORY_SEPARATOR . "テストマルチバイト・パス42";
+touch($dirw . DIRECTORY_SEPARATOR . "dummy.txt");
+
+$old_cp = get_active_cp();
+set_active_cp(65001);
+
+$oldcwd = getcwd();
+var_dump(chdir($dirw));
+var_dump(getcwd());
+
+var_dump(file_exists("dummy.txt"));
+
+set_active_cp($old_cp);
+
+chdir($oldcwd);
+remove_data("dir_mb");
+
+?>
+===DONE===
+
+--EXPECTF--
+Active code page: 65001
+bool(true)
+string(%d) "%s\テストマルチバイト・パス42"
+bool(true)
+Active code page: %d
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_eucjp_to_utf8_0.phpt b/ext/standard/tests/file/windows_mb_path/test_eucjp_to_utf8_0.phpt
new file mode 100644
index 0000000000..0ff640aa35
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_eucjp_to_utf8_0.phpt
@@ -0,0 +1,42 @@
+--TEST--
+Test fopen() for reading eucjp to UTF-8 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+
+?>
+--FILE--
+<?php
+/*
+#vim: set fileencoding=eucjp
+#vim: set encoding=eucjp
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = iconv('eucjp', 'utf-8', "ƥȥޥХȡѥ"); // EUCJP string
+$prefix = create_data("file_eucjp", $item);
+$fn = $prefix . DIRECTORY_SEPARATOR . $item;
+
+$f = fopen($fn, 'r');
+if ($f) {
+ var_dump($f, fread($f, 42));
+ var_dump(fclose($f));
+} else {
+ echo "open utf8 failed\n";
+}
+
+remove_data("file_eucjp");
+
+?>
+===DONE===
+--EXPECTF--
+resource(%d) of type (stream)
+string(37) "reading file wihh multibyte filename
+"
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_eucjp_to_utf8_1.phpt b/ext/standard/tests/file/windows_mb_path/test_eucjp_to_utf8_1.phpt
new file mode 100644
index 0000000000..5a481e8d12
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_eucjp_to_utf8_1.phpt
@@ -0,0 +1,51 @@
+--TEST--
+Test mkdir/rmdir eucjp to UTF-8 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+
+?>
+--FILE--
+<?php
+/*
+#vim: set fileencoding=eucjp
+#vim: set encoding=eucjp
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = iconv('eucjp', 'utf-8', "ƥȥޥХȡѥ"); // EUCJP string
+$prefix = create_data("dir_eucjp", "${item}42");
+$path = $prefix . DIRECTORY_SEPARATOR . "${item}42";
+
+$subpath = $path . DIRECTORY_SEPARATOR . "${item}4";
+
+/* The mb dirname exists*/
+var_dump(file_exists($path));
+
+var_dump(mkdir($subpath));
+var_dump(file_exists($subpath));
+
+get_basename_with_cp($subpath, 65001);
+
+var_dump(rmdir($subpath));
+remove_data("dir_eucjp");
+
+?>
+===DONE===
+--EXPECTF--
+bool(true)
+bool(true)
+bool(true)
+Active code page: 65001
+getting basename of %s\テストマルチバイト・パス42\テストマルチバイト・パス4
+string(37) "テストマルチバイト・パス4"
+bool(true)
+string(%d) "%s\テストマルチバイト・パス42\テストマルチバイト・パス4"
+Active code page: %d
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_eucjp_to_utf8_2.phpt b/ext/standard/tests/file/windows_mb_path/test_eucjp_to_utf8_2.phpt
new file mode 100644
index 0000000000..50727a9d71
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_eucjp_to_utf8_2.phpt
@@ -0,0 +1,52 @@
+--TEST--
+Test fopen() for write eucjp to UTF-8 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+
+?>
+--FILE--
+<?php
+/*
+#vim: set fileencoding=eucjp
+#vim: set encoding=eucjp
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = iconv('eucjp', 'utf-8', "ƥȥޥХȡѥ"); // EUCJP string
+$prefix = create_data("dir_eucjp", "${item}42}");
+$fn = $prefix . DIRECTORY_SEPARATOR . "${item}33";
+
+$f = fopen($fn, 'w');
+if ($f) {
+ var_dump($f, fwrite($f, "writing to an mb filename"));
+ var_dump(fclose($f));
+} else {
+ echo "open utf8 failed\n";
+}
+
+var_dump(file_get_contents($fn));
+
+get_basename_with_cp($fn, 65001);
+
+remove_data("dir_eucjp");
+
+?>
+===DONE===
+--EXPECTF--
+resource(%d) of type (stream)
+int(25)
+bool(true)
+string(25) "writing to an mb filename"
+Active code page: 65001
+getting basename of %s\テストマルチバイト・パス33
+string(38) "テストマルチバイト・パス33"
+bool(true)
+string(%d) "%s\テストマルチバイト・パス33"
+Active code page: %d
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_kartuli_utf8_0.phpt b/ext/standard/tests/file/windows_mb_path/test_kartuli_utf8_0.phpt
new file mode 100644
index 0000000000..32b6f9b066
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_kartuli_utf8_0.phpt
@@ -0,0 +1,42 @@
+--TEST--
+Test fopen() for reading Kartuli UTF-8 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+
+?>
+--FILE--
+<?php
+/*
+#vim: set fileencoding=utf-8
+#vim: set encoding=utf-8
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = "ქართველები";
+$prefix = create_data("file_kartuli", $item);
+$fn = $prefix . DIRECTORY_SEPARATOR . $item;
+
+$f = fopen($fn, 'r');
+if ($f) {
+ var_dump($f, fread($f, 42));
+ var_dump(fclose($f));
+} else {
+ echo "open utf8 failed\n";
+}
+
+remove_data("file_kartuli");
+
+?>
+===DONE===
+--EXPECTF--
+resource(%d) of type (stream)
+string(37) "reading file wihh multibyte filename
+"
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_kartuli_utf8_1.phpt b/ext/standard/tests/file/windows_mb_path/test_kartuli_utf8_1.phpt
new file mode 100644
index 0000000000..a92e7c34ed
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_kartuli_utf8_1.phpt
@@ -0,0 +1,51 @@
+--TEST--
+Test mkdir/rmdir Kartuli UTF-8 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+
+?>
+--FILE--
+<?php
+/*
+#vim: set fileencoding=utf-8
+#vim: set encoding=utf-8
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = "ქართველები";
+$prefix = create_data("dir_kartuli", "${item}42");
+$path = $prefix . DIRECTORY_SEPARATOR . "${item}42";
+
+$subpath = $path . DIRECTORY_SEPARATOR . "${item}4";
+
+/* The mb dirname exists*/
+var_dump(file_exists($path));
+
+var_dump(mkdir($subpath));
+var_dump(file_exists($subpath));
+
+get_basename_with_cp($subpath, 65001);
+
+var_dump(rmdir($subpath));
+remove_data("dir_kartuli");
+
+?>
+===DONE===
+--EXPECTF--
+bool(true)
+bool(true)
+bool(true)
+Active code page: 65001
+getting basename of %s\ქართველები42\ქართველები4
+string(31) "ქართველები4"
+bool(true)
+string(%d) "%s\ქართველები42\ქართველები4"
+Active code page: %d
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_kartuli_utf8_2.phpt b/ext/standard/tests/file/windows_mb_path/test_kartuli_utf8_2.phpt
new file mode 100644
index 0000000000..edfb279cd2
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_kartuli_utf8_2.phpt
@@ -0,0 +1,52 @@
+--TEST--
+Test fopen() for write Kartuli UTF-8 path
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+
+?>
+--FILE--
+<?php
+/*
+#vim: set fileencoding=kartuli
+#vim: set encoding=kartuli
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$item = "ქართველები";
+$prefix = create_data("dir_kartuli", "${item}42}");
+$fn = $prefix . DIRECTORY_SEPARATOR . "${item}33";
+
+$f = fopen($fn, 'w');
+if ($f) {
+ var_dump($f, fwrite($f, "writing to an mb filename"));
+ var_dump(fclose($f));
+} else {
+ echo "open utf8 failed\n";
+}
+
+var_dump(file_get_contents($fn));
+
+get_basename_with_cp($fn, 65001);
+
+remove_data("dir_kartuli");
+
+?>
+===DONE===
+--EXPECTF--
+resource(%d) of type (stream)
+int(25)
+bool(true)
+string(25) "writing to an mb filename"
+Active code page: 65001
+getting basename of %s\ქართველები33
+string(32) "ქართველები33"
+bool(true)
+string(%d) "%s\ქართველები33"
+Active code page: %d
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_kartuli_utf8_3.phpt b/ext/standard/tests/file/windows_mb_path/test_kartuli_utf8_3.phpt
new file mode 100644
index 0000000000..46077258d1
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_kartuli_utf8_3.phpt
@@ -0,0 +1,40 @@
+--TEST--
+Kartuli UTF-8 cmd test
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+
+?>
+--FILE--
+<?php
+/*
+#vim: set fileencoding=cp874
+#vim: set encoding=cp874
+*/
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+
+$item = "ქართველები55";
+$prefix = create_data("file_kartuli", $item);
+$fn = $prefix . DIRECTORY_SEPARATOR . $item;
+
+var_dump($fn);
+var_dump(touch($fn));
+var_dump(file_exists($fn));
+system("dir /b " . $fn);
+
+remove_data("file_kartuli");
+
+?>
+===DONE===
+--EXPECTF--
+string(%d) "%s\ქართველები55"
+bool(true)
+bool(true)
+ქართველები55
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_long_path_0.phpt b/ext/standard/tests/file/windows_mb_path/test_long_path_0.phpt
new file mode 100644
index 0000000000..6bc28110e0
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_long_path_0.phpt
@@ -0,0 +1,47 @@
+--TEST--
+Basic long path test
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts("mbstring");
+
+?>
+--FILE--
+<?php
+
+$p = "";
+$s = str_repeat('a', 50);
+$how_many = 32;
+
+for ($i = 0; $i < $how_many; $i++) {
+ $p .= "$s";
+ $p .= DIRECTORY_SEPARATOR;
+}
+
+$p = realpath(dirname(__FILE__)) . DIRECTORY_SEPARATOR . $p;
+
+echo strlen($p), "\n", $p, "\n";
+
+
+var_dump(mkdir($p, 0777, true));
+var_dump(file_exists($p));
+
+for ($i = 0; $i < $how_many; $i++) {
+ $p0 = substr($p, 0, strlen($p) - $i*51);
+ rmdir($p0);
+}
+
+var_dump(file_exists(realpath(dirname(__FILE__)) . DIRECTORY_SEPARATOR . $s));
+
+?>
+===DONE===
+--EXPECTF--
+%d%d%d%d
+%s\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
+bool(true)
+bool(true)
+bool(false)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_long_path_1.phpt b/ext/standard/tests/file/windows_mb_path/test_long_path_1.phpt
new file mode 100644
index 0000000000..94518e7a65
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_long_path_1.phpt
@@ -0,0 +1,55 @@
+--TEST--
+Basic long path test with file I/O
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+
+?>
+--FILE--
+<?php
+
+$p = "";
+$s = str_repeat('a', 50);
+$how_many = 32;
+
+
+for ($i = 0; $i < $how_many; $i++) {
+ $p .= "$s";
+ $p .= DIRECTORY_SEPARATOR;
+}
+
+$p = realpath(dirname(__FILE__)) . DIRECTORY_SEPARATOR . $p;
+
+echo strlen($p), "\n", $p, "\n";
+
+
+var_dump(mkdir($p, 0777, true));
+var_dump(file_exists($p));
+
+$p7 = $p . "hello.txt";
+
+file_put_contents($p7, "hello");
+var_dump(file_get_contents($p7));
+
+unlink($p7);
+
+for ($i = 0; $i < $how_many; $i++) {
+ $p0 = substr($p, 0, strlen($p) - $i*51);
+ rmdir($p0);
+}
+
+var_dump(file_exists(realpath(dirname(__FILE__)) . DIRECTORY_SEPARATOR . $s));
+
+?>
+===DONE===
+--EXPECTF--
+%d%d%d%d
+%s\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
+bool(true)
+bool(true)
+string(5) "hello"
+bool(false)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_long_path_2.phpt b/ext/standard/tests/file/windows_mb_path/test_long_path_2.phpt
new file mode 100644
index 0000000000..3be1850c5a
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_long_path_2.phpt
@@ -0,0 +1,64 @@
+--TEST--
+Basic long path test with file I/O, multibyte path and realpath() check
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts("mbstring");
+
+?>
+--FILE--
+<?php
+$p = "";
+$s = str_repeat('x', 50) . "ü";
+$how_many = 32;
+
+
+for ($i = 0; $i < $how_many; $i++) {
+ $p .= "$s";
+ $p .= DIRECTORY_SEPARATOR;
+}
+
+/* path doesn't exist at this point! */
+$p = realpath(dirname(__FILE__)) . DIRECTORY_SEPARATOR . $p;
+
+echo strlen($p), "\n", $p, "\n";
+
+var_dump(mkdir($p, 0777, true));
+var_dump(file_exists($p));
+
+/* path exists now, ensure realpath() works! */
+$p2 = realpath($p);
+var_dump($p2);
+/* realpath() will cut off the trailing slash */
+var_dump(substr($p, 0, strlen($p) - 1) == $p2);
+
+$p7 = $p . "hello.txt";
+
+var_dump(file_put_contents($p7, "hello"));
+var_dump(file_get_contents($p7));
+
+unlink($p7);
+
+for ($i = 0; $i < $how_many; $i++) {
+ $p0 = substr($p, 0, strlen($p) - $i*(strlen($s) + 1));
+ rmdir($p0);
+}
+
+var_dump(file_exists(realpath(dirname(__FILE__)) . DIRECTORY_SEPARATOR . $s));
+
+?>
+===DONE===
+--EXPECTF--
+%d%d%d%d
+%s\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\
+bool(true)
+bool(true)
+string(%d%d%d%d) "%s\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxü"
+bool(true)
+int(5)
+string(5) "hello"
+bool(false)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_long_path_bug30730.phpt b/ext/standard/tests/file/windows_mb_path/test_long_path_bug30730.phpt
new file mode 100644
index 0000000000..5ea66058ea
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_long_path_bug30730.phpt
@@ -0,0 +1,45 @@
+--TEST--
+Bug #30730 Filename path length limit broken on NTFS volume, using rename
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+
+?>
+--FILE--
+<?php
+
+$dir = dirname(__FILE__) . DIRECTORY_SEPARATOR . "test_bug30730";
+$file = $dir . DIRECTORY_SEPARATOR . "test_file";
+
+var_dump(mkdir($dir));
+
+// Create a file in that directory
+
+$fp = fopen($file, 'wb+');
+fclose($fp);
+
+// Rename that directory in order that the file full path will be long enough to trigger the bug
+
+$dest_dir =str_pad($dir, 200, '0');
+$dest_file = $dest_dir . DIRECTORY_SEPARATOR . "test_file";
+
+var_dump(rename($dir, $dest_dir));
+
+var_dump(file_exists($dest_file));
+
+var_dump(unlink($dest_file));
+var_dump(rmdir($dest_dir));
+
+?>
+===DONE===
+--EXPECTF--
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+===DONE===
+
diff --git a/ext/standard/tests/file/windows_mb_path/test_long_path_bug70943.phpt b/ext/standard/tests/file/windows_mb_path/test_long_path_bug70943.phpt
new file mode 100644
index 0000000000..6eb484fcd0
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_long_path_bug70943.phpt
@@ -0,0 +1,32 @@
+--TEST--
+Bug #70943 fopen() can't open a file if path is 259 characters long
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+
+?>
+--FILE--
+<?php
+// Generates a sample file whose path is exactly 259 characters long
+$testFile = dirname(__FILE__) . DIRECTORY_SEPARATOR . str_repeat("a", 254 - strlen(dirname(__FILE__))).".dat";
+echo "Generating a file with a path length of ".strlen($testFile)." characters...\r\n";
+touch($testFile);
+
+echo "Opening file... ";
+if ($fp = fopen($testFile, "r")) {
+ fclose($fp);
+ echo "OK", "\n";
+}
+
+unlink($testFile);
+
+?>
+===DONE===
+--EXPECTF--
+Generating a file with a path length of 259 characters...
+Opening file... OK
+===DONE===
+
diff --git a/ext/standard/tests/file/windows_mb_path/test_long_path_bug71103.phpt b/ext/standard/tests/file/windows_mb_path/test_long_path_bug71103.phpt
new file mode 100644
index 0000000000..4b5bd445f4
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_long_path_bug71103.phpt
@@ -0,0 +1,65 @@
+--TEST--
+Bug #71103 file_exists and is_readable fail silently
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+
+?>
+--FILE--
+<?php
+
+$base = dirname(__FILE__);
+$d = $base . '\\dev\\http\\tproj\\app\\cache\\dev_old\\annotations\\72';
+
+$foo = $d . '\\5b53796d666f6e795c42756e646c655c5477696742756e646c655c436f6e74726f6c6c65725c457863657074696f6e436f6e74726f6c6c657223676574416e64436c65616e4f7574707574427566666572696e67405b416e6e6f745d5d5b.doctrinecache.data';
+$bar = $d . '\\5b53796d666f6e795c42756e646c655c5477696742756e646c655c436f6e74726f6c6c65725c457863657074696f6e436f6e74726f6c6c657223676574416e64436c65616e4f7574707574427566666572696e67405b416e6e6f745d5d5b315d.doctrinecache.data';
+
+mkdir($d, NULL, true);
+
+foreach (array($foo, $bar) as $f) {
+ touch($f);
+
+ $foo_obj = new \SplFileInfo($f);
+ var_dump(
+ $f,
+ strlen($f) > 260, /* exceeds _MAX_PATH */
+ file_exists($f),
+ file_exists($foo_obj),
+ is_readable($f),
+ is_readable($foo_obj),
+ is_writable($f),
+ is_writable($foo_obj)
+ );
+
+ unlink($f);
+}
+
+$p = $d;
+do {
+ rmdir($p);
+ $p = dirname($p);
+} while ($p != $base);
+
+?>
+===DONE===
+--EXPECTF--
+string(%d) "%s\dev\http\tproj\app\cache\dev_old\annotations\72\5b53796d666f6e795c42756e646c655c5477696742756e646c655c436f6e74726f6c6c65725c457863657074696f6e436f6e74726f6c6c657223676574416e64436c65616e4f7574707574427566666572696e67405b416e6e6f745d5d5b.doctrinecache.data"
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+string(%d) "%s\dev\http\tproj\app\cache\dev_old\annotations\72\5b53796d666f6e795c42756e646c655c5477696742756e646c655c436f6e74726f6c6c65725c457863657074696f6e436f6e74726f6c6c657223676574416e64436c65616e4f7574707574427566666572696e67405b416e6e6f745d5d5b315d.doctrinecache.data"
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_readdir_mb_names.phpt b/ext/standard/tests/file/windows_mb_path/test_readdir_mb_names.phpt
new file mode 100644
index 0000000000..1033c0e3e6
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_readdir_mb_names.phpt
@@ -0,0 +1,79 @@
+--TEST--
+Test readdir() with a dir for multibyte filenames
+--SKIPIF--
+<?php
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+
+?>
+--FILE--
+<?php
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+$prefix = create_data("mb_names");
+$content = "";
+create_verify_file($prefix, "českýtestování.inc", $content);
+create_verify_file($prefix, "Röd_Statistics.txt", $content);
+create_verify_file($prefix, "š.txt", "");
+create_verify_file($prefix, "tschüß", $content);
+create_verify_file($prefix, "Voláçao", "hola");
+create_verify_file($prefix, "Ελλάδα.txt", "");
+create_verify_file($prefix, "привет", "opened an utf8 filename for reading");
+create_verify_file($prefix, "テストマルチバイト・パス", $content);
+create_verify_file($prefix, "測試多字節路徑", $content);
+create_verify_file($prefix, "żółć.txt", $content);
+create_verify_dir($prefix, "tschüß3");
+create_verify_dir($prefix, "Voláçao3");
+create_verify_dir($prefix, "привет3");
+create_verify_dir($prefix, "テストマルチバイト・パス42");
+create_verify_dir($prefix, "測試多字節路徑5");
+create_verify_dir($prefix, "żółć");
+
+
+$dirw = $prefix . DIRECTORY_SEPARATOR;
+
+$old_cp = get_active_cp();
+set_active_cp(65001);
+
+if (is_dir($dirw)) {
+ if ($dh = opendir($dirw)) {
+ while (($file = readdir($dh)) !== false) {
+ echo "filename: $file : filetype: " . filetype($dirw . $file) . "\n";
+ }
+ closedir($dh);
+ }
+} else {
+ echo "is_dir failed\n";
+}
+set_active_cp($old_cp);
+
+remove_data("mb_names");
+
+?>
+===DONE===
+--EXPECTF--
+Active code page: 65001
+filename: . : filetype: dir
+filename: .. : filetype: dir
+filename: Röd_Statistics.txt : filetype: file
+filename: tschüß : filetype: file
+filename: tschüß3 : filetype: dir
+filename: Voláçao : filetype: file
+filename: Voláçao3 : filetype: dir
+filename: českýtestování.inc : filetype: file
+filename: š.txt : filetype: file
+filename: żółć : filetype: dir
+filename: żółć.txt : filetype: file
+filename: Ελλάδα.txt : filetype: file
+filename: привет : filetype: file
+filename: привет3 : filetype: dir
+filename: テストマルチバイト・パス : filetype: file
+filename: テストマルチバイト・パス42 : filetype: dir
+filename: 測試多字節路徑 : filetype: file
+filename: 測試多字節路徑5 : filetype: dir
+Active code page: %d
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/test_rename_mb_names.phpt b/ext/standard/tests/file/windows_mb_path/test_rename_mb_names.phpt
new file mode 100644
index 0000000000..da87fe9f8b
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/test_rename_mb_names.phpt
@@ -0,0 +1,74 @@
+--TEST--
+Test rename() with a dir for multibyte filenames
+
+--SKIPIF--
+
+<?php
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+
+skip_if_not_win();
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+skip_if_no_required_exts();
+
+
+?>
+
+--FILE--
+
+<?php
+
+
+include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
+
+
+$prefix = create_data("file2_mb");
+
+$fw_orig = $prefix . DIRECTORY_SEPARATOR . "Ελλάδα.txt";
+
+
+
+$fw_copied = $prefix . DIRECTORY_SEPARATOR . "Ελλάδα_copy.txt";
+
+
+$fw_renamed = $prefix . DIRECTORY_SEPARATOR . "測試多字節路徑17.txt";
+
+
+
+$old_cp = get_active_cp();
+
+set_active_cp(65001);
+
+
+
+
+
+var_dump(copy($fw_orig, $fw_copied));
+var_dump(get_basename_with_cp($fw_copied, get_active_cp(), false));
+var_dump(file_exists($fw_copied));
+
+var_dump(rename($fw_copied, $fw_renamed));
+var_dump(get_basename_with_cp($fw_renamed, get_active_cp(), false));
+var_dump(file_exists($fw_renamed));
+
+var_dump(unlink($fw_renamed));
+
+set_active_cp($old_cp);
+
+remove_data("file2_mb");
+
+?>
+===DONE===
+
+--EXPECTF--
+Active code page: 65001
+bool(true)
+string(21) "Ελλάδα_copy.txt"
+bool(true)
+bool(true)
+string(27) "測試多字節路徑17.txt"
+bool(true)
+bool(true)
+Active code page: %d
+===DONE===
diff --git a/ext/standard/tests/file/windows_mb_path/util.inc b/ext/standard/tests/file/windows_mb_path/util.inc
new file mode 100644
index 0000000000..57fd8f9427
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/util.inc
@@ -0,0 +1,149 @@
+<?php
+
+function get_active_cp($kind = "")
+{
+ if (version_compare(PHP_VERSION, '7.1', '<')) {
+ $s = exec("chcp");
+ preg_match(",.*: (\d+),", $s, $m);
+
+ return $m[1];
+ } else {
+ return sapi_windows_cp_get($kind);
+ }
+}
+
+function set_active_cp($cp, $echo = true)
+{
+ if (version_compare(PHP_VERSION, '7.1', '<')) {
+ $ret = exec("chcp $cp");
+ } else {
+ if (!sapi_windows_cp_set($cp)) {
+ echo "Failed to set cp $cp\n";
+ return;
+ }
+
+ if ($echo) echo "Active code page: ", get_active_cp(), "\n";
+ }
+}
+
+function get_basename_with_cp($path, $cp, $echo = true)
+{
+ $old_cp = get_active_cp();
+ set_active_cp($cp, $echo);
+
+ if ($echo) echo "getting basename of $path\n";
+
+ $cmd = "powershell -command \"Get-Item -Path '$path' | Format-Table -HideTableHeaders Name\"";
+ $out = trim(shell_exec($cmd));
+
+ if ($echo) var_dump($out, $out == basename($path));
+ if ($echo) var_dump(realpath($path));
+
+ set_active_cp($old_cp, $echo);
+
+ return $out;
+}
+
+function skip_if_wrong_cp($cp, $kind = "")
+{
+ if (get_active_cp($kind) != $cp) {
+ die("skip this test expect codepage $cp");
+ }
+}
+
+function skip_if_no_required_exts()
+{
+ $exts = func_get_args();
+ $exts[] = "iconv";
+
+ foreach ($exts as $ext) {
+ if (!extension_loaded($ext)) {
+ die("skip $ext is not loaded");
+ }
+ }
+}
+
+function skip_if_not_win()
+{
+ if(substr(PHP_OS, 0, 3) != 'WIN' ) {
+ die('skip windows only test');
+ }
+}
+
+function create_verify_file($prefix, $basename, $content = "", $cp = 65001)
+{
+ $full = $prefix . DIRECTORY_SEPARATOR . $basename;
+
+ if (!touch($full)) {
+ echo "failed to touch create $full\n";
+ return;
+ }
+
+ $now = get_basename_with_cp($full, $cp, false);
+ if ($now !== $basename) {
+ echo "expected '$basename', got '$now'\n";
+ return;
+ }
+
+ if ($content) {
+ file_put_contents($full, $content);
+ }
+}
+
+function create_verify_dir($prefix, $basename, $cp = 65001)
+{
+ $full = $prefix . DIRECTORY_SEPARATOR . $basename;
+
+ if (!mkdir($full) || get_basename_with_cp($full, $cp, false) !== $basename) {
+ echo "failed to create dir '$full'\n";
+ }
+}
+
+function remove_data($id, $dir = NULL)
+{
+ if (!$dir) {
+ $dir = dirname(__FILE__) . DIRECTORY_SEPARATOR . $id;
+ }
+
+ if (is_dir($dir)) {
+ $objects = scandir($dir);
+ foreach ($objects as $object) {
+ if ($object != "." && $object != "..") {
+ if (filetype($dir . DIRECTORY_SEPARATOR . $object) == "dir")
+ remove_data($id, $dir . DIRECTORY_SEPARATOR . $object);
+ else
+ unlink($dir . DIRECTORY_SEPARATOR . $object);
+ }
+ }
+ reset($objects);
+ rmdir($dir);
+ }
+}
+
+function create_data($id, $item = "", $cp = 65001, $utf8 = true)
+{
+ if ($utf8) {
+ /* Keep this file ASCII, so zend.multibyte related stuff can be tasted as well. */
+ include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util_utf8.inc";
+ return create_data_from_utf8($id, $item, $cp);
+ } else {
+
+ $prefix = dirname(__FILE__) . DIRECTORY_SEPARATOR . $id;
+
+ if (!is_dir($prefix)) {
+ mkdir($prefix);
+ }
+
+ if (0 === strpos($id, "dir")) {
+ create_verify_dir($prefix, $item, $cp);
+ } else if (0 === strpos($id, "file")) {
+ /* a bit unhandy, but content can be put from outside, if needed */
+ create_verify_file($prefix, $item, "dummy content", $cp);
+ } else {
+ echo "Item has either to start with \"dir\" or \"file\"";
+ }
+ }
+
+ return $prefix;
+}
+
diff --git a/ext/standard/tests/file/windows_mb_path/util_utf8.inc b/ext/standard/tests/file/windows_mb_path/util_utf8.inc
new file mode 100644
index 0000000000..8d7009f91c
--- /dev/null
+++ b/ext/standard/tests/file/windows_mb_path/util_utf8.inc
@@ -0,0 +1,95 @@
+<?php
+
+function create_data_from_utf8($id, $item = "", $cp = 65001)
+{
+ $prefix = dirname(__FILE__) . DIRECTORY_SEPARATOR . $id;
+
+ if (!is_dir($prefix)) {
+ mkdir($prefix);
+ }
+
+ /* Using a UTF-8 encoded filenames if !zend.multibyte, otherwise files in retrospective charset. */
+ switch ($id) {
+
+ case "file_mb3":
+ $content = "<?php
+
+echo \"hello there from the include\n\";";
+ create_verify_file($prefix, "českýtestování.inc", $content);
+ break;
+
+ case "bug71509":
+ $content = "that's my file with swedish filename
+";
+ create_verify_file($prefix, $item, $content);
+ break;
+
+ case "file_mb4":
+ create_verify_file($prefix, "š.txt", "");
+ break;
+
+ case "file_cp1252":
+ $content = "hallo
+";
+ create_verify_file($prefix, "tschüß", $content);
+ break;
+
+ case "file2_cp1252":
+ create_verify_file($prefix, $item, "hola");
+ break;
+
+ case "file2_mb":
+ create_verify_file($prefix, "Ελλάδα.txt", "");
+ break;
+
+ case "file_cp1251":
+ create_verify_file($prefix, $item, "opened an utf8 filename for reading", $cp);
+ break;
+
+ case "file_cp1255":
+ case "file_cp1254":
+ case "file_cp1253":
+ case "file_kartuli":
+ case "file_cp1250":
+ case "file_cp1256":
+ case "file_cp874":
+ case "file_big5":
+ case "file_cp936":
+ case "file_cp932":
+ case "file_eucjp":
+ case "file_mb":
+ $content = "reading file wihh multibyte filename
+";
+ create_verify_file($prefix, $item, $content, $cp);
+ break;
+
+ case "dir_mb":
+ create_verify_dir($prefix, "テストマルチバイト・パス42");
+ break;
+
+ case "dir_cp1255":
+ case "dir_cp1254":
+ case "dir_cp1253":
+ case "dir_kartuli":
+ case "dir_cp1250":
+ case "dir_cp1256":
+ case "dir_cp874":
+ case "dir_cp932":
+ case "dir_cp936":
+ case "dir_eucjp":
+ case "dir2_cp1252":
+ case "dir_cp1252":
+ case "dir_cp1251":
+ case "dir_big5":
+ case "dir":
+ create_verify_dir($prefix, $item, $cp);
+ break;
+
+ case "file":
+ create_verify_file($prefix, $item, "", $cp);
+ break;
+ }
+
+ return $prefix;
+}
+
diff --git a/ext/standard/tests/general_functions/bug41970.phpt b/ext/standard/tests/general_functions/bug41970.phpt
index 4bce3ac7f7..6f05137afc 100644
--- a/ext/standard/tests/general_functions/bug41970.phpt
+++ b/ext/standard/tests/general_functions/bug41970.phpt
@@ -14,13 +14,13 @@ echo "Done\n";
?>
--EXPECTF--
Warning: Parameter 1 to sort() expected to be a reference, value given in %sbug41970.php on line 5
-NULL
+bool(true)
Warning: strlen() expects parameter 1 to be string, array given in %sbug41970.php on line 6
NULL
Warning: Parameter 1 to sort() expected to be a reference, value given in %sbug41970.php on line 7
-NULL
+bool(true)
Warning: strlen() expects parameter 1 to be string, array given in %sbug41970.php on line 8
NULL
diff --git a/ext/standard/tests/general_functions/bug44394.phpt b/ext/standard/tests/general_functions/bug44394.phpt
index 26351a28a1..4ecd8a1123 100644
--- a/ext/standard/tests/general_functions/bug44394.phpt
+++ b/ext/standard/tests/general_functions/bug44394.phpt
@@ -1,5 +1,8 @@
--TEST--
Bug #44394 (Last two bytes missing from output)
+--INI--
+session.trans_sid_tags="a=href,area=href,frame=src,form="
+url_rewriter.tags="a=href,area=href,frame=src,form="
--FILE--
<?php
diff --git a/ext/standard/tests/general_functions/bug44394_2.phpt b/ext/standard/tests/general_functions/bug44394_2.phpt
index 3ca53974e3..c2d638be7e 100644
--- a/ext/standard/tests/general_functions/bug44394_2.phpt
+++ b/ext/standard/tests/general_functions/bug44394_2.phpt
@@ -5,6 +5,8 @@ Bug #44394 (Last two bytes missing from output) with session.use_trans_id
--INI--
session.name=PHPSESSID
session.use_only_cookies=0
+session.trans_sid_tags="a=href,area=href,frame=src,form="
+url_rewriter.tags="a=href,area=href,frame=src,form="
--FILE--
<?php
@@ -32,4 +34,4 @@ foreach (glob(__DIR__ . '/sess_*') as $filename) {
}
?>
--EXPECTF--
-<a href='a?q=1&PHPSESSID=%s&a=b'>asd</a>
+<a href='a?q=1&a=b&PHPSESSID=%s'>asd</a>
diff --git a/ext/standard/tests/general_functions/bug72920.phpt b/ext/standard/tests/general_functions/bug72920.phpt
new file mode 100644
index 0000000000..b5ca4760c3
--- /dev/null
+++ b/ext/standard/tests/general_functions/bug72920.phpt
@@ -0,0 +1,15 @@
+--TEST--
+Bug #72920 (Accessing a private constant using constant() creates an exception AND warning)
+--FILE--
+<?php
+class Foo {
+ private const C1 = "a";
+}
+
+try {
+ var_dump(constant('Foo::C1'));
+} catch (Error $e) {
+ var_dump($e->getMessage());
+}
+--EXPECT--
+string(35) "Cannot access private const Foo::C1"
diff --git a/ext/standard/tests/general_functions/escapeshellarg_basic-win32-mb.phpt b/ext/standard/tests/general_functions/escapeshellarg_basic-win32-mb.phpt
new file mode 100644
index 0000000000..f85f4a4195
--- /dev/null
+++ b/ext/standard/tests/general_functions/escapeshellarg_basic-win32-mb.phpt
@@ -0,0 +1,31 @@
+--TEST--
+Test escapeshellarg() function: basic test with UTF-8 strings
+--SKIPIF--
+<?php
+if( substr(PHP_OS, 0, 3) != "WIN" )
+ die("skip.. only for Windows");
+?>
+--FILE--
+<?php
+/* Prototype : string escapeshellarg ( string $arg )
+ * Description: Escape a string to be used as a shell argument.
+ * Source code: ext/standard/exec.c
+ * Alias to functions:
+ */
+
+echo "Simple testcase for escapeshellarg() function\n";
+
+var_dump(escapeshellarg("テストマルチバイ'ト・パス"));
+var_dump(escapeshellarg("測試多字\'節路徑"));
+var_dump(escapeshellarg("%füße"));
+var_dump(escapeshellarg("!шницель"));
+
+echo "Done\n";
+?>
+--EXPECT--
+Simple testcase for escapeshellarg() function
+string(39) ""テストマルチバイ'ト・パス""
+string(25) ""測試多字\'節路徑""
+string(9) "" füße""
+string(17) "" шницель""
+Done
diff --git a/ext/standard/tests/general_functions/floatval.phpt b/ext/standard/tests/general_functions/floatval.phpt
index 9b7a3281e4..d7bdffd6ae 100644
--- a/ext/standard/tests/general_functions/floatval.phpt
+++ b/ext/standard/tests/general_functions/floatval.phpt
@@ -155,6 +155,10 @@ float(5000000)
float(-5000000)
*** Testing floatval() on non floating types ***
+
+Notice: A non well formed numeric value encountered in %s on line 69
+
+Notice: A non well formed numeric value encountered in %s on line 70
float(-2147483648)
float(2147483648)
float(%d)
diff --git a/ext/standard/tests/general_functions/floatval_variation1.phpt b/ext/standard/tests/general_functions/floatval_variation1.phpt
index 83925b89b0..aa808cfba1 100644
--- a/ext/standard/tests/general_functions/floatval_variation1.phpt
+++ b/ext/standard/tests/general_functions/floatval_variation1.phpt
@@ -52,6 +52,11 @@ foreach ($not_float_types as $key => $type ) {
?>
===DONE===
--EXPECTF--
+
+Notice: A non well formed numeric value encountered in %s on line %d
+
+Notice: A non well formed numeric value encountered in %s on line %d
+
*** Testing floatval() on non floating types ***
-- Iteration : -2147483648 --
@@ -151,4 +156,4 @@ float(0)
-- Iteration : null --
float(0)
-===DONE=== \ No newline at end of file
+===DONE===
diff --git a/ext/standard/tests/general_functions/getenv.phpt b/ext/standard/tests/general_functions/getenv.phpt
new file mode 100644
index 0000000000..006378a337
--- /dev/null
+++ b/ext/standard/tests/general_functions/getenv.phpt
@@ -0,0 +1,16 @@
+--TEST--
+getenv() basic tests
+--ENV--
+FOO=bar
+--FILE--
+<?php
+
+var_dump(getenv("FOO"));
+var_dump(getenv()["FOO"]);
+
+echo "Done\n";
+?>
+--EXPECTF--
+string(3) "bar"
+string(3) "bar"
+Done
diff --git a/ext/standard/tests/general_functions/getopt_006.phpt b/ext/standard/tests/general_functions/getopt_006.phpt
new file mode 100644
index 0000000000..875c404921
--- /dev/null
+++ b/ext/standard/tests/general_functions/getopt_006.phpt
@@ -0,0 +1,15 @@
+--TEST--
+getopt#006 (optind #1)
+--ARGS--
+-a 1 -b 2 test
+--INI--
+register_argc_argv=On
+variables_order=GPS
+--FILE--
+<?php
+ $optind = null;
+ getopt('a:b:', [], $optind);
+ var_dump($optind);
+?>
+--EXPECT--
+int(5)
diff --git a/ext/standard/tests/general_functions/getopt_007.phpt b/ext/standard/tests/general_functions/getopt_007.phpt
new file mode 100644
index 0000000000..ae96da5c4b
--- /dev/null
+++ b/ext/standard/tests/general_functions/getopt_007.phpt
@@ -0,0 +1,15 @@
+--TEST--
+getopt#007 (optind #2)
+--ARGS--
+-a 1 -b 2 -- -c nope test
+--INI--
+register_argc_argv=On
+variables_order=GPS
+--FILE--
+<?php
+ $optind = null;
+ getopt('a:b:', [], $optind);
+ var_dump($optind);
+?>
+--EXPECT--
+int(6)
diff --git a/ext/standard/tests/general_functions/getopt_008.phpt b/ext/standard/tests/general_functions/getopt_008.phpt
new file mode 100644
index 0000000000..c6bacf4e56
--- /dev/null
+++ b/ext/standard/tests/general_functions/getopt_008.phpt
@@ -0,0 +1,15 @@
+--TEST--
+getopt#008 (optind #3)
+--ARGS--
+-a 1 -b 2
+--INI--
+register_argc_argv=On
+variables_order=GPS
+--FILE--
+<?php
+ $optind = null;
+ getopt('a:b:', [], $optind);
+ var_dump($optind);
+?>
+--EXPECT--
+int(5)
diff --git a/ext/standard/tests/general_functions/getopt_009.phpt b/ext/standard/tests/general_functions/getopt_009.phpt
new file mode 100644
index 0000000000..26da1cc3ef
--- /dev/null
+++ b/ext/standard/tests/general_functions/getopt_009.phpt
@@ -0,0 +1,15 @@
+--TEST--
+getopt#009 (optind #4)
+--ARGS--
+messup -a 1 -b 2
+--INI--
+register_argc_argv=On
+variables_order=GPS
+--FILE--
+<?php
+ $optind = null;
+ getopt('a:b:', [], $optind);
+ var_dump($optind);
+?>
+--EXPECT--
+int(1)
diff --git a/ext/standard/tests/general_functions/gettype_settype_variation2.phpt b/ext/standard/tests/general_functions/gettype_settype_variation2.phpt
index 2242952769..18b05b6a97 100644
--- a/ext/standard/tests/general_functions/gettype_settype_variation2.phpt
+++ b/ext/standard/tests/general_functions/gettype_settype_variation2.phpt
@@ -282,7 +282,7 @@ string(7) "integer"
-- Iteration 18 --
string(6) "string"
bool(true)
-int(1)
+int(100)
string(7) "integer"
-- Iteration 19 --
string(6) "string"
@@ -297,7 +297,7 @@ string(7) "integer"
-- Iteration 21 --
string(6) "string"
bool(true)
-int(-1)
+int(0)
string(7) "integer"
-- Iteration 22 --
string(6) "string"
@@ -312,7 +312,7 @@ string(7) "integer"
-- Iteration 24 --
string(6) "string"
bool(true)
-int(1)
+int(100)
string(7) "integer"
-- Iteration 25 --
string(6) "string"
@@ -327,7 +327,7 @@ string(7) "integer"
-- Iteration 27 --
string(6) "string"
bool(true)
-int(-1)
+int(0)
string(7) "integer"
-- Iteration 28 --
string(6) "string"
@@ -687,7 +687,7 @@ string(7) "integer"
-- Iteration 18 --
string(6) "string"
bool(true)
-int(1)
+int(100)
string(7) "integer"
-- Iteration 19 --
string(6) "string"
@@ -702,7 +702,7 @@ string(7) "integer"
-- Iteration 21 --
string(6) "string"
bool(true)
-int(-1)
+int(0)
string(7) "integer"
-- Iteration 22 --
string(6) "string"
@@ -717,7 +717,7 @@ string(7) "integer"
-- Iteration 24 --
string(6) "string"
bool(true)
-int(1)
+int(100)
string(7) "integer"
-- Iteration 25 --
string(6) "string"
@@ -732,7 +732,7 @@ string(7) "integer"
-- Iteration 27 --
string(6) "string"
bool(true)
-int(-1)
+int(0)
string(7) "integer"
-- Iteration 28 --
string(6) "string"
diff --git a/ext/standard/tests/general_functions/intval_binary_prefix.phpt b/ext/standard/tests/general_functions/intval_binary_prefix.phpt
new file mode 100644
index 0000000000..af49515396
--- /dev/null
+++ b/ext/standard/tests/general_functions/intval_binary_prefix.phpt
@@ -0,0 +1,119 @@
+--TEST--
+Test intval() function with "0b" string prefix
+--SKIPIF--
+--FILE--
+<?php
+
+$isspaceChars = " \t\n\r\f\v";
+
+$goodInputs = [
+ '0b1111111111111111111111111111111',
+ '+0b1111111111111111111111111111111',
+ '-0b1111111111111111111111111111111',
+ $isspaceChars . '0b1111111111111111111111111111111',
+ $isspaceChars . '+0b1111111111111111111111111111111',
+ $isspaceChars . '-0b1111111111111111111111111111111',
+ '0b',
+ '0B',
+ '0B1',
+ '0b000',
+ '0b001',
+ '0b00100',
+ '0b1 1'
+];
+
+$badInputs = [
+ 'b101',
+ '0b00200',
+ '--0b123',
+ '++0b123',
+ '0bb123',
+ '0 b123',
+];
+
+print "--- Good Inputs - Base = 0 ---\n";
+
+foreach ($goodInputs as $input) {
+ var_dump(
+ intval($input, 0)
+ );
+}
+
+print "--- Good Inputs - Base = 2 ---\n";
+
+foreach ($goodInputs as $input) {
+ var_dump(
+ intval($input, 2)
+ );
+}
+
+print "--- Good Inputs - Base = default ---\n";
+
+foreach ($goodInputs as $input) {
+ var_dump(
+ intval($input)
+ );
+}
+
+print "--- Bad Inputs - Base = 0 ---\n";
+
+foreach ($badInputs as $input) {
+ var_dump(
+ intval($input, 0)
+ );
+}
+
+print '--- Done ---';
+
+?>
+--EXPECTF--
+--- Good Inputs - Base = 0 ---
+int(2147483647)
+int(2147483647)
+int(-2147483647)
+int(2147483647)
+int(2147483647)
+int(-2147483647)
+int(0)
+int(0)
+int(1)
+int(0)
+int(1)
+int(4)
+int(1)
+--- Good Inputs - Base = 2 ---
+int(2147483647)
+int(2147483647)
+int(-2147483647)
+int(2147483647)
+int(2147483647)
+int(-2147483647)
+int(0)
+int(0)
+int(1)
+int(0)
+int(1)
+int(4)
+int(1)
+--- Good Inputs - Base = default ---
+int(0)
+int(0)
+int(0)
+int(0)
+int(0)
+int(0)
+int(0)
+int(0)
+int(0)
+int(0)
+int(0)
+int(0)
+int(0)
+--- Bad Inputs - Base = 0 ---
+int(0)
+int(0)
+int(0)
+int(0)
+int(0)
+int(0)
+--- Done ---
diff --git a/ext/standard/tests/general_functions/is_iterable.phpt b/ext/standard/tests/general_functions/is_iterable.phpt
new file mode 100644
index 0000000000..e0822d7eb6
--- /dev/null
+++ b/ext/standard/tests/general_functions/is_iterable.phpt
@@ -0,0 +1,24 @@
+--TEST--
+Test is_iterable() function
+--FILE--
+<?php
+
+function gen() {
+ yield;
+}
+
+var_dump(is_iterable([1, 2, 3]));
+var_dump(is_iterable(new ArrayIterator([1, 2, 3])));
+var_dump(is_iterable(gen()));
+var_dump(is_iterable(1));
+var_dump(is_iterable(3.14));
+var_dump(is_iterable(new stdClass()));
+
+?>
+--EXPECT--
+bool(true)
+bool(true)
+bool(true)
+bool(false)
+bool(false)
+bool(false)
diff --git a/ext/standard/tests/general_functions/output_add_rewrite_var_basic1.phpt b/ext/standard/tests/general_functions/output_add_rewrite_var_basic1.phpt
new file mode 100644
index 0000000000..60bc61395f
--- /dev/null
+++ b/ext/standard/tests/general_functions/output_add_rewrite_var_basic1.phpt
@@ -0,0 +1,122 @@
+--TEST--
+Test output_add_rewrite_var() function basic feature
+--SKIPIF--
+<?php if (!extension_loaded("session")) die("skip session support is not available"); ?>
+--INI--
+session.trans_sid_tags="a=href,area=href,frame=src,form="
+url_rewriter.tags="a=href,area=href,frame=src,form="
+--FILE--
+<?php
+ ob_start();
+// Common setting
+ini_set('url_rewriter.hosts', 'php.net,www.php.net');
+ini_set('session.trans_sid_hosts', 'php.net,www.php.net');
+ini_set('session.use_only_cookies', 0);
+ini_set('session.use_cookies', 0);
+ini_set('session.use_strict_mode', 0);
+session_id('testid');
+
+output_add_rewrite_var('<name>', '<value>');
+?>
+Without session
+<a href=""> </a>
+<a href="./foo.php"> </a>
+<a href="//php.net/foo.php"> </a>
+<a href="http://php.net/foo.php"> </a>
+<a href="bad://php.net/foo.php"> </a>
+<a href="//www.php.net/foo.php"> </a>
+
+<form method="get"> </form>
+<form action="./foo.php" method="get">
+<form action="//php.net/bar.php" method="get">
+<form action="http://php.net/bar.php" method="get">
+<form action="bad://php.net/bar.php" method="get">
+<form action="//www.php.net/bar.php" method="get">
+
+<?php
+ini_set('session.use_trans_sid', 0);
+session_start();
+output_add_rewrite_var('<name>', '<value>');
+?>
+Test use_trans_sid=0
+<a href=""> </a>
+<a href="./foo.php"> </a>
+<a href="//php.net/foo.php"> </a>
+<a href="http://php.net/foo.php"> </a>
+<a href="bad://php.net/foo.php"> </a>
+<a href="//www.php.net/foo.php"> </a>
+
+<form method="get"> </form>
+<form action="./foo.php" method="get"> </form>
+<form action="//php.net/bar.php" method="get"> </form>
+<form action="http://php.net/bar.php" method="get"> </form>
+<form action="bad://php.net/bar.php" method="get"> </form>
+<form action="//www.php.net/bar.php" method="get"> </form>
+
+<?php
+session_commit();
+ini_set('session.use_trans_sid', 1);
+output_reset_rewrite_vars();
+session_start();
+output_add_rewrite_var('<NAME>', '<VALUE>');
+?>
+Test use_trans_sid=1
+<a href=""> </a>
+<a href="./foo.php"> </a>
+<a href="//php.net/foo.php"> </a>
+<a href="http://php.net/foo.php"> </a>
+<a href="bad://php.net/foo.php"> </a>
+<a href="//www.php.net/foo.php"> </a>
+
+<form method="get"> </form>
+<form action="./foo.php" method="get"> </form>
+<form action="//php.net/bar.php" method="get"> </form>
+<form action="http://php.net/bar.php" method="get"> </form>
+<form action="bad://php.net/bar.php" method="get"> </form>
+<form action="//www.php.net/bar.php" method="get"> </form>
+
+--EXPECT--
+Without session
+<a href="?%3CNAME%3E=%3CVALUE%3E"> </a>
+<a href="./foo.php?%3CNAME%3E=%3CVALUE%3E"> </a>
+<a href="//php.net/foo.php?%3CNAME%3E=%3CVALUE%3E"> </a>
+<a href="http://php.net/foo.php?%3CNAME%3E=%3CVALUE%3E"> </a>
+<a href="bad://php.net/foo.php"> </a>
+<a href="//www.php.net/foo.php?%3CNAME%3E=%3CVALUE%3E"> </a>
+
+<form method="get"><input type="hidden" name="&lt;NAME&gt;" value="&lt;VALUE&gt;" /> </form>
+<form action="./foo.php" method="get"><input type="hidden" name="&lt;NAME&gt;" value="&lt;VALUE&gt;" />
+<form action="//php.net/bar.php" method="get"><input type="hidden" name="&lt;NAME&gt;" value="&lt;VALUE&gt;" />
+<form action="http://php.net/bar.php" method="get"><input type="hidden" name="&lt;NAME&gt;" value="&lt;VALUE&gt;" />
+<form action="bad://php.net/bar.php" method="get">
+<form action="//www.php.net/bar.php" method="get"><input type="hidden" name="&lt;NAME&gt;" value="&lt;VALUE&gt;" />
+
+Test use_trans_sid=0
+<a href="?%3CNAME%3E=%3CVALUE%3E"> </a>
+<a href="./foo.php?%3CNAME%3E=%3CVALUE%3E"> </a>
+<a href="//php.net/foo.php?%3CNAME%3E=%3CVALUE%3E"> </a>
+<a href="http://php.net/foo.php?%3CNAME%3E=%3CVALUE%3E"> </a>
+<a href="bad://php.net/foo.php"> </a>
+<a href="//www.php.net/foo.php?%3CNAME%3E=%3CVALUE%3E"> </a>
+
+<form method="get"><input type="hidden" name="&lt;NAME&gt;" value="&lt;VALUE&gt;" /> </form>
+<form action="./foo.php" method="get"><input type="hidden" name="&lt;NAME&gt;" value="&lt;VALUE&gt;" /> </form>
+<form action="//php.net/bar.php" method="get"><input type="hidden" name="&lt;NAME&gt;" value="&lt;VALUE&gt;" /> </form>
+<form action="http://php.net/bar.php" method="get"><input type="hidden" name="&lt;NAME&gt;" value="&lt;VALUE&gt;" /> </form>
+<form action="bad://php.net/bar.php" method="get"> </form>
+<form action="//www.php.net/bar.php" method="get"><input type="hidden" name="&lt;NAME&gt;" value="&lt;VALUE&gt;" /> </form>
+
+Test use_trans_sid=1
+<a href="?PHPSESSID=testid&%3CNAME%3E=%3CVALUE%3E"> </a>
+<a href="./foo.php?PHPSESSID=testid&%3CNAME%3E=%3CVALUE%3E"> </a>
+<a href="//php.net/foo.php?PHPSESSID=testid&%3CNAME%3E=%3CVALUE%3E"> </a>
+<a href="http://php.net/foo.php?PHPSESSID=testid&%3CNAME%3E=%3CVALUE%3E"> </a>
+<a href="bad://php.net/foo.php"> </a>
+<a href="//www.php.net/foo.php?PHPSESSID=testid&%3CNAME%3E=%3CVALUE%3E"> </a>
+
+<form method="get"><input type="hidden" name="&lt;NAME&gt;" value="&lt;VALUE&gt;" /><input type="hidden" name="PHPSESSID" value="testid" /> </form>
+<form action="./foo.php" method="get"><input type="hidden" name="&lt;NAME&gt;" value="&lt;VALUE&gt;" /><input type="hidden" name="PHPSESSID" value="testid" /> </form>
+<form action="//php.net/bar.php" method="get"><input type="hidden" name="&lt;NAME&gt;" value="&lt;VALUE&gt;" /><input type="hidden" name="PHPSESSID" value="testid" /> </form>
+<form action="http://php.net/bar.php" method="get"><input type="hidden" name="&lt;NAME&gt;" value="&lt;VALUE&gt;" /><input type="hidden" name="PHPSESSID" value="testid" /> </form>
+<form action="bad://php.net/bar.php" method="get"> </form>
+<form action="//www.php.net/bar.php" method="get"><input type="hidden" name="&lt;NAME&gt;" value="&lt;VALUE&gt;" /><input type="hidden" name="PHPSESSID" value="testid" /> </form>
diff --git a/ext/standard/tests/general_functions/output_add_rewrite_var_basic2.phpt b/ext/standard/tests/general_functions/output_add_rewrite_var_basic2.phpt
new file mode 100644
index 0000000000..94c79b8a14
--- /dev/null
+++ b/ext/standard/tests/general_functions/output_add_rewrite_var_basic2.phpt
@@ -0,0 +1,122 @@
+--TEST--
+Test output_add_rewrite_var() function basic feature
+--SKIPIF--
+<?php if (!extension_loaded("session")) die("skip session support is not available"); ?>
+--INI--
+session.trans_sid_tags="a=href,area=href,frame=src,form="
+url_rewriter.tags="a=href,area=href,frame=src,form="
+--FILE--
+<?php
+ ob_start();
+// Common setting
+ini_set('url_rewriter.hosts', 'php.net,www.php.net');
+ini_set('session.trans_sid_hosts', 'php.net,www.php.net');
+ini_set('session.use_only_cookies', 1);
+ini_set('session.use_cookies', 1);
+ini_set('session.use_strict_mode', 0);
+session_id('testid');
+
+output_add_rewrite_var('<name>', '<value>');
+?>
+Without session
+<a href=""> </a>
+<a href="./foo.php"> </a>
+<a href="//php.net/foo.php"> </a>
+<a href="http://php.net/foo.php"> </a>
+<a href="bad://php.net/foo.php"> </a>
+<a href="//www.php.net/foo.php"> </a>
+
+<form method="get"> </form>
+<form action="./foo.php" method="get"> </a>
+<form action="//php.net/bar.php" method="get"> </a>
+<form action="http://php.net/bar.php" method="get"> </a>
+<form action="bad://php.net/bar.php" method="get"> </a>
+<form action="//www.php.net/bar.php" method="get"> </a>
+
+<?php
+ini_set('session.use_trans_sid', 0);
+session_start();
+output_add_rewrite_var('<name>', '<value>');
+?>
+Test use_trans_sid=0
+<a href=""> </a>
+<a href="./foo.php"> </a>
+<a href="//php.net/foo.php"> </a>
+<a href="http://php.net/foo.php"> </a>
+<a href="bad://php.net/foo.php"> </a>
+<a href="//www.php.net/foo.php"> </a>
+
+<form method="get"> </form>
+<form action="./foo.php" method="get"> </a>
+<form action="//php.net/bar.php" method="get"> </a>
+<form action="http://php.net/bar.php" method="get"> </a>
+<form action="bad://php.net/bar.php" method="get"> </a>
+<form action="//www.php.net/bar.php" method="get"> </a>
+
+<?php
+session_commit();
+ini_set('session.use_trans_sid', 1);
+output_reset_rewrite_vars();
+session_start();
+output_add_rewrite_var('<NAME>', '<VALUE>');
+?>
+Test use_trans_sid=1
+<a href=""> </a>
+<a href="./foo.php"> </a>
+<a href="//php.net/foo.php"> </a>
+<a href="http://php.net/foo.php"> </a>
+<a href="bad://php.net/foo.php"> </a>
+<a href="//www.php.net/foo.php"> </a>
+
+<form method="get"> </form>
+<form action="./foo.php" method="get"> </a>
+<form action="//php.net/bar.php" method="get"> </a>
+<form action="http://php.net/bar.php" method="get"> </a>
+<form action="bad://php.net/bar.php" method="get"> </a>
+<form action="//www.php.net/bar.php" method="get"> </a>
+
+--EXPECT--
+Without session
+<a href="?%3CNAME%3E=%3CVALUE%3E"> </a>
+<a href="./foo.php?%3CNAME%3E=%3CVALUE%3E"> </a>
+<a href="//php.net/foo.php?%3CNAME%3E=%3CVALUE%3E"> </a>
+<a href="http://php.net/foo.php?%3CNAME%3E=%3CVALUE%3E"> </a>
+<a href="bad://php.net/foo.php"> </a>
+<a href="//www.php.net/foo.php?%3CNAME%3E=%3CVALUE%3E"> </a>
+
+<form method="get"><input type="hidden" name="&lt;NAME&gt;" value="&lt;VALUE&gt;" /> </form>
+<form action="./foo.php" method="get"><input type="hidden" name="&lt;NAME&gt;" value="&lt;VALUE&gt;" /> </a>
+<form action="//php.net/bar.php" method="get"><input type="hidden" name="&lt;NAME&gt;" value="&lt;VALUE&gt;" /> </a>
+<form action="http://php.net/bar.php" method="get"><input type="hidden" name="&lt;NAME&gt;" value="&lt;VALUE&gt;" /> </a>
+<form action="bad://php.net/bar.php" method="get"> </a>
+<form action="//www.php.net/bar.php" method="get"><input type="hidden" name="&lt;NAME&gt;" value="&lt;VALUE&gt;" /> </a>
+
+Test use_trans_sid=0
+<a href="?%3CNAME%3E=%3CVALUE%3E"> </a>
+<a href="./foo.php?%3CNAME%3E=%3CVALUE%3E"> </a>
+<a href="//php.net/foo.php?%3CNAME%3E=%3CVALUE%3E"> </a>
+<a href="http://php.net/foo.php?%3CNAME%3E=%3CVALUE%3E"> </a>
+<a href="bad://php.net/foo.php"> </a>
+<a href="//www.php.net/foo.php?%3CNAME%3E=%3CVALUE%3E"> </a>
+
+<form method="get"><input type="hidden" name="&lt;NAME&gt;" value="&lt;VALUE&gt;" /> </form>
+<form action="./foo.php" method="get"><input type="hidden" name="&lt;NAME&gt;" value="&lt;VALUE&gt;" /> </a>
+<form action="//php.net/bar.php" method="get"><input type="hidden" name="&lt;NAME&gt;" value="&lt;VALUE&gt;" /> </a>
+<form action="http://php.net/bar.php" method="get"><input type="hidden" name="&lt;NAME&gt;" value="&lt;VALUE&gt;" /> </a>
+<form action="bad://php.net/bar.php" method="get"> </a>
+<form action="//www.php.net/bar.php" method="get"><input type="hidden" name="&lt;NAME&gt;" value="&lt;VALUE&gt;" /> </a>
+
+Test use_trans_sid=1
+<a href="?%3CNAME%3E=%3CVALUE%3E"> </a>
+<a href="./foo.php?%3CNAME%3E=%3CVALUE%3E"> </a>
+<a href="//php.net/foo.php?%3CNAME%3E=%3CVALUE%3E"> </a>
+<a href="http://php.net/foo.php?%3CNAME%3E=%3CVALUE%3E"> </a>
+<a href="bad://php.net/foo.php"> </a>
+<a href="//www.php.net/foo.php?%3CNAME%3E=%3CVALUE%3E"> </a>
+
+<form method="get"><input type="hidden" name="&lt;NAME&gt;" value="&lt;VALUE&gt;" /> </form>
+<form action="./foo.php" method="get"><input type="hidden" name="&lt;NAME&gt;" value="&lt;VALUE&gt;" /> </a>
+<form action="//php.net/bar.php" method="get"><input type="hidden" name="&lt;NAME&gt;" value="&lt;VALUE&gt;" /> </a>
+<form action="http://php.net/bar.php" method="get"><input type="hidden" name="&lt;NAME&gt;" value="&lt;VALUE&gt;" /> </a>
+<form action="bad://php.net/bar.php" method="get"> </a>
+<form action="//www.php.net/bar.php" method="get"><input type="hidden" name="&lt;NAME&gt;" value="&lt;VALUE&gt;" /> </a>
diff --git a/ext/standard/tests/general_functions/output_add_rewrite_var_basic3.phpt b/ext/standard/tests/general_functions/output_add_rewrite_var_basic3.phpt
new file mode 100644
index 0000000000..a7a4a94378
--- /dev/null
+++ b/ext/standard/tests/general_functions/output_add_rewrite_var_basic3.phpt
@@ -0,0 +1,121 @@
+--TEST--
+Test output_add_rewrite_var() function basic feature
+--SKIPIF--
+<?php if (!extension_loaded("session")) die("skip session support is not available"); ?>
+--INI--
+session.trans_sid_tags="a=href,area=href,frame=src,form="
+url_rewriter.tags="a=href,area=href,frame=src,form="
+--FILE--
+<?php
+ ob_start();
+// Common setting
+ini_set('url_rewriter.hosts', 'example.com');
+ini_set('session.use_only_cookies', 0);
+ini_set('session.use_cookies', 0);
+ini_set('session.use_strict_mode', 0);
+session_id('testid');
+
+output_add_rewrite_var('<name>', '<value>');
+?>
+Without session
+<a href=""> </a>
+<a href="./foo.php"> </a>
+<a href="//php.net/foo.php"> </a>
+<a href="http://php.net/foo.php"> </a>
+<a href="bad://php.net/foo.php"> </a>
+<a href="//www.php.net/foo.php"> </a>
+
+<form method="get"> </form>
+<form action="./foo.php" method="get"> </a>
+<form action="//php.net/bar.php" method="get"> </a>
+<form action="http://php.net/bar.php" method="get"> </a>
+<form action="bad://php.net/bar.php" method="get"> </a>
+<form action="//www.php.net/bar.php" method="get"> </a>
+
+<?php
+ini_set('session.use_trans_sid', 0);
+session_start();
+output_add_rewrite_var('<name>', '<value>');
+?>
+Test use_trans_sid=0
+<a href=""> </a>
+<a href="./foo.php"> </a>
+<a href="//php.net/foo.php"> </a>
+<a href="http://php.net/foo.php"> </a>
+<a href="bad://php.net/foo.php"> </a>
+<a href="//www.php.net/foo.php"> </a>
+
+<form method="get"> </form>
+<form action="./foo.php" method="get"> </a>
+<form action="//php.net/bar.php" method="get"> </a>
+<form action="http://php.net/bar.php" method="get"> </a>
+<form action="bad://php.net/bar.php" method="get"> </a>
+<form action="//www.php.net/bar.php" method="get"> </a>
+
+<?php
+session_commit();
+ini_set('session.use_trans_sid', 1);
+output_reset_rewrite_vars();
+session_start();
+output_add_rewrite_var('<NAME>', '<VALUE>');
+?>
+Test use_trans_sid=1
+<a href=""> </a>
+<a href="./foo.php"> </a>
+<a href="//php.net/foo.php"> </a>
+<a href="http://php.net/foo.php"> </a>
+<a href="bad://php.net/foo.php"> </a>
+<a href="//www.php.net/foo.php"> </a>
+
+<form method="get"> </form>
+<form action="./foo.php" method="get"> </a>
+<form action="//php.net/bar.php" method="get"> </a>
+<form action="http://php.net/bar.php" method="get"> </a>
+<form action="bad://php.net/bar.php" method="get"> </a>
+<form action="//www.php.net/bar.php" method="get"> </a>
+
+--EXPECT--
+Without session
+<a href="?%3CNAME%3E=%3CVALUE%3E"> </a>
+<a href="./foo.php?%3CNAME%3E=%3CVALUE%3E"> </a>
+<a href="//php.net/foo.php"> </a>
+<a href="http://php.net/foo.php"> </a>
+<a href="bad://php.net/foo.php"> </a>
+<a href="//www.php.net/foo.php"> </a>
+
+<form method="get"><input type="hidden" name="&lt;NAME&gt;" value="&lt;VALUE&gt;" /> </form>
+<form action="./foo.php" method="get"><input type="hidden" name="&lt;NAME&gt;" value="&lt;VALUE&gt;" /> </a>
+<form action="//php.net/bar.php" method="get"> </a>
+<form action="http://php.net/bar.php" method="get"> </a>
+<form action="bad://php.net/bar.php" method="get"> </a>
+<form action="//www.php.net/bar.php" method="get"> </a>
+
+Test use_trans_sid=0
+<a href="?%3CNAME%3E=%3CVALUE%3E"> </a>
+<a href="./foo.php?%3CNAME%3E=%3CVALUE%3E"> </a>
+<a href="//php.net/foo.php"> </a>
+<a href="http://php.net/foo.php"> </a>
+<a href="bad://php.net/foo.php"> </a>
+<a href="//www.php.net/foo.php"> </a>
+
+<form method="get"> </form>
+<form action="./foo.php" method="get"><input type="hidden" name="&lt;NAME&gt;" value="&lt;VALUE&gt;" /> </a>
+<form action="//php.net/bar.php" method="get"> </a>
+<form action="http://php.net/bar.php" method="get"> </a>
+<form action="bad://php.net/bar.php" method="get"> </a>
+<form action="//www.php.net/bar.php" method="get"> </a>
+
+Test use_trans_sid=1
+<a href="?PHPSESSID=testid&%3CNAME%3E=%3CVALUE%3E"> </a>
+<a href="./foo.php?PHPSESSID=testid&%3CNAME%3E=%3CVALUE%3E"> </a>
+<a href="//php.net/foo.php"> </a>
+<a href="http://php.net/foo.php"> </a>
+<a href="bad://php.net/foo.php"> </a>
+<a href="//www.php.net/foo.php"> </a>
+
+<form method="get"><input type="hidden" name="PHPSESSID" value="testid" /> </form>
+<form action="./foo.php" method="get"><input type="hidden" name="&lt;NAME&gt;" value="&lt;VALUE&gt;" /><input type="hidden" name="PHPSESSID" value="testid" /> </a>
+<form action="//php.net/bar.php" method="get"> </a>
+<form action="http://php.net/bar.php" method="get"> </a>
+<form action="bad://php.net/bar.php" method="get"> </a>
+<form action="//www.php.net/bar.php" method="get"> </a>
diff --git a/ext/standard/tests/general_functions/output_add_rewrite_var_basic4.phpt b/ext/standard/tests/general_functions/output_add_rewrite_var_basic4.phpt
new file mode 100644
index 0000000000..775b590202
--- /dev/null
+++ b/ext/standard/tests/general_functions/output_add_rewrite_var_basic4.phpt
@@ -0,0 +1,121 @@
+--TEST--
+Test output_add_rewrite_var() function basic feature
+--SKIPIF--
+<?php if (!extension_loaded("session")) die("skip session support is not available"); ?>
+--INI--
+session.trans_sid_tags="a=href,area=href,frame=src,form="
+url_rewriter.tags="a=href,area=href,frame=src,form="
+--FILE--
+<?php
+ ob_start();
+// Common setting
+ini_set('url_rewriter.hosts', 'example.com');
+ini_set('session.use_only_cookies', 1);
+ini_set('session.use_cookies', 1);
+ini_set('session.use_strict_mode', 0);
+session_id('testid');
+
+output_add_rewrite_var('<name>', '<value>');
+?>
+Without session
+<a href=""> </a>
+<a href="./foo.php"> </a>
+<a href="//php.net/foo.php"> </a>
+<a href="http://php.net/foo.php"> </a>
+<a href="bad://php.net/foo.php"> </a>
+<a href="//www.php.net/foo.php"> </a>
+
+<form method="get"> </form>
+<form action="./foo.php" method="get"> </a>
+<form action="//php.net/bar.php" method="get"> </a>
+<form action="http://php.net/bar.php" method="get"> </a>
+<form action="bad://php.net/bar.php" method="get"> </a>
+<form action="//www.php.net/bar.php" method="get"> </a>
+
+<?php
+ini_set('session.use_trans_sid', 0);
+session_start();
+output_add_rewrite_var('<name>', '<value>');
+?>
+Test use_trans_sid=0
+<a href=""> </a>
+<a href="./foo.php"> </a>
+<a href="//php.net/foo.php"> </a>
+<a href="http://php.net/foo.php"> </a>
+<a href="bad://php.net/foo.php"> </a>
+<a href="//www.php.net/foo.php"> </a>
+
+<form method="get"> </form>
+<form action="./foo.php" method="get"> </a>
+<form action="//php.net/bar.php" method="get"> </a>
+<form action="http://php.net/bar.php" method="get"> </a>
+<form action="bad://php.net/bar.php" method="get"> </a>
+<form action="//www.php.net/bar.php" method="get"> </a>
+
+<?php
+session_commit();
+ini_set('session.use_trans_sid', 1);
+output_reset_rewrite_vars();
+session_start();
+output_add_rewrite_var('<NAME>', '<VALUE>');
+?>
+Test use_trans_sid=1
+<a href=""> </a>
+<a href="./foo.php"> </a>
+<a href="//php.net/foo.php"> </a>
+<a href="http://php.net/foo.php"> </a>
+<a href="bad://php.net/foo.php"> </a>
+<a href="//www.php.net/foo.php"> </a>
+
+<form method="get"> </form>
+<form action="./foo.php" method="get"> </a>
+<form action="//php.net/bar.php" method="get"> </a>
+<form action="http://php.net/bar.php" method="get"> </a>
+<form action="bad://php.net/bar.php" method="get"> </a>
+<form action="//www.php.net/bar.php" method="get"> </a>
+
+--EXPECT--
+Without session
+<a href="?%3CNAME%3E=%3CVALUE%3E"> </a>
+<a href="./foo.php?%3CNAME%3E=%3CVALUE%3E"> </a>
+<a href="//php.net/foo.php"> </a>
+<a href="http://php.net/foo.php"> </a>
+<a href="bad://php.net/foo.php"> </a>
+<a href="//www.php.net/foo.php"> </a>
+
+<form method="get"><input type="hidden" name="&lt;NAME&gt;" value="&lt;VALUE&gt;" /> </form>
+<form action="./foo.php" method="get"><input type="hidden" name="&lt;NAME&gt;" value="&lt;VALUE&gt;" /> </a>
+<form action="//php.net/bar.php" method="get"> </a>
+<form action="http://php.net/bar.php" method="get"> </a>
+<form action="bad://php.net/bar.php" method="get"> </a>
+<form action="//www.php.net/bar.php" method="get"> </a>
+
+Test use_trans_sid=0
+<a href="?%3CNAME%3E=%3CVALUE%3E"> </a>
+<a href="./foo.php?%3CNAME%3E=%3CVALUE%3E"> </a>
+<a href="//php.net/foo.php"> </a>
+<a href="http://php.net/foo.php"> </a>
+<a href="bad://php.net/foo.php"> </a>
+<a href="//www.php.net/foo.php"> </a>
+
+<form method="get"> </form>
+<form action="./foo.php" method="get"><input type="hidden" name="&lt;NAME&gt;" value="&lt;VALUE&gt;" /> </a>
+<form action="//php.net/bar.php" method="get"> </a>
+<form action="http://php.net/bar.php" method="get"> </a>
+<form action="bad://php.net/bar.php" method="get"> </a>
+<form action="//www.php.net/bar.php" method="get"> </a>
+
+Test use_trans_sid=1
+<a href="?%3CNAME%3E=%3CVALUE%3E"> </a>
+<a href="./foo.php?%3CNAME%3E=%3CVALUE%3E"> </a>
+<a href="//php.net/foo.php"> </a>
+<a href="http://php.net/foo.php"> </a>
+<a href="bad://php.net/foo.php"> </a>
+<a href="//www.php.net/foo.php"> </a>
+
+<form method="get"> </form>
+<form action="./foo.php" method="get"><input type="hidden" name="&lt;NAME&gt;" value="&lt;VALUE&gt;" /> </a>
+<form action="//php.net/bar.php" method="get"> </a>
+<form action="http://php.net/bar.php" method="get"> </a>
+<form action="bad://php.net/bar.php" method="get"> </a>
+<form action="//www.php.net/bar.php" method="get"> </a>
diff --git a/ext/standard/tests/general_functions/proc_open-win32-mb0.phpt b/ext/standard/tests/general_functions/proc_open-win32-mb0.phpt
new file mode 100644
index 0000000000..981c4b22aa
--- /dev/null
+++ b/ext/standard/tests/general_functions/proc_open-win32-mb0.phpt
@@ -0,0 +1,55 @@
+--TEST--
+proc_open with bypass_shell subprocess parameter passing
+--SKIPIF--
+<?php # vim:syn=php
+if (substr(PHP_OS, 0, 3) != 'WIN') die("skip Valid only on Windows");
+if (php_sapi_name() != "cli") die('skip CLI only test');
+if (!function_exists("proc_open")) echo "skip proc_open() is not available";
+?>
+--FILE--
+<?php
+
+$php = PHP_BINARY;
+
+$f = dirname(__FILE__) . DIRECTORY_SEPARATOR . "proc_only_mb0.php";
+file_put_contents($f,'<?php var_dump($argv); ?>');
+
+$ds = array(
+ 0 => array("pipe", "r"),
+ 1 => array("pipe", "w"),
+ 2 => array("pipe", "w")
+ );
+
+$p = proc_open(
+ "$php -n $f テストマルチバイト・パス füße карамба",
+ $ds,
+ $pipes,
+ NULL,
+ NULL,
+ array("bypass_shell" => true)
+ );
+
+$out = "";
+
+while (!feof($pipes[1])) {
+ $out .= fread($pipes[1], 1024);
+}
+
+proc_close($p);
+
+echo $out;
+
+?>
+==DONE==
+--EXPECTF--
+array(4) {
+ [0]=>
+ string(%d) "%sproc_only_mb0.php"
+ [1]=>
+ string(36) "テストマルチバイト・パス"
+ [2]=>
+ string(6) "füße"
+ [3]=>
+ string(14) "карамба"
+}
+==DONE==
diff --git a/ext/standard/tests/general_functions/proc_open-win32-mb1.phpt b/ext/standard/tests/general_functions/proc_open-win32-mb1.phpt
new file mode 100644
index 0000000000..f1a70a23e8
--- /dev/null
+++ b/ext/standard/tests/general_functions/proc_open-win32-mb1.phpt
@@ -0,0 +1,52 @@
+--TEST--
+proc_open without bypass_shell subprocess parameter passing
+--SKIPIF--
+<?php # vim:syn=php
+if (substr(PHP_OS, 0, 3) != 'WIN') die("skip Valid only on Windows");
+if (php_sapi_name() != "cli") die('skip CLI only test');
+if (!function_exists("proc_open")) echo "skip proc_open() is not available";
+?>
+--FILE--
+<?php
+
+$php = PHP_BINARY;
+
+$f = dirname(__FILE__) . DIRECTORY_SEPARATOR . "proc_only_mb1.php";
+file_put_contents($f,'<?php var_dump($argv); ?>');
+
+$ds = array(
+ 0 => array("pipe", "r"),
+ 1 => array("pipe", "w"),
+ 2 => array("pipe", "w")
+ );
+
+$p = proc_open(
+ "$php -n $f テストマルチバイト・パス füße карамба",
+ $ds,
+ $pipes
+ );
+
+$out = "";
+
+while (!feof($pipes[1])) {
+ $out .= fread($pipes[1], 1024);
+}
+
+proc_close($p);
+
+echo $out;
+
+?>
+==DONE==
+--EXPECTF--
+array(4) {
+ [0]=>
+ string(%d) "%sproc_only_mb1.php"
+ [1]=>
+ string(36) "テストマルチバイト・パス"
+ [2]=>
+ string(6) "füße"
+ [3]=>
+ string(14) "карамба"
+}
+==DONE==
diff --git a/ext/standard/tests/general_functions/url_rewriter.phpt b/ext/standard/tests/general_functions/url_rewriter.phpt
deleted file mode 100644
index ce91b27a00..0000000000
--- a/ext/standard/tests/general_functions/url_rewriter.phpt
+++ /dev/null
@@ -1,139 +0,0 @@
---TEST--
-URL Rewriter tests
---INI--
-url_rewriter.tags="a=href,form="
-session.use_only_cookies=0
-session.use_trans_sid=1
-session.use_strict_mode=0
---FILE--
-<?php
-session_id('id');
-
-$_SERVER['HTTP_HOST'] = 'php.net';
-session_start();
-output_add_rewrite_var('a','b');
-?>
-
-<a></a>
-<a href=""></a>
-<a href="foo"></a>
-<a href="?foo"></a>
-<a href="/foo"></a>
-<a href="foo=bar"></a>
-<a href="foo.php#bar"></a>
-<a href="../foo.php#bar"></a>
-
-<a href="//bad.net/foo"></a>
-<a href="//bad.net/?foo"></a>
-<a href="//bad.net/foo"></a>
-<a href="//bad.net/foo=bar"></a>
-<a href="//bad.net/foo.php#bar"></a>
-<a href="//bad.net/../foo.php#bar"></a>
-
-<a href="//php.net/foo"></a>
-<a href="//php.net/?foo"></a>
-<a href="//php.net//foo"></a>
-<a href="//php.net/foo=bar"></a>
-<a href="//php.net/foo.php#bar"></a>
-
-<a href="http://bad.net/foo"></a>
-<a href="http://bad.net/?foo"></a>
-<a href="http://bad.net/foo"></a>
-<a href="http://bad.net/foo=bar"></a>
-<a href="http://bad.net/foo.php#bar"></a>
-<a href="http://bad.net/../foo.php#bar"></a>
-
-<a href="http://php.net/foo"></a>
-<a href="http://php.net/?foo"></a>
-<a href="http://php.net//foo"></a>
-<a href="http://php.net/foo=bar"></a>
-<a href="http://php.net/foo.php#bar"></a>
-<a href="http://php.net/../foo.php#bar"></a>
-
-<a href="bad://bad.net/foo"></a>
-<a href="bad://bad.net/?foo"></a>
-<a href="bad://bad.net/foo"></a>
-<a href="bad://bad.net/foo=bar"></a>
-<a href="bad://bad.net/foo.php#bar"></a>
-<a href="bad://bad.net/../foo.php#bar"></a>
-
-<a href="bad://php.net/foo"></a>
-<a href="bad://php.net/?foo"></a>
-<a href="bad://php.net//foo"></a>
-<a href="bad://php.net/foo=bar"></a>
-<a href="bad://php.net/foo.php#bar"></a>
-<a href="bad://php.net/../foo.php#bar"></a>
-
-<form></form>
-<form action=""></form>
-<form action="foo.php"></form>
-<form action="//php.net/foo.php"></form>
-<form action="http://php.net/foo.php"></form>
-
-<form action="bad://php.net/foo.php"></form>
-<form action="//bad.net/foo.php"></form>
-<form action="http://php.net/foo.php"></form>
-<form action="bad://php.net/foo.php"></form>
-<form action="//bad.net/foo.php"></form>
---EXPECT--
-<a></a>
-<a href="?PHPSESSID=id&a=b"></a>
-<a href="foo?PHPSESSID=id&a=b"></a>
-<a href="?foo&PHPSESSID=id&a=b"></a>
-<a href="/foo?PHPSESSID=id&a=b"></a>
-<a href="foo=bar?PHPSESSID=id&a=b"></a>
-<a href="foo.php?PHPSESSID=id&a=b#bar"></a>
-<a href="../foo.php?PHPSESSID=id&a=b#bar"></a>
-
-<a href="//bad.net/foo"></a>
-<a href="//bad.net/?foo"></a>
-<a href="//bad.net/foo"></a>
-<a href="//bad.net/foo=bar"></a>
-<a href="//bad.net/foo.php#bar"></a>
-<a href="//bad.net/../foo.php#bar"></a>
-
-<a href="//php.net/foo?PHPSESSID=id&a=b"></a>
-<a href="//php.net/?foo&PHPSESSID=id&a=b"></a>
-<a href="//php.net//foo?PHPSESSID=id&a=b"></a>
-<a href="//php.net/foo=bar?PHPSESSID=id&a=b"></a>
-<a href="//php.net/foo.php?PHPSESSID=id&a=b#bar"></a>
-
-<a href="http://bad.net/foo"></a>
-<a href="http://bad.net/?foo"></a>
-<a href="http://bad.net/foo"></a>
-<a href="http://bad.net/foo=bar"></a>
-<a href="http://bad.net/foo.php#bar"></a>
-<a href="http://bad.net/../foo.php#bar"></a>
-
-<a href="http://php.net/foo"></a>
-<a href="http://php.net/?foo"></a>
-<a href="http://php.net//foo"></a>
-<a href="http://php.net/foo=bar"></a>
-<a href="http://php.net/foo.php#bar"></a>
-<a href="http://php.net/../foo.php#bar"></a>
-
-<a href="bad://bad.net/foo"></a>
-<a href="bad://bad.net/?foo"></a>
-<a href="bad://bad.net/foo"></a>
-<a href="bad://bad.net/foo=bar"></a>
-<a href="bad://bad.net/foo.php#bar"></a>
-<a href="bad://bad.net/../foo.php#bar"></a>
-
-<a href="bad://php.net/foo"></a>
-<a href="bad://php.net/?foo"></a>
-<a href="bad://php.net//foo"></a>
-<a href="bad://php.net/foo=bar"></a>
-<a href="bad://php.net/foo.php#bar"></a>
-<a href="bad://php.net/../foo.php#bar"></a>
-
-<form><input type="hidden" name="PHPSESSID" value="id" /><input type="hidden" name="a" value="b" /></form>
-<form action=""><input type="hidden" name="PHPSESSID" value="id" /><input type="hidden" name="a" value="b" /></form>
-<form action="foo.php"><input type="hidden" name="PHPSESSID" value="id" /><input type="hidden" name="a" value="b" /></form>
-<form action="//php.net/foo.php"><input type="hidden" name="PHPSESSID" value="id" /><input type="hidden" name="a" value="b" /></form>
-<form action="http://php.net/foo.php"><input type="hidden" name="PHPSESSID" value="id" /><input type="hidden" name="a" value="b" /></form>
-
-<form action="bad://php.net/foo.php"><input type="hidden" name="PHPSESSID" value="id" /><input type="hidden" name="a" value="b" /></form>
-<form action="//bad.net/foo.php"><input type="hidden" name="PHPSESSID" value="id" /><input type="hidden" name="a" value="b" /></form>
-<form action="http://php.net/foo.php"><input type="hidden" name="PHPSESSID" value="id" /><input type="hidden" name="a" value="b" /></form>
-<form action="bad://php.net/foo.php"><input type="hidden" name="PHPSESSID" value="id" /><input type="hidden" name="a" value="b" /></form>
-<form action="//bad.net/foo.php"><input type="hidden" name="PHPSESSID" value="id" /><input type="hidden" name="a" value="b" /></form>
diff --git a/ext/standard/tests/http/http_response_header_01.phpt b/ext/standard/tests/http/http_response_header_01.phpt
new file mode 100644
index 0000000000..2facf4fae5
--- /dev/null
+++ b/ext/standard/tests/http/http_response_header_01.phpt
@@ -0,0 +1,38 @@
+--TEST--
+$http_reponse_header (no redirect)
+--SKIPIF--
+<?php require 'server.inc'; http_server_skipif('tcp://127.0.0.1:22346'); ?>
+--INI--
+allow_url_fopen=1
+allow_url_include=1
+--FILE--
+<?php
+require 'server.inc';
+
+$responses = array(
+ "data://text/plain,HTTP/1.0 200 Ok\r\nSome: Header\r\nSome: Header\r\n\r\nBody",
+);
+
+$pid = http_server("tcp://127.0.0.1:22346", $responses, $output);
+
+function test() {
+ $f = file_get_contents('http://127.0.0.1:22346/');
+ var_dump($f);
+ var_dump($http_response_header);
+}
+test();
+
+http_server_kill($pid);
+?>
+==DONE==
+--EXPECT--
+string(4) "Body"
+array(3) {
+ [0]=>
+ string(15) "HTTP/1.0 200 Ok"
+ [1]=>
+ string(12) "Some: Header"
+ [2]=>
+ string(12) "Some: Header"
+}
+==DONE==
diff --git a/ext/standard/tests/http/http_response_header_02.phpt b/ext/standard/tests/http/http_response_header_02.phpt
new file mode 100644
index 0000000000..0d4da1af14
--- /dev/null
+++ b/ext/standard/tests/http/http_response_header_02.phpt
@@ -0,0 +1,44 @@
+--TEST--
+$http_reponse_header (redirect)
+--SKIPIF--
+<?php require 'server.inc'; http_server_skipif('tcp://127.0.0.1:22347'); ?>
+--INI--
+allow_url_fopen=1
+allow_url_include=1
+--FILE--
+<?php
+require 'server.inc';
+
+$responses = array(
+ "data://text/plain,HTTP/1.0 302 Found\r\n"
+ . "Some: Header\r\nLocation: http://127.0.0.1:22347/try-again\r\n\r\n",
+ "data://test/plain,HTTP/1.0 200 Ok\r\nSome: Header\r\n\r\nBody",
+);
+
+$pid = http_server("tcp://127.0.0.1:22347", $responses, $output);
+
+function test() {
+ $f = file_get_contents('http://127.0.0.1:22347/');
+ var_dump($f);
+ var_dump($http_response_header);
+}
+test();
+
+http_server_kill($pid);
+?>
+==DONE==
+--EXPECT--
+string(4) "Body"
+array(5) {
+ [0]=>
+ string(18) "HTTP/1.0 302 Found"
+ [1]=>
+ string(12) "Some: Header"
+ [2]=>
+ string(42) "Location: http://127.0.0.1:22347/try-again"
+ [3]=>
+ string(15) "HTTP/1.0 200 Ok"
+ [4]=>
+ string(12) "Some: Header"
+}
+==DONE==
diff --git a/ext/standard/tests/http/http_response_header_03.phpt b/ext/standard/tests/http/http_response_header_03.phpt
new file mode 100644
index 0000000000..866a9b3ba5
--- /dev/null
+++ b/ext/standard/tests/http/http_response_header_03.phpt
@@ -0,0 +1,45 @@
+--TEST--
+$http_reponse_header (redirect + not found)
+--SKIPIF--
+<?php require 'server.inc'; http_server_skipif('tcp://127.0.0.1:22348'); ?>
+--INI--
+allow_url_fopen=1
+allow_url_include=1
+--FILE--
+<?php
+require 'server.inc';
+
+$responses = array(
+ "data://text/plain,HTTP/1.0 302 Found\r\n"
+ . "Some: Header\r\nLocation: http://127.0.0.1:22348/try-again\r\n\r\n",
+ "data://test/plain,HTTP/1.0 404 Not Found\r\nSome: Header\r\n\r\nBody",
+);
+
+$pid = http_server("tcp://127.0.0.1:22348", $responses, $output);
+
+function test() {
+ $f = file_get_contents('http://127.0.0.1:22348/');
+ var_dump($f);
+ var_dump($http_response_header);
+}
+test();
+
+http_server_kill($pid);
+?>
+==DONE==
+--EXPECTF--
+Warning: file_get_contents(http://127.0.0.1:22348/): failed to open stream: HTTP request failed! HTTP/1.0 404 Not Found%a
+bool(false)
+array(5) {
+ [0]=>
+ string(18) "HTTP/1.0 302 Found"
+ [1]=>
+ string(12) "Some: Header"
+ [2]=>
+ string(42) "Location: http://127.0.0.1:22348/try-again"
+ [3]=>
+ string(22) "HTTP/1.0 404 Not Found"
+ [4]=>
+ string(12) "Some: Header"
+}
+==DONE==
diff --git a/ext/standard/tests/image/getimagesize.phpt b/ext/standard/tests/image/getimagesize.phpt
index 04ddd8c82b..53c6b2a0b6 100644
--- a/ext/standard/tests/image/getimagesize.phpt
+++ b/ext/standard/tests/image/getimagesize.phpt
@@ -23,7 +23,7 @@ GetImageSize()
var_dump($result);
?>
--EXPECT--
-array(13) {
+array(16) {
["test-1pix.bmp"]=>
array(6) {
[0]=>
@@ -39,6 +39,21 @@ array(13) {
["mime"]=>
string(14) "image/x-ms-bmp"
}
+ ["test12pix.webp"]=>
+ array(6) {
+ [0]=>
+ int(4)
+ [1]=>
+ int(3)
+ [2]=>
+ int(18)
+ [3]=>
+ string(20) "width="4" height="3""
+ ["bits"]=>
+ int(8)
+ ["mime"]=>
+ string(10) "image/webp"
+ }
["test1bpix.bmp"]=>
array(6) {
[0]=>
@@ -137,6 +152,36 @@ array(13) {
["mime"]=>
string(9) "image/gif"
}
+ ["test3llpix.webp"]=>
+ array(6) {
+ [0]=>
+ int(1)
+ [1]=>
+ int(3)
+ [2]=>
+ int(18)
+ [3]=>
+ string(20) "width="1" height="3""
+ ["bits"]=>
+ int(8)
+ ["mime"]=>
+ string(10) "image/webp"
+ }
+ ["test3pix.webp"]=>
+ array(6) {
+ [0]=>
+ int(1)
+ [1]=>
+ int(3)
+ [2]=>
+ int(18)
+ [3]=>
+ string(20) "width="1" height="3""
+ ["bits"]=>
+ int(8)
+ ["mime"]=>
+ string(10) "image/webp"
+ }
["test4pix.gif"]=>
array(7) {
[0]=>
diff --git a/ext/standard/tests/image/image_type_to_extension.phpt b/ext/standard/tests/image/image_type_to_extension.phpt
index c668e3408d..91666af42d 100644
--- a/ext/standard/tests/image/image_type_to_extension.phpt
+++ b/ext/standard/tests/image/image_type_to_extension.phpt
@@ -2,8 +2,8 @@
image_type_to_extension()
--SKIPIF--
<?php
- if (!function_exists('image_type_to_extension')) die('skip image_type_to_extension() not available');
- require_once('skipif_imagetype.inc');
+ if (!function_exists('image_type_to_extension')) die('skip image_type_to_extension() not available');
+ require_once('skipif_imagetype.inc');
?>
--FILE--
<?php
@@ -23,18 +23,19 @@ image_type_to_extension()
"IMAGETYPE_IFF" => IMAGETYPE_IFF,
"IMAGETYPE_WBMP" => IMAGETYPE_WBMP,
"IMAGETYPE_JPEG2000" => IMAGETYPE_JPEG2000,
- "IMAGETYPE_XBM" => IMAGETYPE_XBM
+ "IMAGETYPE_XBM" => IMAGETYPE_XBM,
+ "IMAGETYPE_WEBP" => IMAGETYPE_WEBP
);
foreach($constants as $name => $constant) {
printf("Constant: %s\n\tWith dot: %s\n\tWithout dot: %s\n", $name, image_type_to_extension($constant), image_type_to_extension($constant, false));
}
- var_dump(image_type_to_extension(-1, array()));
- var_dump(image_type_to_extension(new stdclass));
- var_dump(image_type_to_extension(1000000, NULL));
- var_dump(image_type_to_extension());
- var_dump(image_type_to_extension(0));
- var_dump(image_type_to_extension(0, 0, 0));
+ var_dump(image_type_to_extension(-1, array()));
+ var_dump(image_type_to_extension(new stdclass));
+ var_dump(image_type_to_extension(1000000, NULL));
+ var_dump(image_type_to_extension());
+ var_dump(image_type_to_extension(0));
+ var_dump(image_type_to_extension(0, 0, 0));
?>
Done
--EXPECTF--
@@ -86,6 +87,9 @@ Constant: IMAGETYPE_JPEG2000
Constant: IMAGETYPE_XBM
With dot: .xbm
Without dot: xbm
+Constant: IMAGETYPE_WEBP
+ With dot: .webp
+ Without dot: webp
Warning: image_type_to_extension() expects parameter 2 to be boolean, array given in %s on line %d
bool(false)
diff --git a/ext/standard/tests/image/image_type_to_mime_type.phpt b/ext/standard/tests/image/image_type_to_mime_type.phpt
index 9f7ffa1aa3..4ae5680238 100644
--- a/ext/standard/tests/image/image_type_to_mime_type.phpt
+++ b/ext/standard/tests/image/image_type_to_mime_type.phpt
@@ -25,9 +25,11 @@ image_type_to_mime_type()
var_dump($result);
?>
--EXPECT--
-array(13) {
+array(16) {
["test-1pix.bmp"]=>
string(14) "image/x-ms-bmp"
+ ["test12pix.webp"]=>
+ string(10) "image/webp"
["test1bpix.bmp"]=>
string(14) "image/x-ms-bmp"
["test1pix.bmp"]=>
@@ -40,6 +42,10 @@ array(13) {
string(10) "image/jpeg"
["test2pix.gif"]=>
string(9) "image/gif"
+ ["test3llpix.webp"]=>
+ string(10) "image/webp"
+ ["test3pix.webp"]=>
+ string(10) "image/webp"
["test4pix.gif"]=>
string(9) "image/gif"
["test4pix.iff"]=>
diff --git a/ext/standard/tests/image/image_type_to_mime_type_basic.phpt b/ext/standard/tests/image/image_type_to_mime_type_basic.phpt
index b81bdbde5f..5506570494 100644
--- a/ext/standard/tests/image/image_type_to_mime_type_basic.phpt
+++ b/ext/standard/tests/image/image_type_to_mime_type_basic.phpt
@@ -31,7 +31,8 @@ $image_types = array (
IMAGETYPE_IFF,
IMAGETYPE_WBMP,
IMAGETYPE_JPEG2000,
- IMAGETYPE_XBM
+ IMAGETYPE_XBM,
+ IMAGETYPE_WEBP
);
foreach($image_types as $image_type) {
@@ -59,5 +60,6 @@ string(9) "image/iff"
string(18) "image/vnd.wap.wbmp"
string(24) "application/octet-stream"
string(9) "image/xbm"
+string(10) "image/webp"
Done image_type_to_mime_type() test
diff --git a/ext/standard/tests/image/image_type_to_mime_type_variation3.phpt b/ext/standard/tests/image/image_type_to_mime_type_variation3.phpt
index 192f23dcee..7b06c0a145 100644
--- a/ext/standard/tests/image/image_type_to_mime_type_variation3.phpt
+++ b/ext/standard/tests/image/image_type_to_mime_type_variation3.phpt
@@ -75,5 +75,8 @@ string\(9\) "image\/xbm"
string\(24\) "image\/vnd.microsoft.icon"
-- Iteration 18 --
+string\(10\) "image\/webp"
+
+-- Iteration 19 --
string\(24\) "application\/octet-stream"
===DONE===
diff --git a/ext/standard/tests/image/test12pix.webp b/ext/standard/tests/image/test12pix.webp
new file mode 100644
index 0000000000..c87da2423f
--- /dev/null
+++ b/ext/standard/tests/image/test12pix.webp
Binary files differ
diff --git a/ext/standard/tests/image/test3llpix.webp b/ext/standard/tests/image/test3llpix.webp
new file mode 100644
index 0000000000..2243a1b2bc
--- /dev/null
+++ b/ext/standard/tests/image/test3llpix.webp
Binary files differ
diff --git a/ext/standard/tests/image/test3pix.webp b/ext/standard/tests/image/test3pix.webp
new file mode 100644
index 0000000000..e5c6410fe3
--- /dev/null
+++ b/ext/standard/tests/image/test3pix.webp
Binary files differ
diff --git a/ext/standard/tests/mail/mail_log.phpt b/ext/standard/tests/mail/mail_log.phpt
index 86346ec307..8f385d490d 100644
--- a/ext/standard/tests/mail/mail_log.phpt
+++ b/ext/standard/tests/mail/mail_log.phpt
@@ -44,5 +44,5 @@ unlink("/tmp/mail.out");
bool(true)
bool(true)
bool(true)
-[%d-%s-%d %d:%d:%d UTC] mail() on [%smail_log.php:%d]: To: test@example.com -- Headers: X-Test: 1
+[%d-%s-%d %d:%d:%d UTC] mail() on [%smail_log.php:%d]: To: test@example.com -- Headers: X-Test: 1 -- Subject: mail.log test
Done
diff --git a/ext/standard/tests/math/base_convert_error.phpt b/ext/standard/tests/math/base_convert_error.phpt
index 4e35a81f36..a05268ab94 100644
--- a/ext/standard/tests/math/base_convert_error.phpt
+++ b/ext/standard/tests/math/base_convert_error.phpt
@@ -40,4 +40,4 @@ Warning: base_convert(): Invalid `from base' (1) in %s on line %d
Warning: base_convert(): Invalid `to base' (37) in %s on line %s
Incorrect input
-Catchable fatal error: Object of class classA could not be converted to string in %s on line %d \ No newline at end of file
+Recoverable fatal error: Object of class classA could not be converted to string in %s on line %d \ No newline at end of file
diff --git a/ext/standard/tests/math/bindec_error.phpt b/ext/standard/tests/math/bindec_error.phpt
index 8cf3cf7028..aa6ac9cdd1 100644
--- a/ext/standard/tests/math/bindec_error.phpt
+++ b/ext/standard/tests/math/bindec_error.phpt
@@ -34,4 +34,4 @@ Warning: bindec() expects exactly 1 parameter, 0 given in %s on line %d
Warning: bindec() expects exactly 1 parameter, 2 given in %s on line %d
Incorrect input
-Catchable fatal error: Object of class classA could not be converted to string in %s on line %d \ No newline at end of file
+Recoverable fatal error: Object of class classA could not be converted to string in %s on line %d \ No newline at end of file
diff --git a/ext/standard/tests/math/decbin_basic.phpt b/ext/standard/tests/math/decbin_basic.phpt
index b4389956f4..572a04245e 100644
--- a/ext/standard/tests/math/decbin_basic.phpt
+++ b/ext/standard/tests/math/decbin_basic.phpt
@@ -31,7 +31,7 @@ string(2) "11"
string(7) "1011111"
string(4) "1010"
string(12) "111101101110"
-string(2) "11"
+string(12) "111101101110"
string(6) "100111"
string(1) "0"
string(1) "1"
diff --git a/ext/standard/tests/math/dechex_basic.phpt b/ext/standard/tests/math/dechex_basic.phpt
index 2423d8e748..ac53a97b34 100644
--- a/ext/standard/tests/math/dechex_basic.phpt
+++ b/ext/standard/tests/math/dechex_basic.phpt
@@ -30,7 +30,7 @@ string(1) "3"
string(2) "5f"
string(1) "a"
string(3) "f6e"
-string(1) "3"
+string(3) "f6e"
string(2) "27"
string(1) "0"
string(1) "1"
diff --git a/ext/standard/tests/math/decoct_basic.phpt b/ext/standard/tests/math/decoct_basic.phpt
index cc1f0a899a..3a5011b973 100644
--- a/ext/standard/tests/math/decoct_basic.phpt
+++ b/ext/standard/tests/math/decoct_basic.phpt
@@ -30,7 +30,7 @@ string(1) "3"
string(3) "137"
string(2) "12"
string(4) "7556"
-string(1) "3"
+string(4) "7556"
string(2) "47"
string(1) "0"
string(1) "1"
diff --git a/ext/standard/tests/math/hexdec_error.phpt b/ext/standard/tests/math/hexdec_error.phpt
index e9ab9e512d..c45c2aa92b 100644
--- a/ext/standard/tests/math/hexdec_error.phpt
+++ b/ext/standard/tests/math/hexdec_error.phpt
@@ -33,4 +33,4 @@ Warning: hexdec() expects exactly 1 parameter, 2 given in %s on line %d
-- Incorrect input --
-Catchable fatal error: Object of class classA could not be converted to string in %s on line %d \ No newline at end of file
+Recoverable fatal error: Object of class classA could not be converted to string in %s on line %d \ No newline at end of file
diff --git a/ext/standard/tests/math/mt_rand_value.phpt b/ext/standard/tests/math/mt_rand_value.phpt
index 772305ad63..cf7b9f00b6 100644
--- a/ext/standard/tests/math/mt_rand_value.phpt
+++ b/ext/standard/tests/math/mt_rand_value.phpt
@@ -3,7 +3,21 @@ Test mt_rand() output
--FILE--
<?php
-mt_srand(12345678);
+mt_srand(12345678, MT_RAND_PHP);
+
+for ($i=0; $i<16; $i++) {
+ echo mt_rand().PHP_EOL;
+}
+echo PHP_EOL;
+
+$x = 0;
+for ($i=0; $i<1024; $i++) {
+ $x ^= mt_rand();
+}
+echo $x.PHP_EOL;
+echo PHP_EOL;
+
+mt_srand(12345678 /*, MT_RAND_MT19937 */);
for ($i=0; $i<16; $i++) {
echo mt_rand().PHP_EOL;
@@ -42,3 +56,22 @@ echo $x.PHP_EOL;
853386222
1571178311
+
+527860569
+1711027313
+1280820687
+688176834
+770499160
+412773096
+813703253
+898651287
+52508912
+757323740
+511765911
+274407457
+833082629
+1923803667
+1461450755
+1301698200
+
+1612214270
diff --git a/ext/standard/tests/math/mt_srand_error.phpt b/ext/standard/tests/math/mt_srand_error.phpt
index 537727c9bb..aa2fc75b74 100644
--- a/ext/standard/tests/math/mt_srand_error.phpt
+++ b/ext/standard/tests/math/mt_srand_error.phpt
@@ -2,12 +2,12 @@
Test mt_srand() - wrong params test mt_srand()
--FILE--
<?php
-var_dump(mt_srand(500, true));
+var_dump(mt_srand(500, 0, true));
var_dump(mt_srand("fivehundred"));
var_dump(mt_srand("500ABC"));
?>
--EXPECTF--
-Warning: mt_srand() expects at most 1 parameter, 2 given in %s on line 2
+Warning: mt_srand() expects at most 2 parameters, 3 given in %s on line 2
NULL
Warning: mt_srand() expects parameter 1 to be integer, string given in %s on line 3
diff --git a/ext/standard/tests/math/octdec_error.phpt b/ext/standard/tests/math/octdec_error.phpt
index 5d21383774..ba5f2116bb 100644
--- a/ext/standard/tests/math/octdec_error.phpt
+++ b/ext/standard/tests/math/octdec_error.phpt
@@ -34,4 +34,4 @@ Warning: octdec() expects exactly 1 parameter, 2 given in %s on line %d
-- Incorrect input --
-Catchable fatal error: Object of class classA could not be converted to string in %s on line %d \ No newline at end of file
+Recoverable fatal error: Object of class classA could not be converted to string in %s on line %d \ No newline at end of file
diff --git a/ext/standard/tests/math/pow_variation1.phpt b/ext/standard/tests/math/pow_variation1.phpt
index 5576e5b493..c744c4eb9d 100644
--- a/ext/standard/tests/math/pow_variation1.phpt
+++ b/ext/standard/tests/math/pow_variation1.phpt
@@ -143,21 +143,31 @@ int(1)
int(0)
-- Iteration 17 --
+
+Warning: A non-numeric value encountered in %s on line %d
int(0)
-- Iteration 18 --
+
+Warning: A non-numeric value encountered in %s on line %d
int(0)
-- Iteration 19 --
int(0)
-- Iteration 20 --
+
+Warning: A non-numeric value encountered in %s on line %d
int(0)
-- Iteration 21 --
+
+Warning: A non-numeric value encountered in %s on line %d
int(0)
-- Iteration 22 --
+
+Warning: A non-numeric value encountered in %s on line %d
int(0)
-- Iteration 23 --
diff --git a/ext/standard/tests/math/pow_variation1_64bit.phpt b/ext/standard/tests/math/pow_variation1_64bit.phpt
index e1986ba858..ea2ae45d18 100644
--- a/ext/standard/tests/math/pow_variation1_64bit.phpt
+++ b/ext/standard/tests/math/pow_variation1_64bit.phpt
@@ -143,21 +143,31 @@ int(1)
int(0)
-- Iteration 17 --
+
+Warning: A non-numeric value encountered in %s on line %d
int(0)
-- Iteration 18 --
+
+Warning: A non-numeric value encountered in %s on line %d
int(0)
-- Iteration 19 --
int(0)
-- Iteration 20 --
+
+Warning: A non-numeric value encountered in %s on line %d
int(0)
-- Iteration 21 --
+
+Warning: A non-numeric value encountered in %s on line %d
int(0)
-- Iteration 22 --
+
+Warning: A non-numeric value encountered in %s on line %d
int(0)
-- Iteration 23 --
diff --git a/ext/standard/tests/math/pow_variation2.phpt b/ext/standard/tests/math/pow_variation2.phpt
index f571936727..36b085b647 100644
--- a/ext/standard/tests/math/pow_variation2.phpt
+++ b/ext/standard/tests/math/pow_variation2.phpt
@@ -139,21 +139,31 @@ float(20.3)
float(1)
-- Iteration 17 --
+
+Warning: A non-numeric value encountered in %s on line %d
float(1)
-- Iteration 18 --
+
+Warning: A non-numeric value encountered in %s on line %d
float(1)
-- Iteration 19 --
int(1)
-- Iteration 20 --
+
+Warning: A non-numeric value encountered in %s on line %d
float(1)
-- Iteration 21 --
+
+Warning: A non-numeric value encountered in %s on line %d
float(1)
-- Iteration 22 --
+
+Warning: A non-numeric value encountered in %s on line %d
float(1)
-- Iteration 23 --
diff --git a/ext/standard/tests/math/srand_error.phpt b/ext/standard/tests/math/srand_error.phpt
index 6985af515a..ca9e4b939c 100644
--- a/ext/standard/tests/math/srand_error.phpt
+++ b/ext/standard/tests/math/srand_error.phpt
@@ -13,7 +13,7 @@ Test srand() function : error conditions - incorrect number of args
echo "*** Testing srand() : error conditions ***\n";
-var_dump(srand(500, true));
+var_dump(srand(500, 0, true));
var_dump(srand("fivehundred"));
var_dump(srand("500ABC"));
?>
@@ -21,7 +21,7 @@ var_dump(srand("500ABC"));
--EXPECTF--
*** Testing srand() : error conditions ***
-Warning: srand() expects at most 1 parameter, 2 given in %s on line %d
+Warning: srand() expects at most 2 parameters, 3 given in %s on line %d
NULL
Warning: srand() expects parameter 1 to be integer, string given in %s on line %d
diff --git a/ext/standard/tests/network/ip.phpt b/ext/standard/tests/network/ip.phpt
index 3fc1b9dcf4..abe50e7192 100644
--- a/ext/standard/tests/network/ip.phpt
+++ b/ext/standard/tests/network/ip.phpt
@@ -48,20 +48,22 @@ string(7) "0.0.0.0"
int(1118019956)
string(14) "66.163.161.116"
-Warning: ip2long() expects exactly 1 parameter, 0 given in %s on line %d
+Warning: ip2long() expects exactly 1 parameter, 0 given in %sip.php on line %d
NULL
bool(false)
bool(false)
int(1869573999)
-Warning: ip2long() expects parameter 1 to be string, array given in %s on line %d
+Warning: ip2long() expects parameter 1 to be string, array given in %sip.php on line %d
NULL
-Warning: long2ip() expects exactly 1 parameter, 0 given in %s on line %d
+Warning: long2ip() expects exactly 1 parameter, 0 given in %sip.php on line %d
NULL
string(13) "255.254.82.80"
-string(7) "0.0.0.0"
-Warning: long2ip() expects parameter 1 to be string, array given in %s on line %d
+Warning: long2ip() expects parameter 1 to be integer, string given in %sip.php on line %d
+NULL
+
+Warning: long2ip() expects parameter 1 to be integer, array given in %sip.php on line %d
NULL
Done
diff --git a/ext/standard/tests/network/ip_x86_64.phpt b/ext/standard/tests/network/ip_x86_64.phpt
index 1fcb8b2043..45a48ca9df 100644
--- a/ext/standard/tests/network/ip_x86_64.phpt
+++ b/ext/standard/tests/network/ip_x86_64.phpt
@@ -48,20 +48,22 @@ string(7) "0.0.0.0"
int(1118019956)
string(14) "66.163.161.116"
-Warning: ip2long() expects exactly 1 parameter, 0 given in %s on line %d
+Warning: ip2long() expects exactly 1 parameter, 0 given in %sip_x86_64.php on line %d
NULL
bool(false)
bool(false)
int(1869573999)
-Warning: ip2long() expects parameter 1 to be string, array given in %s on line %d
+Warning: ip2long() expects parameter 1 to be string, array given in %sip_x86_64.php on line %d
NULL
-Warning: long2ip() expects exactly 1 parameter, 0 given in %s on line %d
+Warning: long2ip() expects exactly 1 parameter, 0 given in %sip_x86_64.php on line %d
NULL
string(13) "255.254.82.80"
-string(7) "0.0.0.0"
-Warning: long2ip() expects parameter 1 to be string, array given in %s on line %d
+Warning: long2ip() expects parameter 1 to be integer, string given in %sip_x86_64.php on line %d
+NULL
+
+Warning: long2ip() expects parameter 1 to be integer, array given in %sip_x86_64.php on line %d
NULL
Done
diff --git a/ext/standard/tests/network/long2ip_variation1.phpt b/ext/standard/tests/network/long2ip_variation1.phpt
index 2dc6fb123b..e7336712be 100644
--- a/ext/standard/tests/network/long2ip_variation1.phpt
+++ b/ext/standard/tests/network/long2ip_variation1.phpt
@@ -126,19 +126,19 @@ string(15) "255.255.255.246"
string(7) "0.0.0.0"
--empty array--
-Error: 2 - long2ip() expects parameter 1 to be string, array given, %s(%d)
+Error: 2 - long2ip() expects parameter 1 to be integer, array given, %slong2ip_variation1.php(%d)
NULL
--int indexed array--
-Error: 2 - long2ip() expects parameter 1 to be string, array given, %s(%d)
+Error: 2 - long2ip() expects parameter 1 to be integer, array given, %slong2ip_variation1.php(%d)
NULL
--associative array--
-Error: 2 - long2ip() expects parameter 1 to be string, array given, %s(%d)
+Error: 2 - long2ip() expects parameter 1 to be integer, array given, %slong2ip_variation1.php(%d)
NULL
--nested arrays--
-Error: 2 - long2ip() expects parameter 1 to be string, array given, %s(%d)
+Error: 2 - long2ip() expects parameter 1 to be integer, array given, %slong2ip_variation1.php(%d)
NULL
--uppercase NULL--
@@ -160,28 +160,35 @@ string(7) "0.0.0.1"
string(7) "0.0.0.0"
--empty string DQ--
-string(7) "0.0.0.0"
+Error: 2 - long2ip() expects parameter 1 to be integer, string given, %slong2ip_variation1.php(%d)
+NULL
--empty string SQ--
-string(7) "0.0.0.0"
+Error: 2 - long2ip() expects parameter 1 to be integer, string given, %slong2ip_variation1.php(%d)
+NULL
--string DQ--
-string(7) "0.0.0.0"
+Error: 2 - long2ip() expects parameter 1 to be integer, string given, %slong2ip_variation1.php(%d)
+NULL
--string SQ--
-string(7) "0.0.0.0"
+Error: 2 - long2ip() expects parameter 1 to be integer, string given, %slong2ip_variation1.php(%d)
+NULL
--mixed case string--
-string(7) "0.0.0.0"
+Error: 2 - long2ip() expects parameter 1 to be integer, string given, %slong2ip_variation1.php(%d)
+NULL
--heredoc--
-string(7) "0.0.0.0"
+Error: 2 - long2ip() expects parameter 1 to be integer, string given, %slong2ip_variation1.php(%d)
+NULL
--instance of classWithToString--
-string(7) "0.0.0.0"
+Error: 2 - long2ip() expects parameter 1 to be integer, object given, %slong2ip_variation1.php(%d)
+NULL
--instance of classWithoutToString--
-Error: 2 - long2ip() expects parameter 1 to be string, object given, %s(%d)
+Error: 2 - long2ip() expects parameter 1 to be integer, object given, %slong2ip_variation1.php(%d)
NULL
--undefined var--
@@ -191,6 +198,6 @@ string(7) "0.0.0.0"
string(7) "0.0.0.0"
--resource--
-Error: 2 - long2ip() expects parameter 1 to be string, resource given, %s(%d)
+Error: 2 - long2ip() expects parameter 1 to be integer, resource given, %slong2ip_variation1.php(%d)
NULL
===DONE===
diff --git a/ext/standard/tests/serialize/bug69425.phpt b/ext/standard/tests/serialize/bug69425.phpt
index bfa8b9b369..e39f855f23 100644
--- a/ext/standard/tests/serialize/bug69425.phpt
+++ b/ext/standard/tests/serialize/bug69425.phpt
@@ -26,7 +26,7 @@ var_dump($data);
int(1)
array(2) {
[0]=>
- object(DateInterval)#1 (15) {
+ object(DateInterval)#1 (16) {
["y"]=>
int(-1)
["m"]=>
@@ -39,6 +39,8 @@ array(2) {
int(-1)
["s"]=>
int(-1)
+ ["f"]=>
+ float(-1)
["weekday"]=>
int(-1)
["weekday_behavior"]=>
diff --git a/ext/standard/tests/serialize/bug72785.phpt b/ext/standard/tests/serialize/bug72785.phpt
new file mode 100644
index 0000000000..8bcdf635f7
--- /dev/null
+++ b/ext/standard/tests/serialize/bug72785.phpt
@@ -0,0 +1,24 @@
+--TEST--
+Bug #72785: allowed_classes only applies to outermost unserialize()
+--FILE--
+<?php
+
+// Forbidden class
+class A {}
+
+$p = 'x:i:0;a:1:{i:0;O:1:"A":0:{}};m:a:0:{}';
+$s = 'C:11:"ArrayObject":' . strlen($p) . ':{' . $p . '}';
+var_dump(unserialize($s, ['allowed_classes' => ['ArrayObject']]));
+
+?>
+--EXPECT--
+object(ArrayObject)#1 (1) {
+ ["storage":"ArrayObject":private]=>
+ array(1) {
+ [0]=>
+ object(__PHP_Incomplete_Class)#2 (1) {
+ ["__PHP_Incomplete_Class_Name"]=>
+ string(1) "A"
+ }
+ }
+}
diff --git a/ext/standard/tests/serialize/unserialize_error_001.phpt b/ext/standard/tests/serialize/unserialize_error_001.phpt
index 5589cbd835..6773ec748c 100644
--- a/ext/standard/tests/serialize/unserialize_error_001.phpt
+++ b/ext/standard/tests/serialize/unserialize_error_001.phpt
@@ -13,40 +13,11 @@ var_dump(unserialize($s, ["allowed_classes" => 0]));
var_dump(unserialize($s, ["allowed_classes" => 1]));
--EXPECTF--
-array(3) {
- [0]=>
- object(__PHP_Incomplete_Class)#%d (2) {
- ["__PHP_Incomplete_Class_Name"]=>
- string(3) "foo"
- ["x"]=>
- string(3) "bar"
- }
- [1]=>
- int(2)
- [2]=>
- string(1) "3"
-}
-array(3) {
- [0]=>
- object(__PHP_Incomplete_Class)#%d (2) {
- ["__PHP_Incomplete_Class_Name"]=>
- string(3) "foo"
- ["x"]=>
- string(3) "bar"
- }
- [1]=>
- int(2)
- [2]=>
- string(1) "3"
-}
-array(3) {
- [0]=>
- object(foo)#%d (1) {
- ["x"]=>
- string(3) "bar"
- }
- [1]=>
- int(2)
- [2]=>
- string(1) "3"
-}
+Warning: unserialize(): allowed_classes option should be array or boolean in %s on line %d
+bool(false)
+
+Warning: unserialize(): allowed_classes option should be array or boolean in %s on line %d
+bool(false)
+
+Warning: unserialize(): allowed_classes option should be array or boolean in %s on line %d
+bool(false)
diff --git a/ext/standard/tests/streams/bug61115.phpt b/ext/standard/tests/streams/bug61115.phpt
index 29dc7c1ccc..5cfc9c2ac3 100644
--- a/ext/standard/tests/streams/bug61115.phpt
+++ b/ext/standard/tests/streams/bug61115.phpt
@@ -10,4 +10,4 @@ stream_context_set_params($resourceFileTemp, array());
preg_replace('', function() {}, $resourceFileTemp);
?>
--EXPECTF--
-Catchable fatal error: Object of class Closure could not be converted to string in %s on line %d
+Recoverable fatal error: Object of class Closure could not be converted to string in %s on line %d
diff --git a/ext/standard/tests/streams/stream_context_tcp_nodelay.phpt b/ext/standard/tests/streams/stream_context_tcp_nodelay.phpt
new file mode 100644
index 0000000000..401c65bce0
--- /dev/null
+++ b/ext/standard/tests/streams/stream_context_tcp_nodelay.phpt
@@ -0,0 +1,25 @@
+--TEST--
+stream context tcp_nodelay
+--SKIPIF--
+<?php
+if (getenv("SKIP_ONLINE_TESTS")) die("skip online test");
+if (!extension_loaded("sockets")) die("skip: need sockets");
+ ?>
+--FILE--
+<?php
+$ctxt = stream_context_create([
+ "socket" => [
+ "tcp_nodelay" => true
+ ]
+]);
+
+$stream = stream_socket_client(
+ "tcp://www.php.net:80", $errno, $errstr, 10, STREAM_CLIENT_CONNECT, $ctxt);
+
+$socket =
+ socket_import_stream($stream);
+
+var_dump(socket_get_option($socket, SOL_TCP, TCP_NODELAY) > 0);
+?>
+--EXPECT--
+bool(true)
diff --git a/ext/standard/tests/streams/stream_context_tcp_nodelay_fopen.phpt b/ext/standard/tests/streams/stream_context_tcp_nodelay_fopen.phpt
new file mode 100644
index 0000000000..bf35925e4d
--- /dev/null
+++ b/ext/standard/tests/streams/stream_context_tcp_nodelay_fopen.phpt
@@ -0,0 +1,24 @@
+--TEST--
+stream context tcp_nodelay fopen
+--SKIPIF--
+<?php
+if (getenv("SKIP_ONLINE_TESTS")) die("skip online test");
+if (!extension_loaded("sockets")) die("skip: need sockets");
+?>
+--FILE--
+<?php
+$ctxt = stream_context_create([
+ "socket" => [
+ "tcp_nodelay" => true
+ ]
+]);
+
+$stream = fopen("http://www.php.net", "r", false, $ctxt);
+
+$socket =
+ @socket_import_stream($stream);
+
+var_dump(socket_get_option($socket, STREAM_IPPROTO_TCP, TCP_NODELAY) > 0);
+?>
+--EXPECT--
+bool(true)
diff --git a/ext/standard/tests/streams/stream_context_tcp_nodelay_server.phpt b/ext/standard/tests/streams/stream_context_tcp_nodelay_server.phpt
new file mode 100644
index 0000000000..6606a15052
--- /dev/null
+++ b/ext/standard/tests/streams/stream_context_tcp_nodelay_server.phpt
@@ -0,0 +1,47 @@
+--TEST--
+stream context tcp_nodelay server
+--SKIPIF--
+<?php if (!extension_loaded("sockets")) die("skip: need sockets") ?>
+--FILE--
+<?php
+$serverCode = <<<'CODE'
+ $ctxt = stream_context_create([
+ "socket" => [
+ "tcp_nodelay" => true
+ ]
+ ]);
+
+ $server = stream_socket_server(
+ "tcp://127.0.0.1:9099", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctxt);
+
+ $client = stream_socket_accept($server);
+
+ var_dump(socket_get_option(
+ socket_import_stream($server),
+ SOL_TCP, TCP_NODELAY) > 0);
+
+ var_dump(socket_get_option(
+ socket_import_stream($client),
+ SOL_TCP, TCP_NODELAY) > 0);
+
+ fclose($client);
+ fclose($server);
+CODE;
+
+$clientCode = <<<'CODE'
+ $test = stream_socket_client(
+ "tcp://127.0.0.1:9099", $errno, $errstr, 10);
+
+ sleep(1);
+
+ fclose($test);
+CODE;
+
+include sprintf(
+ "%s/../../../openssl/tests/ServerClientTestCase.inc",
+ dirname(__FILE__));
+ServerClientTestCase::getInstance()->run($serverCode, $clientCode);
+?>
+--EXPECT--
+bool(false)
+bool(true)
diff --git a/ext/standard/tests/streams/stream_socket_enable_crypto-win32.phpt b/ext/standard/tests/streams/stream_socket_enable_crypto-win32.phpt
index c61e93d038..7353ee4d97 100644
--- a/ext/standard/tests/streams/stream_socket_enable_crypto-win32.phpt
+++ b/ext/standard/tests/streams/stream_socket_enable_crypto-win32.phpt
@@ -44,7 +44,7 @@ bool(false)
Warning: stream_socket_enable_crypto(): When enabling encryption you must specify the crypto type in %s on line %d
bool(false)
-Warning: stream_socket_enable_crypto(): SSLv2 unavailable in the OpenSSL library against which PHP is linked in %s on line %d
+Warning: stream_socket_enable_crypto(): SSLv2 unavailable in this PHP version in %s on line %d
bool(false)
Warning: stream_socket_enable_crypto(): SSL: A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram socket using a sendto call) no address was supplied.
diff --git a/ext/standard/tests/strings/bug40754.phpt b/ext/standard/tests/strings/bug40754.phpt
index 6cfe47056b..84e4337463 100644
--- a/ext/standard/tests/strings/bug40754.phpt
+++ b/ext/standard/tests/strings/bug40754.phpt
@@ -32,7 +32,7 @@ var_dump(substr("abcde", $v, $v));
bool(false)
bool(false)
-Warning: substr_count(): Offset value 2147483647 exceeds string length in %s on line %d
+Warning: substr_count(): Offset not contained in string in %s on line %d
bool(false)
Warning: substr_compare(): The start position cannot exceed initial string length in %s on line %d
@@ -41,10 +41,10 @@ bool(false)
Warning: stripos(): Offset not contained in string in %s on line %d
bool(false)
-Warning: substr_count(): Offset value 2147483647 exceeds string length in %s on line %d
+Warning: substr_count(): Offset not contained in string in %s on line %d
bool(false)
-Warning: substr_count(): Length value 2147483647 exceeds string length in %s on line %d
+Warning: substr_count(): Invalid length value in %s on line %d
bool(false)
Warning: strpos(): Offset not contained in string in %s on line %d
diff --git a/ext/standard/tests/strings/bug53021.phpt b/ext/standard/tests/strings/bug53021.phpt
index 4a8fbe4f76..38d904761d 100644
--- a/ext/standard/tests/strings/bug53021.phpt
+++ b/ext/standard/tests/strings/bug53021.phpt
@@ -10,11 +10,14 @@ echo html_entity_decode("&quot;", ENT_QUOTES, 'UTF-8'), "\n";
echo html_entity_decode("&#34;", ENT_QUOTES, 'UTF-8'), "\n";
echo html_entity_decode("&quot;", ENT_COMPAT, 'UTF-8'), "\n";
echo html_entity_decode("&#34;", ENT_COMPAT, 'UTF-8'), "\n";
+echo html_entity_decode("&quot;"), "\n";
+echo html_entity_decode("&#34;"), "\n";
echo "\nsingle quotes variations:", "\n";
echo html_entity_decode("&#39;", ENT_NOQUOTES, 'UTF-8'), "\n";
echo html_entity_decode("&#39;", ENT_QUOTES, 'UTF-8'), "\n";
echo html_entity_decode("&#39;", ENT_COMPAT, 'UTF-8'), "\n";
+echo html_entity_decode("&#39;"), "\n";
--EXPECT--
array(1) {
[1]=>
@@ -27,8 +30,11 @@ double quotes variations:
"
"
"
+"
+"
single quotes variations:
&#39;
'
&#39;
+&#39;
diff --git a/ext/standard/tests/strings/bug55871.phpt b/ext/standard/tests/strings/bug55871.phpt
index 249d1bd3a3..0044f50ce7 100644
--- a/ext/standard/tests/strings/bug55871.phpt
+++ b/ext/standard/tests/strings/bug55871.phpt
@@ -41,6 +41,8 @@ array(1) {
[0]=>
string(0) ""
}
+
+Warning: A non-numeric value encountered in %s on line %d
array(1) {
[0]=>
string(40) "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
diff --git a/ext/standard/tests/strings/bug74041.phpt b/ext/standard/tests/strings/bug74041.phpt
new file mode 100644
index 0000000000..598e46d880
--- /dev/null
+++ b/ext/standard/tests/strings/bug74041.phpt
@@ -0,0 +1,12 @@
+--TEST--
+Bug #74041: substr_count with length=0 broken
+--FILE--
+<?php
+
+var_dump(substr_count("aaa", "a", 0, 0));
+var_dump(substr_count("", "a", 0, 0));
+
+?>
+--EXPECT--
+int(0)
+int(0)
diff --git a/ext/standard/tests/strings/html_entity_decode3.phpt b/ext/standard/tests/strings/html_entity_decode3.phpt
index fcf2710679..48cff17bda 100644
--- a/ext/standard/tests/strings/html_entity_decode3.phpt
+++ b/ext/standard/tests/strings/html_entity_decode3.phpt
@@ -14,6 +14,7 @@ $tests = array(
"&#x0E;",
"&#x1F;",
"&#x20;", //allowed always
+ "&#x27;", //single quote, depends on flags
"&#x7F;", //DEL
"&#x80;", //C1
"&#x9F;",
@@ -76,6 +77,17 @@ foreach ($tests as $t) {
}
}
+echo "\n*** Default options ***\n";
+
+foreach ($tests as $t) {
+ $dec = html_entity_decode($t);
+ if ($t == $dec) {
+ echo "$t\tNOT DECODED\n";
+ } else {
+ echo "$t\tDECODED\n";
+ }
+}
+
echo "\nDone.\n";
--EXPECT--
*** HTML 4.01 ***
@@ -89,6 +101,7 @@ echo "\nDone.\n";
&#x0E; NOT DECODED
&#x1F; NOT DECODED
&#x20; DECODED
+&#x27; DECODED
&#x7F; NOT DECODED
&#x80; NOT DECODED
&#x9F; NOT DECODED
@@ -117,6 +130,7 @@ echo "\nDone.\n";
&#x0E; NOT DECODED
&#x1F; NOT DECODED
&#x20; DECODED
+&#x27; DECODED
&#x7F; DECODED
&#x80; DECODED
&#x9F; DECODED
@@ -145,6 +159,7 @@ echo "\nDone.\n";
&#x0E; NOT DECODED
&#x1F; NOT DECODED
&#x20; DECODED
+&#x27; DECODED
&#x7F; NOT DECODED
&#x80; NOT DECODED
&#x9F; NOT DECODED
@@ -173,6 +188,7 @@ echo "\nDone.\n";
&#x0E; NOT DECODED
&#x1F; NOT DECODED
&#x20; DECODED
+&#x27; DECODED
&#x7F; DECODED
&#x80; DECODED
&#x9F; DECODED
@@ -190,4 +206,33 @@ echo "\nDone.\n";
&#x2FFFE; DECODED
&#x2FFFF; DECODED
+*** Default options ***
+&#0; NOT DECODED
+&#1; NOT DECODED
+&#x09; DECODED
+&#x0A; DECODED
+&#x0B; NOT DECODED
+&#x0C; NOT DECODED
+&#x0D; DECODED
+&#x0E; NOT DECODED
+&#x1F; NOT DECODED
+&#x20; DECODED
+&#x27; NOT DECODED
+&#x7F; NOT DECODED
+&#x80; NOT DECODED
+&#x9F; NOT DECODED
+&#xA0; DECODED
+&#xD7FF; DECODED
+&#xD800; NOT DECODED
+&#xDFFF; NOT DECODED
+&#xE000; DECODED
+&#xFFFE; DECODED
+&#xFFFF; DECODED
+&#xFDCF; DECODED
+&#xFDD0; DECODED
+&#xFDEF; DECODED
+&#xFDF0; DECODED
+&#x2FFFE; DECODED
+&#x2FFFF; DECODED
+
Done.
diff --git a/ext/standard/tests/strings/lcfirst.phpt b/ext/standard/tests/strings/lcfirst.phpt
index 6cbd213fbe..5424c5018d 100644
--- a/ext/standard/tests/strings/lcfirst.phpt
+++ b/ext/standard/tests/strings/lcfirst.phpt
Binary files differ
diff --git a/ext/standard/tests/strings/parse_str_memory_error.phpt b/ext/standard/tests/strings/parse_str_memory_error.phpt
new file mode 100644
index 0000000000..0242d97d06
--- /dev/null
+++ b/ext/standard/tests/strings/parse_str_memory_error.phpt
@@ -0,0 +1,19 @@
+--TEST--
+parse_str() should not read uninitialized memory when checking for $this
+--FILE--
+<?php
+
+function test() {
+ // strlen("abcd") == 4 is relevant
+ parse_str('abcd=1', $array);
+ var_dump($array);
+}
+
+test();
+
+?>
+--EXPECT--
+array(1) {
+ ["abcd"]=>
+ string(1) "1"
+}
diff --git a/ext/standard/tests/strings/str_replace.phpt b/ext/standard/tests/strings/str_replace.phpt
index 15c1c8e53d..b53ed665d4 100644
--- a/ext/standard/tests/strings/str_replace.phpt
+++ b/ext/standard/tests/strings/str_replace.phpt
@@ -118,7 +118,7 @@ var_dump($count);
echo "\n-- Testing objects --\n";
-/* we get "Catchable fatal error: saying Object of class could not be converted
+/* we get "Recoverable fatal error: saying Object of class could not be converted
to string" by default, when an object is passed instead of string:
The error can be avoided by choosing the __toString magix method as follows: */
diff --git a/ext/standard/tests/strings/str_replace_variation3.phpt b/ext/standard/tests/strings/str_replace_variation3.phpt
index 7b46f8b286..8f6f38f1f9 100644
--- a/ext/standard/tests/strings/str_replace_variation3.phpt
+++ b/ext/standard/tests/strings/str_replace_variation3.phpt
@@ -31,7 +31,7 @@ var_dump($count);
echo "\n-- Testing objects --\n";
-/* we get "Catchable fatal error: saying Object of class could not be converted
+/* we get "Recoverable fatal error: saying Object of class could not be converted
to string" by default, when an object is passed instead of string:
The error can be avoided by choosing the __toString magix method as follows: */
diff --git a/ext/standard/tests/strings/strcasecmp.phpt b/ext/standard/tests/strings/strcasecmp.phpt
index b3452cfd7c..265b8708ea 100644
--- a/ext/standard/tests/strings/strcasecmp.phpt
+++ b/ext/standard/tests/strings/strcasecmp.phpt
Binary files differ
diff --git a/ext/standard/tests/strings/strcmp.phpt b/ext/standard/tests/strings/strcmp.phpt
index e77ed6e466..dea94644aa 100644
--- a/ext/standard/tests/strings/strcmp.phpt
+++ b/ext/standard/tests/strings/strcmp.phpt
Binary files differ
diff --git a/ext/standard/tests/strings/stripos_basic2.phpt b/ext/standard/tests/strings/stripos_basic2.phpt
index 3022bae168..226d3f61d2 100644
--- a/ext/standard/tests/strings/stripos_basic2.phpt
+++ b/ext/standard/tests/strings/stripos_basic2.phpt
@@ -18,6 +18,9 @@ var_dump( stripos("Hello, World", "Hello", 0) );
var_dump( stripos("Hello, World", 'Hello', 1) );
var_dump( stripos('Hello, World', 'WORLD', 1) );
var_dump( stripos('Hello, World', "WoRld", 5) );
+var_dump( stripos('Hello, World', "WoRld", -6) );
+var_dump( stripos('Hello, World', "WoRld", -3) );
+var_dump( stripos('Hello, World', "WoRld", -12) );
//heredoc string for haystack & needle, with various offsets
var_dump( stripos($heredoc_str, "Hello, World", 0) );
@@ -25,12 +28,19 @@ var_dump( stripos($heredoc_str, 'Hello', 0) );
var_dump( stripos($heredoc_str, 'Hello', 1) );
var_dump( stripos($heredoc_str, $heredoc_str, 0) );
var_dump( stripos($heredoc_str, $heredoc_str, 1) );
+var_dump( stripos($heredoc_str, $heredoc_str, -strlen($heredoc_str)) );
+var_dump( stripos($heredoc_str, $heredoc_str, -strlen($heredoc_str)+1) );
//various offsets
var_dump( stripos("Hello, World", "o", 3) );
var_dump( stripos("Hello, World", "O", 5) );
var_dump( stripos("Hello, World", "o", 6) );
var_dump( stripos("Hello, World", "o", 10) );
+var_dump( stripos("Hello, World", "o", -7) );
+var_dump( stripos("Hello, World", "o", -8) );
+var_dump( stripos("Hello, World", "o", -10) );
+var_dump( stripos("Hello, World", "o", -4) );
+var_dump( stripos("Hello, World", "o", -3) );
echo "*** Done ***";
?>
--EXPECTF--
@@ -40,13 +50,23 @@ int(0)
bool(false)
int(7)
int(7)
+int(7)
+bool(false)
+int(7)
int(0)
int(0)
bool(false)
int(0)
bool(false)
+int(0)
+bool(false)
int(4)
int(8)
int(8)
bool(false)
+int(8)
+int(4)
+int(4)
+int(8)
+bool(false)
*** Done ***
diff --git a/ext/standard/tests/strings/stripos_error.phpt b/ext/standard/tests/strings/stripos_error.phpt
index ef6ad9e6ec..c59473046d 100644
--- a/ext/standard/tests/strings/stripos_error.phpt
+++ b/ext/standard/tests/strings/stripos_error.phpt
@@ -16,6 +16,13 @@ var_dump( stripos("String") );
echo "\n-- With more than expected number of arguments --";
var_dump( stripos("string", "String", 1, 'extra_arg') );
+
+echo "\n-- Offset beyond the end of the string --";
+var_dump( stripos("Hello World", "o", 12) );
+
+echo "\n-- Offset before the start of the string --";
+var_dump( stripos("Hello World", "o", -12) );
+
echo "*** Done ***";
?>
--EXPECTF--
@@ -32,4 +39,12 @@ NULL
-- With more than expected number of arguments --
Warning: stripos() expects at most 3 parameters, 4 given in %s on line %d
NULL
+
+-- Offset beyond the end of the string --
+Warning: stripos(): Offset not contained in string in %s on line %d
+bool(false)
+
+-- Offset before the start of the string --
+Warning: stripos(): Offset not contained in string in %s on line %d
+bool(false)
*** Done ***
diff --git a/ext/standard/tests/strings/stripos_variation14.phpt b/ext/standard/tests/strings/stripos_variation14.phpt
index aabf0e62ea..3339e2f27e 100644
--- a/ext/standard/tests/strings/stripos_variation14.phpt
+++ b/ext/standard/tests/strings/stripos_variation14.phpt
@@ -1,7 +1,5 @@
--TEST--
Test stripos() function : usage variations - unexpected inputs for 'offset' argument
---SKIPIF--
-<?php if (PHP_INT_SIZE != 8) die("skip this test is for 64-bit only");
--FILE--
<?php
/* Prototype : int stripos ( string $haystack, string $needle [, int $offset] );
@@ -37,7 +35,7 @@ $offsets = array (
// float values
1.5,
-1.5,
- 1.5e10,
+ 1.5e6,
1.6E-10,
.5,
@@ -91,8 +89,6 @@ echo "*** Done ***";
-- Iteration 1 --
int(6)
-- Iteration 2 --
-
-Warning: stripos(): Offset not contained in string in %s on line %d
bool(false)
-- Iteration 3 --
diff --git a/ext/standard/tests/strings/strlen.phpt b/ext/standard/tests/strings/strlen.phpt
index ab54445943..52e0200af1 100644
--- a/ext/standard/tests/strings/strlen.phpt
+++ b/ext/standard/tests/strings/strlen.phpt
Binary files differ
diff --git a/ext/standard/tests/strings/strpos.phpt b/ext/standard/tests/strings/strpos.phpt
index 36854d1b37..a2237fe005 100644
--- a/ext/standard/tests/strings/strpos.phpt
+++ b/ext/standard/tests/strings/strpos.phpt
Binary files differ
diff --git a/ext/standard/tests/strings/strstr.phpt b/ext/standard/tests/strings/strstr.phpt
index fd7f58ef1a..a16cda9c93 100644
--- a/ext/standard/tests/strings/strstr.phpt
+++ b/ext/standard/tests/strings/strstr.phpt
Binary files differ
diff --git a/ext/standard/tests/strings/strval_error.phpt b/ext/standard/tests/strings/strval_error.phpt
index c31a2ab35b..629c003679 100644
--- a/ext/standard/tests/strings/strval_error.phpt
+++ b/ext/standard/tests/strings/strval_error.phpt
@@ -48,4 +48,4 @@ NULL
-- Testing strval() function with object which has not toString() method --
-Catchable fatal error: Object of class MyClass could not be converted to string in %s on line %d \ No newline at end of file
+Recoverable fatal error: Object of class MyClass could not be converted to string in %s on line %d \ No newline at end of file
diff --git a/ext/standard/tests/strings/substr_count_basic.phpt b/ext/standard/tests/strings/substr_count_basic.phpt
index f880e9481e..c7c96fd55b 100644
--- a/ext/standard/tests/strings/substr_count_basic.phpt
+++ b/ext/standard/tests/strings/substr_count_basic.phpt
@@ -9,12 +9,17 @@ var_dump(@substr_count("a", ""));
var_dump(@substr_count("", "a"));
var_dump(@substr_count("", "a"));
var_dump(@substr_count("", chr(0)));
+
$a = str_repeat("abcacba", 100);
var_dump(@substr_count($a, "bca"));
+
$a = str_repeat("abcacbabca", 100);
var_dump(@substr_count($a, "bca"));
var_dump(substr_count($a, "bca", 200));
var_dump(substr_count($a, "bca", 200, 50));
+var_dump(substr_count($a, "bca", -200));
+var_dump(substr_count($a, "bca", -200, 50));
+var_dump(substr_count($a, "bca", -200, -50));
echo "Done\n";
@@ -30,4 +35,7 @@ int(100)
int(200)
int(160)
int(10)
+int(40)
+int(10)
+int(30)
Done
diff --git a/ext/standard/tests/strings/substr_count_error.phpt b/ext/standard/tests/strings/substr_count_error.phpt
index f6924217b9..881da391e6 100644
--- a/ext/standard/tests/strings/substr_count_error.phpt
+++ b/ext/standard/tests/strings/substr_count_error.phpt
@@ -4,27 +4,31 @@ Test substr_count() function (error conditions)
<?php
echo "\n*** Testing error conditions ***\n";
+$str = 'abcdefghik';
+
/* Zero argument */
var_dump( substr_count() );
/* more than expected no. of args */
var_dump( substr_count($str, "t", 0, 15, 30) );
-/* offset as negative value */
-var_dump(substr_count($str, "t", -5));
+/* offset before start */
+var_dump(substr_count($str, "t", -20));
/* offset > size of the string */
var_dump(substr_count($str, "t", 25));
/* Using offset and length to go beyond the size of the string:
Warning message expected, as length+offset > length of string */
-var_dump( substr_count($str, "i", 5, 15) );
+var_dump( substr_count($str, "i", 5, 7) );
-/* length as Null */
-var_dump( substr_count($str, "t", "", "") );
-var_dump( substr_count($str, "i", NULL, NULL) );
-
-echo "Done\n";
+/* Invalid offset argument */
+var_dump( substr_count($str, "t", "") );
+
+/* length too small */
+var_dump( substr_count($str, "t", 2, -20) );
+
+echo "Done\n";
?>
--EXPECTF--
@@ -33,33 +37,21 @@ echo "Done\n";
Warning: substr_count() expects at least 2 parameters, 0 given in %s on line %d
NULL
-Notice: Undefined variable: str in %s on line %d
-
Warning: substr_count() expects at most 4 parameters, 5 given in %s on line %d
NULL
-Notice: Undefined variable: str in %s on line %d
-
-Warning: substr_count(): Offset should be greater than or equal to 0 in %s on line %d
+Warning: substr_count(): Offset not contained in string in %s on line %d
bool(false)
-Notice: Undefined variable: str in %s on line %d
-
-Warning: substr_count(): Offset value 25 exceeds string length in %s on line %d
+Warning: substr_count(): Offset not contained in string in %s on line %d
bool(false)
-Notice: Undefined variable: str in %s on line %d
-
-Warning: substr_count(): Offset value 5 exceeds string length in %s on line %d
+Warning: substr_count(): Invalid length value in %s on line %d
bool(false)
-Notice: Undefined variable: str in %s on line %d
-
Warning: substr_count() expects parameter 3 to be integer, string given in %s on line %d
NULL
-Notice: Undefined variable: str in %s on line %d
-
-Warning: substr_count(): Length should be greater than 0 in %s on line %d
+Warning: substr_count(): Invalid length value in %s on line %d
bool(false)
Done
diff --git a/ext/standard/tests/strings/ucfirst.phpt b/ext/standard/tests/strings/ucfirst.phpt
index 8fb1a156b4..143c4bd426 100644
--- a/ext/standard/tests/strings/ucfirst.phpt
+++ b/ext/standard/tests/strings/ucfirst.phpt
Binary files differ
diff --git a/ext/standard/tests/strings/unpack_error.phpt b/ext/standard/tests/strings/unpack_error.phpt
index 75496512b7..484366293b 100644
--- a/ext/standard/tests/strings/unpack_error.phpt
+++ b/ext/standard/tests/strings/unpack_error.phpt
@@ -15,7 +15,7 @@ var_dump( unpack() );
echo "\n-- Testing unpack() function with more than expected no. of arguments --\n";
$extra_arg = 10;
-var_dump(unpack("I", pack("I", 65534), $extra_arg));
+var_dump(unpack("I", pack("I", 65534), 0, $extra_arg));
echo "\n-- Testing unpack() function with invalid format character --\n";
$extra_arg = 10;
@@ -27,12 +27,12 @@ var_dump(unpack("B", pack("I", 65534)));
-- Testing unpack() function with no arguments --
-Warning: unpack() expects exactly 2 parameters, 0 given in %s on line %d
+Warning: unpack() expects at least 2 parameters, 0 given in %s on line %d
NULL
-- Testing unpack() function with more than expected no. of arguments --
-Warning: unpack() expects exactly 2 parameters, 3 given in %s on line %d
+Warning: unpack() expects at most 3 parameters, 4 given in %s on line %d
NULL
-- Testing unpack() function with invalid format character --
diff --git a/ext/standard/tests/strings/unpack_offset.phpt b/ext/standard/tests/strings/unpack_offset.phpt
new file mode 100644
index 0000000000..c8c08e74f2
--- /dev/null
+++ b/ext/standard/tests/strings/unpack_offset.phpt
@@ -0,0 +1,17 @@
+--TEST--
+unpack() with offset
+--FILE--
+<?php
+$data = "pad" . pack("ll", 0x01020304, 0x05060708);
+
+$a = unpack("l2", $data, 3);
+printf("0x%08x 0x%08x\n", $a[1], $a[2]);
+
+printf("0x%08x 0x%08x\n",
+ unpack("l", $data, 3)[1],
+ unpack("@4/l", $data, 3)[1]);
+?>
+--EXPECT--
+0x01020304 0x05060708
+0x01020304 0x05060708
+
diff --git a/ext/standard/tests/url/base64_decode_basic_001.phpt b/ext/standard/tests/url/base64_decode_basic_001.phpt
index 7aba807e19..e1469c37e8 100644
--- a/ext/standard/tests/url/base64_decode_basic_001.phpt
+++ b/ext/standard/tests/url/base64_decode_basic_001.phpt
@@ -9,7 +9,7 @@ Test base64_decode() function : basic functionality - ensure all base64 alphabet
*/
echo "Decode an input string containing the whole base64 alphabet:\n";
-$allbase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
+$allbase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/VV==";
var_dump(bin2hex(base64_decode($allbase64)));
var_dump(bin2hex(base64_decode($allbase64, false)));
var_dump(bin2hex(base64_decode($allbase64, true)));
@@ -18,7 +18,7 @@ echo "Done";
?>
--EXPECTF--
Decode an input string containing the whole base64 alphabet:
-string(96) "00108310518720928b30d38f41149351559761969b71d79f8218a39259a7a29aabb2dbafc31cb3d35db7e39ebbf3dfbf"
-string(96) "00108310518720928b30d38f41149351559761969b71d79f8218a39259a7a29aabb2dbafc31cb3d35db7e39ebbf3dfbf"
-string(96) "00108310518720928b30d38f41149351559761969b71d79f8218a39259a7a29aabb2dbafc31cb3d35db7e39ebbf3dfbf"
+string(98) "00108310518720928b30d38f41149351559761969b71d79f8218a39259a7a29aabb2dbafc31cb3d35db7e39ebbf3dfbf55"
+string(98) "00108310518720928b30d38f41149351559761969b71d79f8218a39259a7a29aabb2dbafc31cb3d35db7e39ebbf3dfbf55"
+string(98) "00108310518720928b30d38f41149351559761969b71d79f8218a39259a7a29aabb2dbafc31cb3d35db7e39ebbf3dfbf55"
Done \ No newline at end of file
diff --git a/ext/standard/tests/url/base64_decode_basic_003.phpt b/ext/standard/tests/url/base64_decode_basic_003.phpt
new file mode 100644
index 0000000000..0407926b02
--- /dev/null
+++ b/ext/standard/tests/url/base64_decode_basic_003.phpt
@@ -0,0 +1,120 @@
+--TEST--
+Test base64_decode() function : basic functionality - padding and whitespace
+--SKIPIF--
+<?php if (!extension_loaded("json")) print "skip"; ?>
+--FILE--
+<?php
+/* Prototype : proto string base64_decode(string str[, bool strict])
+ * Description: Decodes string using MIME base64 algorithm
+ * Source code: ext/standard/base64.c
+ * Alias to functions:
+ */
+
+echo "Test base64_decode (output as JSON):\n";
+$data = [
+ "", "=", "==", "===", "====",
+ "V", "V=", "V==", "V===", "V====",
+ "VV", "VV=", "VV==", "VV===", "VV====",
+ "VVV", "VVV=", "VVV==", "VVV===", "VVV====",
+ "VVVV", "VVVV=", "VVVV==", "VVVV===", "VVVV====",
+ "=V", "=VV", "=VVV",
+ "==V", "==VV", "==VVV",
+ "===V", "===VV", "===VVV",
+ "====V", "====VV", "====VVV",
+ "=VVV", "V=VV", "VV=V", "VVV=",
+ "=VVVV", "V=VVV", "VV=VV", "VVV=V", "VVVV=",
+ "=VVV=", "V=VV=", "VV=V=", "VVV==",
+ "\nVV", "V\nV", "VV\n",
+ "\nVV==", "V\nV==", "VV\n==", "VV=\n=", "VV==\n",
+ "*VV", "V*V", "VV*",
+ "*VV==", "V*V==", "VV*==", "VV=*=", "VV==*",
+ "\0VV==", "V\0V==", "VV\0==", "VV=\0=", "VV==\0",
+ "\0VVV==", "V\0VV==", "VV\0V==", "VVV\0==", "VVV=\0=", "VVV==\0",
+];
+foreach ($data as $a) {
+ $b = base64_decode($a, false);
+ $c = base64_decode($a, true);
+ printf("base64 %-16s non-strict %-8s strict %s\n", json_encode($a), json_encode($b), json_encode($c));
+}
+echo "Done\n";
+?>
+--EXPECTF--
+Test base64_decode (output as JSON):
+base64 "" non-strict "" strict ""
+base64 "=" non-strict "" strict false
+base64 "==" non-strict "" strict false
+base64 "===" non-strict "" strict false
+base64 "====" non-strict "" strict false
+base64 "V" non-strict "" strict false
+base64 "V=" non-strict "" strict false
+base64 "V==" non-strict "" strict false
+base64 "V===" non-strict "" strict false
+base64 "V====" non-strict "" strict false
+base64 "VV" non-strict "U" strict "U"
+base64 "VV=" non-strict "U" strict false
+base64 "VV==" non-strict "U" strict "U"
+base64 "VV===" non-strict "U" strict false
+base64 "VV====" non-strict "U" strict false
+base64 "VVV" non-strict "UU" strict "UU"
+base64 "VVV=" non-strict "UU" strict "UU"
+base64 "VVV==" non-strict "UU" strict false
+base64 "VVV===" non-strict "UU" strict false
+base64 "VVV====" non-strict "UU" strict false
+base64 "VVVV" non-strict "UUU" strict "UUU"
+base64 "VVVV=" non-strict "UUU" strict false
+base64 "VVVV==" non-strict "UUU" strict false
+base64 "VVVV===" non-strict "UUU" strict false
+base64 "VVVV====" non-strict "UUU" strict false
+base64 "=V" non-strict "" strict false
+base64 "=VV" non-strict "U" strict false
+base64 "=VVV" non-strict "UU" strict false
+base64 "==V" non-strict "" strict false
+base64 "==VV" non-strict "U" strict false
+base64 "==VVV" non-strict "UU" strict false
+base64 "===V" non-strict "" strict false
+base64 "===VV" non-strict "U" strict false
+base64 "===VVV" non-strict "UU" strict false
+base64 "====V" non-strict "" strict false
+base64 "====VV" non-strict "U" strict false
+base64 "====VVV" non-strict "UU" strict false
+base64 "=VVV" non-strict "UU" strict false
+base64 "V=VV" non-strict "UU" strict false
+base64 "VV=V" non-strict "UU" strict false
+base64 "VVV=" non-strict "UU" strict "UU"
+base64 "=VVVV" non-strict "UUU" strict false
+base64 "V=VVV" non-strict "UUU" strict false
+base64 "VV=VV" non-strict "UUU" strict false
+base64 "VVV=V" non-strict "UUU" strict false
+base64 "VVVV=" non-strict "UUU" strict false
+base64 "=VVV=" non-strict "UU" strict false
+base64 "V=VV=" non-strict "UU" strict false
+base64 "VV=V=" non-strict "UU" strict false
+base64 "VVV==" non-strict "UU" strict false
+base64 "\nVV" non-strict "U" strict "U"
+base64 "V\nV" non-strict "U" strict "U"
+base64 "VV\n" non-strict "U" strict "U"
+base64 "\nVV==" non-strict "U" strict "U"
+base64 "V\nV==" non-strict "U" strict "U"
+base64 "VV\n==" non-strict "U" strict "U"
+base64 "VV=\n=" non-strict "U" strict "U"
+base64 "VV==\n" non-strict "U" strict "U"
+base64 "*VV" non-strict "U" strict false
+base64 "V*V" non-strict "U" strict false
+base64 "VV*" non-strict "U" strict false
+base64 "*VV==" non-strict "U" strict false
+base64 "V*V==" non-strict "U" strict false
+base64 "VV*==" non-strict "U" strict false
+base64 "VV=*=" non-strict "U" strict false
+base64 "VV==*" non-strict "U" strict false
+base64 "\u0000VV==" non-strict "U" strict false
+base64 "V\u0000V==" non-strict "U" strict false
+base64 "VV\u0000==" non-strict "U" strict false
+base64 "VV=\u0000=" non-strict "U" strict false
+base64 "VV==\u0000" non-strict "U" strict false
+base64 "\u0000VVV==" non-strict "UU" strict false
+base64 "V\u0000VV==" non-strict "UU" strict false
+base64 "VV\u0000V==" non-strict "UU" strict false
+base64 "VVV\u0000==" non-strict "UU" strict false
+base64 "VVV=\u0000=" non-strict "UU" strict false
+base64 "VVV==\u0000" non-strict "UU" strict false
+Done
diff --git a/ext/standard/tests/url/base64_decode_variation_001.phpt b/ext/standard/tests/url/base64_decode_variation_001.phpt
index 8734a96e0b..f4906a774a 100644
--- a/ext/standard/tests/url/base64_decode_variation_001.phpt
+++ b/ext/standard/tests/url/base64_decode_variation_001.phpt
@@ -95,13 +95,13 @@ Error: 8 - Undefined variable: undefined_var, %s(%d)
Error: 8 - Undefined variable: unset_var, %s(%d)
-- Arg value 0 --
-string(0) ""
+bool(false)
-- Arg value 1 --
-string(0) ""
+bool(false)
-- Arg value 12345 --
-string(6) "d76df8"
+bool(false)
-- Arg value -2345 --
bool(false)
@@ -148,13 +148,13 @@ string(0) ""
string(0) ""
-- Arg value true --
-string(0) ""
+bool(false)
-- Arg value false --
string(0) ""
-- Arg value TRUE --
-string(0) ""
+bool(false)
-- Arg value FALSE --
string(0) ""
diff --git a/ext/standard/tests/url/get_headers_error_001.phpt b/ext/standard/tests/url/get_headers_error_001.phpt
index 8d5fd11f60..270c8350c0 100644
--- a/ext/standard/tests/url/get_headers_error_001.phpt
+++ b/ext/standard/tests/url/get_headers_error_001.phpt
@@ -5,7 +5,7 @@ June Henriksen <juneih@redpill-linpro.com>
#PHPTestFest2009 Norway 2009-06-09 \o/
--FILE--
<?php
-/* Prototype : proto array get_headers(string url[, int format])
+/* Prototype : proto array get_headers(string url[, int format[, resource $context]])
* Description: Fetches all the headers sent by the server in response to a HTTP request
* Source code: ext/standard/url.c
* Alias to functions:
@@ -21,8 +21,9 @@ var_dump( get_headers() );
echo "\n-- Testing get_headers() function with more than expected no. of arguments --\n";
$url = 'string_val';
$format = 1;
+$context = stream_context_get_default();
$extra_arg = 10;
-var_dump( get_headers($url, $format, $extra_arg) );
+var_dump( get_headers($url, $format, $context, $extra_arg) );
echo "Done";
?>
@@ -36,7 +37,7 @@ NULL
-- Testing get_headers() function with more than expected no. of arguments --
-Warning: get_headers() expects at most 2 parameters, 3 given in %s on line 19
+Warning: get_headers() expects at most 3 parameters, 4 given in %s on line 20
NULL
Done
diff --git a/ext/standard/tests/url/get_headers_error_003.phpt b/ext/standard/tests/url/get_headers_error_003.phpt
new file mode 100644
index 0000000000..6c8878513c
--- /dev/null
+++ b/ext/standard/tests/url/get_headers_error_003.phpt
@@ -0,0 +1,31 @@
+--TEST--
+Test get_headers() function : test with context
+--FILE--
+<?php
+
+include dirname(__FILE__)."/../../../../sapi/cli/tests/php_cli_server.inc";
+php_cli_server_start('header("X-Request-Method: ".$_SERVER["REQUEST_METHOD"]);');
+
+$opts = array(
+ 'http' => array(
+ 'method' => 'HEAD'
+ )
+);
+
+$context = stream_context_create($opts);
+$headers = get_headers("http://".PHP_CLI_SERVER_ADDRESS, 1, $context);
+echo $headers["X-Request-Method"]."\n";
+
+stream_context_set_default($opts);
+$headers = get_headers("http://".PHP_CLI_SERVER_ADDRESS, 1);
+echo $headers["X-Request-Method"]."\n";
+
+echo "Done";
+?>
+--EXPECTF--
+HEAD
+HEAD
+Done
+
+
+
diff --git a/ext/standard/tests/url/parse_url_basic_001.phpt b/ext/standard/tests/url/parse_url_basic_001.phpt
index e468066a42..bd4bea5b9c 100644
--- a/ext/standard/tests/url/parse_url_basic_001.phpt
+++ b/ext/standard/tests/url/parse_url_basic_001.phpt
@@ -625,6 +625,21 @@ echo "Done";
string(12) "bar=1&boom=0"
}
+--> http://user_me-you:my_pas-word@www.example.com:8080?bar=1&boom=0: array(6) {
+ ["scheme"]=>
+ string(4) "http"
+ ["host"]=>
+ string(15) "www.example.com"
+ ["port"]=>
+ int(8080)
+ ["user"]=>
+ string(11) "user_me-you"
+ ["pass"]=>
+ string(11) "my_pas-word"
+ ["query"]=>
+ string(12) "bar=1&boom=0"
+}
+
--> file:///path/to/file: array(2) {
["scheme"]=>
string(4) "file"
diff --git a/ext/standard/tests/url/parse_url_basic_002.phpt b/ext/standard/tests/url/parse_url_basic_002.phpt
index f222ffccf3..eae78febed 100644
--- a/ext/standard/tests/url/parse_url_basic_002.phpt
+++ b/ext/standard/tests/url/parse_url_basic_002.phpt
@@ -81,6 +81,7 @@ echo "Done";
--> /foo.php?a=b&c=d : NULL
--> foo.php?a=b&c=d : NULL
--> http://user:passwd@www.example.com:8080?bar=1&boom=0 : string(4) "http"
+--> http://user_me-you:my_pas-word@www.example.com:8080?bar=1&boom=0 : string(4) "http"
--> file:///path/to/file : string(4) "file"
--> file://path/to/file : string(4) "file"
--> file:/path/to/file : string(4) "file"
diff --git a/ext/standard/tests/url/parse_url_basic_003.phpt b/ext/standard/tests/url/parse_url_basic_003.phpt
index 70dc4bb90b..0ce27b7a2c 100644
--- a/ext/standard/tests/url/parse_url_basic_003.phpt
+++ b/ext/standard/tests/url/parse_url_basic_003.phpt
@@ -80,6 +80,7 @@ echo "Done";
--> /foo.php?a=b&c=d : NULL
--> foo.php?a=b&c=d : NULL
--> http://user:passwd@www.example.com:8080?bar=1&boom=0 : string(15) "www.example.com"
+--> http://user_me-you:my_pas-word@www.example.com:8080?bar=1&boom=0 : string(15) "www.example.com"
--> file:///path/to/file : NULL
--> file://path/to/file : string(4) "path"
--> file:/path/to/file : NULL
diff --git a/ext/standard/tests/url/parse_url_basic_004.phpt b/ext/standard/tests/url/parse_url_basic_004.phpt
index 7ddddaf716..92d690ae1d 100644
--- a/ext/standard/tests/url/parse_url_basic_004.phpt
+++ b/ext/standard/tests/url/parse_url_basic_004.phpt
@@ -80,6 +80,7 @@ echo "Done";
--> /foo.php?a=b&c=d : NULL
--> foo.php?a=b&c=d : NULL
--> http://user:passwd@www.example.com:8080?bar=1&boom=0 : int(8080)
+--> http://user_me-you:my_pas-word@www.example.com:8080?bar=1&boom=0 : int(8080)
--> file:///path/to/file : NULL
--> file://path/to/file : NULL
--> file:/path/to/file : NULL
diff --git a/ext/standard/tests/url/parse_url_basic_005.phpt b/ext/standard/tests/url/parse_url_basic_005.phpt
index b2ca06ff96..68162594f1 100644
--- a/ext/standard/tests/url/parse_url_basic_005.phpt
+++ b/ext/standard/tests/url/parse_url_basic_005.phpt
@@ -80,6 +80,7 @@ echo "Done";
--> /foo.php?a=b&c=d : NULL
--> foo.php?a=b&c=d : NULL
--> http://user:passwd@www.example.com:8080?bar=1&boom=0 : string(4) "user"
+--> http://user_me-you:my_pas-word@www.example.com:8080?bar=1&boom=0 : string(11) "user_me-you"
--> file:///path/to/file : NULL
--> file://path/to/file : NULL
--> file:/path/to/file : NULL
diff --git a/ext/standard/tests/url/parse_url_basic_006.phpt b/ext/standard/tests/url/parse_url_basic_006.phpt
index f0c251bb55..2628559ee5 100644
--- a/ext/standard/tests/url/parse_url_basic_006.phpt
+++ b/ext/standard/tests/url/parse_url_basic_006.phpt
@@ -80,6 +80,7 @@ echo "Done";
--> /foo.php?a=b&c=d : NULL
--> foo.php?a=b&c=d : NULL
--> http://user:passwd@www.example.com:8080?bar=1&boom=0 : string(6) "passwd"
+--> http://user_me-you:my_pas-word@www.example.com:8080?bar=1&boom=0 : string(11) "my_pas-word"
--> file:///path/to/file : NULL
--> file://path/to/file : NULL
--> file:/path/to/file : NULL
diff --git a/ext/standard/tests/url/parse_url_basic_007.phpt b/ext/standard/tests/url/parse_url_basic_007.phpt
index 1b362bb013..15935bb663 100644
--- a/ext/standard/tests/url/parse_url_basic_007.phpt
+++ b/ext/standard/tests/url/parse_url_basic_007.phpt
@@ -80,6 +80,7 @@ echo "Done";
--> /foo.php?a=b&c=d : string(8) "/foo.php"
--> foo.php?a=b&c=d : string(7) "foo.php"
--> http://user:passwd@www.example.com:8080?bar=1&boom=0 : NULL
+--> http://user_me-you:my_pas-word@www.example.com:8080?bar=1&boom=0 : NULL
--> file:///path/to/file : string(13) "/path/to/file"
--> file://path/to/file : string(8) "/to/file"
--> file:/path/to/file : string(13) "/path/to/file"
diff --git a/ext/standard/tests/url/parse_url_basic_008.phpt b/ext/standard/tests/url/parse_url_basic_008.phpt
index 1271f3838c..48ea6b92b2 100644
--- a/ext/standard/tests/url/parse_url_basic_008.phpt
+++ b/ext/standard/tests/url/parse_url_basic_008.phpt
@@ -80,6 +80,7 @@ echo "Done";
--> /foo.php?a=b&c=d : string(7) "a=b&c=d"
--> foo.php?a=b&c=d : string(7) "a=b&c=d"
--> http://user:passwd@www.example.com:8080?bar=1&boom=0 : string(12) "bar=1&boom=0"
+--> http://user_me-you:my_pas-word@www.example.com:8080?bar=1&boom=0 : string(12) "bar=1&boom=0"
--> file:///path/to/file : NULL
--> file://path/to/file : NULL
--> file:/path/to/file : NULL
diff --git a/ext/standard/tests/url/parse_url_basic_009.phpt b/ext/standard/tests/url/parse_url_basic_009.phpt
index 72f172a55b..19527d87ef 100644
--- a/ext/standard/tests/url/parse_url_basic_009.phpt
+++ b/ext/standard/tests/url/parse_url_basic_009.phpt
@@ -80,6 +80,7 @@ echo "Done";
--> /foo.php?a=b&c=d : NULL
--> foo.php?a=b&c=d : NULL
--> http://user:passwd@www.example.com:8080?bar=1&boom=0 : NULL
+--> http://user_me-you:my_pas-word@www.example.com:8080?bar=1&boom=0 : NULL
--> file:///path/to/file : NULL
--> file://path/to/file : NULL
--> file:/path/to/file : NULL
diff --git a/ext/standard/tests/url/urls.inc b/ext/standard/tests/url/urls.inc
index d8ffe91378..b60af2205e 100644
--- a/ext/standard/tests/url/urls.inc
+++ b/ext/standard/tests/url/urls.inc
@@ -60,6 +60,7 @@ $urls = array(
'/foo.php?a=b&c=d',
'foo.php?a=b&c=d',
'http://user:passwd@www.example.com:8080?bar=1&boom=0',
+'http://user_me-you:my_pas-word@www.example.com:8080?bar=1&boom=0',
'file:///path/to/file',
'file://path/to/file',
'file:/path/to/file',
diff --git a/ext/standard/type.c b/ext/standard/type.c
index a9686a21f6..dcbef77523 100644
--- a/ext/standard/type.c
+++ b/ext/standard/type.c
@@ -148,11 +148,50 @@ PHP_FUNCTION(intval)
Z_PARAM_LONG(base)
ZEND_PARSE_PARAMETERS_END();
- if (Z_TYPE_P(num) != IS_STRING) {
+ if (Z_TYPE_P(num) != IS_STRING || base == 10) {
RETVAL_LONG(zval_get_long(num));
- } else {
- RETVAL_LONG(ZEND_STRTOL(Z_STRVAL_P(num), NULL, base));
+ return;
}
+
+
+ if (base == 0 || base == 2) {
+ char *strval = Z_STRVAL_P(num);
+ size_t strlen = Z_STRLEN_P(num);
+
+ while (isspace(*strval) && strlen) {
+ strval++;
+ strlen--;
+ }
+
+ /* Length of 3+ covers "0b#" and "-0b" (which results in 0) */
+ if (strlen > 2) {
+ int offset = 0;
+ if (strval[0] == '-' || strval[0] == '+') {
+ offset = 1;
+ }
+
+ if (strval[offset] == '0' && (strval[offset + 1] == 'b' || strval[offset + 1] == 'B')) {
+ char *tmpval;
+ strlen -= 2; /* Removing "0b" */
+ tmpval = emalloc(strlen + 1);
+
+ /* Place the unary symbol at pos 0 if there was one */
+ if (offset) {
+ tmpval[0] = strval[0];
+ }
+
+ /* Copy the data from after "0b" to the end of the buffer */
+ memcpy(tmpval + offset, strval + offset + 2, strlen - offset);
+ tmpval[strlen] = 0;
+
+ RETVAL_LONG(ZEND_STRTOL(tmpval, NULL, 2));
+ efree(tmpval);
+ return;
+ }
+ }
+ }
+
+ RETVAL_LONG(ZEND_STRTOL(Z_STRVAL_P(num), NULL, base));
}
/* }}} */
@@ -397,6 +436,20 @@ PHP_FUNCTION(is_callable)
}
/* }}} */
+/* {{{ proto bool is_iterable(mixed var)
+ Returns true if var is iterable (array or instance of Traversable). */
+PHP_FUNCTION(is_iterable)
+{
+ zval *var;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &var) == FAILURE) {
+ return;
+ }
+
+ RETURN_BOOL(zend_is_iterable(var));
+}
+/* }}} */
+
/*
* Local variables:
* tab-width: 4
diff --git a/ext/standard/url.c b/ext/standard/url.c
index 9c42afbdea..8b491baefc 100644
--- a/ext/standard/url.c
+++ b/ext/standard/url.c
@@ -647,22 +647,24 @@ PHPAPI size_t php_raw_url_decode(char *str, size_t len)
}
/* }}} */
-/* {{{ proto array get_headers(string url[, int format])
+/* {{{ proto array get_headers(string url[, int format[, resource context]])
fetches all the headers sent by the server in response to a HTTP request */
PHP_FUNCTION(get_headers)
{
char *url;
size_t url_len;
- php_stream_context *context;
php_stream *stream;
zval *prev_val, *hdr = NULL, *h;
HashTable *hashT;
zend_long format = 0;
+ zval *zcontext = NULL;
+ php_stream_context *context;
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &url, &url_len, &format) == FAILURE) {
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|lr!", &url, &url_len, &format, &zcontext) == FAILURE) {
return;
}
- context = FG(default_context) ? FG(default_context) : (FG(default_context) = php_stream_context_alloc());
+
+ context = php_stream_context_from_zval(zcontext, 0);
if (!(stream = php_stream_open_wrapper_ex(url, "r", REPORT_ERRORS | STREAM_USE_URL | STREAM_ONLY_GET_HEADERS, NULL, context))) {
RETURN_FALSE;
diff --git a/ext/standard/url_scanner_ex.c b/ext/standard/url_scanner_ex.c
index 4469bb5e08..f126dff02b 100644
--- a/ext/standard/url_scanner_ex.c
+++ b/ext/standard/url_scanner_ex.c
@@ -15,6 +15,7 @@
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Author: Sascha Schumann <sascha@schumann.cx> |
+ | Yasuo Ohgaki <yohgaki@ohgaki.net> |
+----------------------------------------------------------------------+
*/
@@ -33,12 +34,14 @@
#include <stdlib.h>
#include <string.h>
+#include "SAPI.h"
#include "php_ini.h"
#include "php_globals.h"
#include "php_string.h"
#define STATE_TAG SOME_OTHER_STATE_TAG
#include "basic_functions.h"
#include "url.h"
+#include "html.h"
#undef STATE_TAG
#define url_scanner url_scanner_ex
@@ -50,14 +53,18 @@ static void tag_dtor(zval *zv)
free(Z_PTR_P(zv));
}
-static PHP_INI_MH(OnUpdateTags)
+static int php_ini_on_update_tags(zend_ini_entry *entry, zend_string *new_value, void *mh_arg1, void *mh_arg2, void *mh_arg3, int stage, int type)
{
url_adapt_state_ex_t *ctx;
char *key;
char *tmp;
char *lasts = NULL;
- ctx = &BG(url_adapt_state_ex);
+ if (type) {
+ ctx = &BG(url_adapt_session_ex);
+ } else {
+ ctx = &BG(url_adapt_output_ex);
+ }
tmp = estrndup(ZSTR_VAL(new_value), ZSTR_LEN(new_value));
@@ -66,6 +73,7 @@ static PHP_INI_MH(OnUpdateTags)
else {
ctx->tags = malloc(sizeof(HashTable));
if (!ctx->tags) {
+ efree(tmp);
return FAILURE;
}
}
@@ -73,8 +81,8 @@ static PHP_INI_MH(OnUpdateTags)
zend_hash_init(ctx->tags, 0, NULL, tag_dtor, 1);
for (key = php_strtok_r(tmp, ",", &lasts);
- key;
- key = php_strtok_r(NULL, ",", &lasts)) {
+ key;
+ key = php_strtok_r(NULL, ",", &lasts)) {
char *val;
val = strchr(key, '=');
@@ -83,11 +91,10 @@ static PHP_INI_MH(OnUpdateTags)
size_t keylen;
*val++ = '\0';
- for (q = key; *q; q++)
+ for (q = key; *q; q++) {
*q = tolower(*q);
+ }
keylen = q - key;
- /* key is stored withOUT NUL
- val is stored WITH NUL */
zend_hash_str_add_mem(ctx->tags, key, keylen, val, strlen(val)+1);
}
}
@@ -97,11 +104,73 @@ static PHP_INI_MH(OnUpdateTags)
return SUCCESS;
}
+static PHP_INI_MH(OnUpdateSessionTags)
+{
+ return php_ini_on_update_tags(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage, 1);
+}
+
+static PHP_INI_MH(OnUpdateOutputTags)
+{
+ return php_ini_on_update_tags(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage, 0);
+}
+
+static int php_ini_on_update_hosts(zend_ini_entry *entry, zend_string *new_value, void *mh_arg1, void *mh_arg2, void *mh_arg3, int stage, int type)
+{
+ HashTable *hosts;
+ char *key;
+ char *tmp;
+ char *lasts = NULL;
+
+ if (type) {
+ hosts = &BG(url_adapt_session_hosts_ht);
+ } else {
+ hosts = &BG(url_adapt_output_hosts_ht);
+ }
+ zend_hash_clean(hosts);
+
+ /* Use user supplied host whitelist */
+ tmp = estrndup(ZSTR_VAL(new_value), ZSTR_LEN(new_value));
+ for (key = php_strtok_r(tmp, ",", &lasts);
+ key;
+ key = php_strtok_r(NULL, ",", &lasts)) {
+ size_t keylen;
+ zend_string *tmp_key;
+ char *q;
+
+ for (q = key; *q; q++) {
+ *q = tolower(*q);
+ }
+ keylen = q - key;
+ if (keylen > 0) {
+ tmp_key = zend_string_init(key, keylen, 0);
+ zend_hash_add_empty_element(hosts, tmp_key);
+ zend_string_release(tmp_key);
+ }
+ }
+ efree(tmp);
+
+ return SUCCESS;
+}
+
+static PHP_INI_MH(OnUpdateSessionHosts)
+{
+ return php_ini_on_update_hosts(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage, 1);
+}
+
+static PHP_INI_MH(OnUpdateOutputHosts)
+{
+ return php_ini_on_update_hosts(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage, 0);
+}
+
+/* FIXME: OnUpdate*Hosts cannot set default to $_SERVER['HTTP_HOST'] at startup */
PHP_INI_BEGIN()
- STD_PHP_INI_ENTRY("url_rewriter.tags", "a=href,area=href,frame=src,form=,fieldset=", PHP_INI_ALL, OnUpdateTags, url_adapt_state_ex, php_basic_globals, basic_globals)
+ STD_PHP_INI_ENTRY("session.trans_sid_tags", "a=href,area=href,frame=src,form=", PHP_INI_ALL, OnUpdateSessionTags, url_adapt_session_ex, php_basic_globals, basic_globals)
+ STD_PHP_INI_ENTRY("session.trans_sid_hosts", "", PHP_INI_ALL, OnUpdateSessionHosts, url_adapt_session_hosts_ht, php_basic_globals, basic_globals)
+ STD_PHP_INI_ENTRY("url_rewriter.tags", "form=", PHP_INI_ALL, OnUpdateOutputTags, url_adapt_session_ex, php_basic_globals, basic_globals)
+ STD_PHP_INI_ENTRY("url_rewriter.hosts", "", PHP_INI_ALL, OnUpdateOutputHosts, url_adapt_session_hosts_ht, php_basic_globals, basic_globals)
PHP_INI_END()
-#line 108 "ext/standard/url_scanner_ex.re"
+#line 177 "ext/standard/url_scanner_ex.re"
#define YYFILL(n) goto done
@@ -112,137 +181,107 @@ PHP_INI_END()
static inline void append_modified_url(smart_str *url, smart_str *dest, smart_str *url_app, const char *separator)
{
- register const char *p, *q;
- const char *bash = NULL;
- const char *sep = "?";
+ php_url *url_parts;
+ char *tmp;
+ size_t tmp_len;
- /*
- * Don't modify "//example.com" full path, unless
- * HTTP_HOST matches.
- */
- if (ZSTR_LEN(url->s) > 2 && ZSTR_VAL(url->s)[0] == '/' && ZSTR_VAL(url->s)[1] == '/') {
- const char *end_chars = "/\"'?>\r\n";
- zval *tmp = NULL, *http_host = NULL;
- size_t target_len, host_len;
- if ((!(tmp = zend_hash_str_find(&EG(symbol_table), ZEND_STRL("_SERVER"))))
- || Z_TYPE_P(tmp) != IS_ARRAY
- || !(http_host = zend_hash_str_find(HASH_OF(tmp), ZEND_STRL("HTTP_HOST")))
- || Z_TYPE_P(http_host) != IS_STRING) {
- smart_str_append_smart_str(dest, url);
- return;
- }
+ smart_str_0(url); /* FIXME: Bug #70480 php_url_parse_ex() crashes by processing chars exceed len */
+ url_parts = php_url_parse_ex(ZSTR_VAL(url->s), ZSTR_LEN(url->s));
- /* HTTP_HOST could be "example.com:8888", etc. */
- /* Need to find end of URL in buffer */
- host_len = strcspn(Z_STRVAL_P(http_host), ":");
- target_len = php_strcspn(
- ZSTR_VAL(url->s) + 2, (char *) end_chars,
- ZSTR_VAL(url->s) + ZSTR_LEN(url->s), (char *) end_chars + strlen(end_chars));
- if (host_len
- && host_len == target_len
- && strncasecmp(Z_STRVAL_P(http_host), ZSTR_VAL(url->s)+2, host_len)) {
- smart_str_append_smart_str(dest, url);
- return;
- }
+ /* Ignore malformed URLs */
+ if (!url_parts) {
+ smart_str_append_smart_str(dest, url);
+ return;
}
- q = (p = ZSTR_VAL(url->s)) + ZSTR_LEN(url->s);
-
-scan:
-
-#line 154 "ext/standard/url_scanner_ex.c"
-{
- YYCTYPE yych;
- static const unsigned char yybm[] = {
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 0, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 0, 128, 128, 128, 128, 0,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- };
- if (YYLIMIT <= YYCURSOR) YYFILL(1);
- yych = *YYCURSOR;
- if (yybm[0+yych] & 128) {
- goto yy2;
- }
- if (yych <= '#') goto yy5;
- if (yych <= ':') goto yy7;
- goto yy9;
-yy2:
- ++YYCURSOR;
- if (YYLIMIT <= YYCURSOR) YYFILL(1);
- yych = *YYCURSOR;
- if (yybm[0+yych] & 128) {
- goto yy2;
+ /* Don't modify URLs of the format "#mark" */
+ if (url_parts->fragment && '#' == ZSTR_VAL(url->s)[0]) {
+ smart_str_append_smart_str(dest, url);
+ php_url_free(url_parts);
+ return;
}
-#line 159 "ext/standard/url_scanner_ex.re"
- { goto scan; }
-#line 208 "ext/standard/url_scanner_ex.c"
-yy5:
- ++YYCURSOR;
-#line 158 "ext/standard/url_scanner_ex.re"
- { bash = p - 1; goto done; }
-#line 213 "ext/standard/url_scanner_ex.c"
-yy7:
- ++YYCURSOR;
-#line 156 "ext/standard/url_scanner_ex.re"
- { smart_str_append_smart_str(dest, url); return; }
-#line 218 "ext/standard/url_scanner_ex.c"
-yy9:
- ++YYCURSOR;
-#line 157 "ext/standard/url_scanner_ex.re"
- { sep = separator; goto scan; }
-#line 223 "ext/standard/url_scanner_ex.c"
-}
-#line 160 "ext/standard/url_scanner_ex.re"
-done:
-
- /* Don't modify URLs of the format "#mark" */
- if (bash && bash - ZSTR_VAL(url->s) == 0) {
+ /* Check protocol. Only http/https is allowed. */
+ if (url_parts->scheme
+ && strcasecmp("http", url_parts->scheme)
+ && strcasecmp("https", url_parts->scheme)) {
smart_str_append_smart_str(dest, url);
+ php_url_free(url_parts);
return;
}
- if (bash)
- smart_str_appendl(dest, ZSTR_VAL(url->s), bash - ZSTR_VAL(url->s));
- else
+ /* Check host whitelist. If it's not listed, do nothing. */
+ if (url_parts->host
+ && (tmp_len = strlen(url_parts->host))
+ && (tmp = php_strtolower(url_parts->host, tmp_len))
+ && !zend_hash_str_find(&BG(url_adapt_session_hosts_ht), tmp, tmp_len)) {
smart_str_append_smart_str(dest, url);
+ php_url_free(url_parts);
+ return;
+ }
- smart_str_appends(dest, sep);
- smart_str_append_smart_str(dest, url_app);
+ /*
+ * When URL does not have path and query string add "/?".
+ * i.e. If URL is only "?foo=bar", should not add "/?".
+ */
+ if (!url_parts->path && !url_parts->query && !url_parts->fragment) {
+ /* URL is http://php.net or like */
+ smart_str_append_smart_str(dest, url);
+ smart_str_appendc(dest, '/');
+ smart_str_appendc(dest, '?');
+ smart_str_append_smart_str(dest, url_app);
+ php_url_free(url_parts);
+ return;
+ }
- if (bash)
- smart_str_appendl(dest, bash, q - bash);
+ if (url_parts->scheme) {
+ smart_str_appends(dest, url_parts->scheme);
+ smart_str_appends(dest, "://");
+ } else if (*(ZSTR_VAL(url->s)) == '/' && *(ZSTR_VAL(url->s)+1) == '/') {
+ smart_str_appends(dest, "//");
+ }
+ if (url_parts->user) {
+ smart_str_appends(dest, url_parts->user);
+ if (url_parts->pass) {
+ smart_str_appends(dest, url_parts->pass);
+ smart_str_appendc(dest, ':');
+ }
+ smart_str_appendc(dest, '@');
+ }
+ if (url_parts->host) {
+ smart_str_appends(dest, url_parts->host);
+ }
+ if (url_parts->port) {
+ smart_str_appendc(dest, ':');
+ smart_str_append_unsigned(dest, (long)url_parts->port);
+ }
+ if (url_parts->path) {
+ smart_str_appends(dest, url_parts->path);
+ }
+ smart_str_appendc(dest, '?');
+ if (url_parts->query) {
+ smart_str_appends(dest, url_parts->query);
+ smart_str_appends(dest, separator);
+ smart_str_append_smart_str(dest, url_app);
+ } else {
+ smart_str_append_smart_str(dest, url_app);
+ }
+ if (url_parts->fragment) {
+ smart_str_appendc(dest, '#');
+ smart_str_appends(dest, url_parts->fragment);
+ }
+ php_url_free(url_parts);
}
+enum {
+ TAG_NORMAL = 0,
+ TAG_FORM
+};
+
+enum {
+ ATTR_NORMAL = 0,
+ ATTR_ACTION
+};
#undef YYFILL
#undef YYCTYPE
@@ -254,18 +293,24 @@ static inline void tag_arg(url_adapt_state_ex_t *ctx, char quotes, char type)
{
char f = 0;
- if (strncasecmp(ZSTR_VAL(ctx->arg.s), ctx->lookup_data, ZSTR_LEN(ctx->arg.s)) == 0)
+ /* arg.s is string WITHOUT NUL.
+ To avoid partial match, NUL is added here */
+ ZSTR_VAL(ctx->arg.s)[ZSTR_LEN(ctx->arg.s)] = '\0';
+ if (!strcasecmp(ZSTR_VAL(ctx->arg.s), ctx->lookup_data)) {
f = 1;
+ }
- if (quotes)
+ if (quotes) {
smart_str_appendc(&ctx->result, type);
+ }
if (f) {
append_modified_url(&ctx->val, &ctx->result, &ctx->url_app, PG(arg_separator).output);
} else {
smart_str_append_smart_str(&ctx->result, &ctx->val);
}
- if (quotes)
+ if (quotes) {
smart_str_appendc(&ctx->result, type);
+ }
}
enum {
@@ -299,11 +344,79 @@ static inline void passthru(STD_PARA)
smart_str_appendl(&ctx->result, start, YYCURSOR - start);
}
+
+static int check_http_host(char *target)
+{
+ zval *host, *tmp;
+ zend_string *host_tmp;
+ char *colon;
+
+ if ((tmp = zend_hash_str_find(&EG(symbol_table), ZEND_STRL("_SERVER"))) &&
+ (host = zend_hash_str_find(Z_ARRVAL_P(tmp), ZEND_STRL("HTTP_HOST"))) &&
+ Z_TYPE_P(host) == IS_STRING) {
+ host_tmp = zend_string_init(Z_STRVAL_P(host), Z_STRLEN_P(host), 0);
+ /* HTTP_HOST could be 'localhost:8888' etc. */
+ colon = strchr(ZSTR_VAL(host_tmp), ':');
+ if (colon) {
+ ZSTR_LEN(host_tmp) = colon - ZSTR_VAL(host_tmp);
+ ZSTR_VAL(host_tmp)[ZSTR_LEN(host_tmp)] = '\0';
+ }
+ if (!strcasecmp(ZSTR_VAL(host_tmp), target)) {
+ zend_string_release(host_tmp);
+ return SUCCESS;
+ }
+ zend_string_release(host_tmp);
+ }
+ return FAILURE;
+}
+
+static int check_host_whitelist(url_adapt_state_ex_t *ctx)
+{
+ php_url *url_parts = NULL;
+ HashTable *allowed_hosts = ctx->type ? &BG(url_adapt_session_hosts_ht) : &BG(url_adapt_output_hosts_ht);
+
+ ZEND_ASSERT(ctx->tag_type == TAG_FORM);
+
+ if (ctx->attr_val.s && ZSTR_LEN(ctx->attr_val.s)) {
+ url_parts = php_url_parse_ex(ZSTR_VAL(ctx->attr_val.s), ZSTR_LEN(ctx->attr_val.s));
+ } else {
+ return SUCCESS; /* empty URL is valid */
+ }
+
+ if (!url_parts) {
+ return FAILURE;
+ }
+ if (url_parts->scheme) {
+ /* Only http/https should be handled.
+ A bit hacky check this here, but saves a URL parse. */
+ if (strcasecmp(url_parts->scheme, "http") &&
+ strcasecmp(url_parts->scheme, "https")) {
+ php_url_free(url_parts);
+ return FAILURE;
+ }
+ }
+ if (!url_parts->host) {
+ php_url_free(url_parts);
+ return SUCCESS;
+ }
+ if (!zend_hash_num_elements(allowed_hosts) &&
+ check_http_host(url_parts->host) == SUCCESS) {
+ php_url_free(url_parts);
+ return SUCCESS;
+ }
+ if (!zend_hash_str_find(allowed_hosts,
+ url_parts->host,
+ strlen(url_parts->host))) {
+ php_url_free(url_parts);
+ return FAILURE;
+ }
+ php_url_free(url_parts);
+ return SUCCESS;
+}
+
/*
- * This function appends a hidden input field after a <form> or
- * <fieldset>. The latter is important for XHTML.
+ * This function appends a hidden input field after a <form>.
*/
-
static void handle_form(STD_PARA)
{
int doit = 0;
@@ -311,32 +424,16 @@ static void handle_form(STD_PARA)
if (ZSTR_LEN(ctx->form_app.s) > 0) {
switch (ZSTR_LEN(ctx->tag.s)) {
case sizeof("form") - 1:
- if (!strncasecmp(ZSTR_VAL(ctx->tag.s), "form", sizeof("form") - 1)) {
- doit = 1;
- }
- if (doit && ctx->val.s && ctx->lookup_data && *ctx->lookup_data) {
- char *e, *p = (char *)zend_memnstr(ZSTR_VAL(ctx->val.s), "://", sizeof("://") - 1, ZSTR_VAL(ctx->val.s) + ZSTR_LEN(ctx->val.s));
- if (p) {
- e = memchr(p, '/', (ZSTR_VAL(ctx->val.s) + ZSTR_LEN(ctx->val.s)) - p);
- if (!e) {
- e = ZSTR_VAL(ctx->val.s) + ZSTR_LEN(ctx->val.s);
- }
- if ((e - p) && strncasecmp(p, ctx->lookup_data, (e - p))) {
- doit = 0;
- }
- }
- }
- break;
-
- case sizeof("fieldset") - 1:
- if (!strncasecmp(ZSTR_VAL(ctx->tag.s), "fieldset", sizeof("fieldset") - 1)) {
+ if (!strncasecmp(ZSTR_VAL(ctx->tag.s), "form", ZSTR_LEN(ctx->tag.s))
+ && check_host_whitelist(ctx) == SUCCESS) {
doit = 1;
}
break;
}
+ }
- if (doit)
- smart_str_append_smart_str(&ctx->result, &ctx->form_app);
+ if (doit) {
+ smart_str_append_smart_str(&ctx->result, &ctx->form_app);
}
}
@@ -359,8 +456,15 @@ static inline void handle_tag(STD_PARA)
for (i = 0; i < ZSTR_LEN(ctx->tag.s); i++)
ZSTR_VAL(ctx->tag.s)[i] = tolower((int)(unsigned char)ZSTR_VAL(ctx->tag.s)[i]);
/* intentionally using str_find here, in case the hash value is set, but the string val is changed later */
- if ((ctx->lookup_data = zend_hash_str_find_ptr(ctx->tags, ZSTR_VAL(ctx->tag.s), ZSTR_LEN(ctx->tag.s))) != NULL)
+ if ((ctx->lookup_data = zend_hash_str_find_ptr(ctx->tags, ZSTR_VAL(ctx->tag.s), ZSTR_LEN(ctx->tag.s))) != NULL) {
ok = 1;
+ if (ZSTR_LEN(ctx->tag.s) == sizeof("form")-1
+ && !strncasecmp(ZSTR_VAL(ctx->tag.s), "form", ZSTR_LEN(ctx->tag.s))) {
+ ctx->tag_type = TAG_FORM;
+ } else {
+ ctx->tag_type = TAG_NORMAL;
+ }
+ }
STATE = ok ? STATE_NEXT_ARG : STATE_PLAIN;
}
@@ -370,11 +474,20 @@ static inline void handle_arg(STD_PARA)
ZSTR_LEN(ctx->arg.s) = 0;
}
smart_str_appendl(&ctx->arg, start, YYCURSOR - start);
+ if (ctx->tag_type == TAG_FORM &&
+ strncasecmp(ZSTR_VAL(ctx->arg.s), "action", ZSTR_LEN(ctx->arg.s)) == 0) {
+ ctx->attr_type = ATTR_ACTION;
+ } else {
+ ctx->attr_type = ATTR_NORMAL;
+ }
}
static inline void handle_val(STD_PARA, char quotes, char type)
{
smart_str_setl(&ctx->val, start + quotes, YYCURSOR - start - quotes * 2);
+ if (ctx->tag_type == TAG_FORM && ctx->attr_type == ATTR_ACTION) {
+ smart_str_setl(&ctx->attr_val, start + quotes, YYCURSOR - start - quotes * 2);
+ }
tag_arg(ctx, quotes, type);
}
@@ -406,7 +519,7 @@ state_plain_begin:
state_plain:
start = YYCURSOR;
-#line 410 "ext/standard/url_scanner_ex.c"
+#line 523 "ext/standard/url_scanner_ex.c"
{
YYCTYPE yych;
static const unsigned char yybm[] = {
@@ -446,32 +559,32 @@ state_plain:
if (YYLIMIT <= YYCURSOR) YYFILL(1);
yych = *YYCURSOR;
if (yybm[0+yych] & 128) {
- goto yy13;
+ goto yy2;
}
- goto yy16;
-yy13:
+ goto yy5;
+yy2:
++YYCURSOR;
if (YYLIMIT <= YYCURSOR) YYFILL(1);
yych = *YYCURSOR;
if (yybm[0+yych] & 128) {
- goto yy13;
+ goto yy2;
}
-#line 345 "ext/standard/url_scanner_ex.re"
+#line 526 "ext/standard/url_scanner_ex.re"
{ passthru(STD_ARGS); goto state_plain; }
-#line 462 "ext/standard/url_scanner_ex.c"
-yy16:
+#line 575 "ext/standard/url_scanner_ex.c"
+yy5:
++YYCURSOR;
-#line 344 "ext/standard/url_scanner_ex.re"
+#line 525 "ext/standard/url_scanner_ex.re"
{ passthru(STD_ARGS); STATE = STATE_TAG; goto state_tag; }
-#line 467 "ext/standard/url_scanner_ex.c"
+#line 580 "ext/standard/url_scanner_ex.c"
}
-#line 346 "ext/standard/url_scanner_ex.re"
+#line 527 "ext/standard/url_scanner_ex.re"
state_tag:
start = YYCURSOR;
-#line 475 "ext/standard/url_scanner_ex.c"
+#line 588 "ext/standard/url_scanner_ex.c"
{
YYCTYPE yych;
static const unsigned char yybm[] = {
@@ -511,24 +624,24 @@ state_tag:
if (YYLIMIT <= YYCURSOR) YYFILL(1);
yych = *YYCURSOR;
if (yybm[0+yych] & 128) {
- goto yy22;
+ goto yy11;
}
++YYCURSOR;
-#line 352 "ext/standard/url_scanner_ex.re"
+#line 533 "ext/standard/url_scanner_ex.re"
{ passthru(STD_ARGS); goto state_plain_begin; }
-#line 520 "ext/standard/url_scanner_ex.c"
-yy22:
+#line 633 "ext/standard/url_scanner_ex.c"
+yy11:
++YYCURSOR;
if (YYLIMIT <= YYCURSOR) YYFILL(1);
yych = *YYCURSOR;
if (yybm[0+yych] & 128) {
- goto yy22;
+ goto yy11;
}
-#line 351 "ext/standard/url_scanner_ex.re"
+#line 532 "ext/standard/url_scanner_ex.re"
{ handle_tag(STD_ARGS); /* Sets STATE */; passthru(STD_ARGS); if (STATE == STATE_PLAIN) goto state_plain; else goto state_next_arg; }
-#line 530 "ext/standard/url_scanner_ex.c"
+#line 643 "ext/standard/url_scanner_ex.c"
}
-#line 353 "ext/standard/url_scanner_ex.re"
+#line 534 "ext/standard/url_scanner_ex.re"
state_next_arg_begin:
@@ -537,7 +650,7 @@ state_next_arg_begin:
state_next_arg:
start = YYCURSOR;
-#line 541 "ext/standard/url_scanner_ex.c"
+#line 654 "ext/standard/url_scanner_ex.c"
{
YYCTYPE yych;
static const unsigned char yybm[] = {
@@ -577,56 +690,56 @@ state_next_arg:
if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2);
yych = *YYCURSOR;
if (yybm[0+yych] & 128) {
- goto yy29;
+ goto yy18;
}
if (yych <= '>') {
- if (yych == '/') goto yy32;
- if (yych >= '>') goto yy33;
+ if (yych == '/') goto yy21;
+ if (yych >= '>') goto yy22;
} else {
if (yych <= 'Z') {
- if (yych >= 'A') goto yy35;
+ if (yych >= 'A') goto yy24;
} else {
- if (yych <= '`') goto yy27;
- if (yych <= 'z') goto yy35;
+ if (yych <= '`') goto yy16;
+ if (yych <= 'z') goto yy24;
}
}
-yy27:
+yy16:
++YYCURSOR;
-yy28:
-#line 364 "ext/standard/url_scanner_ex.re"
+yy17:
+#line 545 "ext/standard/url_scanner_ex.re"
{ passthru(STD_ARGS); goto state_plain_begin; }
-#line 599 "ext/standard/url_scanner_ex.c"
-yy29:
+#line 712 "ext/standard/url_scanner_ex.c"
+yy18:
++YYCURSOR;
if (YYLIMIT <= YYCURSOR) YYFILL(1);
yych = *YYCURSOR;
if (yybm[0+yych] & 128) {
- goto yy29;
+ goto yy18;
}
-#line 362 "ext/standard/url_scanner_ex.re"
+#line 543 "ext/standard/url_scanner_ex.re"
{ passthru(STD_ARGS); goto state_next_arg; }
-#line 609 "ext/standard/url_scanner_ex.c"
-yy32:
+#line 722 "ext/standard/url_scanner_ex.c"
+yy21:
yych = *++YYCURSOR;
- if (yych != '>') goto yy28;
-yy33:
+ if (yych != '>') goto yy17;
+yy22:
++YYCURSOR;
-#line 361 "ext/standard/url_scanner_ex.re"
+#line 542 "ext/standard/url_scanner_ex.re"
{ passthru(STD_ARGS); handle_form(STD_ARGS); goto state_plain_begin; }
-#line 617 "ext/standard/url_scanner_ex.c"
-yy35:
+#line 730 "ext/standard/url_scanner_ex.c"
+yy24:
++YYCURSOR;
-#line 363 "ext/standard/url_scanner_ex.re"
+#line 544 "ext/standard/url_scanner_ex.re"
{ --YYCURSOR; STATE = STATE_ARG; goto state_arg; }
-#line 622 "ext/standard/url_scanner_ex.c"
+#line 735 "ext/standard/url_scanner_ex.c"
}
-#line 365 "ext/standard/url_scanner_ex.re"
+#line 546 "ext/standard/url_scanner_ex.re"
state_arg:
start = YYCURSOR;
-#line 630 "ext/standard/url_scanner_ex.c"
+#line 743 "ext/standard/url_scanner_ex.c"
{
YYCTYPE yych;
static const unsigned char yybm[] = {
@@ -665,33 +778,33 @@ state_arg:
};
if (YYLIMIT <= YYCURSOR) YYFILL(1);
yych = *YYCURSOR;
- if (yych <= '@') goto yy39;
- if (yych <= 'Z') goto yy41;
- if (yych <= '`') goto yy39;
- if (yych <= 'z') goto yy41;
-yy39:
+ if (yych <= '@') goto yy28;
+ if (yych <= 'Z') goto yy30;
+ if (yych <= '`') goto yy28;
+ if (yych <= 'z') goto yy30;
+yy28:
++YYCURSOR;
-#line 371 "ext/standard/url_scanner_ex.re"
+#line 552 "ext/standard/url_scanner_ex.re"
{ passthru(STD_ARGS); STATE = STATE_NEXT_ARG; goto state_next_arg; }
-#line 677 "ext/standard/url_scanner_ex.c"
-yy41:
+#line 790 "ext/standard/url_scanner_ex.c"
+yy30:
++YYCURSOR;
if (YYLIMIT <= YYCURSOR) YYFILL(1);
yych = *YYCURSOR;
if (yybm[0+yych] & 128) {
- goto yy41;
+ goto yy30;
}
-#line 370 "ext/standard/url_scanner_ex.re"
+#line 551 "ext/standard/url_scanner_ex.re"
{ passthru(STD_ARGS); handle_arg(STD_ARGS); STATE = STATE_BEFORE_VAL; goto state_before_val; }
-#line 687 "ext/standard/url_scanner_ex.c"
+#line 800 "ext/standard/url_scanner_ex.c"
}
-#line 372 "ext/standard/url_scanner_ex.re"
+#line 553 "ext/standard/url_scanner_ex.re"
state_before_val:
start = YYCURSOR;
-#line 695 "ext/standard/url_scanner_ex.c"
+#line 808 "ext/standard/url_scanner_ex.c"
{
YYCTYPE yych;
static const unsigned char yybm[] = {
@@ -730,44 +843,44 @@ state_before_val:
};
if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2);
yych = *YYCURSOR;
- if (yych == ' ') goto yy48;
- if (yych == '=') goto yy49;
+ if (yych == ' ') goto yy37;
+ if (yych == '=') goto yy38;
++YYCURSOR;
-yy47:
-#line 378 "ext/standard/url_scanner_ex.re"
+yy36:
+#line 559 "ext/standard/url_scanner_ex.re"
{ --YYCURSOR; goto state_next_arg_begin; }
-#line 740 "ext/standard/url_scanner_ex.c"
-yy48:
+#line 853 "ext/standard/url_scanner_ex.c"
+yy37:
yych = *(YYMARKER = ++YYCURSOR);
- if (yych == ' ') goto yy52;
- if (yych != '=') goto yy47;
-yy49:
+ if (yych == ' ') goto yy41;
+ if (yych != '=') goto yy36;
+yy38:
++YYCURSOR;
if (YYLIMIT <= YYCURSOR) YYFILL(1);
yych = *YYCURSOR;
if (yybm[0+yych] & 128) {
- goto yy49;
+ goto yy38;
}
-#line 377 "ext/standard/url_scanner_ex.re"
+#line 558 "ext/standard/url_scanner_ex.re"
{ passthru(STD_ARGS); STATE = STATE_VAL; goto state_val; }
-#line 754 "ext/standard/url_scanner_ex.c"
-yy52:
+#line 867 "ext/standard/url_scanner_ex.c"
+yy41:
++YYCURSOR;
if (YYLIMIT <= YYCURSOR) YYFILL(1);
yych = *YYCURSOR;
- if (yych == ' ') goto yy52;
- if (yych == '=') goto yy49;
+ if (yych == ' ') goto yy41;
+ if (yych == '=') goto yy38;
YYCURSOR = YYMARKER;
- goto yy47;
+ goto yy36;
}
-#line 379 "ext/standard/url_scanner_ex.re"
+#line 560 "ext/standard/url_scanner_ex.re"
state_val:
start = YYCURSOR;
-#line 771 "ext/standard/url_scanner_ex.c"
+#line 884 "ext/standard/url_scanner_ex.c"
{
YYCTYPE yych;
static const unsigned char yybm[] = {
@@ -807,68 +920,68 @@ state_val:
if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2);
yych = *YYCURSOR;
if (yybm[0+yych] & 32) {
- goto yy57;
+ goto yy46;
}
- if (yych <= ' ') goto yy60;
- if (yych <= '"') goto yy62;
- if (yych <= '\'') goto yy63;
- goto yy60;
-yy57:
+ if (yych <= ' ') goto yy49;
+ if (yych <= '"') goto yy51;
+ if (yych <= '\'') goto yy52;
+ goto yy49;
+yy46:
++YYCURSOR;
if (YYLIMIT <= YYCURSOR) YYFILL(1);
yych = *YYCURSOR;
if (yybm[0+yych] & 32) {
- goto yy57;
+ goto yy46;
}
-#line 387 "ext/standard/url_scanner_ex.re"
+#line 568 "ext/standard/url_scanner_ex.re"
{ handle_val(STD_ARGS, 0, ' '); goto state_next_arg_begin; }
-#line 826 "ext/standard/url_scanner_ex.c"
-yy60:
+#line 939 "ext/standard/url_scanner_ex.c"
+yy49:
++YYCURSOR;
-yy61:
-#line 388 "ext/standard/url_scanner_ex.re"
+yy50:
+#line 569 "ext/standard/url_scanner_ex.re"
{ passthru(STD_ARGS); goto state_next_arg_begin; }
-#line 832 "ext/standard/url_scanner_ex.c"
-yy62:
+#line 945 "ext/standard/url_scanner_ex.c"
+yy51:
yych = *(YYMARKER = ++YYCURSOR);
- if (yych == '>') goto yy61;
- goto yy65;
-yy63:
+ if (yych == '>') goto yy50;
+ goto yy54;
+yy52:
yych = *(YYMARKER = ++YYCURSOR);
- if (yych == '>') goto yy61;
- goto yy70;
-yy64:
+ if (yych == '>') goto yy50;
+ goto yy59;
+yy53:
++YYCURSOR;
if (YYLIMIT <= YYCURSOR) YYFILL(1);
yych = *YYCURSOR;
-yy65:
+yy54:
if (yybm[0+yych] & 64) {
- goto yy64;
+ goto yy53;
}
- if (yych <= '"') goto yy67;
-yy66:
+ if (yych <= '"') goto yy56;
+yy55:
YYCURSOR = YYMARKER;
- goto yy61;
-yy67:
+ goto yy50;
+yy56:
++YYCURSOR;
-#line 385 "ext/standard/url_scanner_ex.re"
+#line 566 "ext/standard/url_scanner_ex.re"
{ handle_val(STD_ARGS, 1, '"'); goto state_next_arg_begin; }
-#line 857 "ext/standard/url_scanner_ex.c"
-yy69:
+#line 970 "ext/standard/url_scanner_ex.c"
+yy58:
++YYCURSOR;
if (YYLIMIT <= YYCURSOR) YYFILL(1);
yych = *YYCURSOR;
-yy70:
+yy59:
if (yybm[0+yych] & 128) {
- goto yy69;
+ goto yy58;
}
- if (yych >= '(') goto yy66;
+ if (yych >= '(') goto yy55;
++YYCURSOR;
-#line 386 "ext/standard/url_scanner_ex.re"
+#line 567 "ext/standard/url_scanner_ex.re"
{ handle_val(STD_ARGS, 1, '\''); goto state_next_arg_begin; }
-#line 870 "ext/standard/url_scanner_ex.c"
+#line 983 "ext/standard/url_scanner_ex.c"
}
-#line 389 "ext/standard/url_scanner_ex.re"
+#line 570 "ext/standard/url_scanner_ex.re"
stop:
@@ -885,7 +998,7 @@ stop:
}
-PHPAPI char *php_url_scanner_adapt_single_url(const char *url, size_t urllen, const char *name, const char *value, size_t *newlen, int urlencode)
+PHPAPI char *php_url_scanner_adapt_single_url(const char *url, size_t urllen, const char *name, const char *value, size_t *newlen, int encode)
{
char *result;
smart_str surl = {0};
@@ -895,7 +1008,7 @@ PHPAPI char *php_url_scanner_adapt_single_url(const char *url, size_t urllen, co
smart_str_appendl(&surl, url, urllen);
- if (urlencode) {
+ if (encode) {
encoded = php_raw_url_encode(name, strlen(name));
smart_str_appendl(&url_app, ZSTR_VAL(encoded), ZSTR_LEN(encoded));
zend_string_free(encoded);
@@ -903,7 +1016,7 @@ PHPAPI char *php_url_scanner_adapt_single_url(const char *url, size_t urllen, co
smart_str_appends(&url_app, name);
}
smart_str_appendc(&url_app, '=');
- if (urlencode) {
+ if (encode) {
encoded = php_raw_url_encode(value, strlen(value));
smart_str_appendl(&url_app, ZSTR_VAL(encoded), ZSTR_LEN(encoded));
zend_string_free(encoded);
@@ -924,13 +1037,10 @@ PHPAPI char *php_url_scanner_adapt_single_url(const char *url, size_t urllen, co
}
-static char *url_adapt_ext(const char *src, size_t srclen, size_t *newlen, zend_bool do_flush)
+static char *url_adapt_ext(const char *src, size_t srclen, size_t *newlen, zend_bool do_flush, url_adapt_state_ex_t *ctx)
{
- url_adapt_state_ex_t *ctx;
char *retval;
- ctx = &BG(url_adapt_state_ex);
-
xx_mainloop(ctx, src, srclen);
if (!ctx->result.s) {
@@ -945,50 +1055,67 @@ static char *url_adapt_ext(const char *src, size_t srclen, size_t *newlen, zend_
*newlen += ZSTR_LEN(ctx->buf.s);
smart_str_free(&ctx->buf);
smart_str_free(&ctx->val);
+ smart_str_free(&ctx->attr_val);
}
retval = estrndup(ZSTR_VAL(ctx->result.s), ZSTR_LEN(ctx->result.s));
smart_str_free(&ctx->result);
return retval;
}
-static int php_url_scanner_ex_activate(void)
+static int php_url_scanner_ex_activate(int type)
{
url_adapt_state_ex_t *ctx;
- ctx = &BG(url_adapt_state_ex);
+ if (type) {
+ ctx = &BG(url_adapt_session_ex);
+ } else {
+ ctx = &BG(url_adapt_output_ex);
+ }
memset(ctx, 0, ((size_t) &((url_adapt_state_ex_t *)0)->tags));
return SUCCESS;
}
-static int php_url_scanner_ex_deactivate(void)
+static int php_url_scanner_ex_deactivate(int type)
{
url_adapt_state_ex_t *ctx;
- ctx = &BG(url_adapt_state_ex);
+ if (type) {
+ ctx = &BG(url_adapt_session_ex);
+ } else {
+ ctx = &BG(url_adapt_output_ex);
+ }
smart_str_free(&ctx->result);
smart_str_free(&ctx->buf);
smart_str_free(&ctx->tag);
smart_str_free(&ctx->arg);
+ smart_str_free(&ctx->attr_val);
return SUCCESS;
}
-static void php_url_scanner_output_handler(char *output, size_t output_len, char **handled_output, size_t *handled_output_len, int mode)
+static inline void php_url_scanner_session_handler_impl(char *output, size_t output_len, char **handled_output, size_t *handled_output_len, int mode, int type)
{
size_t len;
+ url_adapt_state_ex_t *url_state;
+
+ if (type) {
+ url_state = &BG(url_adapt_session_ex);
+ } else {
+ url_state = &BG(url_adapt_output_ex);
+ }
- if (ZSTR_LEN(BG(url_adapt_state_ex).url_app.s) != 0) {
- *handled_output = url_adapt_ext(output, output_len, &len, (zend_bool) (mode & (PHP_OUTPUT_HANDLER_END | PHP_OUTPUT_HANDLER_CONT | PHP_OUTPUT_HANDLER_FLUSH | PHP_OUTPUT_HANDLER_FINAL) ? 1 : 0));
+ if (ZSTR_LEN(url_state->url_app.s) != 0) {
+ *handled_output = url_adapt_ext(output, output_len, &len, (zend_bool) (mode & (PHP_OUTPUT_HANDLER_END | PHP_OUTPUT_HANDLER_CONT | PHP_OUTPUT_HANDLER_FLUSH | PHP_OUTPUT_HANDLER_FINAL) ? 1 : 0), url_state);
if (sizeof(uint) < sizeof(size_t)) {
if (len > UINT_MAX)
len = UINT_MAX;
}
*handled_output_len = len;
- } else if (ZSTR_LEN(BG(url_adapt_state_ex).url_app.s) == 0) {
- url_adapt_state_ex_t *ctx = &BG(url_adapt_state_ex);
+ } else if (ZSTR_LEN(url_state->url_app.s) == 0) {
+ url_adapt_state_ex_t *ctx = url_state;
if (ctx->buf.s && ZSTR_LEN(ctx->buf.s)) {
smart_str_append(&ctx->result, ctx->buf.s);
smart_str_appendl(&ctx->result, output, output_len);
@@ -1006,68 +1133,257 @@ static void php_url_scanner_output_handler(char *output, size_t output_len, char
}
}
-PHPAPI int php_url_scanner_add_var(char *name, size_t name_len, char *value, size_t value_len, int urlencode)
+static void php_url_scanner_session_handler(char *output, size_t output_len, char **handled_output, size_t *handled_output_len, int mode)
+{
+ php_url_scanner_session_handler_impl(output, output_len, handled_output, handled_output_len, mode, 1);
+}
+
+static void php_url_scanner_output_handler(char *output, size_t output_len, char **handled_output, size_t *handled_output_len, int mode)
+{
+ php_url_scanner_session_handler_impl(output, output_len, handled_output, handled_output_len, mode, 0);
+}
+
+static inline int php_url_scanner_add_var_impl(char *name, size_t name_len, char *value, size_t value_len, int encode, int type)
{
smart_str sname = {0};
smart_str svalue = {0};
+ smart_str hname = {0};
+ smart_str hvalue = {0};
zend_string *encoded;
+ url_adapt_state_ex_t *url_state;
+ php_output_handler_func_t handler;
+
+ if (type) {
+ url_state = &BG(url_adapt_session_ex);
+ handler = php_url_scanner_session_handler;
+ } else {
+ url_state = &BG(url_adapt_output_ex);
+ handler = php_url_scanner_output_handler;
+ }
- if (!BG(url_adapt_state_ex).active) {
- php_url_scanner_ex_activate();
- php_output_start_internal(ZEND_STRL("URL-Rewriter"), php_url_scanner_output_handler, 0, PHP_OUTPUT_HANDLER_STDFLAGS);
- BG(url_adapt_state_ex).active = 1;
+ if (!url_state->active) {
+ php_url_scanner_ex_activate(type);
+ php_output_start_internal(ZEND_STRL("URL-Rewriter"), handler, 0, PHP_OUTPUT_HANDLER_STDFLAGS);
+ url_state->active = 1;
}
- if (BG(url_adapt_state_ex).url_app.s && ZSTR_LEN(BG(url_adapt_state_ex).url_app.s) != 0) {
- smart_str_appends(&BG(url_adapt_state_ex).url_app, PG(arg_separator).output);
+ if (url_state->url_app.s && ZSTR_LEN(url_state->url_app.s) != 0) {
+ smart_str_appends(&url_state->url_app, PG(arg_separator).output);
}
- if (urlencode) {
+ if (encode) {
encoded = php_raw_url_encode(name, name_len);
- smart_str_appendl(&sname, ZSTR_VAL(encoded), ZSTR_LEN(encoded));
- zend_string_free(encoded);
+ smart_str_appendl(&sname, ZSTR_VAL(encoded), ZSTR_LEN(encoded)); zend_string_free(encoded);
encoded = php_raw_url_encode(value, value_len);
- smart_str_appendl(&svalue, ZSTR_VAL(encoded), ZSTR_LEN(encoded));
- zend_string_free(encoded);
+ smart_str_appendl(&svalue, ZSTR_VAL(encoded), ZSTR_LEN(encoded)); zend_string_free(encoded);
+ encoded = php_escape_html_entities_ex((unsigned char*)name, name_len, 0, ENT_QUOTES|ENT_SUBSTITUTE, SG(default_charset), 0);
+ smart_str_appendl(&hname, ZSTR_VAL(encoded), ZSTR_LEN(encoded)); zend_string_free(encoded);
+ encoded = php_escape_html_entities_ex((unsigned char*)value, value_len, 0, ENT_QUOTES|ENT_SUBSTITUTE, SG(default_charset), 0);
+ smart_str_appendl(&hvalue, ZSTR_VAL(encoded), ZSTR_LEN(encoded)); zend_string_free(encoded);
} else {
smart_str_appendl(&sname, name, name_len);
smart_str_appendl(&svalue, value, value_len);
+ smart_str_appendl(&hname, name, name_len);
+ smart_str_appendl(&hvalue, value, value_len);
}
- smart_str_append_smart_str(&BG(url_adapt_state_ex).url_app, &sname);
- smart_str_appendc(&BG(url_adapt_state_ex).url_app, '=');
- smart_str_append_smart_str(&BG(url_adapt_state_ex).url_app, &svalue);
+ smart_str_append_smart_str(&url_state->url_app, &sname);
+ smart_str_appendc(&url_state->url_app, '=');
+ smart_str_append_smart_str(&url_state->url_app, &svalue);
- smart_str_appends(&BG(url_adapt_state_ex).form_app, "<input type=\"hidden\" name=\"");
- smart_str_append_smart_str(&BG(url_adapt_state_ex).form_app, &sname);
- smart_str_appends(&BG(url_adapt_state_ex).form_app, "\" value=\"");
- smart_str_append_smart_str(&BG(url_adapt_state_ex).form_app, &svalue);
- smart_str_appends(&BG(url_adapt_state_ex).form_app, "\" />");
+ smart_str_appends(&url_state->form_app, "<input type=\"hidden\" name=\"");
+ smart_str_append_smart_str(&url_state->form_app, &hname);
+ smart_str_appends(&url_state->form_app, "\" value=\"");
+ smart_str_append_smart_str(&url_state->form_app, &hvalue);
+ smart_str_appends(&url_state->form_app, "\" />");
smart_str_free(&sname);
smart_str_free(&svalue);
+ smart_str_free(&hname);
+ smart_str_free(&hvalue);
return SUCCESS;
}
-PHPAPI int php_url_scanner_reset_vars(void)
+
+PHPAPI int php_url_scanner_add_session_var(char *name, size_t name_len, char *value, size_t value_len, int encode)
+{
+ return php_url_scanner_add_var_impl(name, name_len, value, value_len, encode, 1);
+}
+
+
+PHPAPI int php_url_scanner_add_var(char *name, size_t name_len, char *value, size_t value_len, int encode)
{
- if (BG(url_adapt_state_ex).form_app.s) {
- ZSTR_LEN(BG(url_adapt_state_ex).form_app.s) = 0;
+ return php_url_scanner_add_var_impl(name, name_len, value, value_len, encode, 0);
+}
+
+
+static inline void php_url_scanner_reset_vars_impl(int type) {
+ url_adapt_state_ex_t *url_state;
+
+ if (type) {
+ url_state = &BG(url_adapt_session_ex);
+ } else {
+ url_state = &BG(url_adapt_output_ex);
+ }
+
+ if (url_state->form_app.s) {
+ ZSTR_LEN(url_state->form_app.s) = 0;
}
- if (BG(url_adapt_state_ex).url_app.s) {
- ZSTR_LEN(BG(url_adapt_state_ex).url_app.s) = 0;
+ if (url_state->url_app.s) {
+ ZSTR_LEN(url_state->url_app.s) = 0;
}
+}
+
+PHPAPI int php_url_scanner_reset_session_vars(void)
+{
+ php_url_scanner_reset_vars_impl(1);
return SUCCESS;
}
-PHP_MINIT_FUNCTION(url_scanner)
+
+PHPAPI int php_url_scanner_reset_vars(void)
+{
+ php_url_scanner_reset_vars_impl(0);
+ return SUCCESS;
+}
+
+
+static inline int php_url_scanner_reset_var_impl(zend_string *name, int encode, int type)
{
- BG(url_adapt_state_ex).tags = NULL;
+ char *start, *end, *limit;
+ size_t separator_len;
+ smart_str sname = {0};
+ smart_str hname = {0};
+ smart_str url_app = {0};
+ smart_str form_app = {0};
+ zend_string *encoded;
+ int ret = SUCCESS;
+ zend_bool sep_removed = 0;
+ url_adapt_state_ex_t *url_state;
+
+ if (type) {
+ url_state = &BG(url_adapt_session_ex);
+ } else {
+ url_state = &BG(url_adapt_output_ex);
+ }
- BG(url_adapt_state_ex).form_app.s = BG(url_adapt_state_ex).url_app.s = NULL;
+ /* Short circuit check. Only check url_app. */
+ if (!url_state->url_app.s || !ZSTR_LEN(url_state->url_app.s)) {
+ return SUCCESS;
+ }
+ if (encode) {
+ encoded = php_raw_url_encode(ZSTR_VAL(name), ZSTR_LEN(name));
+ smart_str_appendl(&sname, ZSTR_VAL(encoded), ZSTR_LEN(encoded));
+ zend_string_free(encoded);
+ encoded = php_escape_html_entities_ex((unsigned char *)ZSTR_VAL(name), ZSTR_LEN(name), 0, ENT_QUOTES|ENT_SUBSTITUTE, SG(default_charset), 0);
+ smart_str_appendl(&hname, ZSTR_VAL(encoded), ZSTR_LEN(encoded));
+ zend_string_free(encoded);
+ } else {
+ smart_str_appendl(&sname, ZSTR_VAL(name), ZSTR_LEN(name));
+ smart_str_appendl(&hname, ZSTR_VAL(name), ZSTR_LEN(name));
+ }
+ smart_str_0(&sname);
+ smart_str_0(&hname);
+
+ smart_str_append_smart_str(&url_app, &sname);
+ smart_str_appendc(&url_app, '=');
+ smart_str_0(&url_app);
+
+ smart_str_appends(&form_app, "<input type=\"hidden\" name=\"");
+ smart_str_append_smart_str(&form_app, &hname);
+ smart_str_appends(&form_app, "\" value=\"");
+ smart_str_0(&form_app);
+
+ /* Short circuit check. Only check url_app. */
+ start = (char *) php_memnstr(ZSTR_VAL(url_state->url_app.s),
+ ZSTR_VAL(url_app.s), ZSTR_LEN(url_app.s),
+ ZSTR_VAL(url_state->url_app.s) + ZSTR_LEN(url_state->url_app.s));
+ if (!start) {
+ ret = FAILURE;
+ goto finish;
+ }
+
+ /* Get end of url var */
+ limit = ZSTR_VAL(url_state->url_app.s) + ZSTR_LEN(url_state->url_app.s);
+ end = start + ZSTR_LEN(url_app.s);
+ separator_len = strlen(PG(arg_separator).output);
+ while (end < limit) {
+ if (!memcmp(end, PG(arg_separator).output, separator_len)) {
+ end += separator_len;
+ sep_removed = 1;
+ break;
+ }
+ end++;
+ }
+ /* Remove all when this is the only rewrite var */
+ if (ZSTR_LEN(url_state->url_app.s) == end - start) {
+ php_url_scanner_reset_vars_impl(type);
+ goto finish;
+ }
+ /* Check preceeding separator */
+ if (!sep_removed
+ && start - PG(arg_separator).output >= separator_len
+ && !memcmp(start - separator_len, PG(arg_separator).output, separator_len)) {
+ start -= separator_len;
+ }
+ /* Remove partially */
+ memmove(start, end,
+ ZSTR_LEN(url_state->url_app.s) - (end - ZSTR_VAL(url_state->url_app.s)));
+ ZSTR_LEN(url_state->url_app.s) -= end - start;
+ ZSTR_VAL(url_state->url_app.s)[ZSTR_LEN(url_state->url_app.s)] = '\0';
+
+ /* Remove form var */
+ start = (char *) php_memnstr(ZSTR_VAL(url_state->form_app.s),
+ ZSTR_VAL(form_app.s), ZSTR_LEN(form_app.s),
+ ZSTR_VAL(url_state->form_app.s) + ZSTR_LEN(url_state->form_app.s));
+ if (!start) {
+ /* Should not happen */
+ ret = FAILURE;
+ php_url_scanner_reset_vars_impl(type);
+ goto finish;
+ }
+ /* Get end of form var */
+ limit = ZSTR_VAL(url_state->form_app.s) + ZSTR_LEN(url_state->form_app.s);
+ end = start + ZSTR_LEN(form_app.s);
+ while (end < limit) {
+ if (*end == '>') {
+ end += 1;
+ break;
+ }
+ end++;
+ }
+ /* Remove partially */
+ memmove(start, end,
+ ZSTR_LEN(url_state->form_app.s) - (end - ZSTR_VAL(url_state->form_app.s)));
+ ZSTR_LEN(url_state->form_app.s) -= end - start;
+ ZSTR_VAL(url_state->form_app.s)[ZSTR_LEN(url_state->form_app.s)] = '\0';
+
+finish:
+ smart_str_free(&url_app);
+ smart_str_free(&form_app);
+ smart_str_free(&sname);
+ smart_str_free(&hname);
+ return ret;
+}
+
+
+PHPAPI int php_url_scanner_reset_session_var(zend_string *name, int encode)
+{
+ return php_url_scanner_reset_var_impl(name, encode, 1);
+}
+
+
+PHPAPI int php_url_scanner_reset_var(zend_string *name, int encode)
+{
+ return php_url_scanner_reset_var_impl(name, encode, 0);
+}
+
+
+PHP_MINIT_FUNCTION(url_scanner)
+{
REGISTER_INI_ENTRIES();
return SUCCESS;
}
@@ -1081,20 +1397,34 @@ PHP_MSHUTDOWN_FUNCTION(url_scanner)
PHP_RINIT_FUNCTION(url_scanner)
{
- BG(url_adapt_state_ex).active = 0;
-
+ BG(url_adapt_session_ex).active = 0;
+ BG(url_adapt_session_ex).tag_type = 0;
+ BG(url_adapt_session_ex).attr_type = 0;
+ BG(url_adapt_output_ex).active = 0;
+ BG(url_adapt_output_ex).tag_type = 0;
+ BG(url_adapt_output_ex).attr_type = 0;
return SUCCESS;
}
PHP_RSHUTDOWN_FUNCTION(url_scanner)
{
- if (BG(url_adapt_state_ex).active) {
- php_url_scanner_ex_deactivate();
- BG(url_adapt_state_ex).active = 0;
+ if (BG(url_adapt_session_ex).active) {
+ php_url_scanner_ex_deactivate(1);
+ BG(url_adapt_session_ex).active = 0;
+ BG(url_adapt_session_ex).tag_type = 0;
+ BG(url_adapt_session_ex).attr_type = 0;
}
-
- smart_str_free(&BG(url_adapt_state_ex).form_app);
- smart_str_free(&BG(url_adapt_state_ex).url_app);
+ smart_str_free(&BG(url_adapt_session_ex).form_app);
+ smart_str_free(&BG(url_adapt_session_ex).url_app);
+
+ if (BG(url_adapt_output_ex).active) {
+ php_url_scanner_ex_deactivate(0);
+ BG(url_adapt_output_ex).active = 0;
+ BG(url_adapt_output_ex).tag_type = 0;
+ BG(url_adapt_output_ex).attr_type = 0;
+ }
+ smart_str_free(&BG(url_adapt_output_ex).form_app);
+ smart_str_free(&BG(url_adapt_output_ex).url_app);
return SUCCESS;
}
diff --git a/ext/standard/url_scanner_ex.h b/ext/standard/url_scanner_ex.h
index 86334ab396..c3b65e19d4 100644
--- a/ext/standard/url_scanner_ex.h
+++ b/ext/standard/url_scanner_ex.h
@@ -27,8 +27,12 @@ PHP_MSHUTDOWN_FUNCTION(url_scanner_ex);
PHP_RINIT_FUNCTION(url_scanner_ex);
PHP_RSHUTDOWN_FUNCTION(url_scanner_ex);
-PHPAPI char *php_url_scanner_adapt_single_url(const char *url, size_t urllen, const char *name, const char *value, size_t *newlen, int urlencode);
-PHPAPI int php_url_scanner_add_var(char *name, size_t name_len, char *value, size_t value_len, int urlencode);
+PHPAPI char *php_url_scanner_adapt_single_url(const char *url, size_t urllen, const char *name, const char *value, size_t *newlen, int encode);
+PHPAPI int php_url_scanner_add_session_var(char *name, size_t name_len, char *value, size_t value_len, int encode);
+PHPAPI int php_url_scanner_reset_session_var(zend_string *name, int encode);
+PHPAPI int php_url_scanner_reset_session_vars(void);
+PHPAPI int php_url_scanner_add_var(char *name, size_t name_len, char *value, size_t value_len, int encode);
+PHPAPI int php_url_scanner_reset_var(zend_string *name, int encode);
PHPAPI int php_url_scanner_reset_vars(void);
#include "zend_smart_str_public.h"
@@ -51,6 +55,11 @@ typedef struct {
char *lookup_data;
int state;
+ int type;
+ smart_str attr_val;
+ int tag_type;
+ int attr_type;
+
/* Everything above is zeroed in RINIT */
HashTable *tags;
} url_adapt_state_ex_t;
diff --git a/ext/standard/url_scanner_ex.re b/ext/standard/url_scanner_ex.re
index 8bc77db4be..b020b25b6c 100644
--- a/ext/standard/url_scanner_ex.re
+++ b/ext/standard/url_scanner_ex.re
@@ -13,6 +13,7 @@
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Author: Sascha Schumann <sascha@schumann.cx> |
+ | Yasuo Ohgaki <yohgaki@ohgaki.net> |
+----------------------------------------------------------------------+
*/
@@ -31,12 +32,14 @@
#include <stdlib.h>
#include <string.h>
+#include "SAPI.h"
#include "php_ini.h"
#include "php_globals.h"
#include "php_string.h"
#define STATE_TAG SOME_OTHER_STATE_TAG
#include "basic_functions.h"
#include "url.h"
+#include "html.h"
#undef STATE_TAG
#define url_scanner url_scanner_ex
@@ -48,14 +51,18 @@ static void tag_dtor(zval *zv)
free(Z_PTR_P(zv));
}
-static PHP_INI_MH(OnUpdateTags)
+static int php_ini_on_update_tags(zend_ini_entry *entry, zend_string *new_value, void *mh_arg1, void *mh_arg2, void *mh_arg3, int stage, int type)
{
url_adapt_state_ex_t *ctx;
char *key;
char *tmp;
char *lasts = NULL;
- ctx = &BG(url_adapt_state_ex);
+ if (type) {
+ ctx = &BG(url_adapt_session_ex);
+ } else {
+ ctx = &BG(url_adapt_output_ex);
+ }
tmp = estrndup(ZSTR_VAL(new_value), ZSTR_LEN(new_value));
@@ -64,6 +71,7 @@ static PHP_INI_MH(OnUpdateTags)
else {
ctx->tags = malloc(sizeof(HashTable));
if (!ctx->tags) {
+ efree(tmp);
return FAILURE;
}
}
@@ -71,8 +79,8 @@ static PHP_INI_MH(OnUpdateTags)
zend_hash_init(ctx->tags, 0, NULL, tag_dtor, 1);
for (key = php_strtok_r(tmp, ",", &lasts);
- key;
- key = php_strtok_r(NULL, ",", &lasts)) {
+ key;
+ key = php_strtok_r(NULL, ",", &lasts)) {
char *val;
val = strchr(key, '=');
@@ -81,11 +89,10 @@ static PHP_INI_MH(OnUpdateTags)
size_t keylen;
*val++ = '\0';
- for (q = key; *q; q++)
+ for (q = key; *q; q++) {
*q = tolower(*q);
+ }
keylen = q - key;
- /* key is stored withOUT NUL
- val is stored WITH NUL */
zend_hash_str_add_mem(ctx->tags, key, keylen, val, strlen(val)+1);
}
}
@@ -95,8 +102,70 @@ static PHP_INI_MH(OnUpdateTags)
return SUCCESS;
}
+static PHP_INI_MH(OnUpdateSessionTags)
+{
+ return php_ini_on_update_tags(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage, 1);
+}
+
+static PHP_INI_MH(OnUpdateOutputTags)
+{
+ return php_ini_on_update_tags(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage, 0);
+}
+
+static int php_ini_on_update_hosts(zend_ini_entry *entry, zend_string *new_value, void *mh_arg1, void *mh_arg2, void *mh_arg3, int stage, int type)
+{
+ HashTable *hosts;
+ char *key;
+ char *tmp;
+ char *lasts = NULL;
+
+ if (type) {
+ hosts = &BG(url_adapt_session_hosts_ht);
+ } else {
+ hosts = &BG(url_adapt_output_hosts_ht);
+ }
+ zend_hash_clean(hosts);
+
+ /* Use user supplied host whitelist */
+ tmp = estrndup(ZSTR_VAL(new_value), ZSTR_LEN(new_value));
+ for (key = php_strtok_r(tmp, ",", &lasts);
+ key;
+ key = php_strtok_r(NULL, ",", &lasts)) {
+ size_t keylen;
+ zend_string *tmp_key;
+ char *q;
+
+ for (q = key; *q; q++) {
+ *q = tolower(*q);
+ }
+ keylen = q - key;
+ if (keylen > 0) {
+ tmp_key = zend_string_init(key, keylen, 0);
+ zend_hash_add_empty_element(hosts, tmp_key);
+ zend_string_release(tmp_key);
+ }
+ }
+ efree(tmp);
+
+ return SUCCESS;
+}
+
+static PHP_INI_MH(OnUpdateSessionHosts)
+{
+ return php_ini_on_update_hosts(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage, 1);
+}
+
+static PHP_INI_MH(OnUpdateOutputHosts)
+{
+ return php_ini_on_update_hosts(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage, 0);
+}
+
+/* FIXME: OnUpdate*Hosts cannot set default to $_SERVER['HTTP_HOST'] at startup */
PHP_INI_BEGIN()
- STD_PHP_INI_ENTRY("url_rewriter.tags", "a=href,area=href,frame=src,form=,fieldset=", PHP_INI_ALL, OnUpdateTags, url_adapt_state_ex, php_basic_globals, basic_globals)
+ STD_PHP_INI_ENTRY("session.trans_sid_tags", "a=href,area=href,frame=src,form=", PHP_INI_ALL, OnUpdateSessionTags, url_adapt_session_ex, php_basic_globals, basic_globals)
+ STD_PHP_INI_ENTRY("session.trans_sid_hosts", "", PHP_INI_ALL, OnUpdateSessionHosts, url_adapt_session_hosts_ht, php_basic_globals, basic_globals)
+ STD_PHP_INI_ENTRY("url_rewriter.tags", "form=", PHP_INI_ALL, OnUpdateOutputTags, url_adapt_session_ex, php_basic_globals, basic_globals)
+ STD_PHP_INI_ENTRY("url_rewriter.hosts", "", PHP_INI_ALL, OnUpdateOutputHosts, url_adapt_session_hosts_ht, php_basic_globals, basic_globals)
PHP_INI_END()
/*!re2c
@@ -115,69 +184,107 @@ alphadash = ([a-zA-Z] | "-");
static inline void append_modified_url(smart_str *url, smart_str *dest, smart_str *url_app, const char *separator)
{
- register const char *p, *q;
- const char *bash = NULL;
- const char *sep = "?";
+ php_url *url_parts;
+ char *tmp;
+ size_t tmp_len;
- /*
- * Don't modify "//example.com" full path, unless
- * HTTP_HOST matches.
- */
- if (ZSTR_LEN(url->s) > 2 && ZSTR_VAL(url->s)[0] == '/' && ZSTR_VAL(url->s)[1] == '/') {
- const char *end_chars = "/\"'?>\r\n";
- zval *tmp = NULL, *http_host = NULL;
- size_t target_len, host_len;
- if ((!(tmp = zend_hash_str_find(&EG(symbol_table), ZEND_STRL("_SERVER"))))
- || Z_TYPE_P(tmp) != IS_ARRAY
- || !(http_host = zend_hash_str_find(HASH_OF(tmp), ZEND_STRL("HTTP_HOST")))
- || Z_TYPE_P(http_host) != IS_STRING) {
- smart_str_append_smart_str(dest, url);
- return;
- }
+ smart_str_0(url); /* FIXME: Bug #70480 php_url_parse_ex() crashes by processing chars exceed len */
+ url_parts = php_url_parse_ex(ZSTR_VAL(url->s), ZSTR_LEN(url->s));
- /* HTTP_HOST could be "example.com:8888", etc. */
- /* Need to find end of URL in buffer */
- host_len = strcspn(Z_STRVAL_P(http_host), ":");
- target_len = php_strcspn(
- ZSTR_VAL(url->s) + 2, (char *) end_chars,
- ZSTR_VAL(url->s) + ZSTR_LEN(url->s), (char *) end_chars + strlen(end_chars));
- if (host_len
- && host_len == target_len
- && strncasecmp(Z_STRVAL_P(http_host), ZSTR_VAL(url->s)+2, host_len)) {
- smart_str_append_smart_str(dest, url);
- return;
- }
+ /* Ignore malformed URLs */
+ if (!url_parts) {
+ smart_str_append_smart_str(dest, url);
+ return;
}
- q = (p = ZSTR_VAL(url->s)) + ZSTR_LEN(url->s);
-
-scan:
-/*!re2c
- ":" { smart_str_append_smart_str(dest, url); return; }
- "?" { sep = separator; goto scan; }
- "#" { bash = p - 1; goto done; }
- (any\[:?#])+ { goto scan; }
-*/
-done:
-
/* Don't modify URLs of the format "#mark" */
- if (bash && bash - ZSTR_VAL(url->s) == 0) {
+ if (url_parts->fragment && '#' == ZSTR_VAL(url->s)[0]) {
smart_str_append_smart_str(dest, url);
+ php_url_free(url_parts);
return;
}
- if (bash)
- smart_str_appendl(dest, ZSTR_VAL(url->s), bash - ZSTR_VAL(url->s));
- else
+ /* Check protocol. Only http/https is allowed. */
+ if (url_parts->scheme
+ && strcasecmp("http", url_parts->scheme)
+ && strcasecmp("https", url_parts->scheme)) {
smart_str_append_smart_str(dest, url);
+ php_url_free(url_parts);
+ return;
+ }
+
+ /* Check host whitelist. If it's not listed, do nothing. */
+ if (url_parts->host
+ && (tmp_len = strlen(url_parts->host))
+ && (tmp = php_strtolower(url_parts->host, tmp_len))
+ && !zend_hash_str_find(&BG(url_adapt_session_hosts_ht), tmp, tmp_len)) {
+ smart_str_append_smart_str(dest, url);
+ php_url_free(url_parts);
+ return;
+ }
- smart_str_appends(dest, sep);
- smart_str_append_smart_str(dest, url_app);
+ /*
+ * When URL does not have path and query string add "/?".
+ * i.e. If URL is only "?foo=bar", should not add "/?".
+ */
+ if (!url_parts->path && !url_parts->query && !url_parts->fragment) {
+ /* URL is http://php.net or like */
+ smart_str_append_smart_str(dest, url);
+ smart_str_appendc(dest, '/');
+ smart_str_appendc(dest, '?');
+ smart_str_append_smart_str(dest, url_app);
+ php_url_free(url_parts);
+ return;
+ }
- if (bash)
- smart_str_appendl(dest, bash, q - bash);
+ if (url_parts->scheme) {
+ smart_str_appends(dest, url_parts->scheme);
+ smart_str_appends(dest, "://");
+ } else if (*(ZSTR_VAL(url->s)) == '/' && *(ZSTR_VAL(url->s)+1) == '/') {
+ smart_str_appends(dest, "//");
+ }
+ if (url_parts->user) {
+ smart_str_appends(dest, url_parts->user);
+ if (url_parts->pass) {
+ smart_str_appends(dest, url_parts->pass);
+ smart_str_appendc(dest, ':');
+ }
+ smart_str_appendc(dest, '@');
+ }
+ if (url_parts->host) {
+ smart_str_appends(dest, url_parts->host);
+ }
+ if (url_parts->port) {
+ smart_str_appendc(dest, ':');
+ smart_str_append_unsigned(dest, (long)url_parts->port);
+ }
+ if (url_parts->path) {
+ smart_str_appends(dest, url_parts->path);
+ }
+ smart_str_appendc(dest, '?');
+ if (url_parts->query) {
+ smart_str_appends(dest, url_parts->query);
+ smart_str_appends(dest, separator);
+ smart_str_append_smart_str(dest, url_app);
+ } else {
+ smart_str_append_smart_str(dest, url_app);
+ }
+ if (url_parts->fragment) {
+ smart_str_appendc(dest, '#');
+ smart_str_appends(dest, url_parts->fragment);
+ }
+ php_url_free(url_parts);
}
+enum {
+ TAG_NORMAL = 0,
+ TAG_FORM
+};
+
+enum {
+ ATTR_NORMAL = 0,
+ ATTR_ACTION
+};
#undef YYFILL
#undef YYCTYPE
@@ -189,18 +296,24 @@ static inline void tag_arg(url_adapt_state_ex_t *ctx, char quotes, char type)
{
char f = 0;
- if (strncasecmp(ZSTR_VAL(ctx->arg.s), ctx->lookup_data, ZSTR_LEN(ctx->arg.s)) == 0)
+ /* arg.s is string WITHOUT NUL.
+ To avoid partial match, NUL is added here */
+ ZSTR_VAL(ctx->arg.s)[ZSTR_LEN(ctx->arg.s)] = '\0';
+ if (!strcasecmp(ZSTR_VAL(ctx->arg.s), ctx->lookup_data)) {
f = 1;
+ }
- if (quotes)
+ if (quotes) {
smart_str_appendc(&ctx->result, type);
+ }
if (f) {
append_modified_url(&ctx->val, &ctx->result, &ctx->url_app, PG(arg_separator).output);
} else {
smart_str_append_smart_str(&ctx->result, &ctx->val);
}
- if (quotes)
+ if (quotes) {
smart_str_appendc(&ctx->result, type);
+ }
}
enum {
@@ -234,11 +347,79 @@ static inline void passthru(STD_PARA)
smart_str_appendl(&ctx->result, start, YYCURSOR - start);
}
+
+static int check_http_host(char *target)
+{
+ zval *host, *tmp;
+ zend_string *host_tmp;
+ char *colon;
+
+ if ((tmp = zend_hash_str_find(&EG(symbol_table), ZEND_STRL("_SERVER"))) &&
+ (host = zend_hash_str_find(Z_ARRVAL_P(tmp), ZEND_STRL("HTTP_HOST"))) &&
+ Z_TYPE_P(host) == IS_STRING) {
+ host_tmp = zend_string_init(Z_STRVAL_P(host), Z_STRLEN_P(host), 0);
+ /* HTTP_HOST could be 'localhost:8888' etc. */
+ colon = strchr(ZSTR_VAL(host_tmp), ':');
+ if (colon) {
+ ZSTR_LEN(host_tmp) = colon - ZSTR_VAL(host_tmp);
+ ZSTR_VAL(host_tmp)[ZSTR_LEN(host_tmp)] = '\0';
+ }
+ if (!strcasecmp(ZSTR_VAL(host_tmp), target)) {
+ zend_string_release(host_tmp);
+ return SUCCESS;
+ }
+ zend_string_release(host_tmp);
+ }
+ return FAILURE;
+}
+
+static int check_host_whitelist(url_adapt_state_ex_t *ctx)
+{
+ php_url *url_parts = NULL;
+ HashTable *allowed_hosts = ctx->type ? &BG(url_adapt_session_hosts_ht) : &BG(url_adapt_output_hosts_ht);
+
+ ZEND_ASSERT(ctx->tag_type == TAG_FORM);
+
+ if (ctx->attr_val.s && ZSTR_LEN(ctx->attr_val.s)) {
+ url_parts = php_url_parse_ex(ZSTR_VAL(ctx->attr_val.s), ZSTR_LEN(ctx->attr_val.s));
+ } else {
+ return SUCCESS; /* empty URL is valid */
+ }
+
+ if (!url_parts) {
+ return FAILURE;
+ }
+ if (url_parts->scheme) {
+ /* Only http/https should be handled.
+ A bit hacky check this here, but saves a URL parse. */
+ if (strcasecmp(url_parts->scheme, "http") &&
+ strcasecmp(url_parts->scheme, "https")) {
+ php_url_free(url_parts);
+ return FAILURE;
+ }
+ }
+ if (!url_parts->host) {
+ php_url_free(url_parts);
+ return SUCCESS;
+ }
+ if (!zend_hash_num_elements(allowed_hosts) &&
+ check_http_host(url_parts->host) == SUCCESS) {
+ php_url_free(url_parts);
+ return SUCCESS;
+ }
+ if (!zend_hash_str_find(allowed_hosts,
+ url_parts->host,
+ strlen(url_parts->host))) {
+ php_url_free(url_parts);
+ return FAILURE;
+ }
+ php_url_free(url_parts);
+ return SUCCESS;
+}
+
/*
- * This function appends a hidden input field after a <form> or
- * <fieldset>. The latter is important for XHTML.
+ * This function appends a hidden input field after a <form>.
*/
-
static void handle_form(STD_PARA)
{
int doit = 0;
@@ -246,32 +427,16 @@ static void handle_form(STD_PARA)
if (ZSTR_LEN(ctx->form_app.s) > 0) {
switch (ZSTR_LEN(ctx->tag.s)) {
case sizeof("form") - 1:
- if (!strncasecmp(ZSTR_VAL(ctx->tag.s), "form", sizeof("form") - 1)) {
- doit = 1;
- }
- if (doit && ctx->val.s && ctx->lookup_data && *ctx->lookup_data) {
- char *e, *p = (char *)zend_memnstr(ZSTR_VAL(ctx->val.s), "://", sizeof("://") - 1, ZSTR_VAL(ctx->val.s) + ZSTR_LEN(ctx->val.s));
- if (p) {
- e = memchr(p, '/', (ZSTR_VAL(ctx->val.s) + ZSTR_LEN(ctx->val.s)) - p);
- if (!e) {
- e = ZSTR_VAL(ctx->val.s) + ZSTR_LEN(ctx->val.s);
- }
- if ((e - p) && strncasecmp(p, ctx->lookup_data, (e - p))) {
- doit = 0;
- }
- }
- }
- break;
-
- case sizeof("fieldset") - 1:
- if (!strncasecmp(ZSTR_VAL(ctx->tag.s), "fieldset", sizeof("fieldset") - 1)) {
+ if (!strncasecmp(ZSTR_VAL(ctx->tag.s), "form", ZSTR_LEN(ctx->tag.s))
+ && check_host_whitelist(ctx) == SUCCESS) {
doit = 1;
}
break;
}
+ }
- if (doit)
- smart_str_append_smart_str(&ctx->result, &ctx->form_app);
+ if (doit) {
+ smart_str_append_smart_str(&ctx->result, &ctx->form_app);
}
}
@@ -294,8 +459,15 @@ static inline void handle_tag(STD_PARA)
for (i = 0; i < ZSTR_LEN(ctx->tag.s); i++)
ZSTR_VAL(ctx->tag.s)[i] = tolower((int)(unsigned char)ZSTR_VAL(ctx->tag.s)[i]);
/* intentionally using str_find here, in case the hash value is set, but the string val is changed later */
- if ((ctx->lookup_data = zend_hash_str_find_ptr(ctx->tags, ZSTR_VAL(ctx->tag.s), ZSTR_LEN(ctx->tag.s))) != NULL)
+ if ((ctx->lookup_data = zend_hash_str_find_ptr(ctx->tags, ZSTR_VAL(ctx->tag.s), ZSTR_LEN(ctx->tag.s))) != NULL) {
ok = 1;
+ if (ZSTR_LEN(ctx->tag.s) == sizeof("form")-1
+ && !strncasecmp(ZSTR_VAL(ctx->tag.s), "form", ZSTR_LEN(ctx->tag.s))) {
+ ctx->tag_type = TAG_FORM;
+ } else {
+ ctx->tag_type = TAG_NORMAL;
+ }
+ }
STATE = ok ? STATE_NEXT_ARG : STATE_PLAIN;
}
@@ -305,11 +477,20 @@ static inline void handle_arg(STD_PARA)
ZSTR_LEN(ctx->arg.s) = 0;
}
smart_str_appendl(&ctx->arg, start, YYCURSOR - start);
+ if (ctx->tag_type == TAG_FORM &&
+ strncasecmp(ZSTR_VAL(ctx->arg.s), "action", ZSTR_LEN(ctx->arg.s)) == 0) {
+ ctx->attr_type = ATTR_ACTION;
+ } else {
+ ctx->attr_type = ATTR_NORMAL;
+ }
}
static inline void handle_val(STD_PARA, char quotes, char type)
{
smart_str_setl(&ctx->val, start + quotes, YYCURSOR - start - quotes * 2);
+ if (ctx->tag_type == TAG_FORM && ctx->attr_type == ATTR_ACTION) {
+ smart_str_setl(&ctx->attr_val, start + quotes, YYCURSOR - start - quotes * 2);
+ }
tag_arg(ctx, quotes, type);
}
@@ -402,7 +583,7 @@ stop:
}
-PHPAPI char *php_url_scanner_adapt_single_url(const char *url, size_t urllen, const char *name, const char *value, size_t *newlen, int urlencode)
+PHPAPI char *php_url_scanner_adapt_single_url(const char *url, size_t urllen, const char *name, const char *value, size_t *newlen, int encode)
{
char *result;
smart_str surl = {0};
@@ -412,7 +593,7 @@ PHPAPI char *php_url_scanner_adapt_single_url(const char *url, size_t urllen, co
smart_str_appendl(&surl, url, urllen);
- if (urlencode) {
+ if (encode) {
encoded = php_raw_url_encode(name, strlen(name));
smart_str_appendl(&url_app, ZSTR_VAL(encoded), ZSTR_LEN(encoded));
zend_string_free(encoded);
@@ -420,7 +601,7 @@ PHPAPI char *php_url_scanner_adapt_single_url(const char *url, size_t urllen, co
smart_str_appends(&url_app, name);
}
smart_str_appendc(&url_app, '=');
- if (urlencode) {
+ if (encode) {
encoded = php_raw_url_encode(value, strlen(value));
smart_str_appendl(&url_app, ZSTR_VAL(encoded), ZSTR_LEN(encoded));
zend_string_free(encoded);
@@ -441,13 +622,10 @@ PHPAPI char *php_url_scanner_adapt_single_url(const char *url, size_t urllen, co
}
-static char *url_adapt_ext(const char *src, size_t srclen, size_t *newlen, zend_bool do_flush)
+static char *url_adapt_ext(const char *src, size_t srclen, size_t *newlen, zend_bool do_flush, url_adapt_state_ex_t *ctx)
{
- url_adapt_state_ex_t *ctx;
char *retval;
- ctx = &BG(url_adapt_state_ex);
-
xx_mainloop(ctx, src, srclen);
if (!ctx->result.s) {
@@ -462,50 +640,67 @@ static char *url_adapt_ext(const char *src, size_t srclen, size_t *newlen, zend_
*newlen += ZSTR_LEN(ctx->buf.s);
smart_str_free(&ctx->buf);
smart_str_free(&ctx->val);
+ smart_str_free(&ctx->attr_val);
}
retval = estrndup(ZSTR_VAL(ctx->result.s), ZSTR_LEN(ctx->result.s));
smart_str_free(&ctx->result);
return retval;
}
-static int php_url_scanner_ex_activate(void)
+static int php_url_scanner_ex_activate(int type)
{
url_adapt_state_ex_t *ctx;
- ctx = &BG(url_adapt_state_ex);
+ if (type) {
+ ctx = &BG(url_adapt_session_ex);
+ } else {
+ ctx = &BG(url_adapt_output_ex);
+ }
memset(ctx, 0, ((size_t) &((url_adapt_state_ex_t *)0)->tags));
return SUCCESS;
}
-static int php_url_scanner_ex_deactivate(void)
+static int php_url_scanner_ex_deactivate(int type)
{
url_adapt_state_ex_t *ctx;
- ctx = &BG(url_adapt_state_ex);
+ if (type) {
+ ctx = &BG(url_adapt_session_ex);
+ } else {
+ ctx = &BG(url_adapt_output_ex);
+ }
smart_str_free(&ctx->result);
smart_str_free(&ctx->buf);
smart_str_free(&ctx->tag);
smart_str_free(&ctx->arg);
+ smart_str_free(&ctx->attr_val);
return SUCCESS;
}
-static void php_url_scanner_output_handler(char *output, size_t output_len, char **handled_output, size_t *handled_output_len, int mode)
+static inline void php_url_scanner_session_handler_impl(char *output, size_t output_len, char **handled_output, size_t *handled_output_len, int mode, int type)
{
size_t len;
+ url_adapt_state_ex_t *url_state;
+
+ if (type) {
+ url_state = &BG(url_adapt_session_ex);
+ } else {
+ url_state = &BG(url_adapt_output_ex);
+ }
- if (ZSTR_LEN(BG(url_adapt_state_ex).url_app.s) != 0) {
- *handled_output = url_adapt_ext(output, output_len, &len, (zend_bool) (mode & (PHP_OUTPUT_HANDLER_END | PHP_OUTPUT_HANDLER_CONT | PHP_OUTPUT_HANDLER_FLUSH | PHP_OUTPUT_HANDLER_FINAL) ? 1 : 0));
+ if (ZSTR_LEN(url_state->url_app.s) != 0) {
+ *handled_output = url_adapt_ext(output, output_len, &len, (zend_bool) (mode & (PHP_OUTPUT_HANDLER_END | PHP_OUTPUT_HANDLER_CONT | PHP_OUTPUT_HANDLER_FLUSH | PHP_OUTPUT_HANDLER_FINAL) ? 1 : 0), url_state);
if (sizeof(uint) < sizeof(size_t)) {
if (len > UINT_MAX)
len = UINT_MAX;
}
*handled_output_len = len;
- } else if (ZSTR_LEN(BG(url_adapt_state_ex).url_app.s) == 0) {
- url_adapt_state_ex_t *ctx = &BG(url_adapt_state_ex);
+ } else if (ZSTR_LEN(url_state->url_app.s) == 0) {
+ url_adapt_state_ex_t *ctx = url_state;
if (ctx->buf.s && ZSTR_LEN(ctx->buf.s)) {
smart_str_append(&ctx->result, ctx->buf.s);
smart_str_appendl(&ctx->result, output, output_len);
@@ -523,68 +718,257 @@ static void php_url_scanner_output_handler(char *output, size_t output_len, char
}
}
-PHPAPI int php_url_scanner_add_var(char *name, size_t name_len, char *value, size_t value_len, int urlencode)
+static void php_url_scanner_session_handler(char *output, size_t output_len, char **handled_output, size_t *handled_output_len, int mode)
+{
+ php_url_scanner_session_handler_impl(output, output_len, handled_output, handled_output_len, mode, 1);
+}
+
+static void php_url_scanner_output_handler(char *output, size_t output_len, char **handled_output, size_t *handled_output_len, int mode)
+{
+ php_url_scanner_session_handler_impl(output, output_len, handled_output, handled_output_len, mode, 0);
+}
+
+static inline int php_url_scanner_add_var_impl(char *name, size_t name_len, char *value, size_t value_len, int encode, int type)
{
smart_str sname = {0};
smart_str svalue = {0};
+ smart_str hname = {0};
+ smart_str hvalue = {0};
zend_string *encoded;
+ url_adapt_state_ex_t *url_state;
+ php_output_handler_func_t handler;
- if (!BG(url_adapt_state_ex).active) {
- php_url_scanner_ex_activate();
- php_output_start_internal(ZEND_STRL("URL-Rewriter"), php_url_scanner_output_handler, 0, PHP_OUTPUT_HANDLER_STDFLAGS);
- BG(url_adapt_state_ex).active = 1;
+ if (type) {
+ url_state = &BG(url_adapt_session_ex);
+ handler = php_url_scanner_session_handler;
+ } else {
+ url_state = &BG(url_adapt_output_ex);
+ handler = php_url_scanner_output_handler;
+ }
+
+ if (!url_state->active) {
+ php_url_scanner_ex_activate(type);
+ php_output_start_internal(ZEND_STRL("URL-Rewriter"), handler, 0, PHP_OUTPUT_HANDLER_STDFLAGS);
+ url_state->active = 1;
}
- if (BG(url_adapt_state_ex).url_app.s && ZSTR_LEN(BG(url_adapt_state_ex).url_app.s) != 0) {
- smart_str_appends(&BG(url_adapt_state_ex).url_app, PG(arg_separator).output);
+ if (url_state->url_app.s && ZSTR_LEN(url_state->url_app.s) != 0) {
+ smart_str_appends(&url_state->url_app, PG(arg_separator).output);
}
- if (urlencode) {
+ if (encode) {
encoded = php_raw_url_encode(name, name_len);
- smart_str_appendl(&sname, ZSTR_VAL(encoded), ZSTR_LEN(encoded));
- zend_string_free(encoded);
+ smart_str_appendl(&sname, ZSTR_VAL(encoded), ZSTR_LEN(encoded)); zend_string_free(encoded);
encoded = php_raw_url_encode(value, value_len);
- smart_str_appendl(&svalue, ZSTR_VAL(encoded), ZSTR_LEN(encoded));
- zend_string_free(encoded);
+ smart_str_appendl(&svalue, ZSTR_VAL(encoded), ZSTR_LEN(encoded)); zend_string_free(encoded);
+ encoded = php_escape_html_entities_ex((unsigned char*)name, name_len, 0, ENT_QUOTES|ENT_SUBSTITUTE, SG(default_charset), 0);
+ smart_str_appendl(&hname, ZSTR_VAL(encoded), ZSTR_LEN(encoded)); zend_string_free(encoded);
+ encoded = php_escape_html_entities_ex((unsigned char*)value, value_len, 0, ENT_QUOTES|ENT_SUBSTITUTE, SG(default_charset), 0);
+ smart_str_appendl(&hvalue, ZSTR_VAL(encoded), ZSTR_LEN(encoded)); zend_string_free(encoded);
} else {
smart_str_appendl(&sname, name, name_len);
smart_str_appendl(&svalue, value, value_len);
+ smart_str_appendl(&hname, name, name_len);
+ smart_str_appendl(&hvalue, value, value_len);
}
- smart_str_append_smart_str(&BG(url_adapt_state_ex).url_app, &sname);
- smart_str_appendc(&BG(url_adapt_state_ex).url_app, '=');
- smart_str_append_smart_str(&BG(url_adapt_state_ex).url_app, &svalue);
+ smart_str_append_smart_str(&url_state->url_app, &sname);
+ smart_str_appendc(&url_state->url_app, '=');
+ smart_str_append_smart_str(&url_state->url_app, &svalue);
- smart_str_appends(&BG(url_adapt_state_ex).form_app, "<input type=\"hidden\" name=\"");
- smart_str_append_smart_str(&BG(url_adapt_state_ex).form_app, &sname);
- smart_str_appends(&BG(url_adapt_state_ex).form_app, "\" value=\"");
- smart_str_append_smart_str(&BG(url_adapt_state_ex).form_app, &svalue);
- smart_str_appends(&BG(url_adapt_state_ex).form_app, "\" />");
+ smart_str_appends(&url_state->form_app, "<input type=\"hidden\" name=\"");
+ smart_str_append_smart_str(&url_state->form_app, &hname);
+ smart_str_appends(&url_state->form_app, "\" value=\"");
+ smart_str_append_smart_str(&url_state->form_app, &hvalue);
+ smart_str_appends(&url_state->form_app, "\" />");
smart_str_free(&sname);
smart_str_free(&svalue);
+ smart_str_free(&hname);
+ smart_str_free(&hvalue);
return SUCCESS;
}
-PHPAPI int php_url_scanner_reset_vars(void)
+
+PHPAPI int php_url_scanner_add_session_var(char *name, size_t name_len, char *value, size_t value_len, int encode)
+{
+ return php_url_scanner_add_var_impl(name, name_len, value, value_len, encode, 1);
+}
+
+
+PHPAPI int php_url_scanner_add_var(char *name, size_t name_len, char *value, size_t value_len, int encode)
{
- if (BG(url_adapt_state_ex).form_app.s) {
- ZSTR_LEN(BG(url_adapt_state_ex).form_app.s) = 0;
+ return php_url_scanner_add_var_impl(name, name_len, value, value_len, encode, 0);
+}
+
+
+static inline void php_url_scanner_reset_vars_impl(int type) {
+ url_adapt_state_ex_t *url_state;
+
+ if (type) {
+ url_state = &BG(url_adapt_session_ex);
+ } else {
+ url_state = &BG(url_adapt_output_ex);
+ }
+
+ if (url_state->form_app.s) {
+ ZSTR_LEN(url_state->form_app.s) = 0;
}
- if (BG(url_adapt_state_ex).url_app.s) {
- ZSTR_LEN(BG(url_adapt_state_ex).url_app.s) = 0;
+ if (url_state->url_app.s) {
+ ZSTR_LEN(url_state->url_app.s) = 0;
}
+}
+
+PHPAPI int php_url_scanner_reset_session_vars(void)
+{
+ php_url_scanner_reset_vars_impl(1);
return SUCCESS;
}
-PHP_MINIT_FUNCTION(url_scanner)
+
+PHPAPI int php_url_scanner_reset_vars(void)
+{
+ php_url_scanner_reset_vars_impl(0);
+ return SUCCESS;
+}
+
+
+static inline int php_url_scanner_reset_var_impl(zend_string *name, int encode, int type)
+{
+ char *start, *end, *limit;
+ size_t separator_len;
+ smart_str sname = {0};
+ smart_str hname = {0};
+ smart_str url_app = {0};
+ smart_str form_app = {0};
+ zend_string *encoded;
+ int ret = SUCCESS;
+ zend_bool sep_removed = 0;
+ url_adapt_state_ex_t *url_state;
+
+ if (type) {
+ url_state = &BG(url_adapt_session_ex);
+ } else {
+ url_state = &BG(url_adapt_output_ex);
+ }
+
+ /* Short circuit check. Only check url_app. */
+ if (!url_state->url_app.s || !ZSTR_LEN(url_state->url_app.s)) {
+ return SUCCESS;
+ }
+
+ if (encode) {
+ encoded = php_raw_url_encode(ZSTR_VAL(name), ZSTR_LEN(name));
+ smart_str_appendl(&sname, ZSTR_VAL(encoded), ZSTR_LEN(encoded));
+ zend_string_free(encoded);
+ encoded = php_escape_html_entities_ex((unsigned char *)ZSTR_VAL(name), ZSTR_LEN(name), 0, ENT_QUOTES|ENT_SUBSTITUTE, SG(default_charset), 0);
+ smart_str_appendl(&hname, ZSTR_VAL(encoded), ZSTR_LEN(encoded));
+ zend_string_free(encoded);
+ } else {
+ smart_str_appendl(&sname, ZSTR_VAL(name), ZSTR_LEN(name));
+ smart_str_appendl(&hname, ZSTR_VAL(name), ZSTR_LEN(name));
+ }
+ smart_str_0(&sname);
+ smart_str_0(&hname);
+
+ smart_str_append_smart_str(&url_app, &sname);
+ smart_str_appendc(&url_app, '=');
+ smart_str_0(&url_app);
+
+ smart_str_appends(&form_app, "<input type=\"hidden\" name=\"");
+ smart_str_append_smart_str(&form_app, &hname);
+ smart_str_appends(&form_app, "\" value=\"");
+ smart_str_0(&form_app);
+
+ /* Short circuit check. Only check url_app. */
+ start = (char *) php_memnstr(ZSTR_VAL(url_state->url_app.s),
+ ZSTR_VAL(url_app.s), ZSTR_LEN(url_app.s),
+ ZSTR_VAL(url_state->url_app.s) + ZSTR_LEN(url_state->url_app.s));
+ if (!start) {
+ ret = FAILURE;
+ goto finish;
+ }
+
+ /* Get end of url var */
+ limit = ZSTR_VAL(url_state->url_app.s) + ZSTR_LEN(url_state->url_app.s);
+ end = start + ZSTR_LEN(url_app.s);
+ separator_len = strlen(PG(arg_separator).output);
+ while (end < limit) {
+ if (!memcmp(end, PG(arg_separator).output, separator_len)) {
+ end += separator_len;
+ sep_removed = 1;
+ break;
+ }
+ end++;
+ }
+ /* Remove all when this is the only rewrite var */
+ if (ZSTR_LEN(url_state->url_app.s) == end - start) {
+ php_url_scanner_reset_vars_impl(type);
+ goto finish;
+ }
+ /* Check preceeding separator */
+ if (!sep_removed
+ && start - PG(arg_separator).output >= separator_len
+ && !memcmp(start - separator_len, PG(arg_separator).output, separator_len)) {
+ start -= separator_len;
+ }
+ /* Remove partially */
+ memmove(start, end,
+ ZSTR_LEN(url_state->url_app.s) - (end - ZSTR_VAL(url_state->url_app.s)));
+ ZSTR_LEN(url_state->url_app.s) -= end - start;
+ ZSTR_VAL(url_state->url_app.s)[ZSTR_LEN(url_state->url_app.s)] = '\0';
+
+ /* Remove form var */
+ start = (char *) php_memnstr(ZSTR_VAL(url_state->form_app.s),
+ ZSTR_VAL(form_app.s), ZSTR_LEN(form_app.s),
+ ZSTR_VAL(url_state->form_app.s) + ZSTR_LEN(url_state->form_app.s));
+ if (!start) {
+ /* Should not happen */
+ ret = FAILURE;
+ php_url_scanner_reset_vars_impl(type);
+ goto finish;
+ }
+ /* Get end of form var */
+ limit = ZSTR_VAL(url_state->form_app.s) + ZSTR_LEN(url_state->form_app.s);
+ end = start + ZSTR_LEN(form_app.s);
+ while (end < limit) {
+ if (*end == '>') {
+ end += 1;
+ break;
+ }
+ end++;
+ }
+ /* Remove partially */
+ memmove(start, end,
+ ZSTR_LEN(url_state->form_app.s) - (end - ZSTR_VAL(url_state->form_app.s)));
+ ZSTR_LEN(url_state->form_app.s) -= end - start;
+ ZSTR_VAL(url_state->form_app.s)[ZSTR_LEN(url_state->form_app.s)] = '\0';
+
+finish:
+ smart_str_free(&url_app);
+ smart_str_free(&form_app);
+ smart_str_free(&sname);
+ smart_str_free(&hname);
+ return ret;
+}
+
+
+PHPAPI int php_url_scanner_reset_session_var(zend_string *name, int encode)
+{
+ return php_url_scanner_reset_var_impl(name, encode, 1);
+}
+
+
+PHPAPI int php_url_scanner_reset_var(zend_string *name, int encode)
{
- BG(url_adapt_state_ex).tags = NULL;
+ return php_url_scanner_reset_var_impl(name, encode, 0);
+}
- BG(url_adapt_state_ex).form_app.s = BG(url_adapt_state_ex).url_app.s = NULL;
+PHP_MINIT_FUNCTION(url_scanner)
+{
REGISTER_INI_ENTRIES();
return SUCCESS;
}
@@ -598,20 +982,34 @@ PHP_MSHUTDOWN_FUNCTION(url_scanner)
PHP_RINIT_FUNCTION(url_scanner)
{
- BG(url_adapt_state_ex).active = 0;
-
+ BG(url_adapt_session_ex).active = 0;
+ BG(url_adapt_session_ex).tag_type = 0;
+ BG(url_adapt_session_ex).attr_type = 0;
+ BG(url_adapt_output_ex).active = 0;
+ BG(url_adapt_output_ex).tag_type = 0;
+ BG(url_adapt_output_ex).attr_type = 0;
return SUCCESS;
}
PHP_RSHUTDOWN_FUNCTION(url_scanner)
{
- if (BG(url_adapt_state_ex).active) {
- php_url_scanner_ex_deactivate();
- BG(url_adapt_state_ex).active = 0;
+ if (BG(url_adapt_session_ex).active) {
+ php_url_scanner_ex_deactivate(1);
+ BG(url_adapt_session_ex).active = 0;
+ BG(url_adapt_session_ex).tag_type = 0;
+ BG(url_adapt_session_ex).attr_type = 0;
}
-
- smart_str_free(&BG(url_adapt_state_ex).form_app);
- smart_str_free(&BG(url_adapt_state_ex).url_app);
+ smart_str_free(&BG(url_adapt_session_ex).form_app);
+ smart_str_free(&BG(url_adapt_session_ex).url_app);
+
+ if (BG(url_adapt_output_ex).active) {
+ php_url_scanner_ex_deactivate(0);
+ BG(url_adapt_output_ex).active = 0;
+ BG(url_adapt_output_ex).tag_type = 0;
+ BG(url_adapt_output_ex).attr_type = 0;
+ }
+ smart_str_free(&BG(url_adapt_output_ex).form_app);
+ smart_str_free(&BG(url_adapt_output_ex).url_app);
return SUCCESS;
}
diff --git a/ext/standard/var.c b/ext/standard/var.c
index 6e8f0e0014..03368287d7 100644
--- a/ext/standard/var.c
+++ b/ext/standard/var.c
@@ -31,9 +31,14 @@
#include "zend_smart_str.h"
#include "basic_functions.h"
#include "php_incomplete_class.h"
+/* }}} */
+
+struct php_serialize_data {
+ HashTable ht;
+ uint32_t n;
+};
#define COMMON (is_ref ? "&" : "")
-/* }}} */
static void php_array_element_dump(zval *zv, zend_ulong index, zend_string *key, int level) /* {{{ */
{
@@ -171,7 +176,7 @@ again:
break;
case IS_RESOURCE: {
const char *type_name = zend_rsrc_list_get_rsrc_type(Z_RES_P(struc));
- php_printf("%sresource(%pd) of type (%s)\n", COMMON, Z_RES_P(struc)->handle, type_name ? type_name : "Unknown");
+ php_printf("%sresource(%d) of type (%s)\n", COMMON, Z_RES_P(struc)->handle, type_name ? type_name : "Unknown");
break;
}
case IS_REFERENCE:
@@ -436,8 +441,7 @@ static void php_object_element_export(zval *zv, zend_ulong index, zend_string *k
PHPAPI void php_var_export_ex(zval *struc, int level, smart_str *buf) /* {{{ */
{
HashTable *myht;
- char *tmp_str;
- size_t tmp_len;
+ char tmp_str[PHP_DOUBLE_MAX_LENGTH];
zend_string *ztmp, *ztmp2;
zend_ulong index;
zend_string *key;
@@ -458,8 +462,8 @@ again:
smart_str_append_long(buf, Z_LVAL_P(struc));
break;
case IS_DOUBLE:
- tmp_len = spprintf(&tmp_str, 0,"%.*H", PG(serialize_precision), Z_DVAL_P(struc));
- smart_str_appendl(buf, tmp_str, tmp_len);
+ php_gcvt(Z_DVAL_P(struc), (int)PG(serialize_precision), '.', 'E', tmp_str);
+ smart_str_appends(buf, tmp_str);
/* Without a decimal point, PHP treats a number literal as an int.
* This check even works for scientific notation, because the
* mantissa always contains a decimal point.
@@ -469,7 +473,6 @@ again:
if (zend_finite(Z_DVAL_P(struc)) && NULL == strchr(tmp_str, '.')) {
smart_str_appendl(buf, ".0", 2);
}
- efree(tmp_str);
break;
case IS_STRING:
ztmp = php_addcslashes(Z_STR_P(struc), 0, "'\\", 2);
@@ -844,16 +847,13 @@ again:
return;
case IS_DOUBLE: {
- char *s;
-
- smart_str_appendl(buf, "d:", 2);
- s = (char *) safe_emalloc(PG(serialize_precision), 1, MAX_LENGTH_OF_DOUBLE + 1);
- php_gcvt(Z_DVAL_P(struc), (int)PG(serialize_precision), '.', 'E', s);
- smart_str_appends(buf, s);
- smart_str_appendc(buf, ';');
- efree(s);
- return;
- }
+ char tmp_str[PHP_DOUBLE_MAX_LENGTH];
+ smart_str_appendl(buf, "d:", 2);
+ php_gcvt(Z_DVAL_P(struc), (int)PG(serialize_precision), '.', 'E', tmp_str);
+ smart_str_appends(buf, tmp_str);
+ smart_str_appendc(buf, ';');
+ return;
+ }
case IS_STRING:
php_var_serialize_string(buf, Z_STRVAL_P(struc), Z_STRLEN_P(struc));
@@ -1002,6 +1002,35 @@ PHPAPI void php_var_serialize(smart_str *buf, zval *struc, php_serialize_data_t
}
/* }}} */
+PHPAPI php_serialize_data_t php_var_serialize_init() {
+ struct php_serialize_data *d;
+ /* fprintf(stderr, "SERIALIZE_INIT == lock: %u, level: %u\n", BG(serialize_lock), BG(serialize).level); */
+ if (BG(serialize_lock) || !BG(serialize).level) {
+ d = emalloc(sizeof(struct php_serialize_data));
+ zend_hash_init(&d->ht, 16, NULL, ZVAL_PTR_DTOR, 0);
+ d->n = 0;
+ if (!BG(serialize_lock)) {
+ BG(serialize).data = d;
+ BG(serialize).level = 1;
+ }
+ } else {
+ d = BG(serialize).data;
+ ++BG(serialize).level;
+ }
+ return d;
+}
+
+PHPAPI void php_var_serialize_destroy(php_serialize_data_t d) {
+ /* fprintf(stderr, "SERIALIZE_DESTROY == lock: %u, level: %u\n", BG(serialize_lock), BG(serialize).level); */
+ if (BG(serialize_lock) || BG(serialize).level == 1) {
+ zend_hash_destroy(&d->ht);
+ efree(d);
+ }
+ if (!BG(serialize_lock) && !--BG(serialize).level) {
+ BG(serialize).data = NULL;
+ }
+}
+
/* {{{ proto string serialize(mixed variable)
Returns a string representation of variable (which can later be unserialized) */
PHP_FUNCTION(serialize)
@@ -1041,7 +1070,7 @@ PHP_FUNCTION(unserialize)
php_unserialize_data_t var_hash;
zval *options = NULL, *classes = NULL;
zval *retval;
- HashTable *class_hash = NULL;
+ HashTable *class_hash = NULL, *prev_class_hash;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|a", &buf, &buf_len, &options) == FAILURE) {
RETURN_FALSE;
@@ -1053,8 +1082,16 @@ PHP_FUNCTION(unserialize)
p = (const unsigned char*) buf;
PHP_VAR_UNSERIALIZE_INIT(var_hash);
- if(options != NULL) {
+
+ prev_class_hash = php_var_unserialize_get_allowed_classes(var_hash);
+ if (options != NULL) {
classes = zend_hash_str_find(Z_ARRVAL_P(options), "allowed_classes", sizeof("allowed_classes")-1);
+ if (classes && Z_TYPE_P(classes) != IS_ARRAY && Z_TYPE_P(classes) != IS_TRUE && Z_TYPE_P(classes) != IS_FALSE) {
+ php_error_docref(NULL, E_WARNING, "allowed_classes option should be array or boolean");
+ PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
+ RETURN_FALSE;
+ }
+
if(classes && (Z_TYPE_P(classes) == IS_ARRAY || !zend_is_true(classes))) {
ALLOC_HASHTABLE(class_hash);
zend_hash_init(class_hash, (Z_TYPE_P(classes) == IS_ARRAY)?zend_hash_num_elements(Z_ARRVAL_P(classes)):0, NULL, NULL, 0);
@@ -1070,29 +1107,35 @@ PHP_FUNCTION(unserialize)
zend_string_release(lcname);
} ZEND_HASH_FOREACH_END();
}
+ php_var_unserialize_set_allowed_classes(var_hash, class_hash);
}
retval = var_tmp_var(&var_hash);
- if (!php_var_unserialize_ex(retval, &p, p + buf_len, &var_hash, class_hash)) {
- PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
- if (class_hash) {
- zend_hash_destroy(class_hash);
- FREE_HASHTABLE(class_hash);
- }
+ if (!php_var_unserialize(retval, &p, p + buf_len, &var_hash)) {
if (!EG(exception)) {
php_error_docref(NULL, E_NOTICE, "Error at offset " ZEND_LONG_FMT " of %zd bytes",
(zend_long)((char*)p - buf), buf_len);
}
- RETURN_FALSE;
+ RETVAL_FALSE;
+ } else {
+ ZVAL_COPY(return_value, retval);
}
- ZVAL_COPY(return_value, retval);
-
- PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
if (class_hash) {
zend_hash_destroy(class_hash);
FREE_HASHTABLE(class_hash);
}
+
+ /* Reset to previous allowed_classes in case this is a nested call */
+ php_var_unserialize_set_allowed_classes(var_hash, prev_class_hash);
+ PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
+
+ /* Per calling convention we must not return a reference here, so unwrap. We're doing this at
+ * the very end, because __wakeup() calls performed during UNSERIALIZE_DESTROY might affect
+ * the value we unwrap here. This is compatible with behavior in PHP <=7.0. */
+ if (Z_ISREF_P(return_value)) {
+ zend_unwrap_reference(return_value);
+ }
}
/* }}} */
diff --git a/ext/standard/var_unserializer.c b/ext/standard/var_unserializer.c
index c50347d781..2f5559c519 100644
--- a/ext/standard/var_unserializer.c
+++ b/ext/standard/var_unserializer.c
@@ -24,6 +24,49 @@
#include "ext/standard/php_var.h"
#include "php_incomplete_class.h"
+struct php_unserialize_data {
+ void *first;
+ void *last;
+ void *first_dtor;
+ void *last_dtor;
+ HashTable *allowed_classes;
+};
+
+PHPAPI php_unserialize_data_t php_var_unserialize_init() {
+ php_unserialize_data_t d;
+ /* fprintf(stderr, "UNSERIALIZE_INIT == lock: %u, level: %u\n", BG(serialize_lock), BG(unserialize).level); */
+ if (BG(serialize_lock) || !BG(unserialize).level) {
+ d = ecalloc(1, sizeof(struct php_unserialize_data));
+ if (!BG(serialize_lock)) {
+ BG(unserialize).data = d;
+ BG(unserialize).level = 1;
+ }
+ } else {
+ d = BG(unserialize).data;
+ ++BG(unserialize).level;
+ }
+ return d;
+}
+
+PHPAPI void php_var_unserialize_destroy(php_unserialize_data_t d) {
+ /* fprintf(stderr, "UNSERIALIZE_DESTROY == lock: %u, level: %u\n", BG(serialize_lock), BG(unserialize).level); */
+ if (BG(serialize_lock) || BG(unserialize).level == 1) {
+ var_destroy(&d);
+ efree(d);
+ }
+ if (!BG(serialize_lock) && !--BG(unserialize).level) {
+ BG(unserialize).data = NULL;
+ }
+}
+
+PHPAPI HashTable *php_var_unserialize_get_allowed_classes(php_unserialize_data_t d) {
+ return d->allowed_classes;
+}
+PHPAPI void php_var_unserialize_set_allowed_classes(php_unserialize_data_t d, HashTable *classes) {
+ d->allowed_classes = classes;
+}
+
+
/* {{{ reference-handling for unserializer: var_* */
#define VAR_ENTRIES_MAX 1024
#define VAR_ENTRIES_DBG 0
@@ -99,7 +142,7 @@ PHPAPI zval *var_tmp_var(php_unserialize_data_t *var_hashx)
(*var_hashx)->last_dtor = var_hash;
}
ZVAL_UNDEF(&var_hash->data[var_hash->used_slots]);
- Z_VAR_FLAGS(var_hash->data[var_hash->used_slots]) = 0;
+ Z_EXTRA(var_hash->data[var_hash->used_slots]) = 0;
return &var_hash->data[var_hash->used_slots++];
}
@@ -169,7 +212,7 @@ PHPAPI void var_destroy(php_unserialize_data_t *var_hashx)
#endif
/* Perform delayed __wakeup calls */
- if (Z_VAR_FLAGS_P(zv) == VAR_WAKEUP_FLAG) {
+ if (Z_EXTRA_P(zv) == VAR_WAKEUP_FLAG) {
if (!wakeup_failed) {
zval retval;
if (Z_ISUNDEF(wakeup_name)) {
@@ -244,8 +287,10 @@ static zend_string *unserialize_str(const unsigned char **p, size_t len, size_t
return str;
}
-static inline int unserialize_allowed_class(zend_string *class_name, HashTable *classes)
+static inline int unserialize_allowed_class(
+ zend_string *class_name, php_unserialize_data_t *var_hashx)
{
+ HashTable *classes = (*var_hashx)->allowed_classes;
zend_string *lcname;
int res;
ALLOCA_FLAG(use_heap)
@@ -271,7 +316,7 @@ static inline int unserialize_allowed_class(zend_string *class_name, HashTable *
#define YYMARKER marker
-#line 279 "ext/standard/var_unserializer.re"
+#line 324 "ext/standard/var_unserializer.re"
@@ -331,8 +376,8 @@ static inline size_t parse_uiv(const unsigned char *p)
return result;
}
-#define UNSERIALIZE_PARAMETER zval *rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash, HashTable *classes
-#define UNSERIALIZE_PASSTHRU rval, p, max, var_hash, classes
+#define UNSERIALIZE_PARAMETER zval *rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash
+#define UNSERIALIZE_PASSTHRU rval, p, max, var_hash
static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER);
@@ -344,7 +389,7 @@ static zend_always_inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTab
ZVAL_UNDEF(&key);
- if (!php_var_unserialize_internal(&key, p, max, NULL, classes)) {
+ if (!php_var_unserialize_internal(&key, p, max, NULL)) {
zval_dtor(&key);
return 0;
}
@@ -400,7 +445,7 @@ string_key:
}
}
- if (!php_var_unserialize_internal(data, p, max, var_hash, classes)) {
+ if (!php_var_unserialize_internal(data, p, max, var_hash)) {
zval_dtor(&key);
return 0;
}
@@ -445,7 +490,7 @@ static inline int object_custom(UNSERIALIZE_PARAMETER, zend_class_entry *ce)
(*p) += 2;
if (datalen < 0 || (max - (*p)) <= datalen) {
- zend_error(E_WARNING, "Insufficient data for unserializing - %pd required, %pd present", datalen, (zend_long)(max - (*p)));
+ zend_error(E_WARNING, "Insufficient data for unserializing - " ZEND_LONG_FMT " required, " ZEND_LONG_FMT " present", datalen, (zend_long)(max - (*p)));
return 0;
}
@@ -520,7 +565,7 @@ static inline int object_common2(UNSERIALIZE_PARAMETER, zend_long elements)
/* Delay __wakeup call until end of serialization */
zval *wakeup_var = var_tmp_var(var_hash);
ZVAL_COPY(wakeup_var, rval);
- Z_VAR_FLAGS_P(wakeup_var) = VAR_WAKEUP_FLAG;
+ Z_EXTRA_P(wakeup_var) = VAR_WAKEUP_FLAG;
}
return finish_nested_data(UNSERIALIZE_PASSTHRU);
@@ -529,13 +574,7 @@ static inline int object_common2(UNSERIALIZE_PARAMETER, zend_long elements)
# pragma optimize("", on)
#endif
-PHPAPI int php_var_unserialize(zval *rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash)
-{
- HashTable *classes = NULL;
- return php_var_unserialize_ex(UNSERIALIZE_PASSTHRU);
-}
-
-PHPAPI int php_var_unserialize_ex(UNSERIALIZE_PARAMETER)
+PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
{
var_entries *orig_var_entries = (*var_hash)->last;
zend_long orig_used_slots = orig_var_entries ? orig_var_entries->used_slots : 0;
@@ -581,7 +620,7 @@ static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER)
start = cursor;
-#line 585 "ext/standard/var_unserializer.c"
+#line 624 "ext/standard/var_unserializer.c"
{
YYCTYPE yych;
static const unsigned char yybm[] = {
@@ -640,9 +679,9 @@ yy2:
yych = *(YYMARKER = ++YYCURSOR);
if (yych == ':') goto yy95;
yy3:
-#line 962 "ext/standard/var_unserializer.re"
+#line 1001 "ext/standard/var_unserializer.re"
{ return 0; }
-#line 646 "ext/standard/var_unserializer.c"
+#line 685 "ext/standard/var_unserializer.c"
yy4:
yych = *(YYMARKER = ++YYCURSOR);
if (yych == ':') goto yy89;
@@ -685,13 +724,13 @@ yy13:
goto yy3;
yy14:
++YYCURSOR;
-#line 956 "ext/standard/var_unserializer.re"
+#line 995 "ext/standard/var_unserializer.re"
{
/* this is the case where we have less data than planned */
php_error_docref(NULL, E_NOTICE, "Unexpected end of serialized data");
return 0; /* not sure if it should be 0 or 1 here? */
}
-#line 695 "ext/standard/var_unserializer.c"
+#line 734 "ext/standard/var_unserializer.c"
yy16:
yych = *++YYCURSOR;
goto yy3;
@@ -722,7 +761,7 @@ yy20:
yych = *++YYCURSOR;
if (yych != '"') goto yy18;
++YYCURSOR;
-#line 804 "ext/standard/var_unserializer.re"
+#line 843 "ext/standard/var_unserializer.re"
{
size_t len, len2, len3, maxlen;
zend_long elements;
@@ -772,7 +811,7 @@ yy20:
class_name = zend_string_init(str, len, 0);
do {
- if(!unserialize_allowed_class(class_name, classes)) {
+ if(!unserialize_allowed_class(class_name, var_hash)) {
incomplete_class = 1;
ce = PHP_IC_ENTRY;
break;
@@ -874,7 +913,7 @@ yy20:
return object_common2(UNSERIALIZE_PASSTHRU, elements);
}
-#line 878 "ext/standard/var_unserializer.c"
+#line 917 "ext/standard/var_unserializer.c"
yy25:
yych = *++YYCURSOR;
if (yych <= ',') {
@@ -899,7 +938,7 @@ yy27:
yych = *++YYCURSOR;
if (yych != '"') goto yy18;
++YYCURSOR;
-#line 793 "ext/standard/var_unserializer.re"
+#line 832 "ext/standard/var_unserializer.re"
{
zend_long elements;
if (!var_hash) return 0;
@@ -910,7 +949,7 @@ yy27:
}
return object_common2(UNSERIALIZE_PASSTHRU, elements);
}
-#line 914 "ext/standard/var_unserializer.c"
+#line 953 "ext/standard/var_unserializer.c"
yy32:
yych = *++YYCURSOR;
if (yych == '+') goto yy33;
@@ -931,7 +970,7 @@ yy34:
yych = *++YYCURSOR;
if (yych != '{') goto yy18;
++YYCURSOR;
-#line 769 "ext/standard/var_unserializer.re"
+#line 808 "ext/standard/var_unserializer.re"
{
zend_long elements = parse_iv(start + 2);
/* use iv() not uiv() in order to check data range */
@@ -955,7 +994,7 @@ yy34:
return finish_nested_data(UNSERIALIZE_PASSTHRU);
}
-#line 959 "ext/standard/var_unserializer.c"
+#line 998 "ext/standard/var_unserializer.c"
yy39:
yych = *++YYCURSOR;
if (yych == '+') goto yy40;
@@ -976,7 +1015,7 @@ yy41:
yych = *++YYCURSOR;
if (yych != '"') goto yy18;
++YYCURSOR;
-#line 735 "ext/standard/var_unserializer.re"
+#line 774 "ext/standard/var_unserializer.re"
{
size_t len, maxlen;
zend_string *str;
@@ -1010,7 +1049,7 @@ yy41:
ZVAL_STR(rval, str);
return 1;
}
-#line 1014 "ext/standard/var_unserializer.c"
+#line 1053 "ext/standard/var_unserializer.c"
yy46:
yych = *++YYCURSOR;
if (yych == '+') goto yy47;
@@ -1031,7 +1070,7 @@ yy48:
yych = *++YYCURSOR;
if (yych != '"') goto yy18;
++YYCURSOR;
-#line 703 "ext/standard/var_unserializer.re"
+#line 742 "ext/standard/var_unserializer.re"
{
size_t len, maxlen;
char *str;
@@ -1063,7 +1102,7 @@ yy48:
ZVAL_STRINGL(rval, str, len);
return 1;
}
-#line 1067 "ext/standard/var_unserializer.c"
+#line 1106 "ext/standard/var_unserializer.c"
yy53:
yych = *++YYCURSOR;
if (yych <= '/') {
@@ -1151,7 +1190,7 @@ yy61:
}
yy63:
++YYCURSOR;
-#line 694 "ext/standard/var_unserializer.re"
+#line 733 "ext/standard/var_unserializer.re"
{
#if SIZEOF_ZEND_LONG == 4
use_double:
@@ -1160,7 +1199,7 @@ use_double:
ZVAL_DOUBLE(rval, zend_strtod((const char *)start + 2, NULL));
return 1;
}
-#line 1164 "ext/standard/var_unserializer.c"
+#line 1203 "ext/standard/var_unserializer.c"
yy65:
yych = *++YYCURSOR;
if (yych <= ',') {
@@ -1219,7 +1258,7 @@ yy73:
yych = *++YYCURSOR;
if (yych != ';') goto yy18;
++YYCURSOR;
-#line 678 "ext/standard/var_unserializer.re"
+#line 717 "ext/standard/var_unserializer.re"
{
*p = YYCURSOR;
@@ -1235,7 +1274,7 @@ yy73:
return 1;
}
-#line 1239 "ext/standard/var_unserializer.c"
+#line 1278 "ext/standard/var_unserializer.c"
yy76:
yych = *++YYCURSOR;
if (yych == 'N') goto yy73;
@@ -1262,7 +1301,7 @@ yy79:
if (yych <= '9') goto yy79;
if (yych != ';') goto yy18;
++YYCURSOR;
-#line 652 "ext/standard/var_unserializer.re"
+#line 691 "ext/standard/var_unserializer.re"
{
#if SIZEOF_ZEND_LONG == 4
int digits = YYCURSOR - start - 3;
@@ -1288,7 +1327,7 @@ yy79:
ZVAL_LONG(rval, parse_iv(start + 2));
return 1;
}
-#line 1292 "ext/standard/var_unserializer.c"
+#line 1331 "ext/standard/var_unserializer.c"
yy83:
yych = *++YYCURSOR;
if (yych <= '/') goto yy18;
@@ -1296,22 +1335,22 @@ yy83:
yych = *++YYCURSOR;
if (yych != ';') goto yy18;
++YYCURSOR;
-#line 646 "ext/standard/var_unserializer.re"
+#line 685 "ext/standard/var_unserializer.re"
{
*p = YYCURSOR;
ZVAL_BOOL(rval, parse_iv(start + 2));
return 1;
}
-#line 1306 "ext/standard/var_unserializer.c"
+#line 1345 "ext/standard/var_unserializer.c"
yy87:
++YYCURSOR;
-#line 640 "ext/standard/var_unserializer.re"
+#line 679 "ext/standard/var_unserializer.re"
{
*p = YYCURSOR;
ZVAL_NULL(rval);
return 1;
}
-#line 1315 "ext/standard/var_unserializer.c"
+#line 1354 "ext/standard/var_unserializer.c"
yy89:
yych = *++YYCURSOR;
if (yych <= ',') {
@@ -1334,7 +1373,7 @@ yy91:
if (yych <= '9') goto yy91;
if (yych != ';') goto yy18;
++YYCURSOR;
-#line 615 "ext/standard/var_unserializer.re"
+#line 654 "ext/standard/var_unserializer.re"
{
zend_long id;
@@ -1359,7 +1398,7 @@ yy91:
return 1;
}
-#line 1363 "ext/standard/var_unserializer.c"
+#line 1402 "ext/standard/var_unserializer.c"
yy95:
yych = *++YYCURSOR;
if (yych <= ',') {
@@ -1382,7 +1421,7 @@ yy97:
if (yych <= '9') goto yy97;
if (yych != ';') goto yy18;
++YYCURSOR;
-#line 589 "ext/standard/var_unserializer.re"
+#line 628 "ext/standard/var_unserializer.re"
{
zend_long id;
@@ -1408,9 +1447,9 @@ yy97:
return 1;
}
-#line 1412 "ext/standard/var_unserializer.c"
+#line 1451 "ext/standard/var_unserializer.c"
}
-#line 964 "ext/standard/var_unserializer.re"
+#line 1003 "ext/standard/var_unserializer.re"
return 0;
diff --git a/ext/standard/var_unserializer.re b/ext/standard/var_unserializer.re
index f3d17b63f6..dd9fe4915b 100644
--- a/ext/standard/var_unserializer.re
+++ b/ext/standard/var_unserializer.re
@@ -22,6 +22,49 @@
#include "ext/standard/php_var.h"
#include "php_incomplete_class.h"
+struct php_unserialize_data {
+ void *first;
+ void *last;
+ void *first_dtor;
+ void *last_dtor;
+ HashTable *allowed_classes;
+};
+
+PHPAPI php_unserialize_data_t php_var_unserialize_init() {
+ php_unserialize_data_t d;
+ /* fprintf(stderr, "UNSERIALIZE_INIT == lock: %u, level: %u\n", BG(serialize_lock), BG(unserialize).level); */
+ if (BG(serialize_lock) || !BG(unserialize).level) {
+ d = ecalloc(1, sizeof(struct php_unserialize_data));
+ if (!BG(serialize_lock)) {
+ BG(unserialize).data = d;
+ BG(unserialize).level = 1;
+ }
+ } else {
+ d = BG(unserialize).data;
+ ++BG(unserialize).level;
+ }
+ return d;
+}
+
+PHPAPI void php_var_unserialize_destroy(php_unserialize_data_t d) {
+ /* fprintf(stderr, "UNSERIALIZE_DESTROY == lock: %u, level: %u\n", BG(serialize_lock), BG(unserialize).level); */
+ if (BG(serialize_lock) || BG(unserialize).level == 1) {
+ var_destroy(&d);
+ efree(d);
+ }
+ if (!BG(serialize_lock) && !--BG(unserialize).level) {
+ BG(unserialize).data = NULL;
+ }
+}
+
+PHPAPI HashTable *php_var_unserialize_get_allowed_classes(php_unserialize_data_t d) {
+ return d->allowed_classes;
+}
+PHPAPI void php_var_unserialize_set_allowed_classes(php_unserialize_data_t d, HashTable *classes) {
+ d->allowed_classes = classes;
+}
+
+
/* {{{ reference-handling for unserializer: var_* */
#define VAR_ENTRIES_MAX 1024
#define VAR_ENTRIES_DBG 0
@@ -97,7 +140,7 @@ PHPAPI zval *var_tmp_var(php_unserialize_data_t *var_hashx)
(*var_hashx)->last_dtor = var_hash;
}
ZVAL_UNDEF(&var_hash->data[var_hash->used_slots]);
- Z_VAR_FLAGS(var_hash->data[var_hash->used_slots]) = 0;
+ Z_EXTRA(var_hash->data[var_hash->used_slots]) = 0;
return &var_hash->data[var_hash->used_slots++];
}
@@ -167,7 +210,7 @@ PHPAPI void var_destroy(php_unserialize_data_t *var_hashx)
#endif
/* Perform delayed __wakeup calls */
- if (Z_VAR_FLAGS_P(zv) == VAR_WAKEUP_FLAG) {
+ if (Z_EXTRA_P(zv) == VAR_WAKEUP_FLAG) {
if (!wakeup_failed) {
zval retval;
if (Z_ISUNDEF(wakeup_name)) {
@@ -242,8 +285,10 @@ static zend_string *unserialize_str(const unsigned char **p, size_t len, size_t
return str;
}
-static inline int unserialize_allowed_class(zend_string *class_name, HashTable *classes)
+static inline int unserialize_allowed_class(
+ zend_string *class_name, php_unserialize_data_t *var_hashx)
{
+ HashTable *classes = (*var_hashx)->allowed_classes;
zend_string *lcname;
int res;
ALLOCA_FLAG(use_heap)
@@ -335,8 +380,8 @@ static inline size_t parse_uiv(const unsigned char *p)
return result;
}
-#define UNSERIALIZE_PARAMETER zval *rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash, HashTable *classes
-#define UNSERIALIZE_PASSTHRU rval, p, max, var_hash, classes
+#define UNSERIALIZE_PARAMETER zval *rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash
+#define UNSERIALIZE_PASSTHRU rval, p, max, var_hash
static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER);
@@ -348,7 +393,7 @@ static zend_always_inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTab
ZVAL_UNDEF(&key);
- if (!php_var_unserialize_internal(&key, p, max, NULL, classes)) {
+ if (!php_var_unserialize_internal(&key, p, max, NULL)) {
zval_dtor(&key);
return 0;
}
@@ -404,7 +449,7 @@ string_key:
}
}
- if (!php_var_unserialize_internal(data, p, max, var_hash, classes)) {
+ if (!php_var_unserialize_internal(data, p, max, var_hash)) {
zval_dtor(&key);
return 0;
}
@@ -449,7 +494,7 @@ static inline int object_custom(UNSERIALIZE_PARAMETER, zend_class_entry *ce)
(*p) += 2;
if (datalen < 0 || (max - (*p)) <= datalen) {
- zend_error(E_WARNING, "Insufficient data for unserializing - %pd required, %pd present", datalen, (zend_long)(max - (*p)));
+ zend_error(E_WARNING, "Insufficient data for unserializing - " ZEND_LONG_FMT " required, " ZEND_LONG_FMT " present", datalen, (zend_long)(max - (*p)));
return 0;
}
@@ -524,7 +569,7 @@ static inline int object_common2(UNSERIALIZE_PARAMETER, zend_long elements)
/* Delay __wakeup call until end of serialization */
zval *wakeup_var = var_tmp_var(var_hash);
ZVAL_COPY(wakeup_var, rval);
- Z_VAR_FLAGS_P(wakeup_var) = VAR_WAKEUP_FLAG;
+ Z_EXTRA_P(wakeup_var) = VAR_WAKEUP_FLAG;
}
return finish_nested_data(UNSERIALIZE_PASSTHRU);
@@ -533,13 +578,7 @@ static inline int object_common2(UNSERIALIZE_PARAMETER, zend_long elements)
# pragma optimize("", on)
#endif
-PHPAPI int php_var_unserialize(zval *rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash)
-{
- HashTable *classes = NULL;
- return php_var_unserialize_ex(UNSERIALIZE_PASSTHRU);
-}
-
-PHPAPI int php_var_unserialize_ex(UNSERIALIZE_PARAMETER)
+PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
{
var_entries *orig_var_entries = (*var_hash)->last;
zend_long orig_used_slots = orig_var_entries ? orig_var_entries->used_slots : 0;
@@ -850,7 +889,7 @@ object ":" uiv ":" ["] {
class_name = zend_string_init(str, len, 0);
do {
- if(!unserialize_allowed_class(class_name, classes)) {
+ if(!unserialize_allowed_class(class_name, var_hash)) {
incomplete_class = 1;
ce = PHP_IC_ENTRY;
break;
diff --git a/ext/sysvmsg/sysvmsg.c b/ext/sysvmsg/sysvmsg.c
index dfb999763f..37aa0d9028 100644
--- a/ext/sysvmsg/sysvmsg.c
+++ b/ext/sysvmsg/sysvmsg.c
@@ -268,7 +268,7 @@ PHP_FUNCTION(msg_get_queue)
/* doesn't already exist; create it */
mq->id = msgget(key, IPC_CREAT | IPC_EXCL | perms);
if (mq->id < 0) {
- php_error_docref(NULL, E_WARNING, "failed for key 0x%lx: %s", key, strerror(errno));
+ php_error_docref(NULL, E_WARNING, "failed for key 0x" ZEND_XLONG_FMT ": %s", key, strerror(errno));
efree(mq);
RETURN_FALSE;
}
@@ -435,7 +435,7 @@ PHP_FUNCTION(msg_send)
break;
case IS_LONG:
- message_len = spprintf(&p, 0, "%pd", Z_LVAL_P(message));
+ message_len = spprintf(&p, 0, ZEND_LONG_FMT, Z_LVAL_P(message));
break;
case IS_FALSE:
message_len = spprintf(&p, 0, "0");
diff --git a/ext/sysvsem/sysvsem.c b/ext/sysvsem/sysvsem.c
index fb9991e76c..b8b6e42a6f 100644
--- a/ext/sysvsem/sysvsem.c
+++ b/ext/sysvsem/sysvsem.c
@@ -206,7 +206,7 @@ PHP_FUNCTION(sem_get)
semid = semget(key, 3, perm|IPC_CREAT);
if (semid == -1) {
- php_error_docref(NULL, E_WARNING, "failed for key 0x%lx: %s", key, strerror(errno));
+ php_error_docref(NULL, E_WARNING, "failed for key 0x" ZEND_XLONG_FMT ": %s", key, strerror(errno));
RETURN_FALSE;
}
@@ -238,7 +238,7 @@ PHP_FUNCTION(sem_get)
sop[2].sem_flg = SEM_UNDO;
while (semop(semid, sop, 3) == -1) {
if (errno != EINTR) {
- php_error_docref(NULL, E_WARNING, "failed acquiring SYSVSEM_SETVAL for key 0x%lx: %s", key, strerror(errno));
+ php_error_docref(NULL, E_WARNING, "failed acquiring SYSVSEM_SETVAL for key 0x" ZEND_XLONG_FMT ": %s", key, strerror(errno));
break;
}
}
@@ -246,7 +246,7 @@ PHP_FUNCTION(sem_get)
/* Get the usage count. */
count = semctl(semid, SYSVSEM_USAGE, GETVAL, NULL);
if (count == -1) {
- php_error_docref(NULL, E_WARNING, "failed for key 0x%lx: %s", key, strerror(errno));
+ php_error_docref(NULL, E_WARNING, "failed for key 0x" ZEND_XLONG_FMT ": %s", key, strerror(errno));
}
/* If we are the only user, then take this opportunity to set the max. */
@@ -257,7 +257,7 @@ PHP_FUNCTION(sem_get)
union semun semarg;
semarg.val = max_acquire;
if (semctl(semid, SYSVSEM_SEM, SETVAL, semarg) == -1) {
- php_error_docref(NULL, E_WARNING, "failed for key 0x%lx: %s", key, strerror(errno));
+ php_error_docref(NULL, E_WARNING, "failed for key 0x" ZEND_XLONG_FMT ": %s", key, strerror(errno));
}
#elif defined(SETVAL_WANTS_PTR)
/* This is correct for Solaris 2.6 which does not have union semun. */
@@ -279,7 +279,7 @@ PHP_FUNCTION(sem_get)
sop[0].sem_flg = SEM_UNDO;
while (semop(semid, sop, 1) == -1) {
if (errno != EINTR) {
- php_error_docref(NULL, E_WARNING, "failed releasing SYSVSEM_SETVAL for key 0x%lx: %s", key, strerror(errno));
+ php_error_docref(NULL, E_WARNING, "failed releasing SYSVSEM_SETVAL for key 0x" ZEND_XLONG_FMT ": %s", key, strerror(errno));
break;
}
}
@@ -319,7 +319,7 @@ static void php_sysvsem_semop(INTERNAL_FUNCTION_PARAMETERS, int acquire)
}
if (!acquire && sem_ptr->count == 0) {
- php_error_docref(NULL, E_WARNING, "SysV semaphore %ld (key 0x%x) is not currently acquired", Z_LVAL_P(arg_id), sem_ptr->key);
+ php_error_docref(NULL, E_WARNING, "SysV semaphore " ZEND_LONG_FMT " (key 0x%x) is not currently acquired", Z_LVAL_P(arg_id), sem_ptr->key);
RETURN_FALSE;
}
@@ -388,7 +388,7 @@ PHP_FUNCTION(sem_remove)
#else
if (semctl(sem_ptr->semid, 0, IPC_STAT, NULL) < 0) {
#endif
- php_error_docref(NULL, E_WARNING, "SysV semaphore %ld does not (any longer) exist", Z_LVAL_P(arg_id));
+ php_error_docref(NULL, E_WARNING, "SysV semaphore " ZEND_LONG_FMT " does not (any longer) exist", Z_LVAL_P(arg_id));
RETURN_FALSE;
}
@@ -397,7 +397,7 @@ PHP_FUNCTION(sem_remove)
#else
if (semctl(sem_ptr->semid, 0, IPC_RMID, NULL) < 0) {
#endif
- php_error_docref(NULL, E_WARNING, "failed for SysV sempphore %ld: %s", Z_LVAL_P(arg_id), strerror(errno));
+ php_error_docref(NULL, E_WARNING, "failed for SysV sempphore " ZEND_LONG_FMT ": %s", Z_LVAL_P(arg_id), strerror(errno));
RETURN_FALSE;
}
diff --git a/ext/sysvshm/php_sysvshm.h b/ext/sysvshm/php_sysvshm.h
index 0468854d6a..a2bfb76f93 100644
--- a/ext/sysvshm/php_sysvshm.h
+++ b/ext/sysvshm/php_sysvshm.h
@@ -33,7 +33,7 @@ extern zend_module_entry sysvshm_module_entry;
#ifdef PHP_WIN32
# include <TSRM/tsrm_win32.h>
-typedef int key_t;
+# include "win32/ipc.h"
# ifndef THREAD_LS
# define THREAD_LS
# endif
diff --git a/ext/sysvshm/sysvshm.c b/ext/sysvshm/sysvshm.c
index 1b8faa169c..5a5ba7c222 100644
--- a/ext/sysvshm/sysvshm.c
+++ b/ext/sysvshm/sysvshm.c
@@ -169,20 +169,20 @@ PHP_FUNCTION(shm_attach)
/* get the id from a specified key or create new shared memory */
if ((shm_id = shmget(shm_key, 0, 0)) < 0) {
- if (shm_size < sizeof(sysvshm_chunk_head)) {
- php_error_docref(NULL, E_WARNING, "failed for key 0x%px: memorysize too small", shm_key);
+ if (shm_size < (zend_long)sizeof(sysvshm_chunk_head)) {
+ php_error_docref(NULL, E_WARNING, "failed for key 0x" ZEND_XLONG_FMT ": memorysize too small", shm_key);
efree(shm_list_ptr);
RETURN_FALSE;
}
if ((shm_id = shmget(shm_key, shm_size, shm_flag | IPC_CREAT | IPC_EXCL)) < 0) {
- php_error_docref(NULL, E_WARNING, "failed for key 0x%px: %s", shm_key, strerror(errno));
+ php_error_docref(NULL, E_WARNING, "failed for key 0x" ZEND_XLONG_FMT ": %s", shm_key, strerror(errno));
efree(shm_list_ptr);
RETURN_FALSE;
}
}
if ((shm_ptr = shmat(shm_id, NULL, 0)) == (void *) -1) {
- php_error_docref(NULL, E_WARNING, "failed for key 0x%px: %s", shm_key, strerror(errno));
+ php_error_docref(NULL, E_WARNING, "failed for key 0x" ZEND_XLONG_FMT ": %s", shm_key, strerror(errno));
efree(shm_list_ptr);
RETURN_FALSE;
}
@@ -233,7 +233,7 @@ PHP_FUNCTION(shm_remove)
SHM_FETCH_RESOURCE(shm_list_ptr, shm_id);
if (shmctl(shm_list_ptr->id, IPC_RMID, NULL) < 0) {
- php_error_docref(NULL, E_WARNING, "failed for key 0x%x, id %ld: %s", shm_list_ptr->key, Z_LVAL_P(shm_id), strerror(errno));
+ php_error_docref(NULL, E_WARNING, "failed for key 0x%x, id " ZEND_LONG_FMT ": %s", shm_list_ptr->key, Z_LVAL_P(shm_id), strerror(errno));
RETURN_FALSE;
}
@@ -303,7 +303,7 @@ PHP_FUNCTION(shm_get_var)
shm_varpos = php_check_shm_data((shm_list_ptr->ptr), shm_key);
if (shm_varpos < 0) {
- php_error_docref(NULL, E_WARNING, "variable key %pd doesn't exist", shm_key);
+ php_error_docref(NULL, E_WARNING, "variable key " ZEND_LONG_FMT " doesn't exist", shm_key);
RETURN_FALSE;
}
shm_var = (sysvshm_chunk*) ((char *)shm_list_ptr->ptr + shm_varpos);
@@ -350,7 +350,7 @@ PHP_FUNCTION(shm_remove_var)
shm_varpos = php_check_shm_data((shm_list_ptr->ptr), shm_key);
if (shm_varpos < 0) {
- php_error_docref(NULL, E_WARNING, "variable key %pd doesn't exist", shm_key);
+ php_error_docref(NULL, E_WARNING, "variable key " ZEND_LONG_FMT " doesn't exist", shm_key);
RETURN_FALSE;
}
php_remove_shm_data((shm_list_ptr->ptr), shm_varpos);
diff --git a/ext/tidy/config.m4 b/ext/tidy/config.m4
index ff27bd1aaa..88463fa8d4 100644
--- a/ext/tidy/config.m4
+++ b/ext/tidy/config.m4
@@ -25,17 +25,32 @@ if test "$PHP_TIDY" != "no"; then
if test -z "$TIDY_DIR"; then
AC_MSG_ERROR(Cannot find libtidy)
+ else
+ dnl Check for tidybuffio.h (as opposed to simply buffio.h)
+ dnl which indicates that we are building against tidy-html5
+ dnl and not the legacy htmltidy. The two are compatible,
+ dnl except for with regard to this header file.
+ if test -f "$TIDY_INCDIR/tidybuffio.h"; then
+ AC_DEFINE(HAVE_TIDYBUFFIO_H,1,[defined if tidybuffio.h exists])
+ fi
fi
TIDY_LIBDIR=$TIDY_DIR/$PHP_LIBDIR
- PHP_ADD_LIBRARY_WITH_PATH(tidy, $TIDY_LIBDIR, TIDY_SHARED_LIBADD)
- PHP_ADD_INCLUDE($TIDY_INCDIR)
-
+ TIDY_LIB_NAME=tidy
PHP_CHECK_LIBRARY(tidy,tidyOptGetDoc,
[
- AC_DEFINE(HAVE_TIDYOPTGETDOC,1,[ ])
- ],[],[])
+ AC_DEFINE(HAVE_TIDYOPTGETDOC,1,[ ])
+ ],[
+ PHP_CHECK_LIBRARY(tidy5,tidyOptGetDoc,
+ [
+ TIDY_LIB_NAME=tidy5
+ AC_DEFINE(HAVE_TIDYOPTGETDOC,1,[ ])
+ ], [], [])
+ ],[])
+
+ PHP_ADD_LIBRARY_WITH_PATH($TIDY_LIB_NAME, $TIDY_LIBDIR, TIDY_SHARED_LIBADD)
+ PHP_ADD_INCLUDE($TIDY_INCDIR)
PHP_NEW_EXTENSION(tidy, tidy.c, $ext_shared,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1)
diff --git a/ext/tidy/config.w32 b/ext/tidy/config.w32
index a87a833f92..654c1590c9 100644
--- a/ext/tidy/config.w32
+++ b/ext/tidy/config.w32
@@ -4,12 +4,17 @@
ARG_WITH("tidy", "TIDY support", "no");
if (PHP_TIDY != "no") {
- if (CHECK_LIB("libtidy_a.lib;libtidy.lib", "tidy", PHP_TIDY) &&
+ if (CHECK_LIB("libtidy_a.lib;libtidy.lib;tidy_a.lib;tidy.lib", "tidy", PHP_TIDY) &&
(
CHECK_HEADER_ADD_INCLUDE("tidy.h", "CFLAGS_TIDY") ||
CHECK_HEADER_ADD_INCLUDE("tidy/tidy.h", "CFLAGS_TIDY", null, null, true) ||
CHECK_HEADER_ADD_INCLUDE("libtidy/tidy.h", "CFLAGS_TIDY", null, null, true)
)) {
+
+ if (CHECK_HEADER_ADD_INCLUDE("tidybuffio.h", "CFLAGS_TIDY")) {
+ AC_DEFINE('HAVE_TIDYBUFFIO_H', 1, 'Have tidybuffio.h header file');
+ }
+
EXTENSION("tidy", "tidy.c");
AC_DEFINE('HAVE_TIDY', 1, 'Have TIDY library');
ADD_FLAG('CFLAGS_TIDY', '/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1');
diff --git a/ext/tidy/tests/003.phpt b/ext/tidy/tests/003.phpt
index 7201d6a5a2..1a63d44dfd 100644
--- a/ext/tidy/tests/003.phpt
+++ b/ext/tidy/tests/003.phpt
@@ -10,8 +10,8 @@ tidy_clean_repair()
echo tidy_get_output($a);
?>
---EXPECT--
-<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2//EN">
+--EXPECTF--
+<!DOCTYPE html%S>
<html>
<head>
<title></title>
diff --git a/ext/tidy/tests/004.phpt b/ext/tidy/tests/004.phpt
index d13c37dcd8..9d3cd2ef5a 100644
--- a/ext/tidy/tests/004.phpt
+++ b/ext/tidy/tests/004.phpt
@@ -19,13 +19,13 @@ $a = tidy_parse_string($html);
var_dump(tidy_diagnose($a));
echo tidy_get_error_buffer($a);
?>
---EXPECT--
+--EXPECTF--
bool(true)
line 1 column 1 - Warning: missing <!DOCTYPE> declaration
line 1 column 7 - Warning: discarding unexpected </html>
line 1 column 14 - Warning: inserting missing 'title' element
-Info: Document content looks like HTML 3.2
-3 warnings, 0 errors were found!
+Info: Document content looks like HTML%w%d%S
+%S3 warnings%S0 errors%S
bool(true)
Info: Document content looks like HTML 3.2
No warnings or errors were found.
diff --git a/ext/tidy/tests/005-mb.phpt b/ext/tidy/tests/005-mb.phpt
new file mode 100644
index 0000000000..b63ec09635
--- /dev/null
+++ b/ext/tidy/tests/005-mb.phpt
@@ -0,0 +1,18 @@
+--TEST--
+tidy_parse_file()
+--SKIPIF--
+<?php if (!extension_loaded("tidy")) print "skip"; ?>
+--FILE--
+<?php
+ $a = tidy_parse_file(dirname(__FILE__)."/005私はガラスを食べられます.html");
+ echo tidy_get_output($a);
+
+?>
+--EXPECT--
+<html>
+<head>
+<title></title>
+</head>
+<body>
+</body>
+</html> \ No newline at end of file
diff --git a/ext/tidy/tests/005私はガラスを食べられます.html b/ext/tidy/tests/005私はガラスを食べられます.html
new file mode 100644
index 0000000000..8c17451f91
--- /dev/null
+++ b/ext/tidy/tests/005私はガラスを食べられます.html
@@ -0,0 +1 @@
+<HTML></HTML>
diff --git a/ext/tidy/tests/010.phpt b/ext/tidy/tests/010.phpt
index 85d9df5190..695e1c9ce5 100644
--- a/ext/tidy/tests/010.phpt
+++ b/ext/tidy/tests/010.phpt
@@ -11,7 +11,7 @@ var_dump($a->html());
var_dump($a->head());
?>
---EXPECT--
+--EXPECTF--
object(tidyNode)#2 (8) {
["value"]=>
string(94) "<html>
@@ -100,7 +100,7 @@ object(tidyNode)#2 (8) {
["proprietary"]=>
bool(false)
["id"]=>
- int(111)
+ int(%i)
["attribute"]=>
NULL
["child"]=>
@@ -231,7 +231,7 @@ object(tidyNode)#2 (9) {
["proprietary"]=>
bool(false)
["id"]=>
- int(111)
+ int(%i)
["attribute"]=>
NULL
["child"]=>
@@ -307,7 +307,7 @@ object(tidyNode)#2 (9) {
["proprietary"]=>
bool(false)
["id"]=>
- int(111)
+ int(%i)
["attribute"]=>
NULL
["child"]=>
diff --git a/ext/tidy/tests/012.phpt b/ext/tidy/tests/012.phpt
index 39ce22bec1..e86aa6c45c 100644
--- a/ext/tidy/tests/012.phpt
+++ b/ext/tidy/tests/012.phpt
@@ -30,7 +30,7 @@ Accessing children nodes
dump_nodes($html);
?>
---EXPECT--
+--EXPECTF--
bool(true)
object(tidyNode)#3 (9) {
["value"]=>
@@ -70,7 +70,7 @@ object(tidyNode)#3 (9) {
["proprietary"]=>
bool(false)
["id"]=>
- int(111)
+ int(%i)
["attribute"]=>
NULL
["child"]=>
@@ -94,7 +94,7 @@ object(tidyNode)#4 (9) {
["proprietary"]=>
bool(false)
["id"]=>
- int(111)
+ int(%i)
["attribute"]=>
NULL
["child"]=>
@@ -222,7 +222,7 @@ object(tidyNode)#5 (9) {
["proprietary"]=>
bool(false)
["id"]=>
- int(114)
+ int(%i)
["attribute"]=>
NULL
["child"]=>
@@ -365,7 +365,7 @@ object(tidyNode)#8 (9) {
["proprietary"]=>
bool(false)
["id"]=>
- int(114)
+ int(%i)
["attribute"]=>
NULL
["child"]=>
@@ -426,7 +426,7 @@ object(tidyNode)#10 (9) {
["proprietary"]=>
bool(false)
["id"]=>
- int(114)
+ int(%i)
["attribute"]=>
NULL
["child"]=>
diff --git a/ext/tidy/tests/016.phpt b/ext/tidy/tests/016.phpt
index 001371aa3e..05b7cc18f9 100644
--- a/ext/tidy/tests/016.phpt
+++ b/ext/tidy/tests/016.phpt
@@ -4,21 +4,10 @@ Passing configuration file through tidy_parse_file() (may fail with buggy libtid
<?php if (!extension_loaded("tidy")) print "skip"; ?>
--FILE--
<?php
- $tidy = tidy_parse_file(dirname(__FILE__)."/016.html", dirname(__FILE__)."/016.tcfg");
- tidy_clean_repair($tidy);
- echo tidy_get_output($tidy);
+ $tidy = tidy_parse_file(dirname(__FILE__)."/016.html",
+ dirname(__FILE__)."/016.tcfg");
+ $cfg = $tidy->getConfig();
+ echo $cfg["clean"];
?>
--EXPECT--
-<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2//EN">
-<html>
-<head>
-<title></title>
-
-<style type="text/css">
- p.c1 {font-weight: bold}
-</style>
-</head>
-<body>
-<p class="c1">testing</p>
-</body>
-</html>
+1
diff --git a/ext/tidy/tests/017.phpt b/ext/tidy/tests/017.phpt
index ba620a32ec..24597e1a4a 100644
--- a/ext/tidy/tests/017.phpt
+++ b/ext/tidy/tests/017.phpt
@@ -5,8 +5,8 @@ The Tidy Output Buffer Filter
--FILE--
<?php ob_start("ob_tidyhandler"); ?>
<B>testing</I>
---EXPECT--
-<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2//EN">
+--EXPECTF--
+<!DOCTYPE html%S>
<html>
<head>
<title></title>
diff --git a/ext/tidy/tests/020.phpt b/ext/tidy/tests/020.phpt
index dbfda96375..8ff1efbb68 100644
--- a/ext/tidy/tests/020.phpt
+++ b/ext/tidy/tests/020.phpt
@@ -19,12 +19,11 @@ var_dump(strlen($tidy->errorBuffer) > 50);
echo $tidy;
?>
---EXPECT--
+--EXPECTF--
bool(true)
bool(true)
<?xml version="1.0" encoding="iso-8859-1"?>
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<!DOCTYPE html%A>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
diff --git a/ext/tidy/tests/024.phpt b/ext/tidy/tests/024.phpt
index b09f5b4643..6a258b30aa 100644
--- a/ext/tidy/tests/024.phpt
+++ b/ext/tidy/tests/024.phpt
@@ -13,28 +13,27 @@ if (strtotime(tidy_get_release()) < strtotime('20 january 2007')) die ('skip old
$contents = '
<wps:block>
<wps:var>
-<wps:value/>
+<wps:value></wps:value>
</wps:var>
</wps:block>';
$config = array(
+'doctype' => 'omit',
'new-blocklevel-tags' => 'wps:block,wps:var,wps:value',
'newline' => 'LF'
);
$tidy = tidy_parse_string($contents, $config, 'utf8');
$tidy->cleanRepair();
-
-var_dump($tidy->value);
+echo $tidy;
?>
--EXPECTF--
-string(11%d) "<html>
+<html>
<head>
<title></title>
</head>
<body>
-<wps:block>%w<wps:var>
-<wps:value></wps:var>%w</wps:block>
+<wps:block>%w<wps:var>%w<wps:value></wps:value>%w</wps:var>%w</wps:block>
</body>
-</html>"
+</html>
diff --git a/ext/tidy/tests/026.phpt b/ext/tidy/tests/026.phpt
index 24a1e6f4a7..b46cd5464b 100644
--- a/ext/tidy/tests/026.phpt
+++ b/ext/tidy/tests/026.phpt
@@ -12,8 +12,8 @@ echo '<p>xpto</p>';
?>
</html>
---EXPECT--
-<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2//EN">
+--EXPECTF--
+<!DOCTYPE html%S>
<html>
<head>
<title></title>
diff --git a/ext/tidy/tidy.c b/ext/tidy/tidy.c
index 4e307eec06..f764b98c87 100644
--- a/ext/tidy/tidy.c
+++ b/ext/tidy/tidy.c
@@ -31,7 +31,12 @@
#include "ext/standard/info.h"
#include "tidy.h"
+
+#if HAVE_TIDYBUFFIO_H
+#include "tidybuffio.h"
+#else
#include "buffio.h"
+#endif
/* compatibility with older versions of libtidy */
#ifndef TIDY_CALL
@@ -1078,6 +1083,9 @@ static PHP_MINFO_FUNCTION(tidy)
{
php_info_print_table_start();
php_info_print_table_header(2, "Tidy support", "enabled");
+#if HAVE_TIDYBUFFIO_H
+ php_info_print_table_row(2, "libTidy Version", (char *)tidyLibraryVersion());
+#endif
php_info_print_table_row(2, "libTidy Release", (char *)tidyReleaseDate());
php_info_print_table_row(2, "Extension Version", PHP_TIDY_VERSION " ($Id$)");
php_info_print_table_end();
@@ -1849,7 +1857,7 @@ static TIDY_NODE_METHOD(getParent)
__constructor for tidyNode. */
static TIDY_NODE_METHOD(__construct)
{
- php_error_docref(NULL, E_ERROR, "You should not create a tidyNode manually");
+ zend_throw_error(NULL, "You should not create a tidyNode manually");
}
/* }}} */
diff --git a/ext/tokenizer/tokenizer.c b/ext/tokenizer/tokenizer.c
index 3c410a81cc..2ad90d3b3d 100644
--- a/ext/tokenizer/tokenizer.c
+++ b/ext/tokenizer/tokenizer.c
@@ -183,10 +183,9 @@ static zend_bool tokenize(zval *return_value, zend_string *source)
return 1;
}
-zval token_stream;
-
-void on_event(zend_php_scanner_event event, int token, int line)
+void on_event(zend_php_scanner_event event, int token, int line, void *context)
{
+ zval *token_stream = (zval *) context;
zval keyword;
HashTable *tokens_ht;
zval *token_zv;
@@ -199,13 +198,13 @@ void on_event(zend_php_scanner_event event, int token, int line)
add_next_index_long(&keyword, token);
add_next_index_stringl(&keyword, (char *)LANG_SCNG(yy_text), LANG_SCNG(yy_leng));
add_next_index_long(&keyword, line);
- add_next_index_zval(&token_stream, &keyword);
+ add_next_index_zval(token_stream, &keyword);
} else {
- add_next_index_stringl(&token_stream, (char *)LANG_SCNG(yy_text), LANG_SCNG(yy_leng));
+ add_next_index_stringl(token_stream, (char *)LANG_SCNG(yy_text), LANG_SCNG(yy_leng));
}
break;
case ON_FEEDBACK:
- tokens_ht = Z_ARRVAL(token_stream);
+ tokens_ht = Z_ARRVAL_P(token_stream);
token_zv = zend_hash_index_find(tokens_ht, zend_hash_num_elements(tokens_ht) - 1);
if (token_zv && Z_TYPE_P(token_zv) == IS_ARRAY) {
ZVAL_LONG(zend_hash_index_find(Z_ARRVAL_P(token_zv), 0), token);
@@ -218,7 +217,7 @@ void on_event(zend_php_scanner_event event, int token, int line)
add_next_index_stringl(&keyword,
(char *)LANG_SCNG(yy_cursor), LANG_SCNG(yy_limit) - LANG_SCNG(yy_cursor));
add_next_index_long(&keyword, CG(zend_lineno));
- add_next_index_zval(&token_stream, &keyword);
+ add_next_index_zval(token_stream, &keyword);
}
break;
}
@@ -238,12 +237,15 @@ static zend_bool tokenize_parse(zval *return_value, zend_string *source)
zend_save_lexical_state(&original_lex_state);
if ((success = (zend_prepare_string_for_scanning(&source_zval, "") == SUCCESS))) {
+ zval token_stream;
+ array_init(&token_stream);
+
CG(ast) = NULL;
CG(ast_arena) = zend_arena_create(1024 * 32);
LANG_SCNG(yy_state) = yycINITIAL;
LANG_SCNG(on_event) = on_event;
+ LANG_SCNG(on_event_context) = &token_stream;
- array_init(&token_stream);
if((success = (zendparse() == SUCCESS))) {
ZVAL_COPY_VALUE(return_value, &token_stream);
} else {
diff --git a/ext/wddx/tests/bug72750.phpt b/ext/wddx/tests/bug72750.phpt
index 3a6794df28..ae77ff75dc 100644
--- a/ext/wddx/tests/bug72750.phpt
+++ b/ext/wddx/tests/bug72750.phpt
@@ -30,5 +30,5 @@ var_dump($array);
--EXPECT--
array(1) {
["aBinary"]=>
- string(0) ""
-}
+ string(9) "FF"
+} \ No newline at end of file
diff --git a/ext/wddx/wddx.c b/ext/wddx/wddx.c
index c1fe0204a5..4a0a9e9b32 100644
--- a/ext/wddx/wddx.c
+++ b/ext/wddx/wddx.c
@@ -637,7 +637,7 @@ void php_wddx_serialize_var(wddx_packet *packet, zval *var, zend_string *name)
case IS_ARRAY:
ht = Z_ARRVAL_P(var);
if (ht->u.v.nApplyCount > 1) {
- php_error_docref(NULL, E_RECOVERABLE_ERROR, "WDDX doesn't support circular references");
+ zend_throw_error(NULL, "WDDX doesn't support circular references");
return;
}
if (ZEND_HASH_APPLY_PROTECTION(ht)) {
@@ -652,7 +652,7 @@ void php_wddx_serialize_var(wddx_packet *packet, zval *var, zend_string *name)
case IS_OBJECT:
ht = Z_OBJPROP_P(var);
if (ht->u.v.nApplyCount > 1) {
- php_error_docref(NULL, E_RECOVERABLE_ERROR, "WDDX doesn't support circular references");
+ zend_throw_error(NULL, "WDDX doesn't support circular references");
return;
}
ht->u.v.nApplyCount++;
@@ -988,12 +988,8 @@ static void php_wddx_pop_element(void *user_data, const XML_Char *name)
/* Clean up class name var entry */
zval_ptr_dtor(&ent1->data);
} else if (Z_TYPE(ent2->data) == IS_OBJECT) {
- zend_class_entry *old_scope = EG(scope);
-
- EG(scope) = Z_OBJCE(ent2->data);
- add_property_zval(&ent2->data, ent1->varname, &ent1->data);
+ zend_update_property(Z_OBJCE(ent2->data), &ent2->data, ent1->varname, strlen(ent1->varname), &ent1->data);
if Z_REFCOUNTED(ent1->data) Z_DELREF(ent1->data);
- EG(scope) = old_scope;
} else {
zend_symtable_str_update(target_hash, ent1->varname, strlen(ent1->varname), &ent1->data);
}
diff --git a/ext/xml/xml.c b/ext/xml/xml.c
index 4d67905c88..3a6c090e7d 100644
--- a/ext/xml/xml.c
+++ b/ext/xml/xml.c
@@ -115,7 +115,7 @@ ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_xml_set_object, 0, 0, 2)
ZEND_ARG_INFO(0, parser)
- ZEND_ARG_INFO(1, obj)
+ ZEND_ARG_INFO(0, obj)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_xml_set_element_handler, 0, 0, 3)
@@ -290,7 +290,7 @@ xml_encoding xml_encodings[] = {
static XML_Memory_Handling_Suite php_xml_mem_hdlrs;
/* True globals, no need for thread safety */
-static int le_xml_parser;
+static int le_xml_parser;
/* }}} */
@@ -353,7 +353,7 @@ PHP_MINIT_FUNCTION(xml)
REGISTER_LONG_CONSTANT("XML_OPTION_SKIP_WHITE", PHP_XML_OPTION_SKIP_WHITE, CONST_CS|CONST_PERSISTENT);
/* this object should not be pre-initialised at compile time,
- as the order of members may vary */
+ as the order of members may vary */
php_xml_mem_hdlrs.malloc_fcn = php_xml_malloc_wrapper;
php_xml_mem_hdlrs.realloc_fcn = php_xml_realloc_wrapper;
@@ -401,7 +401,7 @@ static void _xml_xmlchar_zval(const XML_Char *s, int len, const XML_Char *encodi
static void xml_parser_dtor(zend_resource *rsrc)
{
xml_parser *parser = (xml_parser *)rsrc->ptr;
-
+
if (parser->parser) {
XML_ParserFree(parser->parser);
}
@@ -487,9 +487,7 @@ static void xml_call_handler(xml_parser *parser, zval *handler, zend_function *f
zend_fcall_info fci;
fci.size = sizeof(fci);
- fci.function_table = EG(function_table);
ZVAL_COPY_VALUE(&fci.function_name, handler);
- fci.symbol_table = NULL;
fci.object = Z_OBJ(parser->object);
fci.retval = retval;
fci.param_count = argc;
@@ -645,7 +643,7 @@ PHP_XML_API zend_string *xml_utf8_decode(const XML_Char *s, size_t len, const XM
c = '?';
}
- ZSTR_VAL(str)[ZSTR_LEN(str)++] = decoder ? decoder(c) : c;
+ ZSTR_VAL(str)[ZSTR_LEN(str)++] = decoder ? (unsigned int)decoder(c) : c;
}
ZSTR_VAL(str)[ZSTR_LEN(str)] = '\0';
if (ZSTR_LEN(str) < len) {
@@ -871,7 +869,7 @@ void _xml_characterDataHandler(void *userData, const XML_Char *s, int len)
}
if (!Z_ISUNDEF(parser->data)) {
- int i;
+ size_t i;
int doprint = 0;
zend_string *decoded_value;
@@ -904,7 +902,7 @@ void _xml_characterDataHandler(void *userData, const XML_Char *s, int len)
} else {
add_assoc_str(parser->ctag, "value", decoded_value);
}
-
+
} else {
zval tag;
zval *curtag, *mytype, *myval;
@@ -982,8 +980,8 @@ void _xml_defaultHandler(void *userData, const XML_Char *s, int len)
/* }}} */
/* {{{ _xml_unparsedEntityDeclHandler() */
-void _xml_unparsedEntityDeclHandler(void *userData,
- const XML_Char *entityName,
+void _xml_unparsedEntityDeclHandler(void *userData,
+ const XML_Char *entityName,
const XML_Char *base,
const XML_Char *systemId,
const XML_Char *publicId,
@@ -1151,15 +1149,15 @@ static void php_xml_parser_create_impl(INTERNAL_FUNCTION_PARAMETERS, int ns_supp
}
/* }}} */
-/* {{{ proto resource xml_parser_create([string encoding])
+/* {{{ proto resource xml_parser_create([string encoding])
Create an XML parser */
PHP_FUNCTION(xml_parser_create)
{
- php_xml_parser_create_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
+ php_xml_parser_create_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
}
/* }}} */
-/* {{{ proto resource xml_parser_create_ns([string encoding [, string sep]])
+/* {{{ proto resource xml_parser_create_ns([string encoding [, string sep]])
Create an XML parser */
PHP_FUNCTION(xml_parser_create_ns)
{
@@ -1167,14 +1165,14 @@ PHP_FUNCTION(xml_parser_create_ns)
}
/* }}} */
-/* {{{ proto int xml_set_object(resource parser, object &obj)
+/* {{{ proto int xml_set_object(resource parser, object &obj)
Set up object which should be used for callbacks */
PHP_FUNCTION(xml_set_object)
{
xml_parser *parser;
zval *pind, *mythis;
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "ro/", &pind, &mythis) == FAILURE) {
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "ro", &pind, &mythis) == FAILURE) {
return;
}
@@ -1188,9 +1186,7 @@ PHP_FUNCTION(xml_set_object)
}
/* please leave this commented - or ask thies@thieso.net before doing it (again) */
-/* #ifdef ZEND_ENGINE_2
- zval_add_ref(&parser->object);
-#endif */
+ /* zval_add_ref(&parser->object); */
ZVAL_COPY(&parser->object, mythis);
@@ -1198,7 +1194,7 @@ PHP_FUNCTION(xml_set_object)
}
/* }}} */
-/* {{{ proto int xml_set_element_handler(resource parser, string shdl, string ehdl)
+/* {{{ proto int xml_set_element_handler(resource parser, string shdl, string ehdl)
Set up start and end element handlers */
PHP_FUNCTION(xml_set_element_handler)
{
@@ -1220,7 +1216,7 @@ PHP_FUNCTION(xml_set_element_handler)
}
/* }}} */
-/* {{{ proto int xml_set_character_data_handler(resource parser, string hdl)
+/* {{{ proto int xml_set_character_data_handler(resource parser, string hdl)
Set up character data handler */
PHP_FUNCTION(xml_set_character_data_handler)
{
@@ -1241,7 +1237,7 @@ PHP_FUNCTION(xml_set_character_data_handler)
}
/* }}} */
-/* {{{ proto int xml_set_processing_instruction_handler(resource parser, string hdl)
+/* {{{ proto int xml_set_processing_instruction_handler(resource parser, string hdl)
Set up processing instruction (PI) handler */
PHP_FUNCTION(xml_set_processing_instruction_handler)
{
@@ -1262,7 +1258,7 @@ PHP_FUNCTION(xml_set_processing_instruction_handler)
}
/* }}} */
-/* {{{ proto int xml_set_default_handler(resource parser, string hdl)
+/* {{{ proto int xml_set_default_handler(resource parser, string hdl)
Set up default handler */
PHP_FUNCTION(xml_set_default_handler)
{
@@ -1283,7 +1279,7 @@ PHP_FUNCTION(xml_set_default_handler)
}
/* }}} */
-/* {{{ proto int xml_set_unparsed_entity_decl_handler(resource parser, string hdl)
+/* {{{ proto int xml_set_unparsed_entity_decl_handler(resource parser, string hdl)
Set up unparsed entity declaration handler */
PHP_FUNCTION(xml_set_unparsed_entity_decl_handler)
{
@@ -1304,7 +1300,7 @@ PHP_FUNCTION(xml_set_unparsed_entity_decl_handler)
}
/* }}} */
-/* {{{ proto int xml_set_notation_decl_handler(resource parser, string hdl)
+/* {{{ proto int xml_set_notation_decl_handler(resource parser, string hdl)
Set up notation declaration handler */
PHP_FUNCTION(xml_set_notation_decl_handler)
{
@@ -1325,7 +1321,7 @@ PHP_FUNCTION(xml_set_notation_decl_handler)
}
/* }}} */
-/* {{{ proto int xml_set_external_entity_ref_handler(resource parser, string hdl)
+/* {{{ proto int xml_set_external_entity_ref_handler(resource parser, string hdl)
Set up external entity reference handler */
PHP_FUNCTION(xml_set_external_entity_ref_handler)
{
@@ -1346,7 +1342,7 @@ PHP_FUNCTION(xml_set_external_entity_ref_handler)
}
/* }}} */
-/* {{{ proto int xml_set_start_namespace_decl_handler(resource parser, string hdl)
+/* {{{ proto int xml_set_start_namespace_decl_handler(resource parser, string hdl)
Set up character data handler */
PHP_FUNCTION(xml_set_start_namespace_decl_handler)
{
@@ -1367,7 +1363,7 @@ PHP_FUNCTION(xml_set_start_namespace_decl_handler)
}
/* }}} */
-/* {{{ proto int xml_set_end_namespace_decl_handler(resource parser, string hdl)
+/* {{{ proto int xml_set_end_namespace_decl_handler(resource parser, string hdl)
Set up character data handler */
PHP_FUNCTION(xml_set_end_namespace_decl_handler)
{
@@ -1447,7 +1443,7 @@ PHP_FUNCTION(xml_parse_into_struct)
if (info) {
ZVAL_COPY_VALUE(&parser->info, info);
}
-
+
parser->level = 0;
parser->ltags = safe_emalloc(XML_MAXLEVEL, sizeof(char *), 0);
@@ -1463,7 +1459,7 @@ PHP_FUNCTION(xml_parse_into_struct)
}
/* }}} */
-/* {{{ proto int xml_get_error_code(resource parser)
+/* {{{ proto int xml_get_error_code(resource parser)
Get XML parser error code */
PHP_FUNCTION(xml_get_error_code)
{
@@ -1500,7 +1496,7 @@ PHP_FUNCTION(xml_error_string)
}
/* }}} */
-/* {{{ proto int xml_get_current_line_number(resource parser)
+/* {{{ proto int xml_get_current_line_number(resource parser)
Get current line number for an XML parser */
PHP_FUNCTION(xml_get_current_line_number)
{
@@ -1538,7 +1534,7 @@ PHP_FUNCTION(xml_get_current_column_number)
}
/* }}} */
-/* {{{ proto int xml_get_current_byte_index(resource parser)
+/* {{{ proto int xml_get_current_byte_index(resource parser)
Get current byte index for an XML parser */
PHP_FUNCTION(xml_get_current_byte_index)
{
@@ -1557,7 +1553,7 @@ PHP_FUNCTION(xml_get_current_byte_index)
}
/* }}} */
-/* {{{ proto int xml_parser_free(resource parser)
+/* {{{ proto int xml_parser_free(resource parser)
Free an XML parser */
PHP_FUNCTION(xml_parser_free)
{
@@ -1585,7 +1581,7 @@ PHP_FUNCTION(xml_parser_free)
}
/* }}} */
-/* {{{ proto int xml_parser_set_option(resource parser, int option, mixed value)
+/* {{{ proto int xml_parser_set_option(resource parser, int option, mixed value)
Set options in an XML parser */
PHP_FUNCTION(xml_parser_set_option)
{
@@ -1638,7 +1634,7 @@ PHP_FUNCTION(xml_parser_set_option)
}
/* }}} */
-/* {{{ proto int xml_parser_get_option(resource parser, int option)
+/* {{{ proto int xml_parser_get_option(resource parser, int option)
Get options from an XML parser */
PHP_FUNCTION(xml_parser_get_option)
{
@@ -1671,7 +1667,7 @@ PHP_FUNCTION(xml_parser_get_option)
}
/* }}} */
-/* {{{ proto string utf8_encode(string data)
+/* {{{ proto string utf8_encode(string data)
Encodes an ISO-8859-1 string to UTF-8 */
PHP_FUNCTION(utf8_encode)
{
@@ -1691,7 +1687,7 @@ PHP_FUNCTION(utf8_encode)
}
/* }}} */
-/* {{{ proto string utf8_decode(string data)
+/* {{{ proto string utf8_decode(string data)
Converts a UTF-8 encoded string to ISO-8859-1 */
PHP_FUNCTION(utf8_decode)
{
diff --git a/ext/xmlreader/tests/003-mb.phpt b/ext/xmlreader/tests/003-mb.phpt
new file mode 100644
index 0000000000..af76382779
--- /dev/null
+++ b/ext/xmlreader/tests/003-mb.phpt
@@ -0,0 +1,84 @@
+--TEST--
+XMLReader: libxml2 XML Reader, attributes test
+--SKIPIF--
+<?php if (!extension_loaded("xmlreader")) print "skip"; ?>
+--FILE--
+<?php
+/* $Id$ */
+$filename = dirname(__FILE__) . '/私はガラスを食べられます_003.xml';
+
+$xmlstring = '<?xml version="1.0" encoding="UTF-8"?>
+<books><book num="1" idx="2">book1</book></books>';
+file_put_contents($filename, $xmlstring);
+
+$reader = new XMLReader();
+if (!$reader->open($filename)) {
+ exit();
+}
+
+// Only go through
+while ($reader->read()) {
+ if ($reader->nodeType != XMLREADER::END_ELEMENT) {
+ if ($reader->nodeType == XMLREADER::ELEMENT && $reader->hasAttributes) {
+ $attr = $reader->moveToFirstAttribute();
+ echo $reader->name . ": ";
+ echo $reader->value . "\n";
+
+ if ($reader->getAttribute($reader->name) == $reader->value) {
+ echo "1st attr (num) failed\n";
+ }
+
+
+ $attr = $reader->moveToNextAttribute();
+ echo $reader->name . ": ";
+ echo $reader->value . "\n";
+
+ if ($reader->getAttribute($reader->name) == $reader->value) {
+ echo "2nd attr (idx) failed\n";
+ }
+
+ // Named attribute
+ $attr = $reader->moveToAttribute('num');
+ echo $reader->name . ": ";
+ echo $reader->value . "\n";
+
+ if ($reader->getAttribute('num') == $reader->value) {
+ echo "attr num failed\n";
+ }
+
+ $attr = $reader->moveToAttribute('idx');
+ echo $reader->name . ": ";
+ echo $reader->value . "\n";
+
+ if ($reader->getAttribute('idx') == $reader->value) {
+ echo "attr idx failed\n";
+ }
+
+ // Numeric positions of attributes
+ $attr = $reader->moveToAttributeNo(0);
+ echo $reader->name . ": ";
+ echo $reader->value . "\n";
+
+ if ($reader->getAttributeNo(0) == $reader->value) {
+ echo "attr 0 failed\n";
+ }
+
+ $attr = $reader->moveToAttributeNo(1);
+ echo $reader->name . ": ";
+ echo $reader->value . "\n";
+
+ }
+ }
+}
+$reader->close();
+unlink($filename);
+?>
+===DONE===
+--EXPECT--
+num: 1
+idx: 2
+num: 1
+idx: 2
+num: 1
+idx: 2
+===DONE===
diff --git a/ext/xmlrpc/xmlrpc-epi-php.c b/ext/xmlrpc/xmlrpc-epi-php.c
index 4704926053..3663913019 100644
--- a/ext/xmlrpc/xmlrpc-epi-php.c
+++ b/ext/xmlrpc/xmlrpc-epi-php.c
@@ -557,7 +557,7 @@ static XMLRPC_VALUE PHP_to_XMLRPC_worker (const char* key, zval* in_val, int dep
ht = HASH_OF(&val);
if (ht && ht->u.v.nApplyCount > 1) {
- php_error_docref(NULL, E_ERROR, "XML-RPC doesn't support circular references");
+ zend_throw_error(NULL, "XML-RPC doesn't support circular references");
return NULL;
}
@@ -577,7 +577,7 @@ static XMLRPC_VALUE PHP_to_XMLRPC_worker (const char* key, zval* in_val, int dep
char *num_str = NULL;
if (vtype != xmlrpc_vector_array) {
- spprintf(&num_str, 0, "%ld", num_index);
+ spprintf(&num_str, 0, ZEND_LONG_FMT, num_index);
}
XMLRPC_AddValueToVector(xReturn, PHP_to_XMLRPC_worker(num_str, pIter, depth++));
diff --git a/ext/xmlwriter/php_xmlwriter.c b/ext/xmlwriter/php_xmlwriter.c
index 813fe0b180..d7304fe548 100644
--- a/ext/xmlwriter/php_xmlwriter.c
+++ b/ext/xmlwriter/php_xmlwriter.c
@@ -568,7 +568,7 @@ static const zend_function_entry xmlwriter_class_functions[] = {
#endif
PHP_ME_MAPPING(outputMemory, xmlwriter_output_memory, arginfo_xmlwriter_method_output_memory, 0)
PHP_ME_MAPPING(flush, xmlwriter_flush, arginfo_xmlwriter_method_flush, 0)
- {NULL, NULL, NULL}
+ PHP_FE_END
};
/* }}} */
@@ -695,7 +695,8 @@ static void php_xmlwriter_string_arg(INTERNAL_FUNCTION_PARAMETERS, xmlwriter_rea
xmlwriter_object *intern;
xmlTextWriterPtr ptr;
char *name;
- size_t name_len, retval;
+ size_t name_len;
+ int retval;
zval *self = getThis();
@@ -1581,7 +1582,8 @@ static PHP_FUNCTION(xmlwriter_start_dtd_entity)
xmlwriter_object *intern;
xmlTextWriterPtr ptr;
char *name;
- size_t name_len, retval;
+ size_t name_len;
+ int retval;
zend_bool isparm;
zval *self = getThis();
diff --git a/ext/xmlwriter/tests/005-mb.phpt b/ext/xmlwriter/tests/005-mb.phpt
new file mode 100644
index 0000000000..63e77dc6bd
--- /dev/null
+++ b/ext/xmlwriter/tests/005-mb.phpt
@@ -0,0 +1,33 @@
+--TEST--
+XMLWriter: libxml2 XML Writer, comments
+--SKIPIF--
+<?php
+if (!extension_loaded("xmlwriter")) die("skip");
+if (!function_exists("xmlwriter_start_comment")) die("skip: libxml2 2.6.7+ required");
+?>
+--FILE--
+<?php
+/* $Id$ */
+
+$doc_dest = '私はガラスを食べられます001.xml';
+$xw = xmlwriter_open_uri($doc_dest);
+xmlwriter_start_document($xw, '1.0', 'UTF-8');
+xmlwriter_start_element($xw, "tag1");
+
+xmlwriter_start_comment($xw);
+xmlwriter_text($xw, 'comment');
+xmlwriter_end_comment($xw);
+xmlwriter_write_comment($xw, "comment #2");
+xmlwriter_end_document($xw);
+
+// Force to write and empty the buffer
+$output_bytes = xmlwriter_flush($xw, true);
+echo file_get_contents($doc_dest);
+unset($xw);
+unlink($doc_dest);
+?>
+===DONE===
+--EXPECT--
+<?xml version="1.0" encoding="UTF-8"?>
+<tag1><!--comment--><!--comment #2--></tag1>
+===DONE===
diff --git a/ext/xsl/php_xsl.h b/ext/xsl/php_xsl.h
index 9e8d6bd9e8..b760304dc4 100644
--- a/ext/xsl/php_xsl.h
+++ b/ext/xsl/php_xsl.h
@@ -115,16 +115,6 @@ ZEND_BEGIN_MODULE_GLOBALS(xsl)
ZEND_END_MODULE_GLOBALS(xsl)
*/
-/* In every utility function you add that needs to use variables
- in php_xsl_globals, call TSRM_FETCH(); after declaring other
- variables used by that function, or better yet, pass in
- after the last function argument and declare your utility function
- with after the last declared argument. Always refer to
- the globals in your function as XSL_G(variable). You are
- encouraged to rename these macros something shorter, see
- examples in any other php module directory.
-*/
-
#ifdef ZTS
#define XSL_G(v) TSRMG(xsl_globals_id, zend_xsl_globals *, v)
#else
diff --git a/ext/xsl/tests/xslt008-mb.phpt b/ext/xsl/tests/xslt008-mb.phpt
new file mode 100644
index 0000000000..e13b576588
--- /dev/null
+++ b/ext/xsl/tests/xslt008-mb.phpt
@@ -0,0 +1,31 @@
+--TEST--
+Test 8: Stream Wrapper Includes
+--SKIPIF--
+<?php
+ require_once dirname(__FILE__) .'/skipif.inc';
+ if (!extension_loaded('zlib')) die('skip zlib extension not available');
+?>
+--FILE--
+<?php
+echo "Test 8: Stream Wrapper Includes ";
+include("prepare.inc");
+$xsl = new domDocument;
+$xsl->load(dirname(__FILE__)."/私はガラスを食べられますstreamsinclude.xsl");
+if(!$xsl) {
+ echo "Error while parsing the document\n";
+ exit;
+}
+chdir(dirname(__FILE__));
+$proc->importStylesheet($xsl);
+print "\n";
+print $proc->transformToXML($dom);
+
+
+--EXPECT--
+Test 8: Stream Wrapper Includes
+<?xml version="1.0" encoding="iso-8859-1"?>
+<html><body>bar
+a1 b1 c1 <br/>
+a2 c2 <br/>
+3 b3 c3 <br/>
+</body></html>
diff --git a/ext/xsl/tests/私はガラスを食べられますstreamsinclude.xsl b/ext/xsl/tests/私はガラスを食べられますstreamsinclude.xsl
new file mode 100644
index 0000000000..6f8bc40ed9
--- /dev/null
+++ b/ext/xsl/tests/私はガラスを食べられますstreamsinclude.xsl
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!-- $Id: streamsinclude.xsl,v 1.1 2003-10-27 15:12:20 chregu Exp $ -->
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
+ <xsl:output method="xml" encoding="iso-8859-1" indent="no"/>
+ <xsl:include href="compress.zlib://xslt.xsl.gz"/>
+</xsl:stylesheet>
diff --git a/ext/xsl/xsltprocessor.c b/ext/xsl/xsltprocessor.c
index d12da0e655..3970e267ba 100644
--- a/ext/xsl/xsltprocessor.c
+++ b/ext/xsl/xsltprocessor.c
@@ -100,7 +100,7 @@ const zend_function_entry php_xsl_xsltprocessor_class_functions[] = {
PHP_FALIAS(setProfiling, xsl_xsltprocessor_set_profiling, arginfo_xsl_xsltprocessor_set_profiling)
PHP_FALIAS(setSecurityPrefs, xsl_xsltprocessor_set_security_prefs, arginfo_xsl_xsltprocessor_set_security_prefs)
PHP_FALIAS(getSecurityPrefs, xsl_xsltprocessor_get_security_prefs, arginfo_xsl_xsltprocessor_get_security_prefs)
- {NULL, NULL, NULL}
+ PHP_FE_END
};
/* {{{ php_xsl_xslt_string_to_xpathexpr()
@@ -138,7 +138,6 @@ static char **php_xsl_xslt_make_params(HashTable *parht, int xpath_params)
zval *value;
char *xpath_expr;
zend_string *string_key;
- zend_ulong num_key;
char **params = NULL;
int i = 0;
@@ -146,7 +145,7 @@ static char **php_xsl_xslt_make_params(HashTable *parht, int xpath_params)
params = (char **)safe_emalloc((2 * zend_hash_num_elements(parht) + 1), sizeof(char *), 0);
memset((char *)params, 0, parsize);
- ZEND_HASH_FOREACH_KEY_VAL(parht, num_key, string_key, value) {
+ ZEND_HASH_FOREACH_STR_KEY_VAL(parht, string_key, value) {
if (string_key == NULL) {
php_error_docref(NULL, E_WARNING, "Invalid argument or parameter array");
efree(params);
@@ -266,10 +265,10 @@ static void xsl_ext_function_php(xmlXPathParserContextPtr ctxt, int nargs, int t
nsparent = node->_private;
curns = xmlNewNs(NULL, node->name, NULL);
if (node->children) {
- curns->prefix = xmlStrdup((char *)node->children);
+ curns->prefix = xmlStrdup((xmlChar *)node->children);
}
if (node->children) {
- node = xmlNewDocNode(node->doc, NULL, (char *) node->children, node->name);
+ node = xmlNewDocNode(node->doc, NULL, (xmlChar *) node->children, node->name);
} else {
node = xmlNewDocNode(node->doc, NULL, (const xmlChar *) "xmlns", node->name);
}
@@ -295,7 +294,6 @@ static void xsl_ext_function_php(xmlXPathParserContextPtr ctxt, int nargs, int t
}
fci.size = sizeof(fci);
- fci.function_table = EG(function_table);
if (fci.param_count > 0) {
fci.params = args;
} else {
@@ -320,7 +318,6 @@ static void xsl_ext_function_php(xmlXPathParserContextPtr ctxt, int nargs, int t
xmlXPathFreeObject(obj);
ZVAL_COPY_VALUE(&fci.function_name, &handler);
- fci.symbol_table = NULL;
fci.object = NULL;
fci.retval = &retval;
fci.no_separation = 0;
diff --git a/ext/zip/php_zip.c b/ext/zip/php_zip.c
index 45bf8afb67..28527cbee1 100644
--- a/ext/zip/php_zip.c
+++ b/ext/zip/php_zip.c
@@ -589,7 +589,7 @@ int php_zip_glob(char *pattern, int pattern_len, zend_long flags, zval *return_v
globfree(&globbuf);
return globbuf.gl_pathc;
#else
- php_error_docref(NULL, E_ERROR, "Glob support is not available");
+ zend_throw_error(NULL, "Glob support is not available");
return 0;
#endif /* HAVE_GLOB */
}
@@ -1694,7 +1694,7 @@ static void php_zip_add_from_pattern(INTERNAL_FUNCTION_PARAMETERS, int type) /*
if (add_path) {
if ((add_path_len + file_stripped_len) > MAXPATHLEN) {
- php_error_docref(NULL, E_WARNING, "Entry name too long (max: %d, %pd given)",
+ php_error_docref(NULL, E_WARNING, "Entry name too long (max: %d, %zd given)",
MAXPATHLEN - 1, (add_path_len + file_stripped_len));
zval_ptr_dtor(return_value);
RETURN_FALSE;
@@ -3003,7 +3003,7 @@ static const zend_function_entry zip_class_functions[] = {
ZIPARCHIVE_ME(getExternalAttributesIndex, arginfo_ziparchive_getextattrindex, ZEND_ACC_PUBLIC)
ZIPARCHIVE_ME(setCompressionName, arginfo_ziparchive_setcompname, ZEND_ACC_PUBLIC)
ZIPARCHIVE_ME(setCompressionIndex, arginfo_ziparchive_setcompindex, ZEND_ACC_PUBLIC)
- {NULL, NULL, NULL}
+ PHP_FE_END
};
/* }}} */
@@ -3047,7 +3047,6 @@ static PHP_MINIT_FUNCTION(zip)
REGISTER_ZIP_CLASS_CONST_LONG("FL_NODIR", ZIP_FL_NODIR);
REGISTER_ZIP_CLASS_CONST_LONG("FL_COMPRESSED", ZIP_FL_COMPRESSED);
REGISTER_ZIP_CLASS_CONST_LONG("FL_UNCHANGED", ZIP_FL_UNCHANGED);
-
#ifdef ZIP_FL_ENC_GUESS
/* Default filename encoding policy. */
REGISTER_ZIP_CLASS_CONST_LONG("FL_ENC_GUESS", ZIP_FL_ENC_GUESS);
@@ -3065,6 +3064,20 @@ static PHP_MINIT_FUNCTION(zip)
REGISTER_ZIP_CLASS_CONST_LONG("FL_ENC_CP437", ZIP_FL_ENC_CP437);
#endif
+/* XXX The below are rather not implemented or to check whether makes sense to expose. */
+/*#ifdef ZIP_FL_RECOMPRESS
+ REGISTER_ZIP_CLASS_CONST_LONG("FL_RECOMPRESS", ZIP_FL_RECOMPRESS);
+#endif
+#ifdef ZIP_FL_ENCRYPTED
+ REGISTER_ZIP_CLASS_CONST_LONG("FL_ENCRYPTED", ZIP_FL_ENCRYPTED);
+#endif
+#ifdef ZIP_FL_LOCAL
+ REGISTER_ZIP_CLASS_CONST_LONG("FL_LOCAL", ZIP_FL_LOCAL);
+#endif
+#ifdef ZIP_FL_CENTRAL
+ REGISTER_ZIP_CLASS_CONST_LONG("FL_CENTRAL", ZIP_FL_CENTRAL);
+#endif */
+
REGISTER_ZIP_CLASS_CONST_LONG("CM_DEFAULT", ZIP_CM_DEFAULT);
REGISTER_ZIP_CLASS_CONST_LONG("CM_STORE", ZIP_CM_STORE);
REGISTER_ZIP_CLASS_CONST_LONG("CM_SHRINK", ZIP_CM_SHRINK);
diff --git a/ext/zip/php_zip.h b/ext/zip/php_zip.h
index fe5b79ad8f..f886da3400 100644
--- a/ext/zip/php_zip.h
+++ b/ext/zip/php_zip.h
@@ -39,22 +39,7 @@ extern zend_module_entry zip_module_entry;
#define PHP_ZIP_VERSION "1.13.5"
-#ifndef Z_SET_REFCOUNT_P
-# if PHP_MAJOR_VERSION < 6 && (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION < 3)
-# define Z_SET_REFCOUNT_P(pz, rc) pz->refcount = rc
-# define Z_UNSET_ISREF_P(pz) pz->is_ref = 0
-# endif
-#endif
-
-/* {{{ ZIP_OPENBASEDIR_CHECKPATH(filename) */
-#if PHP_API_VERSION < 20100412
-# define ZIP_OPENBASEDIR_CHECKPATH(filename) \
- (PG(safe_mode) && (!php_checkuid(filename, NULL, CHECKUID_CHECK_FILE_AND_DIR))) || php_check_open_basedir(filename)
-#else
-#define ZIP_OPENBASEDIR_CHECKPATH(filename) \
- php_check_open_basedir(filename)
-#endif
-/* }}} */
+#define ZIP_OPENBASEDIR_CHECKPATH(filename) php_check_open_basedir(filename)
typedef struct _ze_zip_rsrc {
struct zip *za;
diff --git a/ext/zip/tests/bug40228-mb.phpt b/ext/zip/tests/bug40228-mb.phpt
new file mode 100644
index 0000000000..109172d2d5
--- /dev/null
+++ b/ext/zip/tests/bug40228-mb.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Bug #40228 (extractTo does not create recursive empty path)
+--SKIPIF--
+<?php if (!extension_loaded("zip")) print "skip"; ?>
+--FILE--
+<?php
+$dest = dirname(__FILE__);
+$arc_name = $dest . "/bug40228私はガラスを食べられます.zip";
+$zip = new ZipArchive;
+$zip->open($arc_name, ZIPARCHIVE::CREATE);;
+$zip->extractTo($dest);
+if (is_dir($dest . '/test/empty')) {
+ echo "Ok\n";
+ rmdir($dest . '/test/empty');
+ rmdir($dest . '/test');
+} else {
+ echo "Failed.\n";
+}
+echo "Done\n";
+?>
+--EXPECT--
+Ok
+Done
diff --git a/ext/zip/tests/bug40228私はガラスを食べられます.zip b/ext/zip/tests/bug40228私はガラスを食べられます.zip
new file mode 100644
index 0000000000..bbcd9515f8
--- /dev/null
+++ b/ext/zip/tests/bug40228私はガラスを食べられます.zip
Binary files differ
diff --git a/ext/zip/tests/bug64342_1-mb.phpt b/ext/zip/tests/bug64342_1-mb.phpt
new file mode 100644
index 0000000000..caac92d0f9
--- /dev/null
+++ b/ext/zip/tests/bug64342_1-mb.phpt
@@ -0,0 +1,37 @@
+--TEST--
+Bug #64342 ZipArchive::addFile() has to check file existence (variation 2)
+--SKIPIF--
+<?php
+/* $Id$ */
+if(!extension_loaded('zip')) die('skip');
+?>
+--FILE--
+<?php
+
+$dirname = dirname(__FILE__) . '/';
+include $dirname . 'utils.inc';
+$file = $dirname . '__私はガラスを食べられますtmp_oo_addfile.zip';
+
+copy($dirname . 'test.zip', $file);
+
+$zip = new ZipArchive;
+if (!$zip->open($file)) {
+ exit('failed');
+}
+if (!$zip->addFile($dirname . 'cant_find_me.txt', 'test.php')) {
+ echo "failed\n";
+}
+if ($zip->status == ZIPARCHIVE::ER_OK) {
+ dump_entries_name($zip);
+ $zip->close();
+} else {
+ echo "failed\n";
+}
+@unlink($file);
+?>
+--EXPECTF--
+failed
+0 bar
+1 foobar/
+2 foobar/baz
+3 entry1.txt
diff --git a/ext/zip/zip_stream.c b/ext/zip/zip_stream.c
index de0abbbb5b..2fa828a572 100644
--- a/ext/zip/zip_stream.c
+++ b/ext/zip/zip_stream.c
@@ -21,7 +21,6 @@
#endif
#include "php.h"
#if HAVE_ZIP
-#if defined(ZEND_ENGINE_2) || defined(ZEND_ENGINE_3)
#include "php_streams.h"
#include "ext/standard/file.h"
@@ -348,7 +347,8 @@ static php_stream_wrapper_ops zip_stream_wops = {
NULL, /* unlink */
NULL, /* rename */
NULL, /* mkdir */
- NULL /* rmdir */
+ NULL, /* rmdir */
+ NULL
};
php_stream_wrapper php_stream_zip_wrapper = {
@@ -356,5 +356,4 @@ php_stream_wrapper php_stream_zip_wrapper = {
NULL,
0 /* is_url */
};
-#endif /* defined(ZEND_ENGINE_2) || defined(ZEND_ENGINE_3) */
#endif /* HAVE_ZIP */
diff --git a/ext/zlib/tests/004-mb.phpt b/ext/zlib/tests/004-mb.phpt
new file mode 100644
index 0000000000..a9371f414f
--- /dev/null
+++ b/ext/zlib/tests/004-mb.phpt
@@ -0,0 +1,66 @@
+--TEST--
+gzfile() with various invalid params
+--SKIPIF--
+<?php if (!extension_loaded("zlib")) print "skip"; ?>
+--FILE--
+<?php
+
+var_dump(gzfile());
+var_dump(gzfile("nonexistent_file_gzfile",1));
+var_dump(gzfile(1,1,1));
+
+var_dump(gzfile(dirname(__FILE__)."/004私はガラスを食べられます.txt.gz"));
+var_dump(gzfile(dirname(__FILE__)."/004私はガラスを食べられます.txt.gz", 1));
+
+echo "Done\n";
+?>
+--EXPECTF--
+Warning: gzfile() expects at least 1 parameter, 0 given in %s on line %d
+NULL
+
+Warning: gzfile(nonexistent_file_gzfile): failed to open stream: No such file or directory in %s on line %d
+bool(false)
+
+Warning: gzfile() expects at most 2 parameters, 3 given in %s on line %d
+NULL
+array(6) {
+ [0]=>
+ string(36) "When you're taught through feelings
+"
+ [1]=>
+ string(26) "Destiny flying high above
+"
+ [2]=>
+ string(38) "all I know is that you can realize it
+"
+ [3]=>
+ string(18) "Destiny who cares
+"
+ [4]=>
+ string(19) "as it turns around
+"
+ [5]=>
+ string(39) "and I know that it descends down on me
+"
+}
+array(6) {
+ [0]=>
+ string(36) "When you're taught through feelings
+"
+ [1]=>
+ string(26) "Destiny flying high above
+"
+ [2]=>
+ string(38) "all I know is that you can realize it
+"
+ [3]=>
+ string(18) "Destiny who cares
+"
+ [4]=>
+ string(19) "as it turns around
+"
+ [5]=>
+ string(39) "and I know that it descends down on me
+"
+}
+Done
diff --git a/ext/zlib/tests/004私はガラスを食べられます.txt.gz b/ext/zlib/tests/004私はガラスを食べられます.txt.gz
new file mode 100644
index 0000000000..07805db755
--- /dev/null
+++ b/ext/zlib/tests/004私はガラスを食べられます.txt.gz
Binary files differ
diff --git a/ext/zlib/tests/gzfilegzreadfile.phpt b/ext/zlib/tests/gzfilegzreadfile.phpt
index 2d6843ddd4..ef6378de25 100644
--- a/ext/zlib/tests/gzfilegzreadfile.phpt
+++ b/ext/zlib/tests/gzfilegzreadfile.phpt
@@ -25,7 +25,7 @@ blah blah blah blah blah blah blah
EOD;
-$filename = tempnam("/tmp", "phpt");
+$filename = tempnam(sys_get_temp_dir(), "phpt");
$fp = gzopen($filename, "wb");
gzwrite($fp, $original);
diff --git a/ext/zlib/tests/gzreadgzwrite.phpt b/ext/zlib/tests/gzreadgzwrite.phpt
index 71d728b6ed..d52f12a4ac 100644
--- a/ext/zlib/tests/gzreadgzwrite.phpt
+++ b/ext/zlib/tests/gzreadgzwrite.phpt
@@ -6,7 +6,7 @@ if (!extension_loaded("zlib")) print "skip"; ?>
--FILE--
<?php
$original = str_repeat(b"hallo php",4096);
-$filename = tempnam("/tmp", "phpt");
+$filename = tempnam(sys_get_temp_dir(), "phpt");
$fp = gzopen($filename, "wb");
gzwrite($fp, $original);
diff --git a/ext/zlib/tests/gzreadgzwriteplain.phpt b/ext/zlib/tests/gzreadgzwriteplain.phpt
index 6a752b6212..7a5a5025aa 100644
--- a/ext/zlib/tests/gzreadgzwriteplain.phpt
+++ b/ext/zlib/tests/gzreadgzwriteplain.phpt
@@ -6,7 +6,7 @@ if (!extension_loaded("zlib")) print "skip"; ?>
--FILE--
<?php
$original = str_repeat(b"hallo php",4096);
-$filename = tempnam("/tmp", "phpt");
+$filename = tempnam(sys_get_temp_dir(), "phpt");
$fp = fopen($filename, "wb");
fwrite($fp, $original);
diff --git a/ext/zlib/zlib.c b/ext/zlib/zlib.c
index b3c1a88fc5..e840fb40bf 100644
--- a/ext/zlib/zlib.c
+++ b/ext/zlib/zlib.c
@@ -673,7 +673,7 @@ static PHP_FUNCTION(name) \
} \
} \
if (level < -1 || level > 9) { \
- php_error_docref(NULL, E_WARNING, "compression level (%pd) must be within -1..9", level); \
+ php_error_docref(NULL, E_WARNING, "compression level (" ZEND_LONG_FMT ") must be within -1..9", level); \
RETURN_FALSE; \
} \
switch (encoding) { \
@@ -702,7 +702,7 @@ static PHP_FUNCTION(name) \
return; \
} \
if (max_len < 0) { \
- php_error_docref(NULL, E_WARNING, "length (%pd) must be greater or equal zero", max_len); \
+ php_error_docref(NULL, E_WARNING, "length (" ZEND_LONG_FMT ") must be greater or equal zero", max_len); \
RETURN_FALSE; \
} \
if (SUCCESS != php_zlib_decode(in_buf, in_len, &out_buf, &out_len, encoding, max_len)) { \
@@ -776,7 +776,7 @@ static zend_bool zlib_create_dictionary_string(HashTable *options, char **dict,
zend_string **end, **ptr = strings - 1;
ZEND_HASH_FOREACH_VAL(dictionary, cur) {
- int i;
+ size_t i;
*++ptr = zval_get_string(cur);
if (!*ptr || ZSTR_LEN(*ptr) == 0) {
@@ -845,7 +845,7 @@ PHP_FUNCTION(inflate_init)
window = zval_get_long(option_buffer);
}
if (window < 8 || window > 15) {
- php_error_docref(NULL, E_WARNING, "zlib window size (lograithm) (%pd) must be within 8..15", window);
+ php_error_docref(NULL, E_WARNING, "zlib window size (lograithm) (" ZEND_LONG_FMT ") must be within 8..15", window);
RETURN_FALSE;
}
@@ -1030,7 +1030,7 @@ PHP_FUNCTION(deflate_init)
level = zval_get_long(option_buffer);
}
if (level < -1 || level > 9) {
- php_error_docref(NULL, E_WARNING, "compression level (%pd) must be within -1..9", level);
+ php_error_docref(NULL, E_WARNING, "compression level (" ZEND_LONG_FMT ") must be within -1..9", level);
RETURN_FALSE;
}
@@ -1038,7 +1038,7 @@ PHP_FUNCTION(deflate_init)
memory = zval_get_long(option_buffer);
}
if (memory < 1 || memory > 9) {
- php_error_docref(NULL, E_WARNING, "compression memory level (%pd) must be within 1..9", memory);
+ php_error_docref(NULL, E_WARNING, "compression memory level (" ZEND_LONG_FMT ") must be within 1..9", memory);
RETURN_FALSE;
}
@@ -1046,7 +1046,7 @@ PHP_FUNCTION(deflate_init)
window = zval_get_long(option_buffer);
}
if (window < 8 || window > 15) {
- php_error_docref(NULL, E_WARNING, "zlib window size (logarithm) (%pd) must be within 8..15", window);
+ php_error_docref(NULL, E_WARNING, "zlib window size (logarithm) (" ZEND_LONG_FMT ") must be within 8..15", window);
RETURN_FALSE;
}
diff --git a/ext/zlib/zlib_filter.c b/ext/zlib/zlib_filter.c
index ce4eba7200..c679691bcb 100644
--- a/ext/zlib/zlib_filter.c
+++ b/ext/zlib/zlib_filter.c
@@ -333,7 +333,7 @@ static php_stream_filter *php_zlib_filter_create(const char *filtername, zval *f
/* log-2 base of history window (9 - 15) */
zend_long tmp = zval_get_long(tmpzval);
if (tmp < -MAX_WBITS || tmp > MAX_WBITS + 32) {
- php_error_docref(NULL, E_WARNING, "Invalid parameter give for window size. (%pd)", tmp);
+ php_error_docref(NULL, E_WARNING, "Invalid parameter give for window size. (" ZEND_LONG_FMT ")", tmp);
} else {
windowBits = tmp;
}
@@ -365,7 +365,7 @@ static php_stream_filter *php_zlib_filter_create(const char *filtername, zval *f
/* Memory Level (1 - 9) */
tmp = zval_get_long(tmpzval);
if (tmp < 1 || tmp > MAX_MEM_LEVEL) {
- php_error_docref(NULL, E_WARNING, "Invalid parameter give for memory level. (%pd)", tmp);
+ php_error_docref(NULL, E_WARNING, "Invalid parameter give for memory level. (" ZEND_LONG_FMT ")", tmp);
} else {
memLevel = tmp;
}
@@ -375,7 +375,7 @@ static php_stream_filter *php_zlib_filter_create(const char *filtername, zval *f
/* log-2 base of history window (9 - 15) */
tmp = zval_get_long(tmpzval);
if (tmp < -MAX_WBITS || tmp > MAX_WBITS + 16) {
- php_error_docref(NULL, E_WARNING, "Invalid parameter give for window size. (%pd)", tmp);
+ php_error_docref(NULL, E_WARNING, "Invalid parameter give for window size. (" ZEND_LONG_FMT ")", tmp);
} else {
windowBits = tmp;
}
@@ -395,7 +395,7 @@ static php_stream_filter *php_zlib_filter_create(const char *filtername, zval *f
factory_setlevel:
/* Set compression level within reason (-1 == default, 0 == none, 1-9 == least to most compression */
if (tmp < -1 || tmp > 9) {
- php_error_docref(NULL, E_WARNING, "Invalid compression level specified. (%pd)", tmp);
+ php_error_docref(NULL, E_WARNING, "Invalid compression level specified. (" ZEND_LONG_FMT ")", tmp);
} else {
level = tmp;
}
diff --git a/ext/zlib/zlib_fopen_wrapper.c b/ext/zlib/zlib_fopen_wrapper.c
index e1728b3bdb..01539aec5c 100644
--- a/ext/zlib/zlib_fopen_wrapper.c
+++ b/ext/zlib/zlib_fopen_wrapper.c
@@ -172,7 +172,8 @@ static php_stream_wrapper_ops gzip_stream_wops = {
NULL, /* unlink */
NULL, /* rename */
NULL, /* mkdir */
- NULL /* rmdir */
+ NULL, /* rmdir */
+ NULL
};
php_stream_wrapper php_stream_gzip_wrapper = {